diff --git a/sixtyfps_runtime/corelib/window.rs b/sixtyfps_runtime/corelib/window.rs index f758153fd..e3ec77c14 100644 --- a/sixtyfps_runtime/corelib/window.rs +++ b/sixtyfps_runtime/corelib/window.rs @@ -10,7 +10,7 @@ LICENSE END */ #![warn(missing_docs)] //! Exposed Window API -use crate::component::ComponentRc; +use crate::component::{ComponentRc, ComponentWeak}; use crate::graphics::Point; use crate::input::{KeyEvent, MouseEventType}; use crate::items::{ItemRc, ItemRef}; @@ -24,10 +24,6 @@ use std::rc::Rc; /// /// [`crate::graphics`] provides an implementation of this trait for use with [`crate::graphics::GraphicsBackend`]. pub trait GenericWindow { - /// Associates this window with the specified component. Further event handling and rendering, etc. will be - /// done with that component. - fn set_component(self: Rc, component: &ComponentRc); - /// Draw the items of the specified `component` in the given window. fn draw(self: Rc); /// Receive a mouse event and pass it to the items of the component to @@ -100,21 +96,91 @@ pub trait GenericWindow { ) -> Option>; } +/// Structure that represent a Window in the runtime +pub struct Window { + /// FIXME! use Box instead; + platform_window: Rc, + component: std::cell::RefCell, +} + +impl Window { + /// Create a new instance of the window, given the platform_window + pub fn new(platform_window: Rc) -> Self { + Self { platform_window, component: Default::default() } + } + + /// Associates this window with the specified component. Further event handling and rendering, etc. will be + /// done with that component. + pub fn set_component(&self, component: &ComponentRc) { + self.component.replace(ComponentRc::downgrade(component)); + } + + /// return the component. + /// Panics if it wasn't set. + pub fn component(&self) -> ComponentRc { + self.component.borrow().upgrade().unwrap() + } + + /// Receive a mouse event and pass it to the items of the component to + /// change their state. + /// + /// Arguments: + /// * `pos`: The position of the mouse event in window physical coordinates. + /// * `what`: The type of mouse event. + /// * `component`: The SixtyFPS compiled component that provides the tree of items. + pub fn process_mouse_input(self: Rc, pos: Point, what: MouseEventType) { + self.platform_window.clone().process_mouse_input(pos, what) + } + /// Receive a key event and pass it to the items of the component to + /// change their state. + /// + /// Arguments: + /// * `event`: The key event received by the windowing system. + /// * `component`: The SixtyFPS compiled component that provides the tree of items. + pub fn process_key_input(self: Rc, event: &KeyEvent) { + self.platform_window.clone().process_key_input(event) + } + + /// Installs a binding on the specified property that's toggled whenever the text cursor is supposed to be visible or not. + pub fn set_cursor_blink_binding(&self, prop: &crate::properties::Property) { + self.platform_window.clone().set_cursor_blink_binding(prop) + } + + /// Sets the focus to the item pointed to by item_ptr. This will remove the focus from any + /// currently focused item. + pub fn set_focus_item(self: Rc, focus_item: &ItemRc) { + self.platform_window.clone().set_focus_item(focus_item) + } + /// Sets the focus on the window to true or false, depending on the have_focus argument. + /// This results in WindowFocusReceived and WindowFocusLost events. + pub fn set_focus(self: Rc, have_focus: bool) { + self.platform_window.clone().set_focus(have_focus) + } +} + +impl core::ops::Deref for Window { + type Target = dyn GenericWindow; + + fn deref(&self) -> &Self::Target { + &*self.platform_window + } +} + /// The ComponentWindow is the (rust) facing public type that can render the items /// of components to the screen. #[repr(C)] #[derive(Clone)] -pub struct ComponentWindow(pub std::rc::Rc); +pub struct ComponentWindow(pub std::rc::Rc); impl ComponentWindow { /// Creates a new instance of a CompomentWindow based on the given window implementation. Only used /// internally. - pub fn new(window_impl: std::rc::Rc) -> Self { + pub fn new(window_impl: std::rc::Rc) -> Self { Self(window_impl) } /// Spins an event loop and renders the items of the provided component in this window. pub fn run(&self) { - self.0.clone().run(); + self.0.platform_window.clone().run(); } /// Returns the scale factor set on the window. @@ -130,12 +196,12 @@ impl ComponentWindow { /// This function is called by the generated code when a component and therefore its tree of items are destroyed. The /// implementation typically uses this to free the underlying graphics resources cached via [RenderingCache][`crate::graphics::RenderingCache`]. pub fn free_graphics_resources<'a>(&self, items: &Slice<'a, Pin>>) { - self.0.clone().free_graphics_resources(items); + self.0.platform_window.clone().free_graphics_resources(items); } /// Installs a binding on the specified property that's toggled whenever the text cursor is supposed to be visible or not. pub(crate) fn set_cursor_blink_binding(&self, prop: &crate::properties::Property) { - self.0.clone().set_cursor_blink_binding(prop) + self.0.platform_window.clone().set_cursor_blink_binding(prop) } /// Sets the currently active keyboard notifiers. This is used only for testing or directly @@ -149,31 +215,31 @@ impl ComponentWindow { /// Returns the currently active keyboard notifiers. pub(crate) fn current_keyboard_modifiers(&self) -> crate::input::KeyboardModifiers { - self.0.clone().current_keyboard_modifiers() + self.0.platform_window.clone().current_keyboard_modifiers() } pub(crate) fn process_key_input(&self, event: &KeyEvent) { - self.0.clone().process_key_input(event) + self.0.platform_window.clone().process_key_input(event) } /// Clears the focus on any previously focused item and makes the provided /// item the focus item, in order to receive future key events. pub fn set_focus_item(&self, focus_item: &ItemRc) { - self.0.clone().set_focus_item(focus_item) + self.0.platform_window.clone().set_focus_item(focus_item) } /// Associates this window with the specified component, for future event handling, etc. pub fn set_component(&self, component: &ComponentRc) { - self.0.clone().set_component(component) + self.0.set_component(component) } /// Show a popup at the given position pub fn show_popup(&self, popup: &ComponentRc, position: Point) { - self.0.clone().show_popup(popup, position) + self.0.platform_window.clone().show_popup(popup, position) } /// Close the active popup if any pub fn close_popup(&self) { - self.0.clone().close_popup() + self.0.platform_window.clone().close_popup() } } @@ -187,9 +253,9 @@ pub mod ffi { #[allow(non_camel_case_types)] type c_void = (); - /// Same layout as ComponentWindow (fat pointer) + /// Same layout as ComponentWindow #[repr(C)] - pub struct ComponentWindowOpaque(*const c_void, *const c_void); + pub struct ComponentWindowOpaque(*const c_void); /// Releases the reference to the component window held by handle. #[no_mangle] diff --git a/sixtyfps_runtime/rendering_backends/gl/Cargo.toml b/sixtyfps_runtime/rendering_backends/gl/Cargo.toml index ef888c161..479cfd94d 100644 --- a/sixtyfps_runtime/rendering_backends/gl/Cargo.toml +++ b/sixtyfps_runtime/rendering_backends/gl/Cargo.toml @@ -31,6 +31,7 @@ euclid = "0.22.1" pin-weak = "1" scoped-tls-hkt = "0.1" smallvec = "1.4.1" +once_cell = "1.5" [target.'cfg(target_arch = "wasm32")'.dependencies] web_sys = { version = "0.3", package = "web-sys", features=["console", "WebGlContextAttributes"] } diff --git a/sixtyfps_runtime/rendering_backends/gl/graphics_window.rs b/sixtyfps_runtime/rendering_backends/gl/graphics_window.rs index 4f1f61294..f802ce8de 100644 --- a/sixtyfps_runtime/rendering_backends/gl/graphics_window.rs +++ b/sixtyfps_runtime/rendering_backends/gl/graphics_window.rs @@ -11,10 +11,10 @@ LICENSE END */ use core::cell::{Cell, RefCell}; use core::pin::Pin; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use const_field_offset::FieldOffsets; -use corelib::component::{ComponentRc, ComponentWeak}; +use corelib::component::ComponentRc; use corelib::graphics::*; use corelib::input::{KeyEvent, KeyboardModifiers, MouseEvent, MouseEventType, TextCursorBlinker}; use corelib::items::{ItemRc, ItemRef, ItemWeak}; @@ -33,12 +33,12 @@ type WindowFactoryFn = /// GraphicsWindow is an implementation of the [GenericWindow][`crate::eventloop::GenericWindow`] trait. This is /// typically instantiated by entry factory functions of the different graphics backends. pub struct GraphicsWindow { + pub(crate) self_weak: once_cell::unsync::OnceCell>, window_factory: Box, map_state: RefCell, properties: Pin>, - cursor_blinker: RefCell>, keyboard_modifiers: std::cell::Cell, - component: std::cell::RefCell, + cursor_blinker: RefCell>, /// Gets dirty when the layout restrictions, or some other property of the windows change meta_property_listener: Pin>, focus_item: std::cell::RefCell, @@ -60,12 +60,12 @@ impl GraphicsWindow { + 'static, ) -> Rc { Rc::new(Self { + self_weak: Default::default(), window_factory: Box::new(graphics_backend_factory), map_state: RefCell::new(GraphicsWindowBackendState::Unmapped), properties: Box::pin(WindowProperties::default()), cursor_blinker: Default::default(), keyboard_modifiers: Default::default(), - component: Default::default(), meta_property_listener: Rc::pin(Default::default()), focus_item: Default::default(), mouse_input_state: Default::default(), @@ -129,7 +129,7 @@ impl GraphicsWindow { return; } - let component = self.component.borrow().upgrade().unwrap(); + let component = self.component(); let component = ComponentRc::borrow_pin(&component); let root_item = component.as_ref().get_item_ref(0); @@ -222,6 +222,10 @@ impl GraphicsWindow { existing_blinker.stop(); } } + + fn component(&self) -> ComponentRc { + self.self_weak.get().unwrap().upgrade().unwrap().component() + } } impl Drop for GraphicsWindow { @@ -239,12 +243,8 @@ impl Drop for GraphicsWindow { } impl GenericWindow for GraphicsWindow { - fn set_component(self: Rc, component: &ComponentRc) { - *self.component.borrow_mut() = vtable::VRc::downgrade(&component) - } - fn draw(self: Rc) { - let component_rc = self.component.borrow().upgrade().unwrap(); + let component_rc = self.component(); let component = ComponentRc::borrow_pin(&component_rc); { @@ -326,13 +326,13 @@ impl GenericWindow for GraphicsWindow { } popup.0.clone() } else { - self.component.borrow().upgrade().unwrap() + self.component() }; self.mouse_input_state.set(corelib::input::process_mouse_input( component, MouseEvent { pos, what }, - &ComponentWindow::new(self.clone()), + &ComponentWindow::new(self.self_weak.get().unwrap().upgrade().unwrap()), self.mouse_input_state.take(), )); @@ -347,7 +347,7 @@ impl GenericWindow for GraphicsWindow { fn process_key_input(self: Rc, event: &KeyEvent) { if let Some(focus_item) = self.as_ref().focus_item.borrow().upgrade() { - let window = &ComponentWindow::new(self.clone()); + let window = &ComponentWindow::new(self.self_weak.get().unwrap().upgrade().unwrap()); focus_item.borrow().as_ref().key_event(event, &window); } } @@ -434,7 +434,7 @@ impl GenericWindow for GraphicsWindow { } fn set_focus_item(self: Rc, focus_item: &ItemRc) { - let window = ComponentWindow::new(self.clone()); + let window = &ComponentWindow::new(self.self_weak.get().unwrap().upgrade().unwrap()); if let Some(old_focus_item) = self.as_ref().focus_item.borrow().upgrade() { old_focus_item @@ -449,7 +449,7 @@ impl GenericWindow for GraphicsWindow { } fn set_focus(self: Rc, have_focus: bool) { - let window = ComponentWindow::new(self.clone()); + let window = &ComponentWindow::new(self.self_weak.get().unwrap().upgrade().unwrap()); let event = if have_focus { corelib::input::FocusEvent::WindowReceivedFocus } else { diff --git a/sixtyfps_runtime/rendering_backends/gl/lib.rs b/sixtyfps_runtime/rendering_backends/gl/lib.rs index 1210168b9..a100b925f 100644 --- a/sixtyfps_runtime/rendering_backends/gl/lib.rs +++ b/sixtyfps_runtime/rendering_backends/gl/lib.rs @@ -1053,14 +1053,17 @@ pub const IS_AVAILABLE: bool = true; pub struct Backend; impl sixtyfps_corelib::backend::Backend for Backend { fn create_window(&'static self) -> ComponentWindow { - ComponentWindow::new(GraphicsWindow::new(|event_loop, window_builder| { + let platform_window = GraphicsWindow::new(|event_loop, window_builder| { GLRenderer::new( &event_loop.get_winit_event_loop(), window_builder, #[cfg(target_arch = "wasm32")] "canvas", ) - })) + }); + let window = Rc::new(sixtyfps_corelib::window::Window::new(platform_window.clone())); + platform_window.self_weak.set(Rc::downgrade(&window)).ok().unwrap(); + ComponentWindow(window) } fn register_application_font_from_memory( diff --git a/sixtyfps_runtime/rendering_backends/qt/lib.rs b/sixtyfps_runtime/rendering_backends/qt/lib.rs index 3f801450c..cb8daf3c7 100644 --- a/sixtyfps_runtime/rendering_backends/qt/lib.rs +++ b/sixtyfps_runtime/rendering_backends/qt/lib.rs @@ -9,6 +9,8 @@ LICENSE END */ #![recursion_limit = "512"] +use std::rc::Rc; + use sixtyfps_corelib::window::ComponentWindow; #[cfg(not(no_qt))] @@ -95,7 +97,12 @@ impl sixtyfps_corelib::backend::Backend for Backend { #[cfg(no_qt)] panic!("The Qt backend needs Qt"); #[cfg(not(no_qt))] - ComponentWindow::new(qt_window::QtWindow::new()) + { + let qt_window = qt_window::QtWindow::new(); + let window = Rc::new(sixtyfps_corelib::window::Window::new(qt_window.clone())); + qt_window.self_weak.set(Rc::downgrade(&window)).ok().unwrap(); + ComponentWindow::new(window) + } } fn register_application_font_from_memory( diff --git a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs index 31fd08852..ed4199b19 100644 --- a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs +++ b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs @@ -555,7 +555,7 @@ cpp_class!(unsafe struct QWidgetPtr as "std::unique_ptr"); pub struct QtWindow { widget_ptr: QWidgetPtr, - self_weak: once_cell::unsync::OnceCell>, + pub(crate) self_weak: once_cell::unsync::OnceCell>, component: RefCell, /// Gets dirty when the layout restrictions, or some other property of the windows change meta_property_listener: Pin>, @@ -565,7 +565,7 @@ pub struct QtWindow { focus_item: std::cell::RefCell, cursor_blinker: RefCell>, - popup_window: RefCell, ComponentRc)>>, + popup_window: RefCell, ComponentRc)>>, cache: QtRenderingCache, @@ -596,7 +596,6 @@ impl QtWindow { scale_factor: Box::pin(Property::new(1.)), }); let self_weak = Rc::downgrade(&rc); - rc.self_weak.set(self_weak.clone()).ok().unwrap(); let widget_ptr = rc.widget_ptr(); let rust_window = Rc::as_ptr(&rc); cpp! {unsafe [widget_ptr as "SixtyFPSWidget*", rust_window as "void*"] { @@ -758,10 +757,6 @@ impl QtWindow { #[allow(unused)] impl GenericWindow for QtWindow { - fn set_component(self: Rc, component: &sixtyfps_corelib::component::ComponentRc) { - *self.component.borrow_mut() = vtable::VRc::downgrade(&component) - } - fn draw(self: Rc) { todo!() } @@ -777,7 +772,7 @@ impl GenericWindow for QtWindow { /// ### Candidate to be moved in corelib (same as GraphicsWindow::process_key_input) fn process_key_input(self: Rc, event: &sixtyfps_corelib::input::KeyEvent) { if let Some(focus_item) = self.as_ref().focus_item.borrow().upgrade() { - let window = &ComponentWindow::new(self.clone()); + let window = &ComponentWindow::new(self.self_weak.get().unwrap().upgrade().unwrap()); focus_item.borrow().as_ref().key_event(event, &window); } } @@ -864,7 +859,7 @@ impl GenericWindow for QtWindow { /// ### Candidate to be moved in corelib as this kind of duplicate GraphicsWindow::set_focus_item fn set_focus_item(self: Rc, focus_item: &items::ItemRc) { - let window = ComponentWindow::new(self.clone()); + let window = ComponentWindow::new(self.self_weak.get().unwrap().upgrade().unwrap()); if let Some(old_focus_item) = self.as_ref().focus_item.borrow().upgrade() { old_focus_item @@ -883,7 +878,7 @@ impl GenericWindow for QtWindow { /// ### Candidate to be moved in corelib as this kind of duplicate GraphicsWindow::set_focussixtyfps_ fn set_focus(self: Rc, have_focus: bool) { - let window = ComponentWindow::new(self.clone()); + let window = ComponentWindow::new(self.self_weak.get().unwrap().upgrade().unwrap()); let event = if have_focus { sixtyfps_corelib::input::FocusEvent::WindowReceivedFocus } else { @@ -896,8 +891,10 @@ impl GenericWindow for QtWindow { } fn show_popup(&self, popup: &sixtyfps_corelib::component::ComponentRc, position: Point) { - let popup_window = Self::new(); - popup_window.clone().set_component(popup); + let popup_window = QtWindow::new(); + let window = Rc::new(sixtyfps_corelib::window::Window::new(popup_window.clone())); + popup_window.self_weak.set(Rc::downgrade(&window)).ok().unwrap(); + window.set_component(popup); let popup_ptr = popup_window.widget_ptr(); let pos = qttypes::QPoint { x: position.x as _, y: position.y as _ }; let widget_ptr = self.widget_ptr(); @@ -906,7 +903,7 @@ impl GenericWindow for QtWindow { popup_ptr->move(pos + widget_ptr->pos()); popup_ptr->show(); }}; - self.popup_window.replace(Some((popup_window, popup.clone()))); + self.popup_window.replace(Some((window, popup.clone()))); } fn close_popup(&self) {