Skip to content

Commit

Permalink
feat(ribir): 🎸 introduced AppRunGuard
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo committed Apr 26, 2024
1 parent 672fc01 commit 5dc9c0f
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 89 deletions.
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,34 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

## [@Unreleased] - @ReleaseDate

### Changed

- **core**: rename builtin field of delay_drop_until to keep_alive (#561 @wjian23)

### Features

- **ribir**: Introduced `AppRunGuard` to allow app and window configuration prior to app startup. (#pr, @M-Adoo)
Previously, to configure the app and window before startup, `App::run` couldn't be used:
``` rust
unsafe {
AppCtx::set_app_theme(material::purple::light());
}

App::new_window(root, None).set_title("Counter");
App::exec();
```
Now, with AppRunGuard, you can use `App::run` and chain the configuration methods:

``` rust
App::run(root)
.set_app_theme(material::purple::light())
.on_window(|wnd| { wnd.set_title("Counter"); });
```

### Breaking

- **ribir**: `App::new_window` not accept window size as the second parameter. (#pr, @M-Adoo)

## [0.3.0-alpha.4] - 2024-04-17

## [0.3.0-alpha.3] - 2024-04-10
Expand Down
10 changes: 4 additions & 6 deletions core/src/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ impl Overlay {
/// @{ Label::new("Click to show overlay") }
/// }
/// };
/// App::new_window(w, None);
/// App::exec();
/// App::run(w);
/// ```
pub fn new<M>(widget: M) -> Self
where
Expand Down Expand Up @@ -104,8 +103,8 @@ impl Overlay {
/// @{ Label::new("Click to show overlay") }
/// }
/// };
/// App::new_window(w, Some(Size::new(200., 200.)));
/// App::exec();
///
/// App::run(w).on_window(|wnd| wnd.request_resize(Size::new(200., 200.)));
/// ```
pub fn new_with_handle<O, M>(builder: M) -> Self
where
Expand Down Expand Up @@ -168,8 +167,7 @@ impl Overlay {
/// @{ Label::new("Click to show overlay") }
/// }
/// };
/// App::new_window(w, None);
/// App::exec();
/// App::run(w);
/// ```
pub fn show_map<O, F>(&self, f: F, wnd: Rc<Window>)
where
Expand Down
31 changes: 23 additions & 8 deletions core/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,14 @@ impl Window {
window
}

pub fn set_content_widget(&self, root: impl WidgetBuilder) {
pub fn set_content_widget(&self, root: impl WidgetBuilder) -> &Self {
let build_ctx = BuildCtx::new(None, &self.widget_tree);
let root = root.build(&build_ctx);
self
.widget_tree
.borrow_mut()
.set_content(root.consume())
.set_content(root.consume());
self
}

#[inline]
Expand All @@ -320,37 +321,51 @@ impl Window {
/// device.
pub fn device_pixel_ratio(&self) -> f32 { self.shell_wnd.borrow().device_pixel_ratio() }

pub fn set_title(&self, title: &str) { self.shell_wnd.borrow_mut().set_title(title); }
pub fn set_title(&self, title: &str) -> &Self {
self.shell_wnd.borrow_mut().set_title(title);
self
}

pub fn set_icon(&self, icon: &PixelImage) { self.shell_wnd.borrow_mut().set_icon(icon); }
pub fn set_icon(&self, icon: &PixelImage) -> &Self {
self.shell_wnd.borrow_mut().set_icon(icon);
self
}

/// Returns the cursor icon of the window.
pub fn get_cursor(&self) -> CursorIcon { self.shell_wnd.borrow().cursor() }

/// Modifies the cursor icon of the window.
pub fn set_cursor(&self, cursor: CursorIcon) { self.shell_wnd.borrow_mut().set_cursor(cursor); }
pub fn set_cursor(&self, cursor: CursorIcon) -> &Self {
self.shell_wnd.borrow_mut().set_cursor(cursor);
self
}

/// Sets location of IME candidate box in window global coordinates relative
/// to the top left.
pub fn set_ime_cursor_area(&self, rect: &Rect) {
pub fn set_ime_cursor_area(&self, rect: &Rect) -> &Self {
self
.shell_wnd
.borrow_mut()
.set_ime_cursor_area(rect);
self
}

pub fn set_ime_allowed(&self, allowed: bool) {
pub fn set_ime_allowed(&self, allowed: bool) -> &Self {
self
.shell_wnd
.borrow_mut()
.set_ime_allowed(allowed);
self
}

pub fn request_resize(&self, size: Size) { self.shell_wnd.borrow_mut().request_resize(size) }

pub fn size(&self) -> Size { self.shell_wnd.borrow().inner_size() }

pub fn set_min_size(&self, size: Size) { self.shell_wnd.borrow_mut().set_min_size(size); }
pub fn set_min_size(&self, size: Size) -> &Self {
self.shell_wnd.borrow_mut().set_min_size(size);
self
}

pub fn shell_wnd(&self) -> &RefCell<Box<dyn ShellWindow>> { &self.shell_wnd }

Expand Down
11 changes: 5 additions & 6 deletions examples/counter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ use counter::counter;
use ribir::prelude::*;

fn main() {
unsafe {
AppCtx::set_app_theme(material::purple::light());
}

App::new_window(counter(), None).set_title("Counter");
App::exec();
App::run(counter())
.set_app_theme(material::purple::light())
.on_window(|wnd| {
wnd.set_title("Counter");
});
}

#[cfg(test)]
Expand Down
11 changes: 5 additions & 6 deletions examples/messages/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ use messages::messages;
use ribir::prelude::*;

fn main() {
unsafe {
AppCtx::set_app_theme(material::purple::light());
}

App::new_window(messages(), None).set_title("Messages");
App::exec();
App::run(messages())
.set_app_theme(material::purple::light())
.on_window(|wnd| {
wnd.set_title("Messages");
});
}

#[cfg(test)]
Expand Down
13 changes: 7 additions & 6 deletions examples/storybook/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ use ribir::prelude::*;
use storybook::storybook;

fn main() {
unsafe {
AppCtx::set_app_theme(material::purple::light());
}

App::new_window(storybook(), Some(Size::new(1024., 768.))).set_title("Storybook");
App::exec();
App::run(storybook())
.set_app_theme(material::purple::light())
.on_window(|wnd| {
wnd
.set_title("Storybook")
.request_resize(Size::new(1024., 768.))
});
}

#[cfg(test)]
Expand Down
11 changes: 5 additions & 6 deletions examples/todos/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ mod ui;
use ui::todos;

fn main() {
unsafe {
AppCtx::set_app_theme(material::purple::light());
}

App::new_window(todos(), Some(Size::new(400., 640.))).set_title("Todos");
App::exec();
App::run(todos())
.set_app_theme(material::purple::light())
.on_window(|wnd| {
wnd.set_title("Todos");
});
}

#[cfg(test)]
Expand Down
11 changes: 5 additions & 6 deletions examples/wordle_game/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ mod ui;
mod wordle;

fn main() {
unsafe {
AppCtx::set_app_theme(material::purple::light());
}

App::new_window(wordle_game(), Some(Size::new(700., 620.))).set_title("Messages");
App::exec();
App::run(wordle_game())
.set_app_theme(material::purple::light())
.on_window(|wnd| {
wnd.set_title("Wordle Game");
});
}

#[cfg(test)]
Expand Down
41 changes: 36 additions & 5 deletions ribir/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,20 @@ impl App {
}
}

/// A guard returned by `App::run` that allows you to configure the application
/// before it starts up.
///
/// This will call `App::exec` when it's dropped.
pub struct AppRunGuard(std::rc::Rc<Window>);

impl App {
/// Start an application with the `root` widget, this will use the default
/// theme to create an application and use the `root` widget to create a
/// window, then run the application.
#[track_caller]
pub fn run(root: impl WidgetBuilder + 'static) {
Self::new_window(root, None);
App::exec()
pub fn run(root: impl WidgetBuilder) -> AppRunGuard {
let wnd = Self::new_window(root);
AppRunGuard::new(wnd)
}

/// Get a event sender of the application event loop, you can use this to send
Expand All @@ -192,12 +198,12 @@ impl App {

/// create a new window with the `root` widget
#[track_caller]
pub fn new_window(root: impl WidgetBuilder, size: Option<Size>) -> std::rc::Rc<Window> {
pub fn new_window(root: impl WidgetBuilder) -> std::rc::Rc<Window> {
let app = unsafe { App::shared_mut() };
let event_loop = app.event_loop.as_ref().expect(
" Event loop consumed. You can't create window after `App::exec` called in Web platform.",
);
let shell_wnd = WinitShellWnd::new(size, event_loop);
let shell_wnd = WinitShellWnd::new(event_loop);
let wnd = AppCtx::new_window(Box::new(shell_wnd), root);

#[cfg(not(target_family = "wasm"))]
Expand Down Expand Up @@ -290,6 +296,31 @@ impl App {
}
}

impl AppRunGuard {
fn new(wnd: std::rc::Rc<Window>) -> Self {
static ONCE: std::sync::Once = std::sync::Once::new();
assert!(!ONCE.is_completed(), "App::run can only be called once.");
Self(wnd)
}

/// Set the application theme, this will apply to whole application.
pub fn set_app_theme(&mut self, theme: FullTheme) -> &mut Self {
// Safety: AppRunGuard is only created once and should be always used
// before the application startup.
unsafe { AppCtx::set_app_theme(theme) }
self
}

/// Config the current window with the `f` function.
pub fn on_window(&mut self, f: impl FnOnce(&Window)) -> &mut Self {
f(&self.0);
self
}
}

impl Drop for AppRunGuard {
fn drop(&mut self) { App::exec() }
}
impl EventSender {
pub fn send(&self, e: AppEvent) {
if let Err(err) = self.0.send_event(e) {
Expand Down
77 changes: 37 additions & 40 deletions ribir/src/winit_shell_wnd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,51 +191,48 @@ impl WinitShellWnd {
Self::inner_wnd(wnd)
}

pub(crate) fn new<T>(size: Option<Size>, window_target: &EventLoopWindowTarget<T>) -> Self {
let mut winit_wnd = winit::window::WindowBuilder::new();
if let Some(size) = size {
winit_wnd = winit_wnd.with_inner_size(LogicalSize::new(size.width, size.height));
}

// A canvas need configure in web platform.
#[cfg(target_family = "wasm")]
{
use winit::platform::web::WindowBuilderExtWebSys;
const RIBIR_CANVAS: &str = "ribir_canvas";
const RIBIR_CANVAS_USED: &str = "ribir_canvas_used";

use web_sys::wasm_bindgen::JsCast;
let document = web_sys::window().unwrap().document().unwrap();
let elems = document.get_elements_by_class_name(RIBIR_CANVAS);

let len = elems.length();
for idx in 0..len {
if let Some(elem) = elems.get_with_index(idx) {
let mut classes_name = elem.class_name();
if !classes_name
.split(" ")
.any(|v| v == RIBIR_CANVAS_USED)
{
let mut canvas = elem
.clone()
.dyn_into::<web_sys::HtmlCanvasElement>();
if canvas.is_err() {
let child = document.create_element("canvas").unwrap();
if elem.append_child(&child).is_ok() {
canvas = child.dyn_into::<web_sys::HtmlCanvasElement>();
}
}
if let Ok(canvas) = canvas {
classes_name.push_str(&format!(" {}", RIBIR_CANVAS_USED));
elem.set_class_name(&classes_name);
winit_wnd = winit_wnd.with_canvas(Some(canvas));
}
#[cfg(target_family = "wasm")]
pub(crate) fn new<T>(window_target: &EventLoopWindowTarget<T>) -> Self {
const RIBIR_CANVAS: &str = "ribir_canvas";
const RIBIR_CANVAS_USED: &str = "ribir_canvas_used";

use web_sys::{wasm_bindgen::JsCast, HtmlCanvasElement};
let document = web_sys::window().unwrap().document().unwrap();
let elems = document.get_elements_by_class_name(RIBIR_CANVAS);

let mut canvas = None;
let len = elems.length();
for idx in 0..len {
if let Some(elem) = elems.get_with_index(idx) {
let mut classes_name = elem.class_name();
if !classes_name
.split(" ")
.any(|v| v == RIBIR_CANVAS_USED)
{
if let Ok(c) = elem.clone().dyn_into::<HtmlCanvasElement>() {
classes_name.push_str(&format!(" {}", RIBIR_CANVAS_USED));
elem.set_class_name(&classes_name);
canvas = Some(c);
} else {
let child = document.create_element("canvas").unwrap();
elem.append_child(&child).unwrap();
canvas = Some(child.dyn_into::<HtmlCanvasElement>().unwrap())
}
break;
}
}
}

let wnd = winit_wnd.build(window_target).unwrap();
let canvas = canvas.expect("No unused 'ribir_canvas' class element found.");

return Self::new_with_canvas(canvas, window_target);
}

#[cfg(not(target_family = "wasm"))]
pub(crate) fn new<T>(window_target: &EventLoopWindowTarget<T>) -> Self {
let wnd = winit::window::WindowBuilder::new()
.build(window_target)
.unwrap();
Self::inner_wnd(wnd)
}

Expand Down

0 comments on commit 5dc9c0f

Please sign in to comment.