mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-30 23:27:22 +00:00

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.
170 lines
5.3 KiB
Rust
170 lines
5.3 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
|
|
|
/*!
|
|
This module contains the builtin Path related items.
|
|
|
|
When adding an item or a property, it needs to be kept in sync with different place.
|
|
Lookup the [`crate::items`] module documentation.
|
|
*/
|
|
|
|
use super::{FillRule, Item, ItemConsts, ItemRc, ItemRendererRef, RenderingResult};
|
|
use crate::graphics::{Brush, PathData, PathDataIterator};
|
|
use crate::input::{
|
|
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
|
|
KeyEventResult, MouseEvent,
|
|
};
|
|
use crate::item_rendering::CachedRenderingData;
|
|
|
|
use crate::layout::{LayoutInfo, Orientation};
|
|
use crate::lengths::{
|
|
LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector, PointLengths,
|
|
};
|
|
#[cfg(feature = "rtti")]
|
|
use crate::rtti::*;
|
|
use crate::window::WindowAdapter;
|
|
use crate::{Coord, Property};
|
|
use alloc::rc::Rc;
|
|
use const_field_offset::FieldOffsets;
|
|
use core::pin::Pin;
|
|
use euclid::num::Zero;
|
|
use i_slint_core_macros::*;
|
|
|
|
/// The implementation of the `Path` element
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets, Default, SlintElement)]
|
|
#[pin]
|
|
pub struct Path {
|
|
pub x: Property<LogicalLength>,
|
|
pub y: Property<LogicalLength>,
|
|
pub width: Property<LogicalLength>,
|
|
pub height: Property<LogicalLength>,
|
|
pub elements: Property<PathData>,
|
|
pub fill: Property<Brush>,
|
|
pub fill_rule: Property<FillRule>,
|
|
pub stroke: Property<Brush>,
|
|
pub stroke_width: Property<LogicalLength>,
|
|
pub viewbox_x: Property<f32>,
|
|
pub viewbox_y: Property<f32>,
|
|
pub viewbox_width: Property<f32>,
|
|
pub viewbox_height: Property<f32>,
|
|
pub clip: Property<bool>,
|
|
pub cached_rendering_data: CachedRenderingData,
|
|
}
|
|
|
|
impl Item for Path {
|
|
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 let Some(pos) = event.position() {
|
|
if self.clip()
|
|
&& (pos.x < 0 as _
|
|
|| pos.y < 0 as _
|
|
|| 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 {
|
|
let clip = self.clip();
|
|
if clip {
|
|
(*backend).save_state();
|
|
(*backend).combine_clip(self.geometry(), LogicalLength::zero(), LogicalLength::zero());
|
|
}
|
|
(*backend).draw_path(self, self_rc);
|
|
if clip {
|
|
(*backend).restore_state();
|
|
}
|
|
RenderingResult::ContinueRenderingChildren
|
|
}
|
|
}
|
|
|
|
impl Path {
|
|
/// Returns an iterator of the events of the path and an offset, so that the
|
|
/// shape fits into the width/height of the path while respecting the stroke
|
|
/// width.
|
|
pub fn fitted_path_events(self: Pin<&Self>) -> (LogicalVector, PathDataIterator) {
|
|
let stroke_width = self.stroke_width();
|
|
let bounds_width = (self.width() - stroke_width).max(LogicalLength::zero());
|
|
let bounds_height = (self.height() - stroke_width).max(LogicalLength::zero());
|
|
let offset =
|
|
LogicalVector::from_lengths(stroke_width / 2 as Coord, stroke_width / 2 as Coord);
|
|
|
|
let viewbox_width = self.viewbox_width();
|
|
let viewbox_height = self.viewbox_height();
|
|
|
|
let mut elements_iter = self.elements().iter();
|
|
|
|
let maybe_viewbox = if viewbox_width > 0. && viewbox_height > 0. {
|
|
Some(
|
|
euclid::rect(self.viewbox_x(), self.viewbox_y(), viewbox_width, viewbox_height)
|
|
.to_box2d(),
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
elements_iter.fit(bounds_width.get() as _, bounds_height.get() as _, maybe_viewbox);
|
|
(offset, elements_iter)
|
|
}
|
|
}
|
|
|
|
impl ItemConsts for Path {
|
|
const cached_rendering_data_offset: const_field_offset::FieldOffset<Path, CachedRenderingData> =
|
|
Path::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
|
}
|