From 29ff6784be8599e5e3f791df5d250f1d7aab6ff1 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Wed, 28 Jan 2026 11:43:44 +0100 Subject: [PATCH 1/7] Replace set_clip_path with set_clips_contents method This changes clipping behavior to a boolean flag, which better matches how it was used so far. A follow-up PR will add set_clip_shape to set arbitrary (even non-Rect) shapes. --- masonry/src/tests/paint.rs | 4 +- masonry/src/widgets/canvas.rs | 2 +- masonry/src/widgets/portal.rs | 2 +- masonry/src/widgets/prose.rs | 7 +--- masonry/src/widgets/text_input.rs | 6 +-- masonry/src/widgets/virtual_scroll.rs | 2 +- masonry_core/src/core/contexts.rs | 47 +++++++--------------- masonry_core/src/core/widget.rs | 7 ++-- masonry_core/src/core/widget_state.rs | 50 ++++++++++++++---------- masonry_core/src/doc/masonry_concepts.md | 15 +++++++ masonry_core/src/passes/accessibility.rs | 2 +- masonry_core/src/passes/compose.rs | 4 +- masonry_core/src/passes/paint.rs | 11 +++--- 13 files changed, 79 insertions(+), 80 deletions(-) diff --git a/masonry/src/tests/paint.rs b/masonry/src/tests/paint.rs index 2fc65de6a..02d307d0e 100644 --- a/masonry/src/tests/paint.rs +++ b/masonry/src/tests/paint.rs @@ -124,8 +124,8 @@ fn paint_clipping() { let parent = NewWidget::new( ModularWidget::new(()) .measure_fn(|_, _, _, _, _, _| SQUARE_SIZE) - .layout_fn(|_, ctx, _, size| { - ctx.set_clip_path(size.to_rect()); + .layout_fn(|_, ctx, _, _size| { + ctx.set_clips_contents(true); }) .paint_fn(move |_, ctx, _, scene| { fill(scene, &ctx.content_box(), Color::WHITE); diff --git a/masonry/src/widgets/canvas.rs b/masonry/src/widgets/canvas.rs index 688c0c3f3..8b59fbaeb 100644 --- a/masonry/src/widgets/canvas.rs +++ b/masonry/src/widgets/canvas.rs @@ -116,7 +116,7 @@ impl Widget for Canvas { ctx.submit_action::(CanvasSizeChanged { size }); } // We clip the contents we draw. - ctx.set_clip_path(size.to_rect()); + ctx.set_clips_contents(true); } fn paint(&mut self, _: &mut PaintCtx<'_>, _props: &PropertiesRef<'_>, scene: &mut Scene) { diff --git a/masonry/src/widgets/portal.rs b/masonry/src/widgets/portal.rs index 9d75cbb72..d2aca1c3d 100644 --- a/masonry/src/widgets/portal.rs +++ b/masonry/src/widgets/portal.rs @@ -715,7 +715,7 @@ impl Widget for Portal { self.set_viewport_pos_raw(size, content_size, self.viewport_pos); // TODO - recompute portal progress - ctx.set_clip_path(size.to_rect()); + ctx.set_clips_contents(true); ctx.place_child(&mut self.child, Point::ZERO); diff --git a/masonry/src/widgets/prose.rs b/masonry/src/widgets/prose.rs index 24f581eff..aaf7d1f57 100644 --- a/masonry/src/widgets/prose.rs +++ b/masonry/src/widgets/prose.rs @@ -156,12 +156,7 @@ impl Widget for Prose { ctx.run_layout(&mut self.text, size); ctx.place_child(&mut self.text, Point::ORIGIN); - if self.clip { - let border_box = size.to_rect() + ctx.border_box_insets(); - ctx.set_clip_path(border_box); - } else { - ctx.clear_clip_path(); - } + ctx.set_clips_contents(self.clip); } fn paint(&mut self, _ctx: &mut PaintCtx<'_>, _props: &PropertiesRef<'_>, _scene: &mut Scene) { diff --git a/masonry/src/widgets/text_input.rs b/masonry/src/widgets/text_input.rs index c8b46af2e..63a84e29b 100644 --- a/masonry/src/widgets/text_input.rs +++ b/masonry/src/widgets/text_input.rs @@ -275,11 +275,7 @@ impl Widget for TextInput { ctx.place_child(&mut self.placeholder, child_origin); } - if self.clip { - ctx.set_clip_path(size.to_rect()); - } else { - ctx.clear_clip_path(); - } + ctx.set_clips_contents(self.clip); } fn pre_paint(&mut self, ctx: &mut PaintCtx<'_>, props: &PropertiesRef<'_>, scene: &mut Scene) { diff --git a/masonry/src/widgets/virtual_scroll.rs b/masonry/src/widgets/virtual_scroll.rs index 23703d161..c45a85d38 100644 --- a/masonry/src/widgets/virtual_scroll.rs +++ b/masonry/src/widgets/virtual_scroll.rs @@ -685,7 +685,7 @@ impl Widget for VirtualScroll { } fn layout(&mut self, ctx: &mut LayoutCtx<'_>, _props: &PropertiesRef<'_>, size: Size) { - ctx.set_clip_path(size.to_rect()); + ctx.set_clips_contents(true); // The number of loaded items before the anchor let mut height_before_anchor = 0.; let mut total_height = 0.; diff --git a/masonry_core/src/core/contexts.rs b/masonry_core/src/core/contexts.rs index f50ff95df..ef62996c1 100644 --- a/masonry_core/src/core/contexts.rs +++ b/masonry_core/src/core/contexts.rs @@ -941,45 +941,30 @@ impl LayoutCtx<'_> { self.get_child_state(child).layout_border_box_size } - /// Sets the widget's clip path in the widget's content-box coordinate space. + /// Sets whether the [clip shape] is applied to the widget and its children. /// - /// A widget's clip path will have two effects: + /// A widget's clip shape will have two effects: /// - It serves as a mask for painting operations of this widget and its children. /// Note that while all painting done by children will be clipped by this path, /// only the painting done in [`paint`] by this widget itself will be clipped. /// The remaining painting done in [`pre_paint`] and [`post_paint`] will not be clipped. - /// - Pointer events must be inside this path to reach the widget's children. + /// - Pointer events must be inside that shape to reach the widget's children. /// /// [`paint`]: Widget::paint /// [`pre_paint`]: Widget::pre_paint /// [`post_paint`]: Widget::post_paint - pub fn set_clip_path(&mut self, path: Rect) { - // Translate the clip path to the widget's border-box coordinate space. - let path = path + self.widget_state.border_box_translation(); - // We intentionally always log this because clip paths are: - // 1) Relatively rare in the tree - // 2) An easy potential source of items not being visible when expected - trace!("set_clip_path {path:?}"); - self.widget_state.clip_path = Some(path); - // TODO - Updating the clip path may have - // other knock-on effects we'd need to document. - self.widget_state.request_accessibility = true; - self.widget_state.needs_accessibility = true; - self.widget_state.needs_paint = true; - } + /// [clip shape]: crate::doc::masonry_concepts#clip-shape. + pub fn set_clips_contents(&mut self, clips: bool) { + self.widget_state.clips_contents = clips; - /// Removes the widget's clip path. - /// - /// See [`LayoutCtx::set_clip_path`] for details. - pub fn clear_clip_path(&mut self) { - trace!("clear_clip_path"); - self.widget_state.clip_path = None; - // TODO - Updating the clip path may have - // other knock-on effects we'd need to document. self.widget_state.request_accessibility = true; self.widget_state.needs_accessibility = true; self.widget_state.needs_paint = true; + self.global_state.needs_pointer_pass = true; } + + // TODO - Add set_clip_shape(impl Shape) method + // TODO - Add clear_clip_shape() method } impl ComposeCtx<'_> { @@ -1139,16 +1124,12 @@ impl_context_method!( border_box_baseline - self.widget_state.border_box_insets.y1 } - /// The clip path of the widget, if any was set. - /// - /// The returned clip path will be in this widget's content-box coordinate space. + /// Whether the children clips its own contents and that of its children. /// /// For more information, see - /// [`LayoutCtx::set_clip_path`](crate::core::LayoutCtx::set_clip_path). - pub fn clip_path(&self) -> Option { - // Translate the clip path to the widget's content-box coordinate space. - let translation = self.widget_state.border_box_translation(); - self.widget_state.clip_path.map(|path| path - translation) + /// [`LayoutCtx::set_clips_contents`](crate::core::LayoutCtx::set_clips_contents). + pub fn clips_contents(&self) -> bool { + self.widget_state.clips_contents } /// Returns the [`Vec2`] for translating between this widget's diff --git a/masonry_core/src/core/widget.rs b/masonry_core/src/core/widget.rs index ff11a4ecb..9cf2cc4f5 100644 --- a/masonry_core/src/core/widget.rs +++ b/masonry_core/src/core/widget.rs @@ -562,9 +562,9 @@ pub fn find_widget_under_pointer<'c>( let local_pos = ctx.window_transform().inverse() * pos; - if let Some(clip) = ctx.clip_path() - && !clip.contains(local_pos) - { + let is_inside_clip_shape = ctx.content_box().contains(local_pos); + + if ctx.clips_contents() && !is_inside_clip_shape { return None; } @@ -580,6 +580,7 @@ pub fn find_widget_under_pointer<'c>( } } + // TODO - Use some variant of clip shape instead. // If no child is under pointer, test the current widget. if ctx.accepts_pointer_interaction() && ctx.border_box().contains(local_pos) { Some(WidgetRef { widget, ctx }) diff --git a/masonry_core/src/core/widget_state.rs b/masonry_core/src/core/widget_state.rs index 8b7417203..84807fbca 100644 --- a/masonry_core/src/core/widget_state.rs +++ b/masonry_core/src/core/widget_state.rs @@ -119,14 +119,17 @@ pub(crate) struct WidgetState { /// The pixel-snapped position of the baseline in the parent's border-box coordinate space. pub(crate) baseline_y: f64, - // TODO - Use general Shape - // Currently Kurbo doesn't really provide a type that lets us - // efficiently hold an arbitrary shape. - /// The widget's clip path in the widget's border-box coordinate space. - /// - /// This clips the painting of `Widget::paint` and all the painting of children. - /// It does not clip this widget's `Widget::pre_paint` nor `Widget::post_paint`. - pub(crate) clip_path: Option, + // TODO - Add clip_shape + // TODO - Use more efficient type and avoid allocating by default. + // /// The widget's clip path in the widget's border-box coordinate space. + // /// + // /// This clips the painting of `Widget::paint` and all the painting of children. + // /// It does not clip this widget's `Widget::pre_paint` nor `Widget::post_paint`. + // pub(crate) clip_path: Option, + // + /// Whether the widget and its children's scenes are clipped by the + /// [clip shape](crate::doc::masonry_concepts#clip-shape). + pub(crate) clips_contents: bool, /// Local transform used during the mapping of this widget's border-box coordinate space /// to the parent's border-box coordinate space. @@ -275,7 +278,7 @@ impl WidgetState { bounding_box: Rect::ZERO, layout_baseline_offset: 0.0, baseline_y: 0.0, - clip_path: Option::default(), + clips_contents: false, transform: options.transform, window_transform: Affine::IDENTITY, scroll_translation: Vec2::ZERO, @@ -405,21 +408,26 @@ impl WidgetState { ) } - /// Returns the result of intersecting the widget's clip path (if any) with the given rect. + /// Returns the portion of the given rect, if any, that is within the widget's "clip space". /// - /// Both the argument and the result are in window coordinates. + /// If the widget doesn't clip its children, returns the input rect. + /// If the clip path is a non-rectangle, uses the clip paths' bounding box. + /// If the given rect doesn't intersect with the clipping box, returns `None`. /// - /// Returns `None` if the given rect is clipped out. - pub(crate) fn clip_child(&self, child_rect: Rect) -> Option { - if let Some(clip_path) = self.clip_path { - let clip_path_global = self.window_transform.transform_rect_bbox(clip_path); - if clip_path_global.overlaps(child_rect) { - Some(clip_path_global.intersect(child_rect)) - } else { - None - } + /// Both the argument and the result are in window coordinates. + pub(crate) fn clipped_child_box(&self, child_box: Rect) -> Option { + if !self.clips_contents { + return Some(child_box); + } + + let clip_rect = self.border_box_size().to_rect(); + + let bounding_box_global = self.window_transform.transform_rect_bbox(clip_rect); + + if bounding_box_global.overlaps(child_box) { + Some(bounding_box_global.intersect(child_box)) } else { - Some(child_rect) + None } } diff --git a/masonry_core/src/doc/masonry_concepts.md b/masonry_core/src/doc/masonry_concepts.md index c8e8a21d8..acd8c60ab 100644 --- a/masonry_core/src/doc/masonry_concepts.md +++ b/masonry_core/src/doc/masonry_concepts.md @@ -213,6 +213,21 @@ Generally you'll want to convert any window coordinate space geometry into the w Then easily operate on that geometry and finally convert the results back to the window's coordinate space. +## Clip shape + +Widgets have a shape, usually one that matches their visual appearance, which has two purposes: + +- Pointer events outside of that shape will not affect the pointer. +- If the widget is set to clip its contents, pointer events outside the clip shape won't affect the children either. +- If the widget is set to clip its contents, its scene and the children's scenes will be painted inside of the clip shape. + +Currently, the clip shape is hardcoded to be the layout rect. + + + + + + ## Layers A Masonry application is composed of layers. diff --git a/masonry_core/src/passes/accessibility.rs b/masonry_core/src/passes/accessibility.rs index c8648c320..1bb7ccdc0 100644 --- a/masonry_core/src/passes/accessibility.rs +++ b/masonry_core/src/passes/accessibility.rs @@ -123,7 +123,7 @@ fn build_access_node( if ctx.is_disabled() { node.set_disabled(); } - if ctx.widget_state.clip_path.is_some() { + if ctx.widget_state.clips_contents { node.set_clips_children(); } if ctx.accepts_focus() && !ctx.is_disabled() { diff --git a/masonry_core/src/passes/compose.rs b/masonry_core/src/passes/compose.rs index 9a100b4f7..6e3433b64 100644 --- a/masonry_core/src/passes/compose.rs +++ b/masonry_core/src/passes/compose.rs @@ -71,7 +71,9 @@ fn compose_widget( ); let parent_bounding_box = parent_state.bounding_box; - if let Some(child_bounding_box) = parent_state.clip_child(node.item.state.bounding_box) { + if let Some(child_bounding_box) = + parent_state.clipped_child_box(node.item.state.bounding_box) + { parent_state.bounding_box = parent_bounding_box.union(child_bounding_box); } diff --git a/masonry_core/src/passes/paint.rs b/masonry_core/src/passes/paint.rs index c62149f88..14c37568d 100644 --- a/masonry_core/src/passes/paint.rs +++ b/masonry_core/src/passes/paint.rs @@ -80,7 +80,7 @@ fn paint_widget( let transform = state .window_transform .pre_translate(state.border_box_translation()); - let has_clip = state.clip_path.is_some(); + let clips_contents = state.clips_contents; if !is_stashed { let Some((pre_scene, scene, _)) = &mut scene_cache.get(&id) else { debug_panic!( @@ -91,9 +91,10 @@ fn paint_widget( complete_scene.append(pre_scene, Some(transform)); - if let Some(clip) = state.clip_path { - // The clip path is stored in border-box space, so need just window transform. - complete_scene.push_clip_layer(Fill::NonZero, state.window_transform, &clip); + if clips_contents { + let clip_shape = state.border_box_size().to_rect(); + // The clip shape is in border-box space, so need just window transform. + complete_scene.push_clip_layer(Fill::NonZero, state.window_transform, &clip_shape); } complete_scene.append(scene, Some(transform)); @@ -127,7 +128,7 @@ fn paint_widget( stroke(complete_scene, &rect, color, BORDER_WIDTH); } - if has_clip { + if clips_contents { complete_scene.pop_layer(); } From 89d3404b7e67925b95d07a44614d7982a8333bb4 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Wed, 28 Jan 2026 11:47:49 +0100 Subject: [PATCH 2/7] Fix doc --- masonry_core/src/core/widget.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/masonry_core/src/core/widget.rs b/masonry_core/src/core/widget.rs index 9cf2cc4f5..9ad7ba4e5 100644 --- a/masonry_core/src/core/widget.rs +++ b/masonry_core/src/core/widget.rs @@ -367,8 +367,9 @@ pub trait Widget: AsDynWidget + Any { /// /// This is where box shadow, background, and borders are painted. /// - /// This method is not constrained by the clip defined in [`LayoutCtx::set_clip_path`], - /// and can paint things outside the clip. + /// This method is not restricted by the [clip shape]. + /// + /// [clip shape]: crate::doc::masonry_concepts#clip-shape. fn pre_paint(&mut self, ctx: &mut PaintCtx<'_>, props: &PropertiesRef<'_>, scene: &mut Scene) { pre_paint(ctx, props, scene); } @@ -381,8 +382,9 @@ pub trait Widget: AsDynWidget + Any { /// Final paint method, which paints on top of the widget's children. /// - /// This method is not constrained by the clip defined in [`LayoutCtx::set_clip_path`], - /// and can paint things outside the clip. + /// This method is not restricted by the [clip shape]. + /// + /// [clip shape]: crate::doc::masonry_concepts#clip-shape. fn post_paint(&mut self, ctx: &mut PaintCtx<'_>, props: &PropertiesRef<'_>, scene: &mut Scene) { } From b8b49d7d9ba8d2f8b0ce45bf5a171007cb9b15b5 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Thu, 29 Jan 2026 13:27:28 +0100 Subject: [PATCH 3/7] Address code review --- masonry_core/src/core/contexts.rs | 14 ++++++++------ masonry_core/src/doc/masonry_concepts.md | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/masonry_core/src/core/contexts.rs b/masonry_core/src/core/contexts.rs index ef62996c1..9c8fe886c 100644 --- a/masonry_core/src/core/contexts.rs +++ b/masonry_core/src/core/contexts.rs @@ -955,12 +955,14 @@ impl LayoutCtx<'_> { /// [`post_paint`]: Widget::post_paint /// [clip shape]: crate::doc::masonry_concepts#clip-shape. pub fn set_clips_contents(&mut self, clips: bool) { - self.widget_state.clips_contents = clips; + if self.widget_state.clips_contents != clips { + self.widget_state.clips_contents = clips; - self.widget_state.request_accessibility = true; - self.widget_state.needs_accessibility = true; - self.widget_state.needs_paint = true; - self.global_state.needs_pointer_pass = true; + self.widget_state.request_accessibility = true; + self.widget_state.needs_accessibility = true; + self.widget_state.needs_paint = true; + self.global_state.needs_pointer_pass = true; + } } // TODO - Add set_clip_shape(impl Shape) method @@ -1124,7 +1126,7 @@ impl_context_method!( border_box_baseline - self.widget_state.border_box_insets.y1 } - /// Whether the children clips its own contents and that of its children. + /// Whether the widget clips its own contents and that of its children. /// /// For more information, see /// [`LayoutCtx::set_clips_contents`](crate::core::LayoutCtx::set_clips_contents). diff --git a/masonry_core/src/doc/masonry_concepts.md b/masonry_core/src/doc/masonry_concepts.md index acd8c60ab..ba4dfbda6 100644 --- a/masonry_core/src/doc/masonry_concepts.md +++ b/masonry_core/src/doc/masonry_concepts.md @@ -221,7 +221,7 @@ Widgets have a shape, usually one that matches their visual appearance, which ha - If the widget is set to clip its contents, pointer events outside the clip shape won't affect the children either. - If the widget is set to clip its contents, its scene and the children's scenes will be painted inside of the clip shape. -Currently, the clip shape is hardcoded to be the layout rect. +Currently, the clip shape is hardcoded to be a rect with the widget's size and position. From d2445367d86042a0e4356378e387e4c8da97d8d2 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Tue, 10 Feb 2026 17:33:50 +0100 Subject: [PATCH 4/7] Fix compile error --- masonry/src/widgets/label.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/masonry/src/widgets/label.rs b/masonry/src/widgets/label.rs index 30259a93a..36d986fc3 100644 --- a/masonry/src/widgets/label.rs +++ b/masonry/src/widgets/label.rs @@ -430,12 +430,8 @@ impl Widget for Label { let baseline = 0.; // TODO: Use actual baseline, at least for single line text ctx.set_baseline_offset(baseline); - if *line_break_mode == LineBreaking::Clip { - let border_box = size.to_rect() + ctx.border_box_insets(); - ctx.set_clip_path(border_box); - } else { - ctx.clear_clip_path(); - } + let needs_clip = *line_break_mode == LineBreaking::Clip; + ctx.set_clips_contents(needs_clip); } fn paint(&mut self, ctx: &mut PaintCtx<'_>, props: &PropertiesRef<'_>, scene: &mut Scene) { From a01c659641236ba259f59a9b16f04ec671b21b21 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Tue, 10 Feb 2026 17:49:52 +0100 Subject: [PATCH 5/7] Add clip_shape method and fix paint clipping --- masonry_core/src/core/widget_state.rs | 19 +++++++++++++++++++ masonry_core/src/passes/paint.rs | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/masonry_core/src/core/widget_state.rs b/masonry_core/src/core/widget_state.rs index 84807fbca..c18f8546e 100644 --- a/masonry_core/src/core/widget_state.rs +++ b/masonry_core/src/core/widget_state.rs @@ -431,6 +431,25 @@ impl WidgetState { } } + /// The [clip shape] in border-box space. + /// + /// A widget's clip shape will have two effects: + /// - It serves as a mask for painting operations of this widget and its children. + /// Note that while all painting done by children will be clipped by this path, + /// only the painting done in [`paint`] by this widget itself will be clipped. + /// The remaining painting done in [`pre_paint`] and [`post_paint`] will not be clipped. + /// - Pointer events must be inside that shape to reach the widget's children. + /// + /// This currently returns the border-box rect if `clips_contents` is true and `None` otherwise, but in the future we may want to support more complex clip shapes, in which case this method would need to be updated. + /// + /// [`paint`]: Widget::paint + /// [`pre_paint`]: Widget::pre_paint + /// [`post_paint`]: Widget::post_paint + /// [clip shape]: crate::doc::masonry_concepts#clip-shape. + pub(crate) fn clip_shape(&self) -> Rect { + self.border_box_size().to_rect() - self.border_box_translation() + } + pub(crate) fn needs_rewrite_passes(&self) -> bool { self.needs_layout || self.needs_compose diff --git a/masonry_core/src/passes/paint.rs b/masonry_core/src/passes/paint.rs index 14c37568d..2080c8c58 100644 --- a/masonry_core/src/passes/paint.rs +++ b/masonry_core/src/passes/paint.rs @@ -92,7 +92,7 @@ fn paint_widget( complete_scene.append(pre_scene, Some(transform)); if clips_contents { - let clip_shape = state.border_box_size().to_rect(); + let clip_shape = state.clip_shape(); // The clip shape is in border-box space, so need just window transform. complete_scene.push_clip_layer(Fill::NonZero, state.window_transform, &clip_shape); } From 00e961ebcfca9fa198d27ae079573667f2bcf8c1 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Tue, 10 Feb 2026 17:54:00 +0100 Subject: [PATCH 6/7] Update label screenshot --- .../screenshots/label_line_break_modes.png | Bin 7128 -> 7111 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/masonry/screenshots/label_line_break_modes.png b/masonry/screenshots/label_line_break_modes.png index 644d68c97ca8fb8e11c026a110385d2f56cb4be6..5fa0019c0787142097b132f7524bc550f03dcffa 100644 GIT binary patch delta 3458 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 U>bmCzbDF;g)i=|t(m_Q14{iOx0ssI2 delta 3476 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 Date: Tue, 10 Feb 2026 19:06:26 +0100 Subject: [PATCH 7/7] Fix doc error --- masonry_core/src/core/widget_state.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/masonry_core/src/core/widget_state.rs b/masonry_core/src/core/widget_state.rs index c18f8546e..aabdf1e7e 100644 --- a/masonry_core/src/core/widget_state.rs +++ b/masonry_core/src/core/widget_state.rs @@ -436,15 +436,12 @@ impl WidgetState { /// A widget's clip shape will have two effects: /// - It serves as a mask for painting operations of this widget and its children. /// Note that while all painting done by children will be clipped by this path, - /// only the painting done in [`paint`] by this widget itself will be clipped. - /// The remaining painting done in [`pre_paint`] and [`post_paint`] will not be clipped. + /// only the painting done in `Widget::paint` by this widget itself will be clipped. + /// The remaining painting done in `Widget::pre_paint` and `Widget::post_paint` will not be clipped. /// - Pointer events must be inside that shape to reach the widget's children. /// /// This currently returns the border-box rect if `clips_contents` is true and `None` otherwise, but in the future we may want to support more complex clip shapes, in which case this method would need to be updated. /// - /// [`paint`]: Widget::paint - /// [`pre_paint`]: Widget::pre_paint - /// [`post_paint`]: Widget::post_paint /// [clip shape]: crate::doc::masonry_concepts#clip-shape. pub(crate) fn clip_shape(&self) -> Rect { self.border_box_size().to_rect() - self.border_box_translation()