Skip to content

Commit

Permalink
chore(ribir): 🤖 Streamline the APIs across various platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo committed Apr 6, 2024
1 parent 3733c48 commit 0bbf7d2
Show file tree
Hide file tree
Showing 14 changed files with 109 additions and 122 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ jobs:
find "./docs" -name "*.md"
} | xargs -I {} rustdoc --test {} -L target/debug/deps/ --edition 2018 --extern ribir=target/debug/libribir.rlib
wasm-compile:
needs: lint
name: wasm compile
runs-on: ubuntu-latest
steps:
Expand All @@ -72,5 +73,5 @@ jobs:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- name: compile to wasm
run: cargo build -p ribir --target wasm32-unknown-unknown
run: cargo build --target wasm32-unknown-unknown

1 change: 1 addition & 0 deletions .github/workflows/pr-num.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
continue-on-error: true
with:
github-token: ${{ secrets.RIBIR_RELEASE }}
script: |
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

## [@Unreleased] - @ReleaseDate

- **ribir**: compile wasm (@wjian23)
- **ribir**: compile wasm (#543 @wjian23)

## [0.3.0-alpha.2] - 2024-04-03

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,4 @@ Ribir is [MIT-licensed](./LICENSE)
[Examples]: ./examples/
[Documents]: https://ribir.org/docs/introduction
[中文文档]: https://ribir.org/zh/docs/introduction
[wgpu]: https://github.com/gfx-rs/wgpu
[wgpu]: https://github.com/gfx-rs/wgpu
7 changes: 1 addition & 6 deletions core/src/animation/animate.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use crate::{
prelude::*,
ticker::{FrameMsg, Instant},
window::WindowId,
};
use crate::{prelude::*, ticker::FrameMsg, window::WindowId};
#[simple_declare]
pub struct Animate<S>
where
Expand Down Expand Up @@ -190,7 +186,6 @@ where
mod tests {
use super::*;
use crate::{reset_test_env, test_helper::TestWindow};
use std::time::Duration;

#[test]
fn fix_animate_circular_mut_borrow() {
Expand Down
4 changes: 3 additions & 1 deletion core/src/animation/stagger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ use super::*;
use crate::prelude::*;
use ribir_algo::Sc;
use ribir_macros::rdl;
use std::time::{Duration, Instant};
// fixme: rxRust not use std::time::Instant in web
#[cfg(target_family = "wasm")]
use std::time::Instant;

/// The controller of a stagger animation. It's allow you to transition states
/// and run animation in a stagger way.
Expand Down
1 change: 0 additions & 1 deletion core/src/animation/transition.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::prelude::*;
use ribir_algo::Sc;
use std::time::Duration;

/// Transition use `Easing` trait to calc the rate of change over time.
#[derive(Clone, Debug)]
Expand Down
6 changes: 1 addition & 5 deletions core/src/builtin_widgets/mix_builtin.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
use crate::prelude::*;
use rxrust::prelude::*;
use std::{
cell::Cell,
convert::Infallible,
time::{Duration, Instant},
};
use std::{cell::Cell, convert::Infallible};

use self::focus_mgr::FocusType;

Expand Down
1 change: 0 additions & 1 deletion core/src/window.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::ticker::Instant;
use crate::{
events::{
dispatcher::Dispatcher,
Expand Down
1 change: 0 additions & 1 deletion examples/todos/src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::todos::{Task, Todos};
use ribir::prelude::*;
use std::time::Duration;

impl Compose for Todos {
fn compose(this: impl StateWriter<Value = Self>) -> impl WidgetBuilder {
Expand Down
1 change: 0 additions & 1 deletion gpu/src/gpu_backend/textures_mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,6 @@ pub mod tests {
use crate::gpu_backend::tests::headless;
use crate::{WgpuImpl, WgpuTexture};
use futures::executor::block_on;
use ribir_algo::ShareResource;

use ribir_geom::*;
use ribir_painter::Color;
Expand Down
138 changes: 49 additions & 89 deletions ribir/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,13 @@ use winit::{
event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy},
};

#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
pub struct App {
event_loop: EventLoop<AppEvent>,
active_wnd: Option<WindowId>,
events_stream: MutRefItemSubject<'static, AppEvent, Infallible>,
}

#[cfg(target_family = "wasm")]
pub struct App {
event_loop_proxy: EventLoopProxy<AppEvent>,
/// The event loop of the application, it's only available on native platform
/// after `App::exec` called
event_loop: Option<EventLoop<AppEvent>>,
#[cfg(not(target_family = "wasm"))]
active_wnd: Option<WindowId>,
events_stream: MutRefItemSubject<'static, AppEvent, Infallible>,
}

Expand Down Expand Up @@ -171,77 +168,6 @@ impl App {
}
}

#[cfg(target_family = "wasm")]
static mut APP: Option<App> = None;

#[cfg(target_family = "wasm")]
impl App {
/// build the ribir application from the root widget.
/// the ribir application will be running at the first canvas with class name
/// of ribir_canvas
pub async fn exec(root: impl WidgetBuilder + 'static) {
const RIBIR_CANVAS: &str = "ribir_canvas";
const RIBIR_CANVAS_USED: &str = "ribir_canvas_used";

use web_sys::wasm_bindgen::JsCast;
use winit::platform::web::EventLoopExtWebSys;
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);

let event_loop = EventLoopBuilder::with_user_event().build().unwrap();
App::init(&event_loop);
let shell_wnd = WinitShellWnd::new_with_canvas(canvas, &event_loop).await;
AppCtx::new_window(Box::new(shell_wnd), root);
event_loop.spawn(App::event_loop_handle);
return;
}
}
}
}
}

/// Get a event sender of the application event loop, you can use this to send
/// event.
pub fn event_sender() -> EventSender {
let proxy = App::shared().event_loop_proxy.clone();
EventSender(proxy)
}

fn init(event_loop: &EventLoop<AppEvent>) {
let waker = EventWaker(event_loop.create_proxy());
unsafe {
AppCtx::set_runtime_waker(Box::new(waker));
}
register_platform_app_events_handlers();
unsafe {
APP = Some(App {
event_loop_proxy: event_loop.create_proxy(),
events_stream: <_>::default(),
});
}
}

#[track_caller]
unsafe fn shared_mut() -> &'static mut App { APP.as_mut().unwrap() }
}

#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
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
Expand All @@ -254,24 +180,42 @@ impl App {

/// Get a event sender of the application event loop, you can use this to send
/// event.
pub fn event_sender() -> EventSender {
let proxy = App::shared().event_loop.create_proxy();
EventSender(proxy)
pub fn event_sender() -> EventSender { EventSender(App::shared().event_loop_proxy.clone()) }

/// Creating a new window using the `root` widget and the specified canvas.
/// Note: This is exclusive to the web platform.
#[cfg(target_family = "wasm")]
pub fn new_with_canvas(
root: impl WidgetBuilder,
canvas: web_sys::HtmlCanvasElement,
) -> 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_with_canvas(canvas, &event_loop);
let wnd = AppCtx::new_window(Box::new(shell_wnd), root);
wnd
}

/// create a new window with the `root` widget
#[track_caller]
pub fn new_window(root: impl WidgetBuilder + 'static, size: Option<Size>) -> std::rc::Rc<Window> {
pub fn new_window(root: impl WidgetBuilder, size: Option<Size>) -> std::rc::Rc<Window> {
let app = unsafe { App::shared_mut() };
let shell_wnd = WinitShellWnd::new(size, &app.event_loop);
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 wnd = AppCtx::new_window(Box::new(shell_wnd), root);

#[cfg(not(target_family = "wasm"))]
if app.active_wnd.is_none() {
app.active_wnd = Some(wnd.id());
}
wnd
}

#[cfg(not(target_family = "wasm"))]
pub fn active_window() -> std::rc::Rc<Window> {
App::shared()
.active_wnd
Expand All @@ -281,6 +225,7 @@ impl App {

/// set the window with `id` to be the active window, and the active window.
#[track_caller]
#[cfg(not(target_family = "wasm"))]
pub fn set_active_window(id: WindowId) {
let app = unsafe { App::shared_mut() };
app.active_wnd = Some(id);
Expand All @@ -299,10 +244,22 @@ impl App {
/// thread until the application exit.
#[track_caller]
pub fn exec() {
use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
Self::active_window().draw_frame();
let event_loop = &mut unsafe { App::shared_mut() }.event_loop;
let _ = event_loop.run_on_demand(App::event_loop_handle);
#[cfg(not(target_family = "wasm"))]
{
use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
Self::active_window().draw_frame();
let event_loop = unsafe { App::shared_mut() }.event_loop.as_mut().unwrap();
let _ = event_loop.run_on_demand(App::event_loop_handle);
}

#[cfg(target_family = "wasm")]
{
use winit::platform::web::EventLoopExtWebSys;
let event_loop = unsafe { App::shared_mut() }.event_loop.take().expect(
"Event loop consumed. You can't exec the application after `App::exec` called in Web platform.",
);
event_loop.spawn(App::event_loop_handle);
}
}

#[track_caller]
Expand All @@ -313,13 +270,16 @@ impl App {
let event_loop = EventLoopBuilder::with_user_event().build().unwrap();
let waker = EventWaker(event_loop.create_proxy());
unsafe {
#[cfg(not(target_family = "wasm"))]
AppCtx::set_clipboard(Box::new(crate::clipboard::Clipboard::new().unwrap()));
AppCtx::set_runtime_waker(Box::new(waker));
}
register_platform_app_events_handlers();
APP = Some(App {
event_loop,
event_loop_proxy: event_loop.create_proxy(),
event_loop: Some(event_loop),
events_stream: <_>::default(),
#[cfg(not(target_family = "wasm"))]
active_wnd: None,
})
});
Expand Down
62 changes: 49 additions & 13 deletions ribir/src/winit_shell_wnd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,21 +178,17 @@ pub(crate) fn new_id(id: winit::window::WindowId) -> WindowId {

impl WinitShellWnd {
#[cfg(target_family = "wasm")]
pub(crate) async fn new_with_canvas<T>(
pub(crate) fn new_with_canvas<T>(
canvas: web_sys::HtmlCanvasElement,
window_target: &EventLoopWindowTarget<T>,
) -> Self {
use winit::platform::web::WindowBuilderExtWebSys;
let wnd_builder = winit::window::WindowBuilder::new().with_canvas(Some(canvas));
let winit_wnd = wnd_builder.build(window_target).unwrap();
let wnd = winit::window::WindowBuilder::new()
.with_canvas(Some(canvas))
.build(window_target)
.unwrap();

let ptr: *const winit::window::Window = &winit_wnd as *const winit::window::Window;
let backend = Backend::new(unsafe { &*ptr }).await;
WinitShellWnd {
backend,
winit_wnd,
cursor: CursorIcon::Default,
}
Self::inner_wnd(wnd)
}

pub(crate) fn new<T>(size: Option<Size>, window_target: &EventLoopWindowTarget<T>) -> Self {
Expand All @@ -201,9 +197,49 @@ impl WinitShellWnd {
winit_wnd = winit_wnd.with_inner_size(LogicalSize::new(size.width, size.height));
}

let winit_wnd = winit_wnd.build(window_target).unwrap();
let ptr: *const winit::window::Window = &winit_wnd as *const winit::window::Window;
let backend = AppCtx::wait_future(Backend::new(unsafe { &*ptr }));
// 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));
}
}
}
}
}

let wnd = winit_wnd.build(window_target).unwrap();
Self::inner_wnd(wnd)
}

fn inner_wnd(winit_wnd: winit::window::Window) -> Self {
let ptr = &winit_wnd as *const winit::window::Window;
// Safety: a reference to winit_wnd is valid as long as the WinitShellWnd is
// alive.
let backend = Backend::new(unsafe { &*ptr });
let backend = AppCtx::wait_future(backend);
WinitShellWnd {
backend,
winit_wnd,
Expand Down
2 changes: 1 addition & 1 deletion ribir/tests/timer_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ mod test_single_thread {
use ribir_core::test_helper::TestWindow;
use ribir_dev_helper::*;
use std::cell::Cell;
use std::thread::sleep;
use std::{cell::RefCell, rc::Rc};
use std::{thread::sleep, time::Duration};
use winit::event::{DeviceId, ElementState, MouseButton};

use ribir_core::{prelude::*, test_helper::MockBox};
Expand Down

0 comments on commit 0bbf7d2

Please sign in to comment.