diff --git a/api/rs/slint/tests/partial_renderer.rs b/api/rs/slint/tests/partial_renderer.rs index aee4c0f62b1..40efa85c8cb 100644 --- a/api/rs/slint/tests/partial_renderer.rs +++ b/api/rs/slint/tests/partial_renderer.rs @@ -501,3 +501,129 @@ fn window_background() { ui.set_c(slint::Color::from_rgb_u8(45, 12, 13)); assert!(!window.draw_if_needed(|_| { unreachable!() })); } + +#[test] +fn touch_area_doesnt_cause_redraw() { + slint::slint! { + export component Ui inherits Window { + in property c: yellow; + in property touch-area-1-x <=> ta1.x; + in property touch-area-2-x <=> ta2.x; + in property sole-pixel-color: red; + background: black; + ta1 := TouchArea { + x: 10px; + y: 0px; + width: 20px; + height: 40px; + Rectangle { + x: 1phx; + y: 20phx; + width: 15phx; + height: 17phx; + background: c; + } + } + ta2 := TouchArea { + x: 10px; + y: 0px; + width: 20px; + height: 40px; + } + sole-pixel := Rectangle { + x: 60px; + y: 0px; + width: 1px; + height: 1px; + background: sole-pixel-color; + } + } + } + + slint::platform::set_platform(Box::new(TestPlatform)).ok(); + let ui = Ui::new().unwrap(); + let window = WINDOW.with(|x| x.clone()); + window.set_size(slint::PhysicalSize::new(180, 260)); + ui.show().unwrap(); + assert!(window.draw_if_needed(|renderer| { + do_test_render_region(renderer, 0, 0, 180, 260); + })); + assert!(!window.draw_if_needed(|_| { unreachable!() })); + ui.set_c(slint::Color::from_rgb_u8(45, 12, 13)); + assert!(window.draw_if_needed(|renderer| { + do_test_render_region(renderer, 10 + 1, 20, 10 + 1 + 15, 20 + 17); + })); + assert!(!window.draw_if_needed(|_| { unreachable!() })); + ui.set_touch_area_1_x(20.); + assert!(window.draw_if_needed(|renderer| { + do_test_render_region(renderer, 10 + 1, 20, 10 + 1 + 15 + 10, 20 + 17); + })); + assert!(!window.draw_if_needed(|_| { unreachable!() })); + ui.set_touch_area_2_x(20.); + ui.set_sole_pixel_color(slint::Color::from_rgb_u8(45, 12, 13)); + // Moving the touch area should not cause it to be redrawn. + assert!(window.draw_if_needed(|renderer| { + do_test_render_region(renderer, 60, 0, 61, 1); + })); +} + +#[test] +fn shadow_redraw_beyond_geometry() { + slint::slint! { + export component Ui inherits Window { + in property x-pos: 10px; + Rectangle { + x: root.x-pos; + y: 10px; + width: 20px; + height: 20px; + drop-shadow-blur: 5px; + drop-shadow-offset-x: 15px; + drop-shadow-offset-y: 5px; + drop-shadow-color: red; + } + } + } + + slint::platform::set_platform(Box::new(TestPlatform)).ok(); + + let window = SKIA_WINDOW.with(|w| w.clone()); + NEXT_WINDOW_CHOICE.with(|choice| { + *choice.borrow_mut() = Some(window.clone()); + }); + let ui = Ui::new().unwrap(); + window.set_size(slint::PhysicalSize::new(250, 250).into()); + ui.show().unwrap(); + + assert!(window.draw_if_needed()); + assert_eq!( + window.last_dirty_region_bounding_box_size(), + Some(slint::LogicalSize { width: 250., height: 250. }) + ); + assert_eq!( + window.last_dirty_region_bounding_box_origin(), + Some(slint::LogicalPosition { x: 0., y: 0. }) + ); + + assert!(!window.draw_if_needed()); + + ui.set_x_pos(20.); + + assert!(window.draw_if_needed()); + + let shadow_width = /* rect width */ 20. + 2. * /* blur */ 5.; + let move_delta = 10.; + let shadow_height = /* rect height */ 20. + 2. * /*blur */ 5.; + + let old_shadow_x = /* rect x */ 10. + /* shadow offset */ 15. - /* blur */ 5.; + let old_shadow_y = /* rect y */ 10. + /* shadow offset */ 5. - /* blur */ 5.; + + assert_eq!( + window.last_dirty_region_bounding_box_size(), + Some(slint::LogicalSize { width: shadow_width + move_delta, height: shadow_height }) + ); + assert_eq!( + window.last_dirty_region_bounding_box_origin(), + Some(slint::LogicalPosition { x: old_shadow_x, y: old_shadow_y }) + ); +} diff --git a/internal/backends/qt/qt_widgets/button.rs b/internal/backends/qt/qt_widgets/button.rs index fd72969e464..1aaf5b6a3b2 100644 --- a/internal/backends/qt/qt_widgets/button.rs +++ b/internal/backends/qt/qt_widgets/button.rs @@ -422,6 +422,15 @@ impl Item for NativeButton { qApp->style()->drawControl(QStyle::CE_PushButton, &option, painter->get(), widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeButton { diff --git a/internal/backends/qt/qt_widgets/checkbox.rs b/internal/backends/qt/qt_widgets/checkbox.rs index 1c1dedf730f..fe677adf43f 100644 --- a/internal/backends/qt/qt_widgets/checkbox.rs +++ b/internal/backends/qt/qt_widgets/checkbox.rs @@ -174,6 +174,15 @@ impl Item for NativeCheckBox { qApp->style()->drawControl(QStyle::CE_CheckBox, &option, painter->get(), widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeCheckBox { diff --git a/internal/backends/qt/qt_widgets/combobox.rs b/internal/backends/qt/qt_widgets/combobox.rs index c6059397c4c..6f9fffc22ab 100644 --- a/internal/backends/qt/qt_widgets/combobox.rs +++ b/internal/backends/qt/qt_widgets/combobox.rs @@ -140,6 +140,15 @@ impl Item for NativeComboBox { qApp->style()->drawControl(QStyle::CE_ComboBoxLabel, &option, painter->get(), widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeComboBox { @@ -250,6 +259,15 @@ impl Item for NativeComboBoxPopup { style->drawControl(QStyle::CE_ShapedFrame, &option, painter->get(), widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeComboBoxPopup { diff --git a/internal/backends/qt/qt_widgets/groupbox.rs b/internal/backends/qt/qt_widgets/groupbox.rs index 98095eec2fd..186987901a3 100644 --- a/internal/backends/qt/qt_widgets/groupbox.rs +++ b/internal/backends/qt/qt_widgets/groupbox.rs @@ -234,6 +234,15 @@ impl Item for NativeGroupBox { qApp->style()->drawComplexControl(QStyle::CC_GroupBox, &option, painter->get(), widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeGroupBox { diff --git a/internal/backends/qt/qt_widgets/lineedit.rs b/internal/backends/qt/qt_widgets/lineedit.rs index 92681623838..aa7d74e6ace 100644 --- a/internal/backends/qt/qt_widgets/lineedit.rs +++ b/internal/backends/qt/qt_widgets/lineedit.rs @@ -155,6 +155,15 @@ impl Item for NativeLineEdit { qApp->style()->drawPrimitive(QStyle::PE_PanelLineEdit, &option, painter->get(), widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeLineEdit { diff --git a/internal/backends/qt/qt_widgets/listviewitem.rs b/internal/backends/qt/qt_widgets/listviewitem.rs index fa0ee2ef285..31a88b7ab6c 100644 --- a/internal/backends/qt/qt_widgets/listviewitem.rs +++ b/internal/backends/qt/qt_widgets/listviewitem.rs @@ -193,6 +193,15 @@ impl Item for NativeStandardListViewItem { } }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeStandardListViewItem { diff --git a/internal/backends/qt/qt_widgets/progress_indicator.rs b/internal/backends/qt/qt_widgets/progress_indicator.rs index b415269664e..42a64008c67 100644 --- a/internal/backends/qt/qt_widgets/progress_indicator.rs +++ b/internal/backends/qt/qt_widgets/progress_indicator.rs @@ -129,6 +129,15 @@ impl Item for NativeProgressIndicator { qApp->style()->drawControl(QStyle::CE_ProgressBar, &option, painter_, widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeProgressIndicator { diff --git a/internal/backends/qt/qt_widgets/scrollview.rs b/internal/backends/qt/qt_widgets/scrollview.rs index 7285f60d985..2deaffa905e 100644 --- a/internal/backends/qt/qt_widgets/scrollview.rs +++ b/internal/backends/qt/qt_widgets/scrollview.rs @@ -445,6 +445,15 @@ impl Item for NativeScrollView { ); } } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeScrollView { diff --git a/internal/backends/qt/qt_widgets/slider.rs b/internal/backends/qt/qt_widgets/slider.rs index cc9ce983692..f9668c2ed83 100644 --- a/internal/backends/qt/qt_widgets/slider.rs +++ b/internal/backends/qt/qt_widgets/slider.rs @@ -363,6 +363,15 @@ impl Item for NativeSlider { style->drawComplexControl(QStyle::CC_Slider, &option, painter->get(), widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl NativeSlider { diff --git a/internal/backends/qt/qt_widgets/spinbox.rs b/internal/backends/qt/qt_widgets/spinbox.rs index 1302ccc2172..7d3ec7fe43b 100644 --- a/internal/backends/qt/qt_widgets/spinbox.rs +++ b/internal/backends/qt/qt_widgets/spinbox.rs @@ -338,6 +338,15 @@ impl Item for NativeSpinBox { (*painter)->drawText(text_rect, QString::number(value), QTextOption(static_cast(horizontal_alignment))); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeSpinBox { diff --git a/internal/backends/qt/qt_widgets/tableheadersection.rs b/internal/backends/qt/qt_widgets/tableheadersection.rs index 00396a9535b..3bfe7ee4434 100644 --- a/internal/backends/qt/qt_widgets/tableheadersection.rs +++ b/internal/backends/qt/qt_widgets/tableheadersection.rs @@ -154,6 +154,15 @@ impl Item for NativeTableHeaderSection { #endif }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeTableHeaderSection { diff --git a/internal/backends/qt/qt_widgets/tabwidget.rs b/internal/backends/qt/qt_widgets/tabwidget.rs index bfc625e8d2f..8cac8b5b402 100644 --- a/internal/backends/qt/qt_widgets/tabwidget.rs +++ b/internal/backends/qt/qt_widgets/tabwidget.rs @@ -315,6 +315,15 @@ impl Item for NativeTabWidget { style->drawPrimitive(QStyle::PE_FrameTabBarBase, &optTabBase, painter->get(), widget);*/ }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeTabWidget { @@ -528,6 +537,15 @@ impl Item for NativeTab { qApp->style()->drawControl(QStyle::CE_TabBarTab, &option, painter->get(), widget); }); } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for NativeTab { diff --git a/internal/backends/qt/qt_window.rs b/internal/backends/qt/qt_window.rs index 04fe9606b33..2f5cddf2d48 100644 --- a/internal/backends/qt/qt_window.rs +++ b/internal/backends/qt/qt_window.rs @@ -1591,10 +1591,12 @@ impl QtItemRenderer<'_> { std::mem::swap(&mut self.painter, &mut layer_painter); + let window_adapter = self.window().window_adapter(); + i_slint_core::item_rendering::render_item_children( self, &item_rc.item_tree(), - item_rc.index() as isize, + item_rc.index() as isize, &window_adapter ); std::mem::swap(&mut self.painter, &mut layer_painter); @@ -1713,6 +1715,7 @@ impl QtWindow { fn paint_event(&self, painter: QPainterPtr) { let runtime_window = WindowInner::from_pub(&self.window); + let window_adapter = runtime_window.window_adapter(); runtime_window.draw_contents(|components| { i_slint_core::animations::update_animations(); let mut renderer = QtItemRenderer { @@ -1727,6 +1730,7 @@ impl QtWindow { component, &mut renderer, *origin, + &window_adapter, ); } diff --git a/internal/core/item_rendering.rs b/internal/core/item_rendering.rs index ec3e91a1368..5f652c39fb3 100644 --- a/internal/core/item_rendering.rs +++ b/internal/core/item_rendering.rs @@ -11,12 +11,13 @@ use crate::item_tree::ItemTreeRc; use crate::item_tree::{ItemVisitor, ItemVisitorResult, ItemVisitorVTable, VisitChildrenResult}; use crate::lengths::{ ItemTransform, LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalPx, LogicalRect, - LogicalSize, LogicalVector, + LogicalSize, LogicalVector, SizeLengths, }; use crate::properties::PropertyTracker; -use crate::window::WindowInner; +use crate::window::{WindowAdapter, WindowInner}; use crate::{Brush, Coord, SharedString}; use alloc::boxed::Box; +use alloc::rc::Rc; use core::cell::{Cell, RefCell}; use core::pin::Pin; #[cfg(feature = "std")] @@ -188,13 +189,18 @@ pub fn is_clipping_item(item: Pin) -> bool { } /// Renders the children of the item with the specified index into the renderer. -pub fn render_item_children(renderer: &mut dyn ItemRenderer, component: &ItemTreeRc, index: isize) { +pub fn render_item_children( + renderer: &mut dyn ItemRenderer, + component: &ItemTreeRc, + index: isize, + window_adapter: &Rc, +) { let mut actual_visitor = |component: &ItemTreeRc, index: u32, item: Pin| -> VisitChildrenResult { renderer.save_state(); let item_rc = ItemRc::new(component.clone(), index); - let (do_draw, item_geometry) = renderer.filter_item(&item_rc); + let (do_draw, item_geometry) = renderer.filter_item(&item_rc, window_adapter); let item_origin = item_geometry.origin; renderer.translate(item_origin.to_vector()); @@ -216,7 +222,7 @@ pub fn render_item_children(renderer: &mut dyn ItemRenderer, component: &ItemTre }; if matches!(render_result, RenderingResult::ContinueRenderingChildren) { - render_item_children(renderer, component, index as isize); + render_item_children(renderer, component, index as isize, window_adapter); } renderer.restore_state(); VisitChildrenResult::CONTINUE @@ -235,11 +241,12 @@ pub fn render_component_items( component: &ItemTreeRc, renderer: &mut dyn ItemRenderer, origin: LogicalPoint, + window_adapter: &Rc, ) { renderer.save_state(); renderer.translate(origin.to_vector()); - render_item_children(renderer, component, -1); + render_item_children(renderer, component, -1, window_adapter); renderer.restore_state(); } @@ -322,6 +329,29 @@ pub trait RenderText { fn overflow(self: Pin<&Self>) -> TextOverflow; fn letter_spacing(self: Pin<&Self>) -> LogicalLength; fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle); + + fn text_bounding_rect( + self: Pin<&Self>, + window_adapter: &Rc, + mut geometry: euclid::Rect, + ) -> euclid::Rect { + let window_inner = WindowInner::from_pub(window_adapter.window()); + let text_string = self.text(); + let font_request = self.font_request(window_inner); + let scale_factor = crate::lengths::ScaleFactor::new(window_inner.scale_factor()); + let max_width = geometry.size.width_length(); + geometry.size = window_adapter + .renderer() + .text_size( + font_request.clone(), + text_string.as_str(), + Some(max_width.cast()), + scale_factor, + self.wrap(), + ) + .cast(); + geometry + } } /// Trait used to render each items. @@ -473,9 +503,18 @@ pub trait ItemRenderer { /// Returns /// - if the item needs to be drawn (false means it is clipped or doesn't need to be drawn) /// - the geometry of the item - fn filter_item(&mut self, item: &ItemRc) -> (bool, LogicalRect) { + fn filter_item( + &mut self, + item: &ItemRc, + window_adapter: &Rc, + ) -> (bool, LogicalRect) { let item_geometry = item.geometry(); - (self.get_current_clip().intersects(&item_geometry), item_geometry) + // Query bounding rect untracked, as properties that affect the bounding rect are already tracked + // when rendering the item. + let bounding_rect = crate::properties::evaluate_no_tracking(|| { + item.bounding_rect(&item_geometry, window_adapter) + }); + (self.get_current_clip().intersects(&bounding_rect), item_geometry) } fn window(&self) -> &crate::window::WindowInner; @@ -499,27 +538,85 @@ pub trait ItemRendererFeatures { /// After rendering an item, we cache the geometry and the transform it applies to /// children. #[derive(Clone)] -pub struct CachedItemGeometryAndTransform { - /// The item's reported geometry, via [`ItemRc::geometry`]. - pub geometry: LogicalRect, - /// The item's transform to apply to children, via [`ItemRc::children_transform`]. - pub boxed_children_transform: Option>, + +pub enum CachedItemBoundingBoxAndTransform { + /// A regular item with a translation + RegularItem { + /// The item's bounding rect relative to its parent. + bounding_rect: LogicalRect, + /// The item's offset relative to its parent. + offset: LogicalVector, + }, + /// An item such as Rotate that defines an additional transformation + ItemWithTransform { + /// The item's bounding rect relative to its parent. + bounding_rect: LogicalRect, + /// The item's transform to apply to children. + transform: Box, + }, + /// A clip item. + ClipItem { + /// The item's geometry relative to its parent. + geometry: LogicalRect, + }, } -impl From<(LogicalRect, Option)> for CachedItemGeometryAndTransform { - fn from((geometry, children_transform): (LogicalRect, Option)) -> Self { - Self { geometry, boxed_children_transform: children_transform.map(Into::into) } +impl CachedItemBoundingBoxAndTransform { + fn bounding_rect(&self) -> &LogicalRect { + match self { + CachedItemBoundingBoxAndTransform::RegularItem { bounding_rect, .. } => bounding_rect, + CachedItemBoundingBoxAndTransform::ItemWithTransform { bounding_rect, .. } => { + bounding_rect + } + CachedItemBoundingBoxAndTransform::ClipItem { geometry } => geometry, + } } -} -impl CachedItemGeometryAndTransform { - fn children_transform(&self) -> Option { - self.boxed_children_transform.as_ref().map(|transform| **transform) + fn transform(&self) -> ItemTransform { + match self { + CachedItemBoundingBoxAndTransform::RegularItem { offset, .. } => { + ItemTransform::translation(offset.x as f32, offset.y as f32) + } + CachedItemBoundingBoxAndTransform::ItemWithTransform { transform, .. } => **transform, + CachedItemBoundingBoxAndTransform::ClipItem { geometry } => { + ItemTransform::translation(geometry.origin.x as f32, geometry.origin.y as f32) + } + } + } + + fn new( + item_rc: &ItemRc, + window_adapter: &Rc, + ) -> Self { + let geometry = item_rc.geometry(); + + if is_clipping_item(item_rc.borrow()) { + return Self::ClipItem { geometry }; + } + + // Evaluate the bounding rect untracked, as properties that affect the bounding rect are already tracked + // at rendering time. + let bounding_rect = crate::properties::evaluate_no_tracking(|| { + item_rc.bounding_rect(&geometry, window_adapter) + }); + + if let Some(complex_child_transform) = + T::SUPPORTS_TRANSFORMATIONS.then(|| item_rc.children_transform()).flatten() + { + Self::ItemWithTransform { + bounding_rect, + transform: complex_child_transform + .then_translate(geometry.origin.to_vector().cast()) + .into(), + } + } else { + Self::RegularItem { bounding_rect, offset: geometry.origin.to_vector() } + } } } /// The cache that needs to be held by the Window for the partial rendering -pub type PartialRenderingCache = RenderingCache; +pub type PartialRenderingCache = RenderingCache; /// A region composed of a few rectangles that need to be redrawn. #[derive(Default, Clone, Debug)] @@ -662,16 +759,19 @@ pub struct PartialRenderer<'a, T> { pub dirty_region: DirtyRegion, /// The actual renderer which the drawing call will be forwarded to pub actual_renderer: T, + /// The window adapter the renderer is rendering into. + pub window_adapter: Rc, } -impl<'a, T: ItemRendererFeatures> PartialRenderer<'a, T> { +impl<'a, T: ItemRenderer + ItemRendererFeatures> PartialRenderer<'a, T> { /// Create a new PartialRenderer pub fn new( cache: &'a RefCell, initial_dirty_region: DirtyRegion, actual_renderer: T, ) -> Self { - Self { cache, dirty_region: initial_dirty_region, actual_renderer } + let window_adapter = actual_renderer.window().window_adapter(); + Self { cache, dirty_region: initial_dirty_region, actual_renderer, window_adapter } } /// Visit the tree of item and compute what are the dirty regions @@ -691,22 +791,15 @@ impl<'a, T: ItemRendererFeatures> PartialRenderer<'a, T> { impl ComputeDirtyRegionState { /// Adjust transform_to_screen and old_transform_to_screen to map from item coordinates - /// to the screen when using it on a child, specified by its origin, children transform. + /// to the screen when using it on a child, specified by its children transform. fn adjust_transforms_for_child( &mut self, - child_origin: &LogicalPoint, - children_transform: Option, - old_child_origin: &LogicalPoint, - old_children_transform: Option, + children_transform: &ItemTransform, + old_children_transform: &ItemTransform, ) { - self.transform_to_screen = children_transform - .unwrap_or_default() - .then_translate(child_origin.to_vector().cast()) - .then(&self.transform_to_screen); - self.old_transform_to_screen = old_children_transform - .unwrap_or_default() - .then_translate(old_child_origin.to_vector().cast()) - .then(&self.old_transform_to_screen); + self.transform_to_screen = children_transform.then(&self.transform_to_screen); + self.old_transform_to_screen = + old_children_transform.then(&self.old_transform_to_screen); } } @@ -726,28 +819,27 @@ impl<'a, T: ItemRendererFeatures> PartialRenderer<'a, T> { if tr.is_dirty() { let old_geom = cached_geom.clone(); drop(borrowed); - let (geom, children_transform) = - crate::properties::evaluate_no_tracking(|| { - ( - item_rc.geometry(), - T::SUPPORTS_TRANSFORMATIONS - .then(|| item_rc.children_transform()) - .flatten(), - ) - }); + let new_geom = crate::properties::evaluate_no_tracking(|| { + CachedItemBoundingBoxAndTransform::new::( + &item_rc, + &self.window_adapter, + ) + }); self.mark_dirty_rect( - &old_geom.geometry, + &old_geom.bounding_rect(), state.old_transform_to_screen, &state.clipped, ); - self.mark_dirty_rect(&geom, state.transform_to_screen, &state.clipped); + self.mark_dirty_rect( + &new_geom.bounding_rect(), + state.transform_to_screen, + &state.clipped, + ); new_state.adjust_transforms_for_child( - &geom.origin, - children_transform, - &old_geom.geometry.origin, - old_geom.children_transform(), + &new_geom.transform(), + &old_geom.transform(), ); if ItemRef::downcast_pin::(item).is_some() @@ -767,38 +859,36 @@ impl<'a, T: ItemRendererFeatures> PartialRenderer<'a, T> { != new_state.old_transform_to_screen { self.mark_dirty_rect( - &cached_geom.geometry, + &cached_geom.bounding_rect(), state.old_transform_to_screen, &state.clipped, ); self.mark_dirty_rect( - &cached_geom.geometry, + &cached_geom.bounding_rect(), state.transform_to_screen, &state.clipped, ); } new_state.adjust_transforms_for_child( - &cached_geom.geometry.origin, - cached_geom.children_transform(), - &cached_geom.geometry.origin, - cached_geom.children_transform(), + &cached_geom.transform(), + &cached_geom.transform(), ); - if crate::properties::evaluate_no_tracking(|| is_clipping_item(item)) { + if let CachedItemBoundingBoxAndTransform::ClipItem { geometry } = + &cached_geom + { new_state.clipped = new_state .clipped .intersection( &state .transform_to_screen - .outer_transformed_rect(&cached_geom.geometry.cast()) + .outer_transformed_rect(&geometry.cast()) .cast() .union( &state .old_transform_to_screen - .outer_transformed_rect( - &cached_geom.geometry.cast(), - ) + .outer_transformed_rect(&geometry.cast()) .cast(), ), ) @@ -809,33 +899,33 @@ impl<'a, T: ItemRendererFeatures> PartialRenderer<'a, T> { } _ => { drop(borrowed); - let geom = crate::properties::evaluate_no_tracking(|| { - let geom = item_rc.geometry(); - let children_transform = T::SUPPORTS_TRANSFORMATIONS - .then(|| item_rc.children_transform()) - .flatten(); - - new_state.adjust_transforms_for_child( - &geom.origin, - children_transform, - &geom.origin, - children_transform, + let bounding_rect = crate::properties::evaluate_no_tracking(|| { + let geom = CachedItemBoundingBoxAndTransform::new::( + &item_rc, + &self.window_adapter, ); - if is_clipping_item(item) { + new_state + .adjust_transforms_for_child(&geom.transform(), &geom.transform()); + + if let CachedItemBoundingBoxAndTransform::ClipItem { geometry } = geom { new_state.clipped = new_state .clipped .intersection( &state .transform_to_screen - .outer_transformed_rect(&geom.cast()) + .outer_transformed_rect(&geometry.cast()) .cast(), ) .unwrap_or_default(); } - geom + *geom.bounding_rect() }); - self.mark_dirty_rect(&geom, state.transform_to_screen, &state.clipped); + self.mark_dirty_rect( + &bounding_rect, + state.transform_to_screen, + &state.clipped, + ); ItemVisitorResult::Continue(new_state) } } @@ -871,7 +961,7 @@ impl<'a, T: ItemRendererFeatures> PartialRenderer<'a, T> { fn do_rendering( cache: &RefCell, rendering_data: &CachedRenderingData, - render_fn: impl FnOnce() -> CachedItemGeometryAndTransform, + render_fn: impl FnOnce() -> CachedItemBoundingBoxAndTransform, ) { let mut cache = cache.borrow_mut(); if let Some(entry) = rendering_data.get_entry(&mut cache) { @@ -899,7 +989,7 @@ macro_rules! forward_rendering_call { let mut ret = None; Self::do_rendering(&self.cache, &obj.cached_rendering_data, || { ret = Some(self.actual_renderer.$fn(obj, item_rc, size)); - (item_rc.geometry(), item_rc.children_transform()).into() + CachedItemBoundingBoxAndTransform::new::(&item_rc, &self.window_adapter) }); ret.unwrap_or_default() } @@ -912,7 +1002,7 @@ macro_rules! forward_rendering_call2 { let mut ret = None; Self::do_rendering(&self.cache, &cache, || { ret = Some(self.actual_renderer.$fn(obj, item_rc, size, &cache)); - (item_rc.geometry(), item_rc.children_transform()).into() + CachedItemBoundingBoxAndTransform::new::(&item_rc, &self.window_adapter) }); ret.unwrap_or_default() } @@ -920,45 +1010,45 @@ macro_rules! forward_rendering_call2 { } impl<'a, T: ItemRenderer + ItemRendererFeatures> ItemRenderer for PartialRenderer<'a, T> { - fn filter_item(&mut self, item_rc: &ItemRc) -> (bool, LogicalRect) { + fn filter_item( + &mut self, + item_rc: &ItemRc, + window_adapter: &Rc, + ) -> (bool, LogicalRect) { let item = item_rc.borrow(); let eval = || { - if let Some(clip) = ItemRef::downcast_pin::(item) { - // Make sure we register a dependency on the clip - clip.clip(); - } - ( - item_rc.geometry(), - T::SUPPORTS_TRANSFORMATIONS.then(|| item_rc.children_transform()).flatten(), - ) - .into() + // registers dependencies on the geometry and clip properties. + CachedItemBoundingBoxAndTransform::new::(item_rc, &window_adapter) }; let rendering_data = item.cached_rendering_data_offset(); let mut cache = self.cache.borrow_mut(); - let item_geometry = match rendering_data.get_entry(&mut cache) { + let item_bounding_rect = match rendering_data.get_entry(&mut cache) { Some(CachedGraphicsData { data, dependency_tracker }) => { dependency_tracker .get_or_insert_with(|| Box::pin(PropertyTracker::default())) .as_ref() .evaluate_if_dirty(|| *data = eval()); - data.geometry + *data.bounding_rect() } None => { let cache_entry = crate::graphics::CachedGraphicsData::new(eval); let geom = cache_entry.data.clone(); rendering_data.cache_index.set(cache.insert(cache_entry)); rendering_data.cache_generation.set(cache.generation()); - geom.geometry + *geom.bounding_rect() } }; - let clipped_geom = self.get_current_clip().intersection(&item_geometry); + let clipped_geom = self.get_current_clip().intersection(&item_bounding_rect); let draw = clipped_geom.map_or(false, |clipped_geom| { let clipped_geom = clipped_geom.translate(self.translation()); self.dirty_region.draw_intersects(clipped_geom) }); + // Query untracked, as the bounding rect calculation already registers a dependency on the geometry. + let item_geometry = crate::properties::evaluate_no_tracking(|| item_rc.geometry()); + (draw, item_geometry) } diff --git a/internal/core/item_tree.rs b/internal/core/item_tree.rs index a8a04a08c9b..98299933404 100644 --- a/internal/core/item_tree.rs +++ b/internal/core/item_tree.rs @@ -438,6 +438,14 @@ impl ItemRc { comp_ref_pin.as_ref().item_geometry(self.index) } + pub fn bounding_rect( + &self, + geometry: &LogicalRect, + window_adapter: &WindowAdapterRc, + ) -> LogicalRect { + self.borrow().as_ref().bounding_rect(window_adapter, self, *geometry) + } + /// Returns an absolute position of `p` in the parent item coordinate system /// (does not add this item's x and y) pub fn map_to_window(&self, p: LogicalPoint) -> LogicalPoint { diff --git a/internal/core/items.rs b/internal/core/items.rs index 0c5855de409..646dda02c2a 100644 --- a/internal/core/items.rs +++ b/internal/core/items.rs @@ -29,7 +29,8 @@ use crate::item_rendering::{CachedRenderingData, RenderBorderRectangle, RenderRe pub use crate::item_tree::ItemRc; use crate::layout::LayoutInfo; use crate::lengths::{ - LogicalBorderRadius, LogicalLength, LogicalSize, LogicalVector, PointLengths, RectLengths, + LogicalBorderRadius, LogicalLength, LogicalRect, LogicalSize, LogicalVector, PointLengths, + RectLengths, }; #[cfg(feature = "rtti")] use crate::rtti::*; @@ -168,6 +169,13 @@ pub struct ItemVTable { self_rc: &ItemRc, size: LogicalSize, ) -> RenderingResult, + + pub bounding_rect: extern "C" fn( + core::pin::Pin>, + window_adapter: &WindowAdapterRc, + self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect, } /// Alias for `vtable::VRef` which represent a pointer to a `dyn Item` with @@ -237,6 +245,16 @@ impl Item for Empty { ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + mut geometry: LogicalRect, + ) -> LogicalRect { + geometry.size = LogicalSize::zero(); + geometry + } } impl ItemConsts for Empty { @@ -315,6 +333,15 @@ impl Item for Rectangle { (*backend).draw_rectangle(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl RenderRectangle for Rectangle { @@ -402,6 +429,15 @@ impl Item for BasicBorderRectangle { (*backend).draw_border_rectangle(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl RenderBorderRectangle for BasicBorderRectangle { @@ -502,6 +538,15 @@ impl Item for BorderRectangle { (*backend).draw_border_rectangle(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl RenderBorderRectangle for BorderRectangle { @@ -627,6 +672,15 @@ impl Item for Clip { ) -> RenderingResult { (*backend).visit_clip(self, self_rc, size) } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl Clip { @@ -714,6 +768,15 @@ impl Item for Opacity { ) -> RenderingResult { backend.visit_opacity(self, self_rc, size) } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl Opacity { @@ -818,6 +881,15 @@ impl Item for Layer { ) -> RenderingResult { backend.visit_layer(self, self_rc, size) } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for Layer { @@ -902,6 +974,15 @@ impl Item for Rotate { (*backend).translate(-origin); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for Rotate { @@ -1029,6 +1110,15 @@ impl Item for WindowItem { backend.draw_window_background(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl RenderRectangle for WindowItem { @@ -1154,6 +1244,15 @@ impl Item for ContextMenu { ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ContextMenu {} @@ -1237,6 +1336,17 @@ impl Item for BoxShadow { (*backend).draw_box_shadow(self, self_rc, size); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + .outer_rect(euclid::SideOffsets2D::from_length_all_same(self.blur())) + .translate(LogicalVector::from_lengths(self.offset_x(), self.offset_y())) + } } impl ItemConsts for BoxShadow { diff --git a/internal/core/items/component_container.rs b/internal/core/items/component_container.rs index deab19c0ccf..38db4437370 100644 --- a/internal/core/items/component_container.rs +++ b/internal/core/items/component_container.rs @@ -17,7 +17,7 @@ use crate::item_rendering::{CachedRenderingData, RenderRectangle}; use crate::item_tree::{IndexRange, ItemTreeRc, ItemTreeWeak, ItemWeak}; use crate::item_tree::{ItemTreeNode, ItemVisitorVTable, TraversalOrder, VisitChildrenResult}; use crate::layout::{LayoutInfo, Orientation}; -use crate::lengths::{LogicalLength, LogicalSize}; +use crate::lengths::{LogicalLength, LogicalRect, LogicalSize}; use crate::properties::{Property, PropertyTracker}; #[cfg(feature = "rtti")] use crate::rtti::*; @@ -221,6 +221,15 @@ impl Item for ComponentContainer { backend.draw_rectangle(self, item_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl RenderRectangle for ComponentContainer { diff --git a/internal/core/items/flickable.rs b/internal/core/items/flickable.rs index 7114283162d..5bd73fc73c9 100644 --- a/internal/core/items/flickable.rs +++ b/internal/core/items/flickable.rs @@ -146,6 +146,15 @@ impl Item for Flickable { ); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl ItemConsts for Flickable { diff --git a/internal/core/items/image.rs b/internal/core/items/image.rs index 8288d7147b5..18e4e5b8120 100644 --- a/internal/core/items/image.rs +++ b/internal/core/items/image.rs @@ -18,7 +18,7 @@ use crate::input::{ use crate::item_rendering::ItemRenderer; use crate::item_rendering::{CachedRenderingData, RenderImage}; use crate::layout::{LayoutInfo, Orientation}; -use crate::lengths::{LogicalLength, LogicalSize}; +use crate::lengths::{LogicalLength, LogicalRect, LogicalSize}; #[cfg(feature = "rtti")] use crate::rtti::*; use crate::window::WindowAdapter; @@ -108,6 +108,15 @@ impl Item for ImageItem { (*backend).draw_image(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl RenderImage for ImageItem { @@ -245,6 +254,15 @@ impl Item for ClippedImage { (*backend).draw_image(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl RenderImage for ClippedImage { diff --git a/internal/core/items/input_items.rs b/internal/core/items/input_items.rs index 69b2a8cec6a..900571bd864 100644 --- a/internal/core/items/input_items.rs +++ b/internal/core/items/input_items.rs @@ -228,6 +228,16 @@ impl Item for TouchArea { ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + mut geometry: LogicalRect, + ) -> LogicalRect { + geometry.size = LogicalSize::zero(); + geometry + } } impl ItemConsts for TouchArea { @@ -339,6 +349,16 @@ impl Item for FocusScope { ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + mut geometry: LogicalRect, + ) -> LogicalRect { + geometry.size = LogicalSize::zero(); + geometry + } } impl ItemConsts for FocusScope { @@ -527,6 +547,16 @@ impl Item for SwipeGestureHandler { ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + mut geometry: LogicalRect, + ) -> LogicalRect { + geometry.size = LogicalSize::zero(); + geometry + } } impl ItemConsts for SwipeGestureHandler { diff --git a/internal/core/items/path.rs b/internal/core/items/path.rs index d303e95a4e9..b5b3203ca53 100644 --- a/internal/core/items/path.rs +++ b/internal/core/items/path.rs @@ -18,7 +18,8 @@ use crate::item_rendering::CachedRenderingData; use crate::layout::{LayoutInfo, Orientation}; use crate::lengths::{ - LogicalBorderRadius, LogicalLength, LogicalSize, LogicalVector, PointLengths, RectLengths, + LogicalBorderRadius, LogicalLength, LogicalRect, LogicalSize, LogicalVector, PointLengths, + RectLengths, }; #[cfg(feature = "rtti")] use crate::rtti::*; @@ -129,6 +130,15 @@ impl Item for Path { } RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + geometry + } } impl Path { diff --git a/internal/core/items/text.rs b/internal/core/items/text.rs index 309137d7fa8..f8c521725b7 100644 --- a/internal/core/items/text.rs +++ b/internal/core/items/text.rs @@ -20,7 +20,9 @@ use crate::input::{ }; use crate::item_rendering::{CachedRenderingData, ItemRenderer, RenderText}; use crate::layout::{LayoutInfo, Orientation}; -use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, ScaleFactor}; +use crate::lengths::{ + LogicalLength, LogicalPoint, LogicalRect, LogicalSize, ScaleFactor, SizeLengths, +}; use crate::platform::Clipboard; #[cfg(feature = "rtti")] use crate::rtti::*; @@ -122,6 +124,15 @@ impl Item for ComplexText { (*backend).draw_text(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + self.text_bounding_rect(window_adapter, geometry.cast()).cast() + } } impl ItemConsts for ComplexText { @@ -287,6 +298,15 @@ impl Item for SimpleText { (*backend).draw_text(self, self_rc, size, &self.cached_rendering_data); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: LogicalRect, + ) -> LogicalRect { + self.text_bounding_rect(window_adapter, geometry.cast()).cast() + } } impl ItemConsts for SimpleText { @@ -967,6 +987,27 @@ impl Item for TextInput { (*backend).draw_text_input(self, self_rc, size); RenderingResult::ContinueRenderingChildren } + + fn bounding_rect( + self: core::pin::Pin<&Self>, + window_adapter: &Rc, + _self_rc: &ItemRc, + mut geometry: LogicalRect, + ) -> LogicalRect { + let window_inner = WindowInner::from_pub(window_adapter.window()); + let text_string = self.text(); + let font_request = self.font_request(window_adapter); + let scale_factor = crate::lengths::ScaleFactor::new(window_inner.scale_factor()); + let max_width = geometry.size.width_length(); + geometry.size = window_adapter.renderer().text_size( + font_request.clone(), + text_string.as_str(), + Some(max_width), + scale_factor, + self.wrap(), + ); + geometry + } } impl ItemConsts for TextInput { diff --git a/internal/core/menus.rs b/internal/core/menus.rs index 00a48acbcf0..9420464cef6 100644 --- a/internal/core/menus.rs +++ b/internal/core/menus.rs @@ -181,6 +181,15 @@ impl crate::items::Item for MenuItem { ) -> crate::items::RenderingResult { Default::default() } + + fn bounding_rect( + self: Pin<&Self>, + _window_adapter: &Rc, + _self_rc: &ItemRc, + geometry: crate::lengths::LogicalRect, + ) -> crate::lengths::LogicalRect { + geometry + } } impl crate::items::ItemConsts for MenuItem { diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index e896d93143f..f17052513d6 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -501,6 +501,7 @@ impl SoftwareRenderer { rotation, ); let mut renderer = self.partial_rendering_state.create_partial_renderer(buffer_renderer); + let window_adapter = renderer.window_adapter.clone(); window_inner .draw_contents(|components| { @@ -568,6 +569,7 @@ impl SoftwareRenderer { component, &mut renderer, *origin, + &window_adapter, ); } @@ -992,6 +994,7 @@ fn prepare_scene( ); let mut renderer = software_renderer.partial_rendering_state.create_partial_renderer(prepare_scene); + let window_adapter = renderer.window_adapter.clone(); let mut dirty_region = PhysicalRegion::default(); window.draw_contents(|components| { @@ -1034,7 +1037,12 @@ fn prepare_scene( drop(i); for (component, origin) in components { - crate::item_rendering::render_component_items(component, &mut renderer, *origin); + crate::item_rendering::render_component_items( + component, + &mut renderer, + *origin, + &window_adapter, + ); } }); diff --git a/internal/renderers/femtovg/itemrenderer.rs b/internal/renderers/femtovg/itemrenderer.rs index b8698dbaa31..b52df7e259b 100644 --- a/internal/renderers/femtovg/itemrenderer.rs +++ b/internal/renderers/femtovg/itemrenderer.rs @@ -1192,10 +1192,13 @@ impl<'a> GLItemRenderer<'a> { current_render_target: layer_image.as_render_target(), }; + let window_adapter = self.window().window_adapter(); + i_slint_core::item_rendering::render_item_children( self, item_rc.item_tree(), item_rc.index() as isize, + &window_adapter, ); { diff --git a/internal/renderers/femtovg/lib.rs b/internal/renderers/femtovg/lib.rs index 50df786ff85..296e166f514 100644 --- a/internal/renderers/femtovg/lib.rs +++ b/internal/renderers/femtovg/lib.rs @@ -286,6 +286,7 @@ impl FemtoVGRenderer { component, &mut item_renderer, *origin, + &self.window_adapter()?, ); } diff --git a/internal/renderers/skia/itemrenderer.rs b/internal/renderers/skia/itemrenderer.rs index 4de5889e804..d6d9fcfc5fd 100644 --- a/internal/renderers/skia/itemrenderer.rs +++ b/internal/renderers/skia/itemrenderer.rs @@ -344,6 +344,7 @@ impl<'a> SkiaItemRenderer<'a> { &mut sub_renderer, &item_rc.item_tree(), item_rc.index() as isize, + &WindowInner::from_pub(self.window).window_adapter(), ); Some(surface.image_snapshot()) @@ -972,10 +973,13 @@ impl<'a> ItemRenderer for SkiaItemRenderer<'a> { self.state_stack.push(self.current_state); self.current_state.alpha = 1.0; + let window_adapter = WindowInner::from_pub(self.window).window_adapter(); + i_slint_core::item_rendering::render_item_children( self, &item_rc.item_tree(), item_rc.index() as isize, + &window_adapter, ); self.current_state = self.state_stack.pop().unwrap(); diff --git a/internal/renderers/skia/lib.rs b/internal/renderers/skia/lib.rs index b3adde3dc38..6ece9f5582c 100644 --- a/internal/renderers/skia/lib.rs +++ b/internal/renderers/skia/lib.rs @@ -489,6 +489,7 @@ impl SkiaRenderer { components: &[(&i_slint_core::item_tree::ItemTreeRc, LogicalPoint)], ) -> Option { let window_inner = WindowInner::from_pub(window); + let window_adapter = window_inner.window_adapter(); let mut box_shadow_cache = Default::default(); @@ -612,6 +613,7 @@ impl SkiaRenderer { component, item_renderer, *origin, + &window_adapter, ); } diff --git a/tests/manual/partial-rendering-circus.slint b/tests/manual/partial-rendering-circus.slint index 25234a7d403..41e67da7bd9 100644 --- a/tests/manual/partial-rendering-circus.slint +++ b/tests/manual/partial-rendering-circus.slint @@ -14,4 +14,28 @@ export component AppWindow inherits Window { rotation-angle: mod(animation-tick(), 2s) / 2s * 360deg; colorize: green; } + + Rectangle { + x: 10px + mod(animation-tick(), 5s) / 5s * 100px; + y: 10px; + width: 20px; + height: 20px; + drop-shadow-blur: 5px; + drop-shadow-offset-x: 15px; + drop-shadow-offset-y: 5px; + drop-shadow-color: red; + } + + Text { + x: 10px + mod(animation-tick(), 5s) / 5s * 100px; + y: 40px; + width: 40px; + height: 30px; + text: "This is a long piece\nof text that\nwill render out of\nthe bounds."; + + Rectangle { + border-width: 1px; + border-color: green; + } + } }