From 0996ba79d961660356b5551c098bf652743e2d37 Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Fri, 13 Dec 2024 02:45:30 +0700 Subject: [PATCH] Initializes WindowAdapter implementation (#1178) --- gui/main_window.cpp | 11 +--------- gui/main_window.hpp | 4 ---- gui/src/rt/context.rs | 37 ++++++++++++++++++++++++++++++++ gui/src/rt/mod.rs | 10 +++++++-- gui/src/ui/backend/mod.rs | 26 +++++++++++++++++++++-- gui/src/ui/backend/window.rs | 41 ++++++++++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 gui/src/rt/context.rs create mode 100644 gui/src/ui/backend/window.rs diff --git a/gui/main_window.cpp b/gui/main_window.cpp index 587324aa7..57c24ba15 100644 --- a/gui/main_window.cpp +++ b/gui/main_window.cpp @@ -25,8 +25,7 @@ #include #endif -MainWindow::MainWindow() : - m_main(nullptr) +MainWindow::MainWindow() { // File menu. auto fileMenu = menuBar()->addMenu("&File"); @@ -36,19 +35,11 @@ MainWindow::MainWindow() : // Help menu. auto helpMenu = menuBar()->addMenu("&Help"); - auto aboutQt = new QAction("About &Qt", this); auto about = new QAction("&About Obliteration", this); - connect(aboutQt, &QAction::triggered, &QApplication::aboutQt); connect(about, &QAction::triggered, this, &MainWindow::aboutObliteration); - helpMenu->addAction(aboutQt); helpMenu->addAction(about); - - // Central widget. - m_main = new QStackedWidget(); - - setCentralWidget(m_main); } MainWindow::~MainWindow() diff --git a/gui/main_window.hpp b/gui/main_window.hpp index becbfb79a..7d63084b2 100644 --- a/gui/main_window.hpp +++ b/gui/main_window.hpp @@ -3,14 +3,10 @@ #include #include -class QStackedWidget; - class MainWindow final : public QMainWindow { public: MainWindow(); ~MainWindow() override; private slots: void aboutObliteration(); -private: - QStackedWidget *m_main; }; diff --git a/gui/src/rt/context.rs b/gui/src/rt/context.rs new file mode 100644 index 000000000..c64fde3f1 --- /dev/null +++ b/gui/src/rt/context.rs @@ -0,0 +1,37 @@ +use std::cell::Cell; +use std::mem::transmute; +use std::ptr::null; +use winit::event_loop::ActiveEventLoop; + +/// Execution context of the runtime. +pub struct RuntimeContext<'a> { + el: &'a ActiveEventLoop, +} + +impl<'a> RuntimeContext<'a> { + pub(super) fn new(el: &'a ActiveEventLoop) -> Self { + Self { el } + } + + /// # Panics + /// If called from the other thread than main thread. + pub fn with(f: impl FnOnce(&Self) -> R) -> R { + let cx = CONTEXT.get(); + assert!(!cx.is_null()); + unsafe { f(&*cx) } + } + + pub fn event_loop(&self) -> &ActiveEventLoop { + self.el + } + + pub(super) fn run(&self, f: impl FnOnce()) { + assert!(CONTEXT.replace(unsafe { transmute(self) }).is_null()); + f(); + CONTEXT.set(null()); + } +} + +thread_local! { + static CONTEXT: Cell<*const RuntimeContext<'static>> = Cell::new(null()); +} diff --git a/gui/src/rt/mod.rs b/gui/src/rt/mod.rs index 66e863417..f9296ce60 100644 --- a/gui/src/rt/mod.rs +++ b/gui/src/rt/mod.rs @@ -1,3 +1,5 @@ +pub use self::context::*; + use futures::executor::LocalPool; use futures::task::LocalSpawnExt; use std::future::Future; @@ -8,6 +10,8 @@ use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::window::WindowId; +mod context; + pub fn block_on(main: impl Future + 'static) -> Result<(), RuntimeError> { // Setup winit event loop. let mut el = EventLoop::::with_user_event(); @@ -17,7 +21,7 @@ pub fn block_on(main: impl Future + 'static) -> Result<(), RuntimeE exe.spawner() .spawn_local(async move { main.await; - todo!() + RuntimeContext::with(|cx| cx.event_loop().exit()); }) .unwrap(); @@ -31,7 +35,9 @@ struct AsyncExecutor(LocalPool); impl ApplicationHandler for AsyncExecutor { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - self.0.run_until_stalled(); + let cx = RuntimeContext::new(event_loop); + + cx.run(|| self.0.run_until_stalled()); } fn window_event( diff --git a/gui/src/ui/backend/mod.rs b/gui/src/ui/backend/mod.rs index 1970948a2..87402c092 100644 --- a/gui/src/ui/backend/mod.rs +++ b/gui/src/ui/backend/mod.rs @@ -1,7 +1,12 @@ +use self::window::Window; +use crate::rt::RuntimeContext; +use i_slint_renderer_skia::SkiaRenderer; use slint::platform::WindowAdapter; -use slint::PlatformError; +use slint::{PhysicalSize, PlatformError}; use std::rc::Rc; +mod window; + /// Back-end for Slint to run on top of winit event loop. /// /// This back-end does not supports [`slint::run_event_loop()`]. @@ -15,6 +20,23 @@ impl SlintBackend { impl slint::platform::Platform for SlintBackend { fn create_window_adapter(&self) -> Result, PlatformError> { - todo!() + // Create winit window. + let attrs = winit::window::Window::default_attributes(); + let win = match RuntimeContext::with(move |cx| cx.event_loop().create_window(attrs)) { + Ok(v) => Rc::new(v), + Err(e) => return Err(PlatformError::OtherError(Box::new(e))), + }; + + // Create WindowAdapter. + let size = win.inner_size(); + let renderer = SkiaRenderer::new( + win.clone(), + win.clone(), + PhysicalSize::new(size.width, size.height), + )?; + + Ok(Rc::::new_cyclic(move |weak| { + Window::new(win, slint::Window::new(weak.clone()), renderer) + })) } } diff --git a/gui/src/ui/backend/window.rs b/gui/src/ui/backend/window.rs new file mode 100644 index 000000000..ab6529f31 --- /dev/null +++ b/gui/src/ui/backend/window.rs @@ -0,0 +1,41 @@ +use i_slint_renderer_skia::SkiaRenderer; +use slint::platform::{Renderer, WindowAdapter}; +use slint::PhysicalSize; +use std::rc::Rc; + +/// Implementation of [`WindowAdapter`]. +pub struct Window { + winit: Rc, + slint: slint::Window, + renderer: SkiaRenderer, +} + +impl Window { + pub fn new( + winit: Rc, + slint: slint::Window, + renderer: SkiaRenderer, + ) -> Self { + Self { + winit, + slint, + renderer, + } + } +} + +impl WindowAdapter for Window { + fn window(&self) -> &slint::Window { + &self.slint + } + + fn size(&self) -> PhysicalSize { + let s = self.winit.inner_size(); + + PhysicalSize::new(s.width, s.height) + } + + fn renderer(&self) -> &dyn Renderer { + &self.renderer + } +}