slint/internal/core/items.rs
Olivier Goffart 63b38d7f89 Move the Flickable item and implementation in the same module
The flickable module was created to hold the implementation for the Flickable
at a time were all the items lived in the items.rs module.
Now that some items moved into sub modules of items, we can also move the Flickable
in such module, alongside its implementation
2022-04-22 15:26:13 +02:00

1160 lines
35 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, Rect};
use crate::input::{
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
KeyEventResult, MouseEvent,
};
use crate::item_rendering::CachedRenderingData;
pub use crate::item_tree::ItemRc;
use crate::layout::{LayoutInfo, Orientation};
#[cfg(feature = "rtti")]
use crate::rtti::*;
use crate::window::WindowRc;
use crate::{Callback, Coord, Property, SharedString};
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: &WindowRc),
/// Returns the geometry of this item (relative to its parent item)
pub geometry: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> Rect,
/// 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: &WindowRc,
) -> 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: &WindowRc,
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: &WindowRc,
self_rc: &ItemRc,
) -> InputEventResult,
pub focus_event: extern "C" fn(
core::pin::Pin<VRef<ItemVTable>>,
&FocusEvent,
window: &WindowRc,
) -> FocusEventResult,
pub key_event: extern "C" fn(
core::pin::Pin<VRef<ItemVTable>>,
&KeyEvent,
window: &WindowRc,
) -> 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<Coord>,
pub y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for Rectangle {
fn init(self: Pin<&Self>, _window: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_rectangle(self);
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<Coord>,
pub y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
pub border_width: Property<Coord>,
pub border_radius: Property<Coord>,
pub border_color: Property<Brush>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for BorderRectangle {
fn init(self: Pin<&Self>, _window: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_border_rectangle(self);
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<Coord>,
pub y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
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<Coord>,
pub pressed_y: Property<Coord>,
/// FIXME: should maybe be as parameter to the mouse event instead. Or at least just one property
pub mouse_x: Property<Coord>,
pub mouse_y: Property<Coord>,
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: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo::default()
}
fn input_event_filter_before_children(
self: Pin<&Self>,
event: MouseEvent,
window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
if !self.enabled() {
return InputEventFilterResult::ForwardAndIgnore;
}
if let Some(pos) = event.pos() {
Self::FIELD_OFFSETS.mouse_x.apply_pin(self).set(pos.x);
Self::FIELD_OFFSETS.mouse_y.apply_pin(self).set(pos.y);
}
let hovering = !matches!(event, MouseEvent::MouseExit);
Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(hovering);
if hovering {
window.set_mouse_cursor(self.mouse_cursor());
}
InputEventFilterResult::ForwardAndInterceptGrab
}
fn input_event(
self: Pin<&Self>,
event: MouseEvent,
window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
if matches!(event, MouseEvent::MouseExit) {
Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
window.set_mouse_cursor(MouseCursor::default);
}
if !self.enabled() {
return InputEventResult::EventIgnored;
}
let result = if let MouseEvent::MouseReleased { pos, button } = event {
if button == PointerEventButton::left
&& euclid::rect(0 as Coord, 0 as Coord, self.width(), self.height()).contains(pos)
{
Self::FIELD_OFFSETS.clicked.apply_pin(self).call(&());
}
InputEventResult::EventAccepted
} else {
InputEventResult::GrabMouse
};
match event {
MouseEvent::MousePressed { pos, button } => {
self.grabbed.set(true);
if button == PointerEventButton::left {
Self::FIELD_OFFSETS.pressed_x.apply_pin(self).set(pos.x);
Self::FIELD_OFFSETS.pressed_y.apply_pin(self).set(pos.y);
Self::FIELD_OFFSETS.pressed.apply_pin(self).set(true);
}
Self::FIELD_OFFSETS
.pointer_event
.apply_pin(self)
.call(&(PointerEvent { button, kind: PointerEventKind::down },));
}
MouseEvent::MouseExit => {
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::MouseReleased { 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::MouseMoved { .. } => {
return if self.grabbed.get() {
Self::FIELD_OFFSETS.moved.apply_pin(self).call(&());
InputEventResult::GrabMouse
} else {
InputEventResult::EventAccepted
}
}
MouseEvent::MouseWheel { .. } => {
return if self.grabbed.get() {
InputEventResult::GrabMouse
} else {
InputEventResult::EventAccepted
}
}
};
result
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> 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<Coord>,
pub y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
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: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo::default()
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardEvent
}
fn input_event(
self: Pin<&Self>,
event: MouseEvent,
window: &WindowRc,
self_rc: &ItemRc,
) -> InputEventResult {
if self.enabled() && matches!(event, MouseEvent::MousePressed { .. }) && !self.has_focus() {
window.clone().set_focus_item(self_rc);
}
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, event: &KeyEvent, _window: &WindowRc) -> 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(),))
}
};
match r {
EventResult::accept => KeyEventResult::EventAccepted,
EventResult::reject => KeyEventResult::EventIgnored,
}
}
fn focus_event(self: Pin<&Self>, event: &FocusEvent, _window: &WindowRc) -> 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<Coord>,
pub y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
pub border_radius: Property<Coord>,
pub border_width: Property<Coord>,
pub cached_rendering_data: CachedRenderingData,
pub clip: Property<bool>,
}
impl Item for Clip {
fn init(self: Pin<&Self>, _window: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
event: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
if let Some(pos) = event.pos() {
if self.clip()
&& (pos.x < 0 as Coord
|| pos.y < 0 as Coord
|| pos.x > self.width()
|| pos.y > self.height())
{
return InputEventFilterResult::Intercept;
}
}
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> 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<Coord>,
pub y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
pub opacity: Property<f32>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for Opacity {
fn init(self: Pin<&Self>, _window: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> 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<Coord>,
pub y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
pub cache_rendering_hint: Property<bool>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for Layer {
fn init(self: Pin<&Self>, _window: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> 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 angle: Property<f32>,
pub origin_x: Property<Coord>,
pub origin_y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for Rotate {
fn init(self: Pin<&Self>, _window: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(0, 0, 0, 0).cast()
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
) -> RenderingResult {
(*backend).translate(self.origin_x(), self.origin_y());
(*backend).rotate(self.angle());
(*backend).translate(-self.origin_x(), -self.origin_y());
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<Coord>,
pub height: Property<Coord>,
pub background: Property<Color>,
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<Coord>,
pub default_font_weight: Property<i32>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for WindowItem {
fn init(self: Pin<&Self>, _window: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(0 as _, 0 as _, self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo::default()
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_event: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
_backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
) -> RenderingResult {
RenderingResult::ContinueRenderingChildren
}
}
impl WindowItem {
/// Returns the font properties that can be used as defaults for child items
pub fn default_font_properties(self: Pin<&Self>) -> crate::graphics::FontRequest {
crate::graphics::FontRequest {
family: {
let maybe_family = self.default_font_family();
if !maybe_family.is_empty() {
Some(maybe_family)
} else {
None
}
},
pixel_size: {
let font_size = self.default_font_size();
if font_size <= 0 as Coord {
None
} else {
Some(font_size)
}
},
weight: {
let font_weight = self.default_font_weight();
if font_weight == 0 {
None
} else {
Some(font_weight)
}
},
..Default::default()
}
}
}
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<Coord>,
pub y: Property<Coord>,
pub width: Property<Coord>,
pub height: Property<Coord>,
pub border_radius: Property<Coord>,
// Shadow specific properties
pub offset_x: Property<Coord>,
pub offset_y: Property<Coord>,
pub color: Property<Color>,
pub blur: Property<Coord>,
pub cached_rendering_data: CachedRenderingData,
}
impl Item for BoxShadow {
fn init(self: Pin<&Self>, _window: &WindowRc) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layout_info(self: Pin<&Self>, _orientation: Orientation, _window: &WindowRc) -> LayoutInfo {
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
}
fn input_event_filter_before_children(
self: Pin<&Self>,
_: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventFilterResult {
InputEventFilterResult::ForwardAndIgnore
}
fn input_event(
self: Pin<&Self>,
_event: MouseEvent,
_window: &WindowRc,
_self_rc: &ItemRc,
) -> InputEventResult {
InputEventResult::EventIgnored
}
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &WindowRc) -> KeyEventResult {
KeyEventResult::EventIgnored
}
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &WindowRc) -> FocusEventResult {
FocusEventResult::FocusIgnored
}
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_box_shadow(self);
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)]
#[allow(non_camel_case_types)]
$(#[$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 key event sent by the windowing system.
#[derive(Debug, Clone, PartialEq, Default)]
#[repr(C)]
pub struct PointerEvent {
pub button: PointerEventButton,
pub kind: PointerEventKind,
}