// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.0 OR LicenseRef-Slint-commercial // cSpell: ignore nesw /*! 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: builtins.slint - In the interpreter (new item only): dynamic_component.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::graphics::{Brush, Color, Point}; use crate::input::{ FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent, KeyEventResult, KeyEventType, MouseEvent, }; use crate::item_rendering::CachedRenderingData; pub use crate::item_tree::ItemRc; use crate::layout::{LayoutInfo, Orientation}; use crate::lengths::{ LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector, PointLengths, }; #[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::pin::Pin; use i_slint_core_macros::*; use vtable::*; mod flickable; pub use flickable::*; mod text; pub use text::*; mod image; pub use self::image::*; #[cfg(feature = "std")] mod path; #[cfg(feature = "std")] pub use path::*; /// 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,); type PointerEventArg = (PointerEvent,); type PointArg = (Point,); #[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! { #[no_mangle] pub static $item_vtable_ty for $item_ty } #[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! { #[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. #[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>), /// Returns the geometry of this item (relative to its parent item) pub geometry: extern "C" fn(core::pin::Pin>) -> LogicalRect, /// offset in bytes from the *const ItemImpl. /// isize::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 pub layout_info: extern "C" fn( core::pin::Pin>, orientation: Orientation, window_adapter: &WindowAdapterRc, ) -> 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. pub input_event_filter_before_children: extern "C" fn( core::pin::Pin>, MouseEvent, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, ) -> 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, ) -> InputEventResult, pub focus_event: extern "C" fn( core::pin::Pin>, &FocusEvent, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, ) -> FocusEventResult, pub key_event: extern "C" fn( core::pin::Pin>, &KeyEvent, window_adapter: &WindowAdapterRc, self_rc: &ItemRc, ) -> KeyEventResult, pub render: extern "C" fn( core::pin::Pin>, backend: &mut ItemRendererRef, self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult, } /// 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 x: Property, pub y: Property, pub width: Property, pub height: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Empty { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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 } } 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 x: Property, pub y: Property, pub width: Property, pub height: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Rectangle { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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); RenderingResult::ContinueRenderingChildren } } 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 `BorderRectangle` element pub struct BorderRectangle { pub background: Property, pub x: Property, pub y: Property, pub width: Property, pub height: Property, pub border_width: Property, pub border_radius: Property, pub border_color: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for BorderRectangle { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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); RenderingResult::ContinueRenderingChildren } } 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 } /// The implementation of the `TouchArea` element #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct TouchArea { pub x: Property, pub y: Property, pub width: Property, pub height: Property, pub enabled: Property, /// FIXME: We should annotate this as an "output" property. pub pressed: Property, pub has_hover: Property, /// FIXME: there should be just one property for the point instead of two. /// Could even be merged with pressed in a `Property>` (of course, in the /// implementation item only, for the compiler it would stay separate properties) pub pressed_x: Property, pub pressed_y: Property, /// FIXME: should maybe be as parameter to the mouse event instead. Or at least just one property pub mouse_x: Property, pub mouse_y: Property, pub mouse_cursor: Property, pub clicked: Callback, pub moved: Callback, pub pointer_event: Callback, /// FIXME: remove this pub cached_rendering_data: CachedRenderingData, /// true when we are currently grabbing the mouse grabbed: Cell, } impl Item for TouchArea { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, event: MouseEvent, window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { if !self.enabled() { 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 hovering = !matches!(event, MouseEvent::Exit); Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(hovering); if hovering { if let Some(x) = window_adapter.internal(crate::InternalToken) { x.set_mouse_cursor(self.mouse_cursor()); } } InputEventFilterResult::ForwardAndInterceptGrab } fn input_event( self: Pin<&Self>, event: MouseEvent, window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { if matches!(event, MouseEvent::Exit) { Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false); if let Some(x) = window_adapter.internal(crate::InternalToken) { x.set_mouse_cursor(MouseCursor::Default); } } if !self.enabled() { return InputEventResult::EventIgnored; } let result = if let MouseEvent::Released { position, button, .. } = event { if button == PointerEventButton::Left && LogicalRect::new( LogicalPoint::default(), LogicalSize::from_lengths(self.width(), self.height()), ) .contains(position) && self.pressed() { Self::FIELD_OFFSETS.clicked.apply_pin(self).call(&()); } InputEventResult::EventAccepted } else { InputEventResult::GrabMouse }; match event { MouseEvent::Pressed { position, button, .. } => { self.grabbed.set(true); if button == PointerEventButton::Left { Self::FIELD_OFFSETS.pressed_x.apply_pin(self).set(position.x_length()); Self::FIELD_OFFSETS.pressed_y.apply_pin(self).set(position.y_length()); Self::FIELD_OFFSETS.pressed.apply_pin(self).set(true); } Self::FIELD_OFFSETS .pointer_event .apply_pin(self) .call(&(PointerEvent { button, kind: PointerEventKind::Down },)); } MouseEvent::Exit => { Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false); if self.grabbed.replace(false) { Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent { button: PointerEventButton::Other, kind: PointerEventKind::Cancel, },)); } } MouseEvent::Released { button, .. } => { self.grabbed.set(false); if button == PointerEventButton::Left { Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false); } Self::FIELD_OFFSETS .pointer_event .apply_pin(self) .call(&(PointerEvent { button, kind: PointerEventKind::Up },)); } MouseEvent::Moved { .. } => { return if self.grabbed.get() { Self::FIELD_OFFSETS.moved.apply_pin(self).call(&()); InputEventResult::GrabMouse } else { InputEventResult::EventAccepted } } MouseEvent::Wheel { .. } => { return if self.grabbed.get() { InputEventResult::GrabMouse } else { InputEventResult::EventAccepted } } }; result } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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 } } impl ItemConsts for TouchArea { const cached_rendering_data_offset: const_field_offset::FieldOffset< TouchArea, CachedRenderingData, > = TouchArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); } declare_item_vtable! { fn slint_get_TouchAreaVTable() -> TouchAreaVTable for TouchArea } /// A runtime item that exposes key #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct FocusScope { pub x: Property, pub y: Property, pub width: Property, pub height: Property, pub enabled: Property, pub has_focus: Property, pub key_pressed: Callback, pub key_released: Callback, /// FIXME: remove this pub cached_rendering_data: CachedRenderingData, } impl Item for FocusScope { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardEvent } fn input_event( self: Pin<&Self>, event: MouseEvent, window_adapter: &Rc, self_rc: &ItemRc, ) -> InputEventResult { if self.enabled() && matches!(event, MouseEvent::Pressed { .. }) && !self.has_focus() { WindowInner::from_pub(window_adapter.window()).set_focus_item(self_rc); InputEventResult::EventAccepted } else { InputEventResult::EventIgnored } } fn key_event( self: Pin<&Self>, event: &KeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { let r = match event.event_type { KeyEventType::KeyPressed => { Self::FIELD_OFFSETS.key_pressed.apply_pin(self).call(&(event.clone(),)) } KeyEventType::KeyReleased => { Self::FIELD_OFFSETS.key_released.apply_pin(self).call(&(event.clone(),)) } KeyEventType::UpdateComposition | KeyEventType::CommitComposition => { EventResult::Reject } }; match r { EventResult::Accept => KeyEventResult::EventAccepted, EventResult::Reject => KeyEventResult::EventIgnored, } } fn focus_event( self: Pin<&Self>, event: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { if !self.enabled() { return FocusEventResult::FocusIgnored; } match event { FocusEvent::FocusIn | FocusEvent::WindowReceivedFocus => { self.has_focus.set(true); } FocusEvent::FocusOut | FocusEvent::WindowLostFocus => { self.has_focus.set(false); } } FocusEventResult::FocusAccepted } fn render( self: Pin<&Self>, _backend: &mut ItemRendererRef, _self_rc: &ItemRc, _size: LogicalSize, ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } } impl ItemConsts for FocusScope { const cached_rendering_data_offset: const_field_offset::FieldOffset< FocusScope, CachedRenderingData, > = FocusScope::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); } declare_item_vtable! { fn slint_get_FocusScopeVTable() -> FocusScopeVTable for FocusScope } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of the `Clip` element pub struct Clip { pub x: Property, pub y: Property, pub width: Property, pub height: Property, pub border_radius: Property, pub border_width: Property, pub cached_rendering_data: CachedRenderingData, pub clip: Property, } impl Item for Clip { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, event: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { if let Some(pos) = event.position() { if self.clip() && (pos.x < 0 as Coord || pos.y < 0 as Coord || pos.x_length() > self.width() || pos.y_length() > self.height()) { return InputEventFilterResult::Intercept; } } InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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) } } 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 x: Property, pub y: Property, pub width: Property, pub height: Property, pub opacity: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Opacity { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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) } } 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 { // FIXME: this element shouldn't need these geometry property pub x: Property, pub y: Property, pub width: Property, pub height: Property, pub cache_rendering_hint: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Layer { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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) } } 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 `Rotate` element pub struct Rotate { pub x: Property, pub y: Property, pub rotation_angle: Property, pub rotation_origin_x: Property, pub rotation_origin_y: Property, pub width: Property, pub height: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for Rotate { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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 = LogicalVector::from_lengths(self.rotation_origin_x(), self.rotation_origin_y()); (*backend).translate(origin); (*backend).rotate(self.rotation_angle()); (*backend).translate(-origin); RenderingResult::ContinueRenderingChildren } } impl ItemConsts for Rotate { const cached_rendering_data_offset: const_field_offset::FieldOffset< Rotate, CachedRenderingData, > = Rotate::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); } declare_item_vtable! { fn slint_get_RotateVTable() -> RotateVTable for Rotate } declare_item_vtable! { fn slint_get_FlickableVTable() -> FlickableVTable for Flickable } /// The implementation of the `PropertyAnimation` element #[repr(C)] #[derive(FieldOffsets, SlintElement, Clone, Debug)] #[pin] pub struct PropertyAnimation { #[rtti_field] pub delay: i32, #[rtti_field] pub duration: i32, #[rtti_field] pub iteration_count: f32, #[rtti_field] pub easing: crate::animations::EasingCurve, } impl Default for PropertyAnimation { fn default() -> Self { // Defaults for PropertyAnimation are defined here (for internal Rust code doing programmatic animations) // as well as in `builtins.slint` (for generated C++ and Rust code) Self { delay: 0, duration: 0, iteration_count: 1., easing: Default::default() } } } /// The implementation of the `Window` element #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct WindowItem { pub width: Property, pub height: Property, pub background: Property, pub title: Property, pub no_frame: Property, pub always_on_top: 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>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::default(), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo::default() } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _event: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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 } } 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) } } } impl ItemConsts for WindowItem { const cached_rendering_data_offset: const_field_offset::FieldOffset = Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); } declare_item_vtable! { fn slint_get_WindowItemVTable() -> WindowItemVTable for WindowItem } /// The implementation of the `BoxShadow` element #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct BoxShadow { // Rectangle properties pub x: Property, pub y: Property, pub width: Property, pub height: Property, pub border_radius: Property, // Shadow specific properties pub offset_x: Property, pub offset_y: Property, pub color: Property, pub blur: Property, pub cached_rendering_data: CachedRenderingData, } impl Item for BoxShadow { fn init(self: Pin<&Self>) {} fn geometry(self: Pin<&Self>) -> LogicalRect { LogicalRect::new( LogicalPoint::from_lengths(self.x(), self.y()), LogicalSize::from_lengths(self.width(), self.height()), ) } fn layout_info( self: Pin<&Self>, _orientation: Orientation, _window_adapter: &Rc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _event: MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _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 } } 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_TextVTable() -> TextVTable for Text } 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 = "std")] declare_item_vtable! { fn slint_get_PathVTable() -> PathVTable for Path } macro_rules! declare_enums { ($( $(#[$enum_doc:meta])* enum $Name:ident { $( $(#[$value_doc:meta])* $Value:ident,)* })*) => { $( #[derive(Copy, Clone, Debug, PartialEq, Eq, strum::EnumString, strum::Display, Hash)] #[repr(C)] #[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); /// Represents a Pointer event sent by the windowing system. #[derive(Debug, Clone, PartialEq, Default)] #[repr(C)] pub struct PointerEvent { pub button: PointerEventButton, pub kind: PointerEventKind, } #[cfg(feature = "ffi")] #[no_mangle] pub unsafe extern "C" fn slint_item_absolute_position( self_component: &vtable::VRc, self_index: usize, ) -> LogicalPoint { let self_rc = ItemRc::new(self_component.clone(), self_index); self_rc.map_to_window(Default::default()) }