slint/internal/core/items.rs
Simon Hausmann c16253d29f Use Property<LogicalLength> instead of Property<Coord>
This removes the special code for the generated property getters and
ensures type safety in the run-time library for property value setting.

In the Rust generated code we continue to do arithmetic on the scalar
values, that means we immediately extract the scalar, do arithmetic and
rely on the compiler to only allow compatible units.

Danger zone alert: In the interpreter Value::Number can now be converted
to LogicalLength as-is.
2022-10-24 12:49:37 +02:00

1343 lines
40 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only 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)]
pub enum RenderingResult {
ContinueRenderingChildren,
ContinueRenderingWithoutChildren,
}
impl Default for RenderingResult {
fn default() -> Self {
Self::ContinueRenderingChildren
}
}
/// 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<VRef<ItemVTable>>, window_adapter: &WindowAdapterRc),
/// Returns the geometry of this item (relative to its parent item)
pub geometry: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> 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<VRef<ItemVTable>>,
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<VRef<ItemVTable>>,
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<VRef<ItemVTable>>,
MouseEvent,
window_adapter: &WindowAdapterRc,
self_rc: &ItemRc,
) -> InputEventResult,
pub focus_event: extern "C" fn(
core::pin::Pin<VRef<ItemVTable>>,
&FocusEvent,
window_adapter: &WindowAdapterRc,
self_rc: &ItemRc,
) -> FocusEventResult,
pub key_event: extern "C" fn(
core::pin::Pin<VRef<ItemVTable>>,
&KeyEvent,
window_adapter: &WindowAdapterRc,
self_rc: &ItemRc,
) -> KeyEventResult,
pub render: extern "C" fn(
core::pin::Pin<VRef<ItemVTable>>,
backend: &mut ItemRendererRef,
self_rc: &ItemRc,
) -> RenderingResult,
}
/// Alias for `vtable::VRef<ItemVTable>` 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 the `Rectangle` element
pub struct Rectangle {
pub background: Property<Brush>,
pub x: Property<LogicalLength>,
pub y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for Rectangle {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(
self: Pin<&Self>,
_: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_rectangle(self, self_rc);
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<Brush>,
pub x: Property<LogicalLength>,
pub y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub border_width: Property<LogicalLength>,
pub border_radius: Property<LogicalLength>,
pub border_color: Property<Brush>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for BorderRectangle {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(
self: Pin<&Self>,
_: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_border_rectangle(self, self_rc);
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<LogicalLength>,
pub y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub enabled: Property<bool>,
/// FIXME: We should annotate this as an "output" property.
pub pressed: Property<bool>,
pub has_hover: Property<bool>,
/// FIXME: there should be just one property for the point instead of two.
/// Could even be merged with pressed in a `Property<Option<Point>>` (of course, in the
/// implementation item only, for the compiler it would stay separate properties)
pub pressed_x: Property<LogicalLength>,
pub pressed_y: Property<LogicalLength>,
/// FIXME: should maybe be as parameter to the mouse event instead. Or at least just one property
pub mouse_x: Property<LogicalLength>,
pub mouse_y: Property<LogicalLength>,
pub mouse_cursor: Property<MouseCursor>,
pub clicked: Callback<VoidArg>,
pub moved: Callback<VoidArg>,
pub pointer_event: Callback<PointerEventArg>,
/// FIXME: remove this
pub cached_rendering_data: CachedRenderingData,
/// true when we are currently grabbing the mouse
grabbed: Cell<bool>,
}
impl Item for TouchArea {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo::default()
}
fn input_event_filter_before_children(
self: Pin<&Self>,
event: MouseEvent,
window_adapter: &Rc<dyn WindowAdapter>,
_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 {
window_adapter.set_mouse_cursor(self.mouse_cursor());
}
InputEventFilterResult::ForwardAndInterceptGrab
}
fn input_event(
self: Pin<&Self>,
event: MouseEvent,
window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
if matches!(event, MouseEvent::Exit) {
Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
window_adapter.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::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::None,
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<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
_backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
) -> 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<LogicalLength>,
pub y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub enabled: Property<bool>,
pub has_focus: Property<bool>,
pub key_pressed: Callback<KeyEventArg, EventResult>,
pub key_released: Callback<KeyEventArg, EventResult>,
/// FIXME: remove this
pub cached_rendering_data: CachedRenderingData,
}
impl Item for FocusScope {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo::default()
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardEvent
}
fn input_event(
self: Pin<&Self>,
event: MouseEvent,
window_adapter: &Rc<dyn WindowAdapter>,
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::EventIgnored
}
fn key_event(
self: Pin<&Self>,
event: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_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<dyn WindowAdapter>,
_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,
) -> 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<LogicalLength>,
pub y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub border_radius: Property<LogicalLength>,
pub border_width: Property<LogicalLength>,
pub cached_rendering_data: CachedRenderingData,
pub clip: Property<bool>,
}
impl Item for Clip {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
event: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_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<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(
self: Pin<&Self>,
_: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).visit_clip(self, self_rc)
}
}
impl ItemConsts for Clip {
const cached_rendering_data_offset: const_field_offset::FieldOffset<Clip, CachedRenderingData> =
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<LogicalLength>,
pub y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub opacity: Property<f32>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for Opacity {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(
self: Pin<&Self>,
_: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
self_rc: &ItemRc,
) -> RenderingResult {
backend.visit_opacity(self, self_rc)
}
}
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<LogicalLength>,
pub y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub cache_rendering_hint: Property<bool>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for Layer {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(
self: Pin<&Self>,
_: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
self_rc: &ItemRc,
) -> RenderingResult {
backend.visit_layer(self, self_rc)
}
}
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<LogicalLength>,
pub y: Property<LogicalLength>,
pub rotation_angle: Property<f32>,
pub rotation_origin_x: Property<LogicalLength>,
pub rotation_origin_y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for Rotate {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(
self: Pin<&Self>,
_: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
) -> 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<LogicalLength>,
pub height: Property<LogicalLength>,
pub background: Property<Brush>,
pub title: Property<SharedString>,
pub no_frame: Property<bool>,
pub icon: Property<crate::graphics::Image>,
pub default_font_family: Property<SharedString>,
pub default_font_size: Property<LogicalLength>,
pub default_font_weight: Property<i32>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for WindowItem {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo::default()
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_event: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(
self: Pin<&Self>,
_: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
_backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
) -> RenderingResult {
RenderingResult::ContinueRenderingChildren
}
}
impl WindowItem {
pub fn font_family(self: Pin<&Self>) -> Option<SharedString> {
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<LogicalLength> {
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<i32> {
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, CachedRenderingData> =
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<LogicalLength>,
pub y: Property<LogicalLength>,
pub width: Property<LogicalLength>,
pub height: Property<LogicalLength>,
pub border_radius: Property<LogicalLength>,
// Shadow specific properties
pub offset_x: Property<LogicalLength>,
pub offset_y: Property<LogicalLength>,
pub color: Property<Color>,
pub blur: Property<LogicalLength>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for BoxShadow {
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
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<dyn WindowAdapter>,
) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_event: MouseEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(
self: Pin<&Self>,
_: &KeyEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(
self: Pin<&Self>,
_: &FocusEvent,
_window_adapter: &Rc<dyn WindowAdapter>,
_self_rc: &ItemRc,
) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_box_shadow(self, self_rc);
RenderingResult::ContinueRenderingChildren
}
}
impl ItemConsts for BoxShadow {
const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
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,
}