/* LICENSE BEGIN This file is part of the SixtyFPS Project -- https://sixtyfps.io Copyright (c) 2020 Olivier Goffart Copyright (c) 2020 Simon Hausmann SPDX-License-Identifier: GPL-3.0-only This file is also available under commercial licensing terms. Please contact info@sixtyfps.io for more information. LICENSE END */ #![warn(missing_docs)] //! Exposed Window API use crate::component::{ComponentRc, ComponentWeak}; use crate::graphics::Point; use crate::input::{KeyEvent, MouseEventType, MouseInputState, TextCursorBlinker}; use crate::items::{ItemRc, ItemRef, ItemWeak}; use crate::slice::Slice; use crate::Resource; use core::cell::Cell; use core::pin::Pin; use std::cell::RefCell; use std::rc::Rc; /// This trait represents the interface that the generated code and the run-time /// require in order to implement functionality such as device-independent pixels, /// window resizing and other typicaly windowing system related tasks. pub trait PlatformWindow { /// Registers the window with the windowing system. fn show(self: Rc); /// Deregisters the window from the windowing system. fn hide(self: Rc); /// Issue a request to the windowing system to re-render the contents of the window. This is typically an asynchronous /// request. fn request_redraw(&self); /// Returns the scale factor set on the window, as provided by the windowing system. fn scale_factor(&self) -> f32; /// Sets an overriding scale factor for the window. This is typically only used for testing. fn set_scale_factor(&self, factor: f32); /// Returns the geometry of the window fn get_geometry(&self) -> crate::graphics::Rect; /// 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 [`crate::graphics::RenderingCache`]. fn free_graphics_resources<'a>(self: Rc, items: &Slice<'a, Pin>>); /// Show a popup at the given position fn show_popup(&self, popup: &ComponentRc, position: Point); /// Close the active popup if any fn close_popup(&self); /// Return a font metrics trait object for the given font request. This is typically provided by the backend and /// requested by text related items in order to measure text metrics with the item's chosen font. /// Note that if the FontRequest's pixel_size is 0, it is interpreted as the undefined size and that the /// system default font size should be used for the returned font. /// With some backends this may return none unless the window is mapped. fn font_metrics( &self, request: crate::graphics::FontRequest, ) -> Option>; /// Return the size of the image referenced by the specified resource, multiplied by the window /// scale factor. fn image_size( &self, item_graphics_cache: &crate::item_rendering::CachedRenderingData, source: Pin<&crate::properties::Property>, ) -> crate::graphics::Size; } /// Structure that represent a Window in the runtime pub struct Window { /// FIXME! use Box instead; platform_window: Rc, component: RefCell, mouse_input_state: Cell, focus_item: RefCell, cursor_blinker: RefCell>, } impl Drop for Window { fn drop(&mut self) { if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() { existing_blinker.stop(); } } } 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(), mouse_input_state: Default::default(), focus_item: Default::default(), cursor_blinker: 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) { crate::animations::update_animations(); let component = self.component.borrow().upgrade().unwrap(); self.mouse_input_state.set(crate::input::process_mouse_input( component, crate::input::MouseEvent { pos, what }, &ComponentWindow::new(self.clone()), self.mouse_input_state.take(), )); } /// 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) { let mut item = self.focus_item.borrow().clone(); while let Some(focus_item) = item.upgrade() { let window = &ComponentWindow::new(self.clone()); if focus_item.borrow().as_ref().key_event(event, &window) == crate::input::KeyEventResult::EventAccepted { return; } item = focus_item.parent_item(); } } /// 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::Property) { let existing_blinker = self.cursor_blinker.borrow().clone(); let blinker = existing_blinker.upgrade().unwrap_or_else(|| { let new_blinker = TextCursorBlinker::new(); *self.cursor_blinker.borrow_mut() = pin_weak::rc::PinWeak::downgrade(new_blinker.clone()); new_blinker }); TextCursorBlinker::set_binding(blinker, 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) { let window = ComponentWindow::new(self.clone()); if let Some(old_focus_item) = self.as_ref().focus_item.borrow().upgrade() { old_focus_item .borrow() .as_ref() .focus_event(&crate::input::FocusEvent::FocusOut, &window); } *self.as_ref().focus_item.borrow_mut() = focus_item.downgrade(); focus_item.borrow().as_ref().focus_event(&crate::input::FocusEvent::FocusIn, &window); } /// 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) { let window = ComponentWindow::new(self.clone()); let event = if have_focus { crate::input::FocusEvent::WindowReceivedFocus } else { crate::input::FocusEvent::WindowLostFocus }; if let Some(focus_item) = self.as_ref().focus_item.borrow().upgrade() { focus_item.borrow().as_ref().focus_event(&event, &window); } } } impl core::ops::Deref for Window { type Target = dyn PlatformWindow; 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); 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 { Self(window_impl) } /// Registers the window with the windowing system, in order to render the component's items and react /// to input events once the event loop spins. pub fn show(&self) { self.0.platform_window.clone().show(); } /// De-registers the window with the windowing system. pub fn hide(&self) { self.0.platform_window.clone().hide(); } /// Returns the scale factor set on the window. pub fn scale_factor(&self) -> f32 { self.0.scale_factor() } /// Sets an overriding scale factor for the window. This is typically only used for testing. pub fn set_scale_factor(&self, factor: f32) { self.0.set_scale_factor(factor) } /// 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.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) } pub(crate) fn process_key_input(&self, event: &KeyEvent) { self.0.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) } /// Associates this window with the specified component, for future event handling, etc. pub fn set_component(&self, component: &ComponentRc) { self.0.set_component(component) } /// Show a popup at the given position pub fn show_popup(&self, popup: &ComponentRc, position: Point) { self.0.platform_window.clone().show_popup(popup, position) } /// Close the active popup if any pub fn close_popup(&self) { self.0.platform_window.clone().close_popup() } } /// This module contains the functions needed to interface with the event loop and window traits /// from outside the Rust language. pub mod ffi { #![allow(unsafe_code)] use super::*; #[allow(non_camel_case_types)] type c_void = (); /// Same layout as ComponentWindow #[repr(C)] pub struct ComponentWindowOpaque(*const c_void); /// Releases the reference to the component window held by handle. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_drop(handle: *mut ComponentWindowOpaque) { assert_eq!( core::mem::size_of::(), core::mem::size_of::() ); core::ptr::read(handle as *mut ComponentWindow); } /// Releases the reference to the component window held by handle. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_clone( source: *const ComponentWindowOpaque, target: *mut ComponentWindowOpaque, ) { assert_eq!( core::mem::size_of::(), core::mem::size_of::() ); let window = &*(source as *const ComponentWindow); core::ptr::write(target as *mut ComponentWindow, window.clone()); } /// Spins an event loop and renders the items of the provided component in this window. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_show(handle: *const ComponentWindowOpaque) { let window = &*(handle as *const ComponentWindow); window.show(); } /// Spins an event loop and renders the items of the provided component in this window. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_hide(handle: *const ComponentWindowOpaque) { let window = &*(handle as *const ComponentWindow); window.hide(); } /// Returns the window scale factor. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_get_scale_factor( handle: *const ComponentWindowOpaque, ) -> f32 { assert_eq!( core::mem::size_of::(), core::mem::size_of::() ); let window = &*(handle as *const ComponentWindow); window.scale_factor() } /// Sets the window scale factor, merely for testing purposes. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_set_scale_factor( handle: *const ComponentWindowOpaque, value: f32, ) { let window = &*(handle as *const ComponentWindow); window.set_scale_factor(value) } /// Sets the window scale factor, merely for testing purposes. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_free_graphics_resources<'a>( handle: *const ComponentWindowOpaque, items: &Slice<'a, Pin>>, ) { let window = &*(handle as *const ComponentWindow); window.free_graphics_resources(items) } /// Sets the focus item. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_set_focus_item( handle: *const ComponentWindowOpaque, focus_item: &ItemRc, ) { let window = &*(handle as *const ComponentWindow); window.set_focus_item(focus_item) } /// Associates the window with the given component. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_set_component( handle: *const ComponentWindowOpaque, component: &ComponentRc, ) { let window = &*(handle as *const ComponentWindow); window.set_component(component) } /// Show a popup. #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_show_popup( handle: *const ComponentWindowOpaque, popup: &ComponentRc, position: crate::graphics::Point, ) { let window = &*(handle as *const ComponentWindow); window.show_popup(popup, position); } /// Close the current popup pub unsafe extern "C" fn sixtyfps_component_window_close_popup( handle: *const ComponentWindowOpaque, ) { let window = &*(handle as *const ComponentWindow); window.close_popup(); } }