// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // cSpell: ignore nesw windowitem /*! This module contains the builtin items, either in this file or in sub-modules. When adding an item or a property, it needs to be kept in sync with different place. (This is less than ideal and maybe we can have some automation later) - It needs to be changed in this module - In the compiler: internal/compiler/builtin_elements.rs - In the interpreter (new item only): item_registry.rs - For the C++ code (new item only): the cbindgen.rs to export the new item - Don't forget to update the documentation */ #![allow(unsafe_code)] #![allow(non_upper_case_globals)] #![allow(missing_docs)] // because documenting each property of items is redundant use crate::api::LogicalPosition; use crate::cursor::MouseCursorInner; use crate::data_transfer::DataTransfer; use crate::graphics::{Brush, Color, FontRequest, Image}; use crate::input::{ FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, InternalKeyEvent, KeyEventResult, KeyEventType, Keys, MouseEvent, }; use crate::item_rendering::{CachedRenderingData, RenderBorderRectangle, RenderRectangle}; use crate::item_tree::ItemTreeRc; pub use crate::item_tree::{ItemRc, ItemTreeVTable}; use crate::layout::LayoutInfo; use crate::lengths::{ LogicalBorderRadius, LogicalLength, LogicalRect, LogicalSize, LogicalVector, PointLengths, RectLengths, }; pub use crate::menus::MenuItem; #[cfg(feature = "rtti")] use crate::rtti::*; use crate::window::{WindowAdapter, WindowAdapterRc, WindowInner}; use crate::{Callback, Coord, Property, SharedString}; use alloc::rc::Rc; use const_field_offset::FieldOffsets; use core::cell::Cell; use core::num::NonZeroU32; use core::pin::Pin; use core::time::Duration; use i_slint_core_macros::*; pub use system_tray::SystemTrayIcon; use vtable::*; mod component_container; pub use self::component_container::*; pub mod flickable; // vendored: `pub` for the jank-hunt FLICK_* probes pub use flickable::Flickable; mod text; pub use text::*; mod input_items; pub use input_items::*; mod image; pub use self::image::*; mod drag_n_drop; pub use drag_n_drop::*; #[cfg(feature = "path")] mod path; #[cfg(feature = "path")] pub use path::*; pub mod system_tray; /// Alias for `&mut dyn ItemRenderer`. Required so cbindgen generates the ItemVTable /// despite the presence of trait object type ItemRendererRef<'a> = &'a mut dyn crate::item_rendering::ItemRenderer; /// Workarounds for cbindgen pub type VoidArg = (); pub type KeyEventArg = (KeyEvent,); pub type DragActionArg = (DragAction,); type FocusReasonArg = (FocusReason,); type PointerEventArg = (PointerEvent,); type PointerScrollEventArg = (PointerScrollEvent,); type PointArg = (LogicalPosition,); type MenuEntryArg = (MenuEntry,); type StringArg = (SharedString,); type MenuEntryModel = crate::model::ModelRc; #[cfg(all(feature = "ffi", windows))] #[macro_export] macro_rules! declare_item_vtable { (fn $getter:ident() -> $item_vtable_ty:ident for $item_ty:ty) => { ItemVTable_static! { #[unsafe(no_mangle)] pub static $item_vtable_ty for $item_ty } #[unsafe(no_mangle)] pub extern "C" fn $getter() -> *const ItemVTable { use vtable::HasStaticVTable; <$item_ty>::STATIC_VTABLE } }; } #[cfg(not(all(feature = "ffi", windows)))] #[macro_export] macro_rules! declare_item_vtable { (fn $getter:ident() -> $item_vtable_ty:ident for $item_ty:ty) => { ItemVTable_static! { #[unsafe(no_mangle)] pub static $item_vtable_ty for $item_ty } }; } /// Returned by the `render()` function on items to indicate whether the rendering of /// children should be handled by the caller, of if the item took care of that (for example /// through layer indirection) #[repr(C)] #[derive(Default)] pub enum RenderingResult { #[default] ContinueRenderingChildren, ContinueRenderingWithoutChildren, } /// Items are the nodes in the render tree. #[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)] #[vtable] #[repr(C)] pub struct ItemVTable { /// This function is called by the run-time after the memory for the item /// has been allocated and initialized. It will be called before any user specified /// bindings are set. pub init: extern "C" fn(core::pin::Pin>, my_item: &ItemRc), pub deinit: extern "C" fn(core::pin::Pin>, window_adapter: &WindowAdapterRc), /// offset in bytes from the *const ItemImpl. /// usize::MAX means None #[allow(non_upper_case_globals)] #[field_offset(CachedRenderingData)] pub cached_rendering_data_offset: usize, /// We would need max/min/preferred size, and all layout info. /// `cross_axis_constraint` is the available width when querying vertical /// layout info, enabling height-for-width (Text word-wrap, Image aspect /// ratio). Negative values mean unconstrained. pub layout_info: extern "C" fn( core::pin::Pin>, orientation: Orientation, cross_axis_constraint: Coord, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, ) -> LayoutInfo, /// Event handler for mouse and touch event. This function is called before being called on children. /// Then, depending on the return value, it is called for the children, and their children, then /// [`Self::input_event`] is called on the children, and finally [`Self::input_event`] is called /// on this item again. /// /// The `cursor` argument needs to be changed by either this function ot the `input_event` function /// if this item wants to change the cursor. /// The value of `cursor` is always reset to `MouseCursor::Default` before dispatching the event, /// so any call to this function need to set the cursor pub input_event_filter_before_children: extern "C" fn( core::pin::Pin>, &MouseEvent, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, cursor: &mut MouseCursorInner, ) -> InputEventFilterResult, /// Handle input event for mouse and touch event pub input_event: extern "C" fn( core::pin::Pin>, &MouseEvent, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, cursor: &mut MouseCursorInner, ) -> InputEventResult, pub focus_event: extern "C" fn( core::pin::Pin>, &FocusEvent, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, ) -> FocusEventResult, /// Called on the parents of the focused item, allowing for global shortcuts and similar /// overrides of the default actions. pub capture_key_event: extern "C" fn( core::pin::Pin>, &InternalKeyEvent, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, ) -> KeyEventResult, pub key_event: extern "C" fn( core::pin::Pin>, &InternalKeyEvent, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, ) -> KeyEventResult, pub render: extern "C" fn( core::pin::Pin>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult, /// Returns the rendering bounding rect for that particular item in the parent's item coordinate /// (same coordinate system as the geometry) pub bounding_rect: extern "C" fn( core::pin::Pin>, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect, pub clips_children: extern "C" fn(core::pin::Pin>) -> bool, } /// Alias for `vtable::VRef` which represent a pointer to a `dyn Item` with /// the associated vtable pub type ItemRef<'a> = vtable::VRef<'a, ItemVTable>; #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of an empty items that does nothing pub struct Empty { pub cached_rendering_data: CachedRenderingData, } impl Item for Empty { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, _backend: &mut ItemRendererRef, _self_rc: &ItemRc, _size: LogicalSize, ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, mut geometry: LogicalRect, ) -> LogicalRect { geometry.size = LogicalSize::zero(); geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl ItemConsts for Empty { const cached_rendering_data_offset: const_field_offset::FieldOffset< Empty, CachedRenderingData, > = Empty::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_EmptyVTable() -> EmptyVTable for Empty } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of the `Rectangle` element pub struct Rectangle { pub background: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Rectangle { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult { (*backend).draw_rectangle(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl RenderRectangle for Rectangle { fn background(self: Pin<&Self>) -> Brush { self.background() } } impl ItemConsts for Rectangle { const cached_rendering_data_offset: const_field_offset::FieldOffset< Rectangle, CachedRenderingData, > = Rectangle::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_RectangleVTable() -> RectangleVTable for Rectangle } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of the `BasicBorderRectangle` element pub struct BasicBorderRectangle { pub background: Property, pub border_width: Property, pub border_radius: Property, pub border_color: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for BasicBorderRectangle { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult { (*backend).draw_border_rectangle(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl RenderBorderRectangle for BasicBorderRectangle { fn background(self: Pin<&Self>) -> Brush { self.background() } fn border_width(self: Pin<&Self>) -> LogicalLength { self.border_width() } fn border_radius(self: Pin<&Self>) -> LogicalBorderRadius { LogicalBorderRadius::from_length(self.border_radius()) } fn border_color(self: Pin<&Self>) -> Brush { self.border_color() } } impl ItemConsts for BasicBorderRectangle { const cached_rendering_data_offset: const_field_offset::FieldOffset< BasicBorderRectangle, CachedRenderingData, > = BasicBorderRectangle::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_BasicBorderRectangleVTable() -> BasicBorderRectangleVTable for BasicBorderRectangle } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of the `BorderRectangle` element pub struct BorderRectangle { pub background: Property, pub border_width: Property, pub border_radius: Property, pub border_top_left_radius: Property, pub border_top_right_radius: Property, pub border_bottom_left_radius: Property, pub border_bottom_right_radius: Property, pub border_color: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for BorderRectangle { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult { (*backend).draw_border_rectangle(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl RenderBorderRectangle for BorderRectangle { fn background(self: Pin<&Self>) -> Brush { self.background() } fn border_width(self: Pin<&Self>) -> LogicalLength { self.border_width() } fn border_radius(self: Pin<&Self>) -> LogicalBorderRadius { LogicalBorderRadius::from_lengths( self.border_top_left_radius(), self.border_top_right_radius(), self.border_bottom_right_radius(), self.border_bottom_left_radius(), ) } fn border_color(self: Pin<&Self>) -> Brush { self.border_color() } } impl ItemConsts for BorderRectangle { const cached_rendering_data_offset: const_field_offset::FieldOffset< BorderRectangle, CachedRenderingData, > = BorderRectangle::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_BorderRectangleVTable() -> BorderRectangleVTable for BorderRectangle } declare_item_vtable! { fn slint_get_TouchAreaVTable() -> TouchAreaVTable for TouchArea } declare_item_vtable! { fn slint_get_FocusScopeVTable() -> FocusScopeVTable for FocusScope } crate::declare_item_vtable! { fn slint_get_KeyBindingVTable() -> KeyBindingVTable for KeyBinding } declare_item_vtable! { fn slint_get_SwipeGestureHandlerVTable() -> SwipeGestureHandlerVTable for SwipeGestureHandler } declare_item_vtable! { fn slint_get_ScaleRotateGestureHandlerVTable() -> ScaleRotateGestureHandlerVTable for ScaleRotateGestureHandler } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of the `Clip` element pub struct Clip { pub border_top_left_radius: Property, pub border_top_right_radius: Property, pub border_bottom_left_radius: Property, pub border_bottom_right_radius: Property, pub border_width: Property, pub cached_rendering_data: CachedRenderingData, pub clip: Property, pub is_visibility_clip: Property, } impl Item for Clip { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, event: &MouseEvent, _window_adapter: &Rc, self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { if let Some(pos) = event.position() { let geometry = self_rc.geometry(); if self.clip() && (pos.x < 0 as Coord || pos.y < 0 as Coord || pos.x_length() > geometry.width_length() || pos.y_length() > geometry.height_length()) { return InputEventFilterResult::Intercept; } } InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult { (*backend).visit_clip(self, self_rc, size) } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { self.clip() } } impl Clip { pub fn logical_border_radius(self: Pin<&Self>) -> LogicalBorderRadius { LogicalBorderRadius::from_lengths( self.border_top_left_radius(), self.border_top_right_radius(), self.border_bottom_right_radius(), self.border_bottom_left_radius(), ) } } impl ItemConsts for Clip { const cached_rendering_data_offset: const_field_offset::FieldOffset = Clip::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_ClipVTable() -> ClipVTable for Clip } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The Opacity Item is not meant to be used directly by the .slint code, instead, the `opacity: xxx` or `visible: false` should be used pub struct Opacity { // FIXME: this element shouldn't need these geometry property pub opacity: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Opacity { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult { backend.visit_opacity(self, self_rc, size) } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl Opacity { // This function determines the optimization opportunities for not having to render the // children of the Opacity element into a layer: // * The opacity item typically only one child (this is not guaranteed). If that item has // no children, then we can skip the layer and apply the opacity directly. This is not perfect though, // for example if the compiler inserts another synthetic element between the `Opacity` and the actual child, // then this check will apply a layer even though it might not actually be necessary. // * If the vale of the opacity is 1.0 then we don't need to do anything. pub fn need_layer(self_rc: &ItemRc, opacity: f32) -> bool { if opacity == 1.0 { return false; } let opacity_child = match self_rc.first_child() { Some(first_child) => first_child, None => return false, // No children? Don't need a layer then. }; if opacity_child.next_sibling().is_some() { return true; // If the opacity item has more than one child, then we need a layer } // If the target of the opacity has any children then we need a layer opacity_child.first_child().is_some() } } impl ItemConsts for Opacity { const cached_rendering_data_offset: const_field_offset::FieldOffset< Opacity, CachedRenderingData, > = Opacity::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_OpacityVTable() -> OpacityVTable for Opacity } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The Layer Item is not meant to be used directly by the .slint code, instead, the `layer: xxx` property should be used pub struct Layer { pub cache_rendering_hint: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Layer { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult { backend.visit_layer(self, self_rc, size) } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl ItemConsts for Layer { const cached_rendering_data_offset: const_field_offset::FieldOffset< Layer, CachedRenderingData, > = Layer::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_LayerVTable() -> LayerVTable for Layer } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of the `Transform` item. /// This item is generated by the compiler as soon as any transform property is used on any element. pub struct Transform { pub transform_rotation: Property, pub transform_scale_x: Property, pub transform_scale_y: Property, pub transform_origin: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Transform { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, _self_rc: &ItemRc, _size: LogicalSize, ) -> RenderingResult { let origin = self.transform_origin().to_euclid().to_vector(); (*backend).translate(origin); (*backend).scale(self.transform_scale_x(), self.transform_scale_y()); (*backend).rotate(self.transform_rotation()); (*backend).translate(-origin); RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl ItemConsts for Transform { const cached_rendering_data_offset: const_field_offset::FieldOffset< Transform, CachedRenderingData, > = Transform::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_TransformVTable() -> TransformVTable for Transform } declare_item_vtable! { fn slint_get_FlickableVTable() -> FlickableVTable for Flickable } declare_item_vtable! { fn slint_get_DragAreaVTable() -> DragAreaVTable for DragArea } declare_item_vtable! { fn slint_get_DropAreaVTable() -> DropAreaVTable for DropArea } declare_item_vtable! { fn slint_get_WindowMoveAreaVTable() -> WindowMoveAreaVTable for WindowMoveArea } /// The implementation of the `PropertyAnimation` element /// This animation has the time as animation limit #[repr(C)] #[derive(FieldOffsets, SlintElement, Clone, Debug)] #[pin] pub struct PropertyAnimation { #[rtti_field] pub delay: i32, /// duration in millisecond #[rtti_field] pub duration: i32, #[rtti_field] pub iteration_count: f32, #[rtti_field] pub direction: AnimationDirection, #[rtti_field] pub easing: crate::animations::EasingCurve, #[rtti_field] pub enabled: bool, } impl Default for PropertyAnimation { fn default() -> Self { // Defaults for PropertyAnimation are defined here (for internal Rust code doing programmatic animations) // as well as in `internal/compiler/builtin_elements.rs` (for generated C++ and Rust code) Self { delay: 0, duration: 0, iteration_count: 1., direction: Default::default(), easing: Default::default(), enabled: true, } } } /// The implementation of the `Window` element #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct WindowItem { pub width: Property, pub height: Property, pub safe_area_insets: Property, pub virtual_keyboard_position: Property, pub virtual_keyboard_size: Property, pub background: Property, pub title: Property, pub no_frame: Property, pub resize_border_width: Property, pub always_on_top: Property, pub full_screen: Property, pub minimized: Property, pub maximized: Property, pub icon: Property, pub default_font_family: Property, pub default_font_size: Property, pub default_font_weight: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for WindowItem { fn init(self: Pin<&Self>, _self_rc: &ItemRc) { #[cfg(feature = "std")] self.full_screen.set(std::env::var("SLINT_FULLSCREEN").is_ok()); } fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo::default() } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult { if self_rc.parent_item(crate::item_tree::ParentItemTraversalMode::StopAtPopups).is_none() { backend.draw_window_background(self, self_rc, size, &self.cached_rendering_data); } else { // Dialogs and other nested Window items backend.draw_rectangle(self, self_rc, size, &self.cached_rendering_data); } RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl RenderRectangle for WindowItem { fn background(self: Pin<&Self>) -> Brush { self.background() } } fn next_window_item(item: &ItemRc) -> Option { let root_item_in_local_item_tree = ItemRc::new_root(item.item_tree().clone()); if root_item_in_local_item_tree.downcast::().is_some() { Some(root_item_in_local_item_tree) } else { root_item_in_local_item_tree .parent_item(crate::item_tree::ParentItemTraversalMode::FindAllParents) .and_then(|parent| next_window_item(&parent)) } } impl WindowItem { pub fn font_family(self: Pin<&Self>) -> Option { let maybe_family = self.default_font_family(); if !maybe_family.is_empty() { Some(maybe_family) } else { None } } pub fn font_size(self: Pin<&Self>) -> Option { let font_size = self.default_font_size(); if font_size.get() <= 0 as Coord { None } else { Some(font_size) } } pub fn font_weight(self: Pin<&Self>) -> Option { let font_weight = self.default_font_weight(); if font_weight == 0 { None } else { Some(font_weight) } } pub fn resolved_default_font_size(item_tree: ItemTreeRc) -> LogicalLength { let first_item = ItemRc::new_root(item_tree); let window_item = next_window_item(&first_item).unwrap(); Self::resolve_font_property(&window_item, Self::font_size) .or_else(|| Self::platform_default_font_size(&first_item)) .unwrap_or(crate::textlayout::DEFAULT_FONT_SIZE) } /// Returns the default font size reported by the platform (e.g. iOS Dynamic Type), /// or `None` when the backend doesn't report one. Used as fallback when no /// `default-font-size` is set in the .slint code, before the renderer's built-in /// default applies. fn platform_default_font_size(item: &ItemRc) -> Option { item.window_adapter().and_then(|adapter| { WindowInner::from_pub(adapter.window()).context().platform_default_font_size() }) } fn resolve_font_property( self_rc: &ItemRc, property_fn: impl Fn(Pin<&Self>) -> Option, ) -> Option { let mut window_item_rc = self_rc.clone(); loop { let window_item = window_item_rc.downcast::()?; if let Some(result) = property_fn(window_item.as_pin_ref()) { return Some(result); } window_item_rc = window_item_rc .parent_item(crate::item_tree::ParentItemTraversalMode::FindAllParents) .and_then(|p| next_window_item(&p))?; } } /// Creates a new FontRequest that uses the provide local font properties. If they're not set, i.e. /// the family is an empty string, or the weight is zero, the corresponding properties are fetched /// from the next parent WindowItem. pub fn resolved_font_request( self_rc: &crate::items::ItemRc, local_font_family: SharedString, local_font_weight: i32, local_font_size: LogicalLength, local_letter_spacing: LogicalLength, local_line_height_factor: f32, local_italic: bool, ) -> FontRequest { let Some(window_item_rc) = next_window_item(self_rc) else { return FontRequest::default(); }; FontRequest { family: { if !local_font_family.is_empty() { Some(local_font_family) } else { Self::resolve_font_property( &window_item_rc, crate::items::WindowItem::font_family, ) } }, weight: { if local_font_weight == 0 { Self::resolve_font_property( &window_item_rc, crate::items::WindowItem::font_weight, ) } else { Some(local_font_weight) } }, pixel_size: { if local_font_size.get() == 0 as Coord { Self::resolve_font_property( &window_item_rc, crate::items::WindowItem::font_size, ) .or_else(|| Self::platform_default_font_size(self_rc)) } else { Some(local_font_size) } }, letter_spacing: Some(local_letter_spacing), // 1 is neutral and negative or non-finite values behave like 1, all mapping to // None (the font's natural line height); 0 is a valid factor and collapses lines. line_height_factor: (local_line_height_factor.is_finite() && local_line_height_factor >= 0.0 && local_line_height_factor != 1.0) .then_some(local_line_height_factor), italic: local_italic, } } pub fn close( self: Pin<&Self>, window_adapter: &Rc, self_rc: &ItemRc, ) -> bool { if !is_root_window_item(window_adapter, self_rc) { return false; } let inner = WindowInner::from_pub(window_adapter.window()); let accepted = inner.request_close(); if accepted && let Err(err) = inner.hide() { crate::debug_log!("Slint: Failed to hide window after close request: {err}"); } accepted } pub fn hide(self: Pin<&Self>, window_adapter: &Rc, self_rc: &ItemRc) { if !is_root_window_item(window_adapter, self_rc) { return; } let _ = WindowInner::from_pub(window_adapter.window()).hide(); } } /// A `WindowItem` is considered the adapter's root window only when it is at index 0 of /// the component item tree currently bound to the adapter. fn is_root_window_item(window_adapter: &Rc, self_rc: &ItemRc) -> bool { if !self_rc.is_root() { return false; } WindowInner::from_pub(window_adapter.window()) .try_component() .is_some_and(|component| VRc::ptr_eq(&component, self_rc.item_tree())) } impl ItemConsts for WindowItem { const cached_rendering_data_offset: const_field_offset::FieldOffset = Self::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } #[cfg(feature = "ffi")] #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_windowitem_close( window_item: Pin<&WindowItem>, window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque, self_component: &vtable::VRc, self_index: u32, ) -> bool { unsafe { let window_adapter = &*(window_adapter as *const Rc); let item_rc = ItemRc::new(self_component.clone(), self_index); window_item.close(window_adapter, &item_rc) } } #[cfg(feature = "ffi")] #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_windowitem_hide( window_item: Pin<&WindowItem>, window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque, self_component: &vtable::VRc, self_index: u32, ) { unsafe { let window_adapter = &*(window_adapter as *const Rc); let item_rc = ItemRc::new(self_component.clone(), self_index); window_item.hide(window_adapter, &item_rc); } } declare_item_vtable! { fn slint_get_WindowItemVTable() -> WindowItemVTable for WindowItem } /// The implementation used for `ContextMenuArea` and `ContextMenuInternal` elements #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct ContextMenu { //pub entries: Property>, pub sub_menu: Callback, pub activated: Callback, pub show: Callback, pub cached_rendering_data: CachedRenderingData, pub popup_id: Cell>, pub enabled: Property, #[cfg(target_os = "android")] long_press_timer: crate::timers::Timer, } impl Item for ContextMenu { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo::default() } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardEvent } fn input_event( self: Pin<&Self>, event: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { if !self.enabled() { return InputEventResult::EventIgnored; } match event { MouseEvent::Pressed { position, button: PointerEventButton::Right, .. } => { self.show.call(&(LogicalPosition::from_euclid(*position),)); InputEventResult::EventAccepted } #[cfg(target_os = "android")] MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => { let self_weak = _self_rc.downgrade(); let position = *position; let ctx = WindowInner::from_pub(_window_adapter.window()).context(); self.long_press_timer.start_on( ctx, crate::timers::TimerMode::SingleShot, ctx.platform().long_press_interval(crate::InternalToken), move || { let Some(self_rc) = self_weak.upgrade() else { return }; let Some(self_) = self_rc.downcast::() else { return }; self_.show.call(&(LogicalPosition::from_euclid(position),)); }, ); InputEventResult::GrabMouse } #[cfg(target_os = "android")] MouseEvent::Released { .. } | MouseEvent::Exit => { self.long_press_timer.stop(); InputEventResult::EventIgnored } #[cfg(target_os = "android")] MouseEvent::Moved { .. } => InputEventResult::EventAccepted, _ => InputEventResult::EventIgnored, } } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, event: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { if !self.enabled() { return KeyEventResult::EventIgnored; } fn is_menu_key(event: &InternalKeyEvent) -> bool { #[allow(unused_mut)] let mut is_menu_key = event.key_event.text.contains(crate::input::key_codes::Menu); #[cfg(target_os = "windows")] { // Windows maps Shift + F10 to open the context menu is_menu_key |= event.key_event.text.contains(crate::input::key_codes::F10) && event.key_event.modifiers.shift; } is_menu_key } if is_menu_key(event) { self.show.call(&(Default::default(),)); KeyEventResult::EventAccepted } else { KeyEventResult::EventIgnored } } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, _backend: &mut ItemRendererRef, _self_rc: &ItemRc, _size: LogicalSize, ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl ContextMenu { pub fn close(self: Pin<&Self>, window_adapter: &Rc, _: &ItemRc) { if let Some(id) = self.popup_id.take() { WindowInner::from_pub(window_adapter.window()).close_popup(id); } } pub fn is_open(self: Pin<&Self>, window_adapter: &Rc, _: &ItemRc) -> bool { self.popup_id.get().is_some_and(|id| { WindowInner::from_pub(window_adapter.window()) .active_popups() .iter() .any(|p| p.popup_id == id) }) } } impl ItemConsts for ContextMenu { const cached_rendering_data_offset: const_field_offset::FieldOffset = Self::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_ContextMenuVTable() -> ContextMenuVTable for ContextMenu } #[cfg(feature = "ffi")] #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_contextmenu_close( s: Pin<&ContextMenu>, window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque, self_component: &vtable::VRc, self_index: u32, ) { unsafe { let window_adapter = &*(window_adapter as *const Rc); let self_rc = ItemRc::new(self_component.clone(), self_index); s.close(window_adapter, &self_rc); } } #[cfg(feature = "ffi")] #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_contextmenu_is_open( s: Pin<&ContextMenu>, window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque, self_component: &vtable::VRc, self_index: u32, ) -> bool { unsafe { let window_adapter = &*(window_adapter as *const Rc); let self_rc = ItemRc::new(self_component.clone(), self_index); s.is_open(window_adapter, &self_rc) } } /// The implementation of the `BoxShadow` element #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct BoxShadow { pub border_top_left_radius: Property, pub border_top_right_radius: Property, pub border_bottom_left_radius: Property, pub border_bottom_right_radius: Property, // Shadow specific properties pub offset_x: Property, pub offset_y: Property, pub color: Property, pub blur: Property, pub spread: Property, pub inset: Property, pub cached_rendering_data: CachedRenderingData, } impl BoxShadow { pub fn logical_border_radius(self: Pin<&Self>) -> LogicalBorderRadius { LogicalBorderRadius::from_lengths( self.border_top_left_radius(), self.border_top_right_radius(), self.border_bottom_right_radius(), self.border_bottom_left_radius(), ) } } impl Item for BoxShadow { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult { (*backend).draw_box_shadow(self, self_rc, size); RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { if self.inset() { // Inset shadow paints inside the geometry; never extends outside. geometry } else { let pad = self.blur() + LogicalLength::new(self.spread().get().max(0 as crate::Coord)); geometry .outer_rect(euclid::SideOffsets2D::from_length_all_same(pad)) .translate(LogicalVector::from_lengths(self.offset_x(), self.offset_y())) } } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl ItemConsts for BoxShadow { const cached_rendering_data_offset: const_field_offset::FieldOffset = Self::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_BoxShadowVTable() -> BoxShadowVTable for BoxShadow } declare_item_vtable! { fn slint_get_ComponentContainerVTable() -> ComponentContainerVTable for ComponentContainer } declare_item_vtable! { fn slint_get_ComplexTextVTable() -> ComplexTextVTable for ComplexText } declare_item_vtable! { fn slint_get_StyledTextItemVTable() -> StyledTextItemVTable for StyledTextItem } declare_item_vtable! { fn slint_get_SimpleTextVTable() -> SimpleTextVTable for SimpleText } declare_item_vtable! { fn slint_get_TextInputVTable() -> TextInputVTable for TextInput } declare_item_vtable! { fn slint_get_ImageItemVTable() -> ImageItemVTable for ImageItem } declare_item_vtable! { fn slint_get_ClippedImageVTable() -> ClippedImageVTable for ClippedImage } #[cfg(feature = "path")] declare_item_vtable! { fn slint_get_PathVTable() -> PathVTable for Path } declare_item_vtable! { fn slint_get_MenuItemVTable() -> MenuItemVTable for MenuItem } declare_item_vtable! { fn slint_get_SystemTrayIconVTable() -> SystemTrayIconVTable for SystemTrayIcon } macro_rules! declare_enums { ($( $(#[$enum_doc:meta])* $vis:vis enum $Name:ident { $( $(#[$value_doc:meta])* $Value:ident,)* })*) => { $( #[derive(Copy, Clone, Debug, PartialEq, Eq, strum::EnumString, strum::Display, Hash)] #[repr(u32)] #[strum(serialize_all = "kebab-case")] $(#[$enum_doc])* pub enum $Name { $( $(#[$value_doc])* $Value),* } impl Default for $Name { fn default() -> Self { // Always return the first value ($(Self::$Value,)*).0 } } )* }; } i_slint_common::for_each_enums!(declare_enums); /// Internal transparent hover tracker synthesized by tooltip lowering. #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct TooltipArea { pub has_hover: Property, pub mouse_x: Property, pub mouse_y: Property, pub text: Property, pub delay: Property, pub offset: Property, pub show: Callback, pub hide: Callback, pub cached_rendering_data: CachedRenderingData, popup_visible: Cell, timer: crate::timers::Timer, } impl Item for TooltipArea { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn deinit(self: Pin<&Self>, _window_adapter: &Rc) {} fn layout_info( self: Pin<&Self>, _orientation: Orientation, _cross_axis_constraint: Coord, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo::default() } fn input_event_filter_before_children( self: Pin<&Self>, event: &MouseEvent, _window_adapter: &Rc, self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventFilterResult { // Track hover in the filter stage so enter/leave transitions are reliable, // independent of later input handling. if matches!(event, MouseEvent::DragMove { .. } | MouseEvent::Drop { .. }) { self.set_hover_state(false, self_rc); return InputEventFilterResult::ForwardAndIgnore; } if let Some(pos) = event.position() { Self::FIELD_OFFSETS.mouse_x().apply_pin(self).set(pos.x_length()); Self::FIELD_OFFSETS.mouse_y().apply_pin(self).set(pos.y_length()); } let next_hover = !matches!(event, MouseEvent::Exit); self.set_hover_state(next_hover, self_rc); if next_hover && !self.popup_visible.get() && matches!(event, MouseEvent::Moved { .. }) { self.schedule_show(self_rc); } // Observe without claiming: siblings still receive the event; the routing tracks // this item on its observers side-list and delivers Exit when the pointer leaves. InputEventFilterResult::ForwardAndObserve } fn input_event( self: Pin<&Self>, event: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, _: &mut MouseCursorInner, ) -> InputEventResult { if matches!(event, MouseEvent::Exit) { self.set_hover_state(false, _self_rc); } InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &InternalKeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, _backend: &mut ItemRendererRef, _self_rc: &ItemRc, _size: LogicalSize, ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl TooltipArea { fn schedule_show(self: Pin<&Self>, self_rc: &ItemRc) { let delay_ms = self.delay().max(0) as u64; if delay_ms == 0 { if self.has_hover() { self.show.call(&()); self.popup_visible.set(true); } return; } let self_weak = self_rc.downgrade(); // Start on the context this item's window belongs to, not on whichever one is // current: a component built with `new_with_context` must keep its timers there. let Some(window_adapter) = self_rc.window_adapter() else { return }; let ctx = crate::window::WindowInner::from_pub(window_adapter.window()).context(); self.timer.start_on( ctx, crate::timers::TimerMode::SingleShot, Duration::from_millis(delay_ms), move || { let Some(self_rc) = self_weak.upgrade() else { return }; let Some(tooltip_area) = self_rc.downcast::() else { return }; let tooltip_area = tooltip_area.as_pin_ref(); if tooltip_area.has_hover() { tooltip_area.show.call(&()); tooltip_area.popup_visible.set(true); } }, ); } fn hide_now(self: Pin<&Self>) { self.timer.stop(); if self.popup_visible.replace(false) { self.hide.call(&()); } } fn set_hover_state(self: Pin<&Self>, new_hover: bool, self_rc: &ItemRc) { let old_hover = self.has_hover(); if old_hover == new_hover { return; } Self::FIELD_OFFSETS.has_hover().apply_pin(self).set(new_hover); if new_hover { self.schedule_show(self_rc); } else { self.hide_now(); } } } impl ItemConsts for TooltipArea { const cached_rendering_data_offset: const_field_offset::FieldOffset< TooltipArea, CachedRenderingData, > = TooltipArea::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection(); } declare_item_vtable! { fn slint_get_TooltipAreaVTable() -> TooltipAreaVTable for TooltipArea } /// Expands to a builtin struct field's declared default value, /// or to the zero value of the field's type when no default is declared. /// Number literals for `Coord` fields are cast, because `Coord` is `f32` or `i32` /// depending on `cfg(slint_int_coord)`. macro_rules! builtin_struct_field_default { (Coord, $default:expr) => { ($default) as Coord }; ($field_type:ident, $default:expr) => { $default }; ($field_type:ident) => { ::core::default::Default::default() }; } /// Expands to the documentation text of a builtin struct field's declared default value: /// an intra-doc link to the variant for an enum value, plain code for a literal. /// The parentheses an enum value needs to be a single token tree are dropped. macro_rules! builtin_struct_field_default_doc { (($($default:tt)*)) => { builtin_struct_field_default_doc!($($default)*) }; ($enum:ident :: $value:ident) => { concat!("[`", stringify!($enum), "::", stringify!($value), "`]") }; ($default:literal) => { concat!("`", stringify!($default), "`") }; } macro_rules! declare_builtin_structs { ($( $(#[$struct_attr:meta])* $vis:vis struct $Name:ident { $( $(#[$field_attr:meta])* $field:ident : $field_type:ident $(= $field_default:tt)?, )* } )*) => { $( #[derive(Clone, Debug, PartialEq)] #[repr(C)] $(#[$struct_attr])* pub struct $Name { $( $(#[$field_attr])* $( #[doc = ""] #[doc = concat!("Defaults to ", builtin_struct_field_default_doc!($field_default), ".")] )? pub $field : $field_type, )* } // Not derived, so that the fields take their declared default values impl ::core::default::Default for $Name { fn default() -> Self { Self { $($field: builtin_struct_field_default!($field_type $(, $field_default)?),)* } } } )* }; } i_slint_common::for_each_builtin_structs!(declare_builtin_structs); #[test] fn builtin_struct_field_defaults() { // Fields without a declared default value take the zero value of their type, // like with derive(Default) let table_column = TableColumn::default(); assert_eq!(table_column.sort_order, SortOrder::Unsorted); assert_eq!(table_column.min_width, 0 as Coord); assert_eq!(table_column.horizontal_stretch, 0.0); assert_eq!(table_column.title, SharedString::default()); assert!(!KeyEvent::default().repeat); assert_eq!(PointerEvent::default().touch_finger_id, 0); // Fields with a declared default value take it let hints = InputMethodHints::default(); assert_eq!(hints.capitalization, CapitalizationMode::Sentences); assert!(hints.auto_correct); assert!(hints.auto_complete); } #[cfg(feature = "ffi")] #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_item_absolute_position( self_component: &vtable::VRc, self_index: u32, ) -> crate::lengths::LogicalPoint { let self_rc = ItemRc::new(self_component.clone(), self_index); // Map the item's own geometry origin through the ancestor transforms so the result is the // item's absolute position, not its parent's. self_rc.map_to_window(self_rc.geometry().origin) }