From 1a575550bff35b5be4764601fd4d23191e39380b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 15 Aug 2025 21:23:21 +0200 Subject: [PATCH 01/34] Implement `Default` for `Task` --- runtime/src/task.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtime/src/task.rs b/runtime/src/task.rs index 731c0be8..c4b07994 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -383,6 +383,12 @@ impl Task> { } } +impl Default for Task { + fn default() -> Self { + Task::none() + } +} + impl From<()> for Task { fn from(_value: ()) -> Self { Self::none() From 55a13f72323b473950c6f3d454f8b629faf523b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 15 Aug 2025 21:24:01 +0200 Subject: [PATCH 02/34] Use `Self` in `Default` implementation of `Task` --- runtime/src/task.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/task.rs b/runtime/src/task.rs index c4b07994..34ee1df7 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -385,7 +385,7 @@ impl Task> { impl Default for Task { fn default() -> Self { - Task::none() + Self::none() } } From d8f08e78c6da4e9519d197943f2505a648a9c3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 15 Aug 2025 22:37:50 +0200 Subject: [PATCH 03/34] Fix `tooltip` spamming redraw requests on `FollowCursor` --- widget/src/tooltip.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 8ef6280c..78543f7f 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -202,6 +202,7 @@ where ) { let state = tree.state.downcast_mut::(); + let previous_state = *state; let was_idle = *state == State::Idle; *state = cursor @@ -214,7 +215,9 @@ where if was_idle != is_idle { shell.invalidate_layout(); shell.request_redraw(); - } else if !is_idle && self.position == Position::FollowCursor { + } else if self.position == Position::FollowCursor + && previous_state != *state + { shell.request_redraw(); } From 81ed466164e8bfaae08fb3e1f9e3146e63c46105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 16 Aug 2025 09:27:54 +0200 Subject: [PATCH 04/34] Turn URL in `Color` documentation into an actual link --- core/src/color.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/color.rs b/core/src/color.rs index ad60592a..954aad66 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -197,7 +197,7 @@ impl Color { } /// Returns the relative luminance of the [`Color`]. - /// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance + /// pub fn relative_luminance(self) -> f32 { let linear = self.into_linear(); 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2] From 95769fcd593fcec78a3bdde63e010a3eb09156e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 16 Aug 2025 09:28:49 +0200 Subject: [PATCH 05/34] Add `crop` method to `image` widget :tada: --- widget/src/image.rs | 88 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 8 deletions(-) diff --git a/widget/src/image.rs b/widget/src/image.rs index 80ebd721..20b7b70c 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -59,6 +59,7 @@ pub struct Image { handle: Handle, width: Length, height: Length, + crop: Option>, content_fit: ContentFit, filter_method: FilterMethod, rotation: Rotation, @@ -74,6 +75,7 @@ impl Image { handle: handle.into(), width: Length::Shrink, height: Length::Shrink, + crop: None, content_fit: ContentFit::default(), filter_method: FilterMethod::default(), rotation: Rotation::default(), @@ -145,6 +147,24 @@ impl Image { self.scale = scale.into(); self } + + /// Crops the [`Image`] to the given region described by the [`Rectangle`] in absolute + /// coordinates. + /// + /// Cropping is done before applying any transformation or [`ContentFit`]. In practice, + /// this means that cropping an [`Image`] with this method should produce the same result + /// as cropping it externally (e.g. with an image editor) and creating a new [`Handle`] + /// for the cropped version. + /// + /// However, this method is much more efficient; since it just leverages scissoring during + /// rendering and no image cropping actually takes place. Instead, it reuses the existing + /// image allocations and should be as efficient as not cropping at all! + /// + /// The `region` coordinates will be clamped to the image dimensions, if necessary. + pub fn crop(mut self, region: Rectangle) -> Self { + self.crop = Some(region); + self + } } /// Computes the layout of an [`Image`]. @@ -154,6 +174,7 @@ pub fn layout( handle: &Handle, width: Length, height: Length, + region: Option>, content_fit: ContentFit, rotation: Rotation, expand: bool, @@ -162,9 +183,7 @@ where Renderer: image::Renderer, { // The raw w/h of the underlying image - let image_size = renderer.measure_image(handle); - let image_size = - Size::new(image_size.width as f32, image_size.height as f32); + let image_size = crop(renderer.measure_image(handle), region); // The rotated size of the image let rotated_size = rotation.apply(image_size); @@ -198,6 +217,7 @@ fn drawing_bounds( renderer: &Renderer, bounds: Rectangle, handle: &Handle, + region: Option>, content_fit: ContentFit, rotation: Rotation, scale: f32, @@ -205,8 +225,8 @@ fn drawing_bounds( where Renderer: image::Renderer, { - let Size { width, height } = renderer.measure_image(handle); - let image_size = Size::new(width as f32, height as f32); + let original_size = renderer.measure_image(handle); + let image_size = crop(original_size, region); let rotated_size = rotation.apply(image_size); let adjusted_fit = content_fit.fit(rotated_size, bounds.size()); @@ -217,6 +237,37 @@ where let final_size = image_size * fit_scale * scale; + let (crop_offset, final_size) = if let Some(region) = region { + let x = region.x.min(original_size.width) as f32; + let y = region.y.min(original_size.height) as f32; + let width = image_size.width; + let height = image_size.height; + + let ratio = Vector::new( + original_size.width as f32 / width, + original_size.height as f32 / height, + ); + + let final_size = final_size * ratio; + + let scale = Vector::new( + final_size.width / original_size.width as f32, + final_size.height / original_size.height as f32, + ); + + let offset = match content_fit { + ContentFit::None => Vector::new(x * scale.x, y * scale.y), + _ => Vector::new( + ((original_size.width as f32 - width) / 2.0 - x) * scale.x, + ((original_size.height as f32 - height) / 2.0 - y) * scale.y, + ), + }; + + (offset, final_size) + } else { + (Vector::ZERO, final_size) + }; + let position = match content_fit { ContentFit::None => Point::new( bounds.x + (rotated_size.width - adjusted_fit.width) / 2.0, @@ -228,19 +279,31 @@ where ), }; - Rectangle::new(position, final_size) + Rectangle::new(position + crop_offset, final_size) } fn must_clip(bounds: Rectangle, drawing_bounds: Rectangle) -> bool { drawing_bounds.width > bounds.width || drawing_bounds.height > bounds.height } +fn crop(size: Size, region: Option>) -> Size { + if let Some(region) = region { + Size::new( + region.width.min(size.width) as f32, + region.height.min(size.height) as f32, + ) + } else { + Size::new(size.width as f32, size.height as f32) + } +} + /// Draws an [`Image`] pub fn draw( renderer: &mut Renderer, layout: Layout<'_>, viewport: &Rectangle, handle: &Handle, + crop: Option>, content_fit: ContentFit, filter_method: FilterMethod, rotation: Rotation, @@ -251,8 +314,15 @@ pub fn draw( Handle: Clone, { let bounds = layout.bounds(); - let drawing_bounds = - drawing_bounds(renderer, bounds, handle, content_fit, rotation, scale); + let drawing_bounds = drawing_bounds( + renderer, + bounds, + handle, + crop, + content_fit, + rotation, + scale, + ); if must_clip(bounds, drawing_bounds) { if let Some(bounds) = bounds.intersection(viewport) { @@ -327,6 +397,7 @@ where &self.handle, self.width, self.height, + self.crop, self.content_fit, self.rotation, self.expand, @@ -348,6 +419,7 @@ where layout, viewport, &self.handle, + self.crop, self.content_fit, self.filter_method, self.rotation, From d3e954707952b5425c4c2a067880a088e6c31172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 16 Aug 2025 23:15:20 +0200 Subject: [PATCH 06/34] Implement basic layer merging for `graphics::layer::Stack` --- graphics/src/layer.rs | 32 +++++++++++++++++++++++++++++++- tiny_skia/src/layer.rs | 29 ++++++++++++++++++++++++++++- wgpu/src/image/null.rs | 2 ++ wgpu/src/layer.rs | 32 ++++++++++++++++++++++++++++++++ wgpu/src/quad.rs | 6 ++++++ widget/src/stack.rs | 38 +++++++++++++++++++++++--------------- 6 files changed, 122 insertions(+), 17 deletions(-) diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index c9a818fb..f404ec20 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -9,6 +9,9 @@ pub trait Layer: Default { /// Creates a new [`Layer`] with the given bounds. fn with_bounds(bounds: Rectangle) -> Self; + /// Returns the current bounds of the [`Layer`]. + fn bounds(&self) -> Rectangle; + /// Flushes and settles any pending group of primitives in the [`Layer`]. /// /// This will be called when a [`Layer`] is finished. It allows layers to efficiently @@ -20,6 +23,21 @@ pub trait Layer: Default { /// Clears all the layers contents and resets its bounds. fn reset(&mut self); + + /// Returns the level of the [`Layer`]. + /// + /// The level is the lowest "sublayer" index inside of a [`Layer`]. + /// + /// A [`Layer`] may draw multiple primitive types in a certain order. + /// The level represents the lowest index of the primitive types it + /// contains. + /// + /// Two layers A and B can therefore be merged if they have the same bounds, + /// and the level of A is lower or equal than the level of B. + fn level(&self) -> usize; + + /// Merges a [`Layer`] with the current one. + fn merge(&mut self, _layer: &mut Self); } /// A stack of layers used for drawing. @@ -82,7 +100,19 @@ impl Stack { pub fn pop_clip(&mut self) { self.flush(); - self.current = self.previous.pop().unwrap(); + let previous = self.previous.pop().unwrap(); + + let (head, tail) = self.layers.split_at_mut(previous + 1); + let previous_layer = &mut head[previous]; + let current_layer = &mut tail[self.current - previous - 1]; + + if previous_layer.level() <= current_layer.level() + && previous_layer.bounds() == current_layer.bounds() + { + previous_layer.merge(current_layer); + } + + self.current = previous; } /// Pushes a new [`Transformation`] in the [`Stack`]. diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index 24e62ecb..65e8d469 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -17,8 +17,8 @@ pub struct Layer { pub bounds: Rectangle, pub quads: Vec<(Quad, Background)>, pub primitives: Vec>, - pub text: Vec>, pub images: Vec, + pub text: Vec>, } impl Layer { @@ -284,6 +284,10 @@ impl graphics::Layer for Layer { } } + fn bounds(&self) -> Rectangle { + self.bounds + } + fn flush(&mut self) {} fn resize(&mut self, bounds: Rectangle) { @@ -298,6 +302,29 @@ impl graphics::Layer for Layer { self.text.clear(); self.images.clear(); } + + fn level(&self) -> usize { + if !self.text.is_empty() { + return 3; + } + + if !self.images.is_empty() { + return 2; + } + + if !self.primitives.is_empty() { + return 1; + } + + 0 + } + + fn merge(&mut self, layer: &mut Self) { + self.quads.append(&mut layer.quads); + self.primitives.append(&mut layer.primitives); + self.text.append(&mut layer.text); + self.images.append(&mut layer.images); + } } #[derive(Debug, Clone)] diff --git a/wgpu/src/image/null.rs b/wgpu/src/image/null.rs index cfcd53fc..d0d74cfc 100644 --- a/wgpu/src/image/null.rs +++ b/wgpu/src/image/null.rs @@ -11,4 +11,6 @@ impl Batch { pub fn is_empty(&self) -> bool { true } + + pub fn append(&mut self, _batch: &mut Self) {} } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 2d8fcee5..10b1431d 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -268,6 +268,10 @@ impl graphics::Layer for Layer { } } + fn bounds(&self) -> Rectangle { + self.bounds + } + fn flush(&mut self) { self.flush_meshes(); self.flush_text(); @@ -288,6 +292,34 @@ impl graphics::Layer for Layer { self.pending_meshes.clear(); self.pending_text.clear(); } + + fn level(&self) -> usize { + if !self.text.is_empty() { + return 4; + } + + if !self.images.is_empty() { + return 3; + } + + if !self.primitives.is_empty() { + return 2; + } + + if !self.triangles.is_empty() { + return 1; + } + + 0 + } + + fn merge(&mut self, layer: &mut Self) { + self.quads.append(&mut layer.quads); + self.triangles.append(&mut layer.triangles); + self.primitives.append(&mut layer.primitives); + self.images.append(&mut layer.images); + self.text.append(&mut layer.text); + } } impl Default for Layer { diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index bf9d8961..31bf69cf 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -304,6 +304,12 @@ impl Batch { self.gradients.clear(); self.order.clear(); } + + pub fn append(&mut self, batch: &mut Batch) { + self.solids.append(&mut batch.solids); + self.gradients.append(&mut batch.gradients); + self.order.append(&mut batch.order); + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/widget/src/stack.rs b/widget/src/stack.rs index ee81e4de..16e82678 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -23,6 +23,7 @@ pub struct Stack<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> width: Length, height: Length, children: Vec>, + clip: bool, } impl<'a, Message, Theme, Renderer> Stack<'a, Message, Theme, Renderer> @@ -62,6 +63,7 @@ where width: Length::Shrink, height: Length::Shrink, children, + clip: false, } } @@ -114,6 +116,16 @@ where ) -> Self { children.into_iter().fold(self, Self::push) } + + /// Sets whether the [`Stack`] should clip overflowing content. + /// + /// It has a slight performance overhead during presentation. + /// + /// By default, it is set to `false`. + pub fn clip(mut self, clip: bool) -> Self { + self.clip = clip; + self + } } impl Default for Stack<'_, Message, Renderer> @@ -277,6 +289,12 @@ where viewport: &Rectangle, ) { if let Some(clipped_viewport) = layout.bounds().intersection(viewport) { + let viewport = if self.clip { + &clipped_viewport + } else { + viewport + }; + let layers_below = if cursor.is_over(layout.bounds()) { self.children .iter() @@ -312,26 +330,16 @@ where layout, cursor| { if i > 0 { - renderer.with_layer(clipped_viewport, |renderer| { + renderer.with_layer(*viewport, |renderer| { layer.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor, - &clipped_viewport, + state, renderer, theme, style, layout, cursor, + viewport, ); }); } else { layer.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor, - &clipped_viewport, + state, renderer, theme, style, layout, cursor, + viewport, ); } }; From c639c185d3cc6645a1b36aa451be8a3011baf7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Aug 2025 00:04:45 +0200 Subject: [PATCH 07/34] Fix naive merging logic in `layer::Stack` --- graphics/src/layer.rs | 13 ++++++++----- tiny_skia/src/layer.rs | 18 +++++++++++++++++- wgpu/src/layer.rs | 22 +++++++++++++++++++++- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index f404ec20..4f4219fd 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -24,17 +24,20 @@ pub trait Layer: Default { /// Clears all the layers contents and resets its bounds. fn reset(&mut self); - /// Returns the level of the [`Layer`]. + /// Returns the start level of the [`Layer`]. /// - /// The level is the lowest "sublayer" index inside of a [`Layer`]. + /// A level is a "sublayer" index inside of a [`Layer`]. /// /// A [`Layer`] may draw multiple primitive types in a certain order. /// The level represents the lowest index of the primitive types it /// contains. /// /// Two layers A and B can therefore be merged if they have the same bounds, - /// and the level of A is lower or equal than the level of B. - fn level(&self) -> usize; + /// and the end level of A is lower or equal than the start level of B. + fn start(&self) -> usize; + + /// Returns the end level of the [`Layer`]. + fn end(&self) -> usize; /// Merges a [`Layer`] with the current one. fn merge(&mut self, _layer: &mut Self); @@ -106,7 +109,7 @@ impl Stack { let previous_layer = &mut head[previous]; let current_layer = &mut tail[self.current - previous - 1]; - if previous_layer.level() <= current_layer.level() + if previous_layer.end() <= current_layer.start() && previous_layer.bounds() == current_layer.bounds() { previous_layer.merge(current_layer); diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index 65e8d469..b14458b8 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -303,7 +303,23 @@ impl graphics::Layer for Layer { self.images.clear(); } - fn level(&self) -> usize { + fn start(&self) -> usize { + if !self.quads.is_empty() { + return 0; + } + + if !self.primitives.is_empty() { + return 1; + } + + if !self.images.is_empty() { + return 2; + } + + return 3; + } + + fn end(&self) -> usize { if !self.text.is_empty() { return 3; } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 10b1431d..93968886 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -293,7 +293,27 @@ impl graphics::Layer for Layer { self.pending_text.clear(); } - fn level(&self) -> usize { + fn start(&self) -> usize { + if !self.quads.is_empty() { + return 0; + } + + if !self.triangles.is_empty() { + return 1; + } + + if !self.primitives.is_empty() { + return 2; + } + + if !self.images.is_empty() { + return 3; + } + + 4 + } + + fn end(&self) -> usize { if !self.text.is_empty() { return 4; } From 46167c78c7fbbaff92f4a80f5a65a0e82081644b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Aug 2025 00:07:58 +0200 Subject: [PATCH 08/34] Return `0` in `Layer::start` for empty layers --- tiny_skia/src/layer.rs | 6 +++++- wgpu/src/layer.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index b14458b8..4555cdfa 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -316,7 +316,11 @@ impl graphics::Layer for Layer { return 2; } - return 3; + if !self.text.is_empty() { + return 3; + } + + 0 } fn end(&self) -> usize { diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 93968886..a7d8b446 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -310,7 +310,11 @@ impl graphics::Layer for Layer { return 3; } - 4 + if !self.text.is_empty() { + return 4; + } + + 0 } fn end(&self) -> usize { From d2f36a0a58c0ce3acd53a39226dd3b77d892d527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Aug 2025 00:58:37 +0200 Subject: [PATCH 09/34] Resize base layer in `Stack` before drawing --- core/src/renderer.rs | 4 ++-- core/src/renderer/null.rs | 2 +- graphics/src/layer.rs | 5 ++++- renderer/src/fallback.rs | 4 ++-- runtime/src/user_interface.rs | 4 +--- tiny_skia/src/lib.rs | 4 ++-- wgpu/src/lib.rs | 4 ++-- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 53f59303..84d48304 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -60,8 +60,8 @@ pub trait Renderer { /// Fills a [`Quad`] with the provided [`Background`]. fn fill_quad(&mut self, quad: Quad, background: impl Into); - /// Clears all of the recorded primitives in the [`Renderer`]. - fn clear(&mut self); + /// Resets the [`Renderer`] to start drawing in the `new_bounds` from scratch. + fn reset(&mut self, new_bounds: Rectangle); } /// A polygon with four sides. diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 2251e527..60c87a81 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -16,7 +16,7 @@ impl Renderer for () { fn end_transformation(&mut self) {} - fn clear(&mut self) {} + fn reset(&mut self, _new_bounds: Rectangle) {} fn fill_quad( &mut self, diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index 4f4219fd..dbab0192 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -158,12 +158,15 @@ impl Stack { /// Clears the layers of the [`Stack`], allowing reuse. /// + /// It resizes the base layer bounds to the `new_bounds`. + /// /// This will normally keep layer allocations for future drawing operations. - pub fn clear(&mut self) { + pub fn reset(&mut self, new_bounds: Rectangle) { for layer in self.layers[..self.active_count].iter_mut() { layer.reset(); } + self.layers[0].resize(new_bounds); self.current = 0; self.active_count = 1; self.previous.clear(); diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index 79e22c77..411b5862 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -46,8 +46,8 @@ where delegate!(self, renderer, renderer.fill_quad(quad, background.into())); } - fn clear(&mut self) { - delegate!(self, renderer, renderer.clear()); + fn reset(&mut self, new_bounds: Rectangle) { + delegate!(self, renderer, renderer.reset(new_bounds)); } fn start_layer(&mut self, bounds: Rectangle) { diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 95e7574f..482ffafa 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -482,10 +482,8 @@ where style: &renderer::Style, cursor: mouse::Cursor, ) { - // TODO: Move to shell level (?) - renderer.clear(); - let viewport = Rectangle::with_size(self.bounds); + renderer.reset(viewport); let base_cursor = match &self.overlay { None diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 8f343277..bc379981 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -225,8 +225,8 @@ impl core::Renderer for Renderer { layer.draw_quad(quad, background.into(), transformation); } - fn clear(&mut self) { - self.layers.clear(); + fn reset(&mut self, new_bounds: Rectangle) { + self.layers.reset(new_bounds); } } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index b5af87b7..4361d605 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -648,8 +648,8 @@ impl core::Renderer for Renderer { layer.draw_quad(quad, background.into(), transformation); } - fn clear(&mut self) { - self.layers.clear(); + fn reset(&mut self, new_bounds: Rectangle) { + self.layers.reset(new_bounds); } } From d3a0d0b51292bf9943f5765f53eb3c783e0896e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Aug 2025 04:47:23 +0200 Subject: [PATCH 10/34] Fix dimensions of `image` when `expand` is enabled --- widget/src/image.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/src/image.rs b/widget/src/image.rs index 20b7b70c..f8546b16 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -190,7 +190,7 @@ where // The size to be available to the widget prior to `Shrink`ing let bounds = if expand { - limits.max() + limits.width(width).height(height).max() } else { limits.resolve(width, height, rotated_size) }; From ab04751bc4909b8dc2da117f50bcc0af4678a930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Aug 2025 09:11:19 +0200 Subject: [PATCH 11/34] Apply `Transformation::scale_factor` to `Quad` styling --- core/src/border.rs | 13 +++++++++++++ wgpu/src/layer.rs | 11 +++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/src/border.rs b/core/src/border.rs index da0aaa28..cf73284c 100644 --- a/core/src/border.rs +++ b/core/src/border.rs @@ -263,3 +263,16 @@ impl From for [f32; 4] { ] } } + +impl std::ops::Mul for Radius { + type Output = Self; + + fn mul(self, scale: f32) -> Self::Output { + Self { + top_left: self.top_left * scale, + top_right: self.top_right * scale, + bottom_right: self.bottom_right * scale, + bottom_left: self.bottom_left * scale, + } + } +} diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index a7d8b446..1ad709d0 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -49,11 +49,14 @@ impl Layer { position: [bounds.x, bounds.y], size: [bounds.width, bounds.height], border_color: color::pack(quad.border.color), - border_radius: quad.border.radius.into(), - border_width: quad.border.width, + border_radius: (quad.border.radius * transformation.scale_factor()) + .into(), + border_width: quad.border.width * transformation.scale_factor(), shadow_color: color::pack(quad.shadow.color), - shadow_offset: quad.shadow.offset.into(), - shadow_blur_radius: quad.shadow.blur_radius, + shadow_offset: (quad.shadow.offset * transformation.scale_factor()) + .into(), + shadow_blur_radius: quad.shadow.blur_radius + * transformation.scale_factor(), snap: quad.snap as u32, }; From 4d93837094a98fad97ccc387aef97de83733b8d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Aug 2025 09:31:46 +0200 Subject: [PATCH 12/34] Merge only contiguous layers in `layer::Stack` --- graphics/src/layer.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index dbab0192..de5fa522 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -105,14 +105,18 @@ impl Stack { let previous = self.previous.pop().unwrap(); - let (head, tail) = self.layers.split_at_mut(previous + 1); - let previous_layer = &mut head[previous]; - let current_layer = &mut tail[self.current - previous - 1]; + // Try to merge contiguous layers + if previous + 1 == self.current { + let (head, tail) = self.layers.split_at_mut(previous + 1); + let previous_layer = &mut head[previous]; + let current_layer = &mut tail[0]; - if previous_layer.end() <= current_layer.start() - && previous_layer.bounds() == current_layer.bounds() - { - previous_layer.merge(current_layer); + if previous_layer.end() <= current_layer.start() + && previous_layer.bounds() == current_layer.bounds() + { + previous_layer.merge(current_layer); + self.active_count -= 1; + } } self.current = previous; From c1f7345ceb8a9029df8ff47229831789094737e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Aug 2025 10:05:45 +0200 Subject: [PATCH 13/34] Reuse only the last layer after merging in `layer::Stack` --- graphics/src/layer.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index de5fa522..116a24f3 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -115,7 +115,11 @@ impl Stack { && previous_layer.bounds() == current_layer.bounds() { previous_layer.merge(current_layer); - self.active_count -= 1; + + // We can reuse the last layer + if self.current + 1 == self.active_count { + self.active_count -= 1; + } } } From f2aa570aac4f3f0dff7140f0c6dc36fdad01cac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Aug 2025 22:31:58 +0200 Subject: [PATCH 14/34] Introduce `draw_with_bounds` to `canvas::Cache` Also: - Change `Rectangle::INFINITE` to have coordinates at `f32::NEG_INFINITY` - Change `Frame::with_clip` to _not_ adjust the coordinate system - Rename `Size::INFINITY` to `INFINITE` --- core/src/layout/limits.rs | 2 +- core/src/rectangle.rs | 7 +++++-- core/src/size.rs | 2 +- graphics/src/geometry.rs | 6 +++--- graphics/src/geometry/cache.rs | 24 +++++++++++++++++++----- graphics/src/geometry/frame.rs | 10 +++++++++- renderer/src/fallback.rs | 6 +++--- tiny_skia/src/geometry.rs | 17 +++++------------ tiny_skia/src/lib.rs | 4 ++-- wgpu/src/geometry.rs | 13 +++---------- wgpu/src/lib.rs | 4 ++-- widget/src/tooltip.rs | 4 ++-- winit/src/window.rs | 2 +- 13 files changed, 56 insertions(+), 45 deletions(-) diff --git a/core/src/layout/limits.rs b/core/src/layout/limits.rs index 7fbc7b9d..1aaed526 100644 --- a/core/src/layout/limits.rs +++ b/core/src/layout/limits.rs @@ -12,7 +12,7 @@ impl Limits { /// No limits pub const NONE: Limits = Limits { min: Size::ZERO, - max: Size::INFINITY, + max: Size::INFINITE, }; /// Creates new [`Limits`] with the given minimum and maximum [`Size`]. diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 8adb272f..9f2808dd 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -34,8 +34,11 @@ where } impl Rectangle { - /// A rectangle starting at [`Point::ORIGIN`] with infinite width and height. - pub const INFINITE: Self = Self::new(Point::ORIGIN, Size::INFINITY); + /// A rectangle starting at negative infinity and with infinite width and height. + pub const INFINITE: Self = Self::new( + Point::new(f32::NEG_INFINITY, f32::NEG_INFINITY), + Size::INFINITE, + ); /// Creates a new [`Rectangle`] with its top-left corner in the given /// [`Point`] and with the provided [`Size`]. diff --git a/core/src/size.rs b/core/src/size.rs index 9503cf59..539135bc 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -24,7 +24,7 @@ impl Size { pub const UNIT: Size = Size::new(1., 1.); /// A [`Size`] with infinite width and height. - pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY); + pub const INFINITE: Size = Size::new(f32::INFINITY, f32::INFINITY); /// Returns the minimum of each component of this size and another. pub fn min(self, other: Self) -> Self { diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 2b4b45a6..b32387f8 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -20,7 +20,7 @@ pub use crate::core::{Image, Svg}; pub use crate::gradient::{self, Gradient}; use crate::cache::Cached; -use crate::core::{self, Size}; +use crate::core::{self, Rectangle}; /// A renderer capable of drawing some [`Self::Geometry`]. pub trait Renderer: core::Renderer { @@ -31,7 +31,7 @@ pub trait Renderer: core::Renderer { type Frame: frame::Backend; /// Creates a new [`Self::Frame`]. - fn new_frame(&self, size: Size) -> Self::Frame; + fn new_frame(&self, bounds: Rectangle) -> Self::Frame; /// Draws the given [`Self::Geometry`]. fn draw_geometry(&mut self, geometry: Self::Geometry); @@ -42,7 +42,7 @@ impl Renderer for () { type Geometry = (); type Frame = (); - fn new_frame(&self, _size: Size) -> Self::Frame {} + fn new_frame(&self, _bounds: Rectangle) -> Self::Frame {} fn draw_geometry(&mut self, _geometry: Self::Geometry) {} } diff --git a/graphics/src/geometry/cache.rs b/graphics/src/geometry/cache.rs index d70cee0b..e17506a3 100644 --- a/graphics/src/geometry/cache.rs +++ b/graphics/src/geometry/cache.rs @@ -1,5 +1,5 @@ use crate::cache::{self, Cached}; -use crate::core::Size; +use crate::core::{Rectangle, Size}; use crate::geometry::{self, Frame}; pub use cache::Group; @@ -17,7 +17,7 @@ where #[derive(Debug, Clone)] struct Data { - bounds: Size, + bounds: Rectangle, geometry: T, } @@ -53,7 +53,7 @@ where /// [`Cache`]. /// /// The closure will only be called when - /// - the bounds have changed since the previous draw call. + /// - the size has changed since the previous draw call. /// - the [`Cache`] is empty or has been explicitly cleared. /// /// Otherwise, the previously stored geometry will be returned. The @@ -62,7 +62,21 @@ where pub fn draw( &self, renderer: &Renderer, - bounds: Size, + size: Size, + draw_fn: impl FnOnce(&mut Frame), + ) -> Renderer::Geometry { + self.draw_with_bounds(renderer, Rectangle::with_size(size), draw_fn) + } + + /// Draws geometry using the provided closure and stores it in the + /// [`Cache`]. + /// + /// Analogous to [`draw`](Self::draw), but takes a clipping [`Rectangle`] instead of + /// a [`Size`]. + pub fn draw_with_bounds( + &self, + renderer: &Renderer, + bounds: Rectangle, draw_fn: impl FnOnce(&mut Frame), ) -> Renderer::Geometry { use std::ops::Deref; @@ -82,7 +96,7 @@ where } }; - let mut frame = Frame::new(renderer, bounds); + let mut frame = Frame::with_bounds(renderer, bounds); draw_fn(&mut frame); let geometry = frame.into_geometry().cache(self.raw.group(), previous); diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index 29b534f8..5372ec16 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -16,9 +16,17 @@ where Renderer: geometry::Renderer, { /// Creates a new [`Frame`] with the given dimensions. + /// + /// Any geometry drawn outside of the dimensions will be clipped. + /// If you need further control, use [`with_bounds`](Self::with_bounds). pub fn new(renderer: &Renderer, size: Size) -> Self { + Self::with_bounds(renderer, Rectangle::with_size(size)) + } + + /// Creates a new [`Frame`] with the given clip bounds. + pub fn with_bounds(renderer: &Renderer, bounds: Rectangle) -> Self { Self { - raw: renderer.new_frame(size), + raw: renderer.new_frame(bounds), } } diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index 411b5862..d8d04d45 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -409,13 +409,13 @@ mod geometry { type Geometry = Geometry; type Frame = Frame; - fn new_frame(&self, size: iced_graphics::core::Size) -> Self::Frame { + fn new_frame(&self, bounds: Rectangle) -> Self::Frame { match self { Self::Primary(renderer) => { - Frame::Primary(renderer.new_frame(size)) + Frame::Primary(renderer.new_frame(bounds)) } Self::Secondary(renderer) => { - Frame::Secondary(renderer.new_frame(size)) + Frame::Secondary(renderer.new_frame(bounds)) } } } diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index af55f2a5..a1c0d8a6 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -64,21 +64,14 @@ pub struct Frame { } impl Frame { - pub fn new(size: Size) -> Self { - Self::with_clip(Rectangle::with_size(size)) - } - - pub fn with_clip(clip_bounds: Rectangle) -> Self { + pub fn new(bounds: Rectangle) -> Self { Self { - clip_bounds, + clip_bounds: bounds, stack: Vec::new(), primitives: Vec::new(), images: Vec::new(), text: Vec::new(), - transform: tiny_skia::Transform::from_translate( - clip_bounds.x, - clip_bounds.y, - ), + transform: tiny_skia::Transform::identity(), } } } @@ -238,7 +231,7 @@ impl geometry::frame::Backend for Frame { align_x: text.align_x, align_y: text.align_y, shaping: text.shaping, - clip_bounds: Rectangle::with_size(Size::INFINITY), + clip_bounds: Rectangle::with_size(Size::INFINITE), }); } else { text.draw_with(|path, color| self.fill(&path, color)); @@ -265,7 +258,7 @@ impl geometry::frame::Backend for Frame { } fn draft(&mut self, clip_bounds: Rectangle) -> Self { - Self::with_clip(clip_bounds) + Self::new(clip_bounds) } fn paste(&mut self, frame: Self) { diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index bc379981..ca00737f 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -294,8 +294,8 @@ impl graphics::geometry::Renderer for Renderer { type Geometry = Geometry; type Frame = geometry::Frame; - fn new_frame(&self, size: core::Size) -> Self::Frame { - geometry::Frame::new(size) + fn new_frame(&self, bounds: Rectangle) -> Self::Frame { + geometry::Frame::new(bounds) } fn draw_geometry(&mut self, geometry: Self::Geometry) { diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index c3c66464..18e26794 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -105,13 +105,8 @@ pub struct Frame { } impl Frame { - /// Creates a new [`Frame`] with the given [`Size`]. - pub fn new(size: Size) -> Frame { - Self::with_clip(Rectangle::with_size(size)) - } - /// Creates a new [`Frame`] with the given clip bounds. - pub fn with_clip(bounds: Rectangle) -> Frame { + pub fn new(bounds: Rectangle) -> Frame { Frame { clip_bounds: bounds, buffers: BufferStack::new(), @@ -120,9 +115,7 @@ impl Frame { text: Vec::new(), transforms: Transforms { previous: Vec::new(), - current: Transform(lyon::math::Transform::translation( - bounds.x, bounds.y, - )), + current: Transform(lyon::math::Transform::identity()), }, fill_tessellator: tessellation::FillTessellator::new(), stroke_tessellator: tessellation::StrokeTessellator::new(), @@ -409,7 +402,7 @@ impl geometry::frame::Backend for Frame { } fn draft(&mut self, clip_bounds: Rectangle) -> Frame { - Frame::with_clip(clip_bounds) + Frame::new(clip_bounds) } fn paste(&mut self, frame: Frame) { diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 4361d605..28f6cb48 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -760,8 +760,8 @@ impl graphics::geometry::Renderer for Renderer { type Geometry = Geometry; type Frame = geometry::Frame; - fn new_frame(&self, size: core::Size) -> Self::Frame { - geometry::Frame::new(size) + fn new_frame(&self, bounds: Rectangle) -> Self::Frame { + geometry::Frame::new(bounds) } fn draw_geometry(&mut self, geometry: Self::Geometry) { diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 78543f7f..f3dea455 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -394,7 +394,7 @@ where if self.snap_within_viewport { viewport.size() } else { - Size::INFINITY + Size::INFINITE }, ) .shrink(Padding::new(self.padding)), @@ -508,7 +508,7 @@ where &defaults, layout.children().next().unwrap(), cursor_position, - &Rectangle::with_size(Size::INFINITY), + &Rectangle::with_size(Size::INFINITE), ); } } diff --git a/winit/src/window.rs b/winit/src/window.rs index e9a058af..f2d12677 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -346,7 +346,7 @@ where self.content = Renderer::Paragraph::with_spans(Text { content: &spans, - bounds: Size::INFINITY, + bounds: Size::INFINITE, size: preedit .text_size .unwrap_or_else(|| renderer.default_size()), From 47f0d5bae4198e471cf913898b1f7102193ba399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Aug 2025 02:29:13 +0200 Subject: [PATCH 15/34] Add `child` method to `Layout` --- core/src/layout.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/core/src/layout.rs b/core/src/layout.rs index 98d05602..9938342f 100644 --- a/core/src/layout.rs +++ b/core/src/layout.rs @@ -53,7 +53,7 @@ impl<'a> Layout<'a> { } } - /// Returns an iterator over the [`Layout`] of the children of a [`Node`]. + /// Returns an iterator over the children of this [`Layout`]. pub fn children(self) -> impl DoubleEndedIterator> { self.node.children().iter().map(move |node| { Layout::with_offset( @@ -62,6 +62,19 @@ impl<'a> Layout<'a> { ) }) } + + /// Returns the [`Layout`] of the child at the given index. + /// + /// This can be useful if you ever need to access children out of order + /// for layering purposes. + /// + /// # Panics + /// Panics if index is out of bounds. + pub fn child(self, index: usize) -> Layout<'a> { + let node = &self.node.children()[index]; + + Layout::with_offset(Vector::new(self.position.x, self.position.y), node) + } } /// Produces a [`Node`] with two children nodes one right next to each other. From ab4fdaf91d05496ad10c9db8352d8b1cd6e8bdf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Aug 2025 20:04:55 +0200 Subject: [PATCH 16/34] Consider pending geometry in `wgpu::Layer` --- wgpu/src/layer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 1ad709d0..c8850e25 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -301,7 +301,7 @@ impl graphics::Layer for Layer { return 0; } - if !self.triangles.is_empty() { + if !self.triangles.is_empty() || !self.pending_meshes.is_empty() { return 1; } @@ -313,7 +313,7 @@ impl graphics::Layer for Layer { return 3; } - if !self.text.is_empty() { + if !self.text.is_empty() || !self.pending_text.is_empty() { return 4; } @@ -321,7 +321,7 @@ impl graphics::Layer for Layer { } fn end(&self) -> usize { - if !self.text.is_empty() { + if !self.text.is_empty() || !self.pending_text.is_empty() { return 4; } @@ -333,7 +333,7 @@ impl graphics::Layer for Layer { return 2; } - if !self.triangles.is_empty() { + if !self.triangles.is_empty() || !self.pending_meshes.is_empty() { return 1; } From fb5ac7dcb0d01893a2ff2a04aab5213b48ac5a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Aug 2025 20:51:57 +0200 Subject: [PATCH 17/34] Perform `layer::Stack::merge` before rendering --- graphics/src/layer.rs | 57 +++++++++++++++++++++---------------------- wgpu/src/layer.rs | 8 +++--- wgpu/src/lib.rs | 4 ++- 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index 116a24f3..ea7911f9 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -103,27 +103,7 @@ impl Stack { pub fn pop_clip(&mut self) { self.flush(); - let previous = self.previous.pop().unwrap(); - - // Try to merge contiguous layers - if previous + 1 == self.current { - let (head, tail) = self.layers.split_at_mut(previous + 1); - let previous_layer = &mut head[previous]; - let current_layer = &mut tail[0]; - - if previous_layer.end() <= current_layer.start() - && previous_layer.bounds() == current_layer.bounds() - { - previous_layer.merge(current_layer); - - // We can reuse the last layer - if self.current + 1 == self.active_count { - self.active_count -= 1; - } - } - } - - self.current = previous; + self.current = self.previous.pop().unwrap(); } /// Pushes a new [`Transformation`] in the [`Stack`]. @@ -142,13 +122,6 @@ impl Stack { let _ = self.transformations.pop(); } - /// Returns an iterator over mutable references to the layers in the [`Stack`]. - pub fn iter_mut(&mut self) -> impl Iterator { - self.flush(); - - self.layers[..self.active_count].iter_mut() - } - /// Returns an iterator over immutable references to the layers in the [`Stack`]. pub fn iter(&self) -> impl Iterator { self.layers[..self.active_count].iter() @@ -159,11 +132,37 @@ impl Stack { &self.layers[..self.active_count] } - /// Flushes and settles any primitives in the current layer of the [`Stack`]. + /// Flushes and settles any primitives in the [`Stack`]. pub fn flush(&mut self) { self.layers[self.current].flush(); } + /// Performs layer merging wherever possible. + /// + /// Flushes and settles any primitives in the [`Stack`]. + pub fn merge(&mut self) { + self.flush(); + + let mut current = 0; + + while current < self.active_count { + let (head, tail) = self.layers.split_at_mut(current + 1); + + let layer = &mut head[current]; + let mut candidate = 0; + + while let Some(next_layer) = tail.get_mut(candidate) + && layer.end() <= next_layer.start() + && layer.bounds() == next_layer.bounds() + { + layer.merge(next_layer); + candidate += 1; + } + + current += candidate + 1; + } + } + /// Clears the layers of the [`Stack`], allowing reuse. /// /// It resizes the base layer bounds to the `new_bounds`. diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index c8850e25..1ad709d0 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -301,7 +301,7 @@ impl graphics::Layer for Layer { return 0; } - if !self.triangles.is_empty() || !self.pending_meshes.is_empty() { + if !self.triangles.is_empty() { return 1; } @@ -313,7 +313,7 @@ impl graphics::Layer for Layer { return 3; } - if !self.text.is_empty() || !self.pending_text.is_empty() { + if !self.text.is_empty() { return 4; } @@ -321,7 +321,7 @@ impl graphics::Layer for Layer { } fn end(&self) -> usize { - if !self.text.is_empty() || !self.pending_text.is_empty() { + if !self.text.is_empty() { return 4; } @@ -333,7 +333,7 @@ impl graphics::Layer for Layer { return 2; } - if !self.triangles.is_empty() || !self.pending_meshes.is_empty() { + if !self.triangles.is_empty() { return 1; } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 28f6cb48..a24677bd 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -311,7 +311,9 @@ impl Renderer { viewport.physical_size(), )); - for layer in self.layers.iter_mut() { + self.layers.merge(); + + for layer in self.layers.iter() { if physical_bounds .intersection(&(layer.bounds * scale_factor)) .and_then(Rectangle::snap) From 806500048d211e561eeffc61be8314dac8f42a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 19 Aug 2025 05:45:43 +0200 Subject: [PATCH 18/34] Merge layers from top to bottom in `layer::Stack` --- graphics/src/layer.rs | 41 +++++++++++++++++++++++++++++------------ tiny_skia/src/layer.rs | 22 +++++++++++++--------- wgpu/src/layer.rs | 26 +++++++++++++++----------- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index ea7911f9..bd14718c 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -143,23 +143,40 @@ impl Stack { pub fn merge(&mut self) { self.flush(); - let mut current = 0; + let mut left = self.active_count; - while current < self.active_count { - let (head, tail) = self.layers.split_at_mut(current + 1); + while left > 1 { + let mut current = left - 1; + let mut last = &self.layers[current]; + let mut last_start = last.start(); - let layer = &mut head[current]; - let mut candidate = 0; + while current > 0 { + let candidate = &self.layers[current - 1]; + let start = candidate.start(); + let end = candidate.end(); - while let Some(next_layer) = tail.get_mut(candidate) - && layer.end() <= next_layer.start() - && layer.bounds() == next_layer.bounds() - { - layer.merge(next_layer); - candidate += 1; + if end == 0 { + current -= 1; + continue; + } + + if end > last_start || candidate.bounds() != last.bounds() { + break; + } + + last = candidate; + last_start = start; + current -= 1; } - current += candidate + 1; + let (head, tail) = self.layers.split_at_mut(current + 1); + let layer = &mut head[current]; + + for middle in &mut tail[0..left - current - 1] { + layer.merge(middle); + } + + left = current; } } diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index 4555cdfa..00097f83 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -305,34 +305,38 @@ impl graphics::Layer for Layer { fn start(&self) -> usize { if !self.quads.is_empty() { - return 0; - } - - if !self.primitives.is_empty() { return 1; } - if !self.images.is_empty() { + if !self.primitives.is_empty() { return 2; } - if !self.text.is_empty() { + if !self.images.is_empty() { return 3; } - 0 + if !self.text.is_empty() { + return 4; + } + + usize::MAX } fn end(&self) -> usize { if !self.text.is_empty() { - return 3; + return 4; } if !self.images.is_empty() { - return 2; + return 3; } if !self.primitives.is_empty() { + return 2; + } + + if !self.quads.is_empty() { return 1; } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 1ad709d0..cd3a7de7 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -298,42 +298,46 @@ impl graphics::Layer for Layer { fn start(&self) -> usize { if !self.quads.is_empty() { - return 0; - } - - if !self.triangles.is_empty() { return 1; } - if !self.primitives.is_empty() { + if !self.triangles.is_empty() { return 2; } - if !self.images.is_empty() { + if !self.primitives.is_empty() { return 3; } - if !self.text.is_empty() { + if !self.images.is_empty() { return 4; } - 0 + if !self.text.is_empty() { + return 5; + } + + usize::MAX } fn end(&self) -> usize { if !self.text.is_empty() { - return 4; + return 5; } if !self.images.is_empty() { - return 3; + return 4; } if !self.primitives.is_empty() { - return 2; + return 3; } if !self.triangles.is_empty() { + return 2; + } + + if !self.quads.is_empty() { return 1; } From 9490d735c531ee128484d6a0ffd11f1aef6be29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 19 Aug 2025 08:06:25 +0200 Subject: [PATCH 19/34] Avoid merging into empty layers in `layer::Stack` --- graphics/src/layer.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index bd14718c..31cfe68a 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -143,39 +143,56 @@ impl Stack { pub fn merge(&mut self) { self.flush(); + // These are the layers left to process let mut left = self.active_count; + // There must be at least 2 or more layers to merge while left > 1 { + // We set our target as the topmost layer left to process let mut current = left - 1; - let mut last = &self.layers[current]; - let mut last_start = last.start(); + let mut target = &self.layers[current]; + let mut target_start = target.start(); + let mut target_index = current; + // We scan downwards for a contiguous block of mergeable layer candidates while current > 0 { let candidate = &self.layers[current - 1]; let start = candidate.start(); let end = candidate.end(); + // We skip empty layers if end == 0 { current -= 1; continue; } - if end > last_start || candidate.bounds() != last.bounds() { + // Candidate can be merged if primitive sublayers do not overlap with + // previous targets and the clipping bounds match + if end > target_start || candidate.bounds() != target.bounds() { break; } - last = candidate; - last_start = start; + // Candidate is not empty and can be merged into + target = candidate; + target_start = start; + target_index = current; current -= 1; } - let (head, tail) = self.layers.split_at_mut(current + 1); - let layer = &mut head[current]; + // We merge all the layers scanned into the target + // + // Since we use `target_index` instead of `current`, we + // deliberately avoid merging into empty layers. + // + // If no candidates were mergeable, this is a no-op. + let (head, tail) = self.layers.split_at_mut(target_index + 1); + let layer = &mut head[target_index]; - for middle in &mut tail[0..left - current - 1] { + for middle in &mut tail[0..left - target_index - 1] { layer.merge(middle); } + // Empty layers found after the target can be skipped left = current; } } From 31bc6d48cd114c4129ed9fe661c2f9060289e248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Aug 2025 22:42:15 +0200 Subject: [PATCH 20/34] Make `Widget::layout` and `operate` mutable ... and bless `responsive`! --- core/src/element.rs | 10 +- core/src/layout/flex.rs | 21 +- core/src/widget.rs | 4 +- core/src/widget/text.rs | 4 +- examples/custom_quad/src/main.rs | 2 +- examples/custom_widget/src/main.rs | 2 +- examples/geometry/src/main.rs | 2 +- examples/loading_spinners/src/circular.rs | 2 +- examples/loading_spinners/src/linear.rs | 2 +- examples/loupe/src/main.rs | 4 +- examples/toast/src/main.rs | 12 +- runtime/src/user_interface.rs | 10 +- widget/src/button.rs | 8 +- widget/src/canvas.rs | 2 +- widget/src/checkbox.rs | 4 +- widget/src/column.rs | 10 +- widget/src/combo_box.rs | 2 +- widget/src/container.rs | 10 +- widget/src/float.rs | 8 +- widget/src/grid.rs | 12 +- widget/src/helpers.rs | 22 +- widget/src/image.rs | 2 +- widget/src/image/viewer.rs | 2 +- widget/src/keyed/column.rs | 10 +- widget/src/lazy.rs | 18 +- widget/src/lazy/component.rs | 12 +- widget/src/lazy/responsive.rs | 384 ++++------------------ widget/src/mouse_area.rs | 14 +- widget/src/overlay/menu.rs | 2 +- widget/src/pane_grid.rs | 20 +- widget/src/pane_grid/content.rs | 14 +- widget/src/pane_grid/title_bar.rs | 26 +- widget/src/pick_list.rs | 2 +- widget/src/pin.rs | 8 +- widget/src/progress_bar.rs | 2 +- widget/src/qr_code.rs | 2 +- widget/src/radio.rs | 2 +- widget/src/row.rs | 18 +- widget/src/rule.rs | 2 +- widget/src/scrollable.rs | 8 +- widget/src/sensor.rs | 14 +- widget/src/shader.rs | 2 +- widget/src/slider.rs | 2 +- widget/src/space.rs | 2 +- widget/src/stack.rs | 23 +- widget/src/svg.rs | 2 +- widget/src/table.rs | 17 +- widget/src/text/rich.rs | 2 +- widget/src/text_editor.rs | 4 +- widget/src/text_input.rs | 6 +- widget/src/themer.rs | 8 +- widget/src/toggler.rs | 2 +- widget/src/tooltip.rs | 16 +- widget/src/vertical_slider.rs | 2 +- 54 files changed, 284 insertions(+), 519 deletions(-) diff --git a/core/src/element.rs b/core/src/element.rs index a3f60127..6a71296c 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -291,7 +291,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -300,7 +300,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -426,7 +426,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -435,7 +435,7 @@ where } fn operate( - &self, + &mut self, state: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -554,7 +554,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, _limits: &layout::Limits, diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index f178a993..3f28f0d9 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -68,7 +68,7 @@ pub fn resolve( padding: Padding, spacing: f32, align_items: Alignment, - items: &[Element<'_, Message, Theme, Renderer>], + items: &mut [Element<'_, Message, Theme, Renderer>], trees: &mut [widget::Tree], ) -> Node where @@ -95,7 +95,8 @@ where // We lay out non-fluid elements in the main axis. // If we need to compress the cross axis, then we skip any of these elements // that are also fluid in the cross axis. - for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { + for (i, (child, tree)) in items.iter_mut().zip(trees.iter_mut()).enumerate() + { let (fill_main_factor, fill_cross_factor) = { let size = child.as_widget().size(); @@ -117,7 +118,7 @@ where Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = - child.as_widget().layout(tree, renderer, &child_limits); + child.as_widget_mut().layout(tree, renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -141,7 +142,8 @@ where // We can defer the layout of any elements that have a fixed size in the main axis, // allowing them to use the cross calculations of the next pass. if cross_compress && some_fill_cross { - for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() + for (i, (child, tree)) in + items.iter_mut().zip(trees.iter_mut()).enumerate() { let (main_size, cross_size) = { let size = child.as_widget().size(); @@ -161,7 +163,7 @@ where Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = - child.as_widget().layout(tree, renderer, &child_limits); + child.as_widget_mut().layout(tree, renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -186,7 +188,8 @@ where // THIRD PASS // We lay out the elements that are fluid in the main axis. // We use the remaining space to evenly allocate space based on fill factors. - for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { + for (i, (child, tree)) in items.iter_mut().zip(trees.iter_mut()).enumerate() + { let (fill_main_factor, fill_cross_factor) = { let size = child.as_widget().size(); @@ -225,7 +228,7 @@ where ); let layout = - child.as_widget().layout(tree, renderer, &child_limits); + child.as_widget_mut().layout(tree, renderer, &child_limits); cross = cross.max(axis.cross(layout.size())); nodes[i] = layout; @@ -237,7 +240,7 @@ where // These are elements that must be compressed in their cross axis and have // a fixed length in the main axis. if cross_compress && some_fill_cross { - for (i, (child, tree)) in items.iter().zip(trees).enumerate() { + for (i, (child, tree)) in items.iter_mut().zip(trees).enumerate() { let (main_size, cross_size) = { let size = child.as_widget().size(); @@ -255,7 +258,7 @@ where Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = - child.as_widget().layout(tree, renderer, &child_limits); + child.as_widget_mut().layout(tree, renderer, &child_limits); let size = layout.size(); cross = cross.max(axis.cross(size)); diff --git a/core/src/widget.rs b/core/src/widget.rs index 655c010b..3b39ffcc 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -58,7 +58,7 @@ where /// This [`layout::Node`] is used by the runtime to compute the [`Layout`] of the /// user interface. fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -102,7 +102,7 @@ where /// Applies an [`Operation`] to the [`Widget`]. fn operate( - &self, + &mut self, _state: &mut Tree, _layout: Layout<'_>, _renderer: &Renderer, diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 24966211..c1f1e87e 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -207,7 +207,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -245,7 +245,7 @@ where } fn operate( - &self, + &mut self, _state: &mut Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs index 4ea58511..fdc3c0c2 100644 --- a/examples/custom_quad/src/main.rs +++ b/examples/custom_quad/src/main.rs @@ -175,7 +175,7 @@ mod quad { } fn layout( - &self, + &mut self, _tree: &mut widget::Tree, _renderer: &Renderer, _limits: &layout::Limits, diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index e5db6139..c44775be 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -33,7 +33,7 @@ mod circle { } fn layout( - &self, + &mut self, _tree: &mut widget::Tree, _renderer: &Renderer, _limits: &layout::Limits, diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index f1025675..1334a52f 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -27,7 +27,7 @@ mod rainbow { } fn layout( - &self, + &mut self, _tree: &mut widget::Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 24293138..c5d6df6f 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -253,7 +253,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/examples/loading_spinners/src/linear.rs b/examples/loading_spinners/src/linear.rs index a6713c7a..e9b4a20e 100644 --- a/examples/loading_spinners/src/linear.rs +++ b/examples/loading_spinners/src/linear.rs @@ -167,7 +167,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs index c141e758..89bc82da 100644 --- a/examples/loupe/src/main.rs +++ b/examples/loupe/src/main.rs @@ -96,12 +96,12 @@ mod loupe { } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout(tree, renderer, limits) + self.content.as_widget_mut().layout(tree, renderer, limits) } fn draw( diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index b4e93a32..dc8c2593 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -287,12 +287,12 @@ mod toast { } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout( + self.content.as_widget_mut().layout( &mut tree.children[0], renderer, limits, @@ -343,14 +343,14 @@ mod toast { } fn operate( - &self, + &mut self, state: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { operation.container(None, layout.bounds(), &mut |operation| { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut state.children[0], layout, renderer, @@ -582,12 +582,12 @@ mod toast { ) { operation.container(None, layout.bounds(), &mut |operation| { self.toasts - .iter() + .iter_mut() .zip(self.state.iter_mut()) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 482ffafa..26c96201 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -97,12 +97,12 @@ where cache: Cache, renderer: &mut Renderer, ) -> Self { - let root = root.into(); + let mut root = root.into(); let Cache { mut state } = cache; state.diff(root.as_widget()); - let base = root.as_widget().layout( + let base = root.as_widget_mut().layout( &mut state, renderer, &layout::Limits::new(Size::ZERO, bounds), @@ -234,7 +234,7 @@ where if shell.is_layout_invalid() { drop(maybe_overlay); - self.base = self.root.as_widget().layout( + self.base = self.root.as_widget_mut().layout( &mut self.state, renderer, &layout::Limits::new(Size::ZERO, self.bounds), @@ -335,7 +335,7 @@ where input_method.merge(shell.input_method()); shell.revalidate_layout(|| { - self.base = self.root.as_widget().layout( + self.base = self.root.as_widget_mut().layout( &mut self.state, renderer, &layout::Limits::new(Size::ZERO, self.bounds), @@ -539,7 +539,7 @@ where ) { let viewport = Rectangle::with_size(self.bounds); - self.root.as_widget().operate( + self.root.as_widget_mut().operate( &mut self.state, Layout::new(&self.base), renderer, diff --git a/widget/src/button.rs b/widget/src/button.rs index ac4a27cb..6ae8d3e5 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -235,7 +235,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -246,7 +246,7 @@ where self.height, self.padding, |limits| { - self.content.as_widget().layout( + self.content.as_widget_mut().layout( &mut tree.children[0], renderer, limits, @@ -256,14 +256,14 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { operation.container(None, layout.bounds(), &mut |operation| { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout.children().next().unwrap(), renderer, diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 50acade5..b1b6ac13 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -207,7 +207,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 42ef89fd..32e81538 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -269,7 +269,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -447,7 +447,7 @@ where } fn operate( - &self, + &mut self, _state: &mut Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/widget/src/column.rs b/widget/src/column.rs index 6c126048..39507a78 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -206,7 +206,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -222,13 +222,13 @@ where self.padding, self.spacing, self.align, - &self.children, + &mut self.children, &mut tree.children, ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -236,12 +236,12 @@ where ) { operation.container(None, layout.bounds(), &mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index 581a4fcd..f94705d8 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -471,7 +471,7 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/container.rs b/widget/src/container.rs index ab332b32..286c5a63 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -259,7 +259,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -273,12 +273,14 @@ where self.padding, self.horizontal_alignment, self.vertical_alignment, - |limits| self.content.as_widget().layout(tree, renderer, limits), + |limits| { + self.content.as_widget_mut().layout(tree, renderer, limits) + }, ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -288,7 +290,7 @@ where self.id.as_ref().map(|id| &id.0), layout.bounds(), &mut |operation| { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( tree, layout.children().next().unwrap(), renderer, diff --git a/widget/src/float.rs b/widget/src/float.rs index b61b4319..1ff6ab10 100644 --- a/widget/src/float.rs +++ b/widget/src/float.rs @@ -116,12 +116,12 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout(tree, renderer, limits) + self.content.as_widget_mut().layout(tree, renderer, limits) } fn update( @@ -197,14 +197,14 @@ where } fn operate( - &self, + &mut self, state: &mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { self.content - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); } diff --git a/widget/src/grid.rs b/widget/src/grid.rs index 4a08dc55..25e3ebe0 100644 --- a/widget/src/grid.rs +++ b/widget/src/grid.rs @@ -176,7 +176,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -220,10 +220,10 @@ where let mut row_height = 0.0f32; for (i, (child, tree)) in - self.children.iter().zip(&mut tree.children).enumerate() + self.children.iter_mut().zip(&mut tree.children).enumerate() { let node = child - .as_widget() + .as_widget_mut() .layout(tree, renderer, &cell_limits) .move_to((x, y)); @@ -251,7 +251,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -259,12 +259,12 @@ where ) { operation.container(None, layout.bounds(), &mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 6815c3fc..9e01fa99 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -609,12 +609,12 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout(tree, renderer, limits) + self.content.as_widget_mut().layout(tree, renderer, limits) } fn draw( @@ -633,14 +633,14 @@ where } fn operate( - &self, + &mut self, state: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn operation::Operation, ) { self.content - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); } @@ -772,18 +772,18 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let base = self.base.as_widget().layout( + let base = self.base.as_widget_mut().layout( &mut tree.children[0], renderer, limits, ); - let top = self.top.as_widget().layout( + let top = self.top.as_widget_mut().layout( &mut tree.children[1], renderer, &layout::Limits::new(Size::ZERO, base.size()), @@ -834,18 +834,20 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn operation::Operation, ) { - let children = [&self.base, &self.top] + let children = [&mut self.base, &mut self.top] .into_iter() .zip(layout.children().zip(&mut tree.children)); for (child, (layout, tree)) in children { - child.as_widget().operate(tree, layout, renderer, operation); + child + .as_widget_mut() + .operate(tree, layout, renderer, operation); } } diff --git a/widget/src/image.rs b/widget/src/image.rs index f8546b16..3b29e4f5 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -386,7 +386,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 811241a9..205f84d5 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -117,7 +117,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index a774c239..6ca6f485 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -253,7 +253,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -272,13 +272,13 @@ where self.padding, self.spacing, self.align_items, - &self.children, + &mut self.children, &mut tree.children, ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -286,12 +286,12 @@ where ) { operation.container(None, layout.bounds(), &mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index b538b460..0d56269b 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -166,27 +166,29 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.with_element(|element| { - element - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.with_element_mut(|element| { + element.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) }) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - self.with_element(|element| { - element.as_widget().operate( + self.with_element_mut(|element| { + element.as_widget_mut().operate( &mut tree.children[0], layout, renderer, diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index 8bd04d64..1b582dee 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -299,15 +299,15 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { let t = tree.state.downcast_mut::>>>(); - self.with_element(|element| { - element.as_widget().layout( + self.with_element_mut(|element| { + element.as_widget_mut().layout( &mut t.borrow_mut().as_mut().unwrap().children[0], renderer, limits, @@ -378,7 +378,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -387,8 +387,8 @@ where self.rebuild_element_with_operation(layout, operation); let tree = tree.state.downcast_mut::>>>(); - self.with_element(|element| { - element.as_widget().operate( + self.with_element_mut(|element| { + element.as_widget_mut().operate( &mut tree.borrow_mut().as_mut().unwrap().children[0], layout, renderer, diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index 4e90a178..f72250c6 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -5,16 +5,10 @@ use crate::core::renderer; use crate::core::widget; use crate::core::widget::tree::{self, Tree}; use crate::core::{ - self, Clipboard, Element, Event, Length, Point, Rectangle, Shell, Size, - Vector, Widget, + self, Clipboard, Element, Event, Length, Rectangle, Shell, Size, Vector, + Widget, }; use crate::horizontal_space; -use crate::runtime::overlay::Nested; - -use ouroboros::self_referencing; -use std::cell::{RefCell, RefMut}; -use std::marker::PhantomData; -use std::ops::Deref; /// A widget that is aware of its dimensions. /// @@ -29,7 +23,7 @@ pub struct Responsive< Renderer = crate::Renderer, > { view: Box Element<'a, Message, Theme, Renderer> + 'a>, - content: RefCell>, + content: Element<'a, Message, Theme, Renderer>, } impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer> @@ -47,91 +41,13 @@ where ) -> Self { Self { view: Box::new(view), - content: RefCell::new(Content { - size: Size::ZERO, - layout: None, - is_layout_invalid: true, - element: Element::new(horizontal_space().width(0)), - }), + content: Element::new(horizontal_space().width(0)), } } } -struct Content<'a, Message, Theme, Renderer> { - size: Size, - layout: Option, - is_layout_invalid: bool, - element: Element<'a, Message, Theme, Renderer>, -} - -impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - fn layout(&mut self, tree: &mut Tree, renderer: &Renderer) { - if self.layout.is_none() || self.is_layout_invalid { - self.layout = Some(self.element.as_widget().layout( - tree, - renderer, - &layout::Limits::new(Size::ZERO, self.size), - )); - self.is_layout_invalid = false; - } - } - - fn update( - &mut self, - tree: &mut Tree, - new_size: Size, - view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>, - ) { - if self.size != new_size { - self.element = view(new_size); - self.size = new_size; - self.layout = None; - - tree.diff(&self.element); - } else { - let is_tree_empty = - tree.tag == tree::Tag::stateless() && tree.children.is_empty(); - - if is_tree_empty { - self.layout = None; - tree.diff(&self.element); - } - } - } - - fn resolve( - &mut self, - tree: &mut Tree, - renderer: R, - layout: Layout<'_>, - view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>, - f: impl FnOnce( - &mut Tree, - R, - Layout<'_>, - &mut Element<'a, Message, Theme, Renderer>, - ) -> T, - ) -> T - where - R: Deref, - { - self.update(tree, layout.bounds().size(), view); - self.layout(tree, renderer.deref()); - - let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - self.layout.as_ref().unwrap(), - ); - - f(tree, renderer, content_layout, &mut self.element) - } -} - struct State { - tree: RefCell, + tree: Tree, } impl Widget @@ -145,7 +61,7 @@ where fn state(&self) -> tree::State { tree::State::new(State { - tree: RefCell::new(Tree::empty()), + tree: Tree::empty(), }) } @@ -157,35 +73,22 @@ where } fn layout( - &self, - _tree: &mut Tree, - _renderer: &Renderer, + &mut self, + tree: &mut Tree, + renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - layout::Node::new(limits.max()) - } - - fn operate( - &self, - tree: &mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation, - ) { let state = tree.state.downcast_mut::(); - let mut content = self.content.borrow_mut(); + let size = limits.max(); - content.resolve( - &mut state.tree.borrow_mut(), + self.content = (self.view)(size); + state.tree.diff(&self.content); + + self.content.as_widget_mut().layout( + &mut state.tree, renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element - .as_widget() - .operate(tree, layout, renderer, operation); - }, - ); + &limits.loose(), + ) } fn update( @@ -200,35 +103,17 @@ where viewport: &Rectangle, ) { let state = tree.state.downcast_mut::(); - let mut content = self.content.borrow_mut(); - let mut local_messages = vec![]; - let mut local_shell = Shell::new(&mut local_messages); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, + self.content.as_widget_mut().update( + &mut state.tree, + event, layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget_mut().update( - tree, - event, - layout, - cursor, - renderer, - clipboard, - &mut local_shell, - viewport, - ); - }, + cursor, + renderer, + clipboard, + shell, + viewport, ); - - if local_shell.is_layout_invalid() { - content.layout = None; - } - - shell.merge(local_shell, std::convert::identity); } fn draw( @@ -242,18 +127,15 @@ where viewport: &Rectangle, ) { let state = tree.state.downcast_ref::(); - let mut content = self.content.borrow_mut(); - content.resolve( - &mut state.tree.borrow_mut(), + self.content.as_widget().draw( + &state.tree, renderer, + theme, + style, layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget().draw( - tree, renderer, theme, style, layout, cursor, viewport, - ); - }, + cursor, + viewport, ); } @@ -266,79 +148,50 @@ where renderer: &Renderer, ) -> mouse::Interaction { let state = tree.state.downcast_ref::(); - let mut content = self.content.borrow_mut(); - content.resolve( - &mut state.tree.borrow_mut(), - renderer, + self.content.as_widget().mouse_interaction( + &state.tree, layout, - &self.view, - |tree, renderer, layout, element| { - element - .as_widget() - .mouse_interaction(tree, layout, cursor, viewport, renderer) - }, + cursor, + viewport, + renderer, ) } - fn overlay<'b>( - &'b mut self, - tree: &'b mut Tree, - layout: Layout<'b>, + fn operate( + &mut self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + let state = tree.state.downcast_mut::(); + + self.content.as_widget_mut().operate( + &mut state.tree, + layout, + renderer, + operation, + ); + } + + fn overlay<'a>( + &'a mut self, + tree: &'a mut Tree, + layout: Layout<'a>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, - ) -> Option> { - use std::ops::DerefMut; + ) -> Option> { + let state = tree.state.downcast_mut::(); - let state = tree.state.downcast_ref::(); - - let overlay = OverlayBuilder { - content: self.content.borrow_mut(), - tree: state.tree.borrow_mut(), - types: PhantomData, - overlay_builder: |content: &mut RefMut< - '_, - Content<'_, _, _, _>, - >, - tree| { - content.update(tree, layout.bounds().size(), &self.view); - content.layout(tree, renderer); - - let Content { - element, - layout: content_layout_node, - is_layout_invalid, - .. - } = content.deref_mut(); - - let content_layout = Layout::with_offset( - layout.bounds().position() - Point::ORIGIN, - content_layout_node.as_ref().unwrap(), - ); - - ( - element - .as_widget_mut() - .overlay( - tree, - content_layout, - renderer, - viewport, - translation, - ) - .map(|overlay| RefCell::new(Nested::new(overlay))), - is_layout_invalid, - ) - }, - } - .build(); - - if overlay.with_overlay(|(overlay, _layout)| overlay.is_some()) { - Some(overlay::Element::new(Box::new(overlay))) - } else { - None - } + self.content.as_widget_mut().overlay( + &mut state.tree, + layout, + renderer, + viewport, + translation, + ) } } @@ -354,108 +207,3 @@ where Self::new(responsive) } } - -#[self_referencing] -struct Overlay<'a, 'b, Message, Theme, Renderer> { - content: RefMut<'a, Content<'b, Message, Theme, Renderer>>, - tree: RefMut<'a, Tree>, - types: PhantomData, - - #[borrows(mut content, mut tree)] - #[not_covariant] - overlay: ( - Option>>, - &'this mut bool, - ), -} - -impl Overlay<'_, '_, Message, Theme, Renderer> { - fn with_overlay_maybe( - &self, - f: impl FnOnce(&mut Nested<'_, Message, Theme, Renderer>) -> T, - ) -> Option { - self.with_overlay(|(overlay, _layout)| { - overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) - }) - } - - fn with_overlay_mut_maybe( - &mut self, - f: impl FnOnce(&mut Nested<'_, Message, Theme, Renderer>) -> T, - ) -> Option { - self.with_overlay_mut(|(overlay, _layout)| { - overlay.as_mut().map(|nested| (f)(nested.get_mut())) - }) - } -} - -impl overlay::Overlay - for Overlay<'_, '_, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node { - self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds)) - .unwrap_or_default() - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - ) { - let _ = self.with_overlay_maybe(|overlay| { - overlay.draw(renderer, theme, style, layout, cursor); - }); - } - - fn mouse_interaction( - &self, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - ) -> mouse::Interaction { - self.with_overlay_maybe(|overlay| { - overlay.mouse_interaction(layout, cursor, renderer) - }) - .unwrap_or_default() - } - - fn update( - &mut self, - event: &Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) { - let mut is_layout_invalid = false; - - let _ = self.with_overlay_mut_maybe(|overlay| { - overlay.update(event, layout, cursor, renderer, clipboard, shell); - - is_layout_invalid = shell.is_layout_invalid(); - }); - - if is_layout_invalid { - self.with_overlay_mut(|(_overlay, layout)| { - **layout = true; - }); - } - } - - fn operate( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation, - ) { - let _ = self.with_overlay_mut_maybe(|overlay| { - overlay.operate(layout, renderer, operation); - }); - } -} diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index fefa68d5..f4a76d3b 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -190,24 +190,26 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.content.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout, renderer, diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index 741dbf15..c4c5b521 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -369,7 +369,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 158c265b..8c63fe63 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -418,7 +418,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -432,20 +432,19 @@ where let children = self .panes - .iter() - .copied() - .zip(&self.contents) + .iter_mut() + .zip(&mut self.contents) .zip(tree.children.iter_mut()) .filter_map(|((pane, content), tree)| { if self .internal .maximized() - .is_some_and(|maximized| maximized != pane) + .is_some_and(|maximized| maximized != *pane) { return Some(layout::Node::new(Size::ZERO)); } - let region = regions.get(&pane)?; + let region = regions.get(pane)?; let size = Size::new(region.width, region.height); let node = content.layout( @@ -462,7 +461,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -470,15 +469,14 @@ where ) { operation.container(None, layout.bounds(), &mut |operation| { self.panes - .iter() - .copied() - .zip(&self.contents) + .iter_mut() + .zip(&mut self.contents) .zip(&mut tree.children) .zip(layout.children()) .filter(|(((pane, _), _), _)| { self.internal .maximized() - .is_none_or(|maximized| *pane == maximized) + .is_none_or(|maximized| **pane == maximized) }) .for_each(|(((_, content), state), layout)| { content.operate(state, layout, renderer, operation); diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index 8800d13b..6592694b 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -165,12 +165,12 @@ where } pub(crate) fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - if let Some(title_bar) = &self.title_bar { + if let Some(title_bar) = &mut self.title_bar { let max_size = limits.max(); let title_bar_layout = title_bar.layout( @@ -181,7 +181,7 @@ where let title_bar_size = title_bar_layout.size(); - let body_layout = self.body.as_widget().layout( + let body_layout = self.body.as_widget_mut().layout( &mut tree.children[0], renderer, &layout::Limits::new( @@ -201,7 +201,7 @@ where ], ) } else { - self.body.as_widget().layout( + self.body.as_widget_mut().layout( &mut tree.children[0], renderer, limits, @@ -210,13 +210,13 @@ where } pub(crate) fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - let body_layout = if let Some(title_bar) = &self.title_bar { + let body_layout = if let Some(title_bar) = &mut self.title_bar { let mut children = layout.children(); title_bar.operate( @@ -231,7 +231,7 @@ where layout }; - self.body.as_widget().operate( + self.body.as_widget_mut().operate( &mut tree.children[0], body_layout, renderer, diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index e47f4a10..7d15bf80 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -274,7 +274,7 @@ where } pub(crate) fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -282,7 +282,7 @@ where let limits = limits.shrink(self.padding); let max_size = limits.max(); - let title_layout = self.content.as_widget().layout( + let title_layout = self.content.as_widget_mut().layout( &mut tree.children[0], renderer, &layout::Limits::new(Size::ZERO, max_size), @@ -290,8 +290,8 @@ where let title_size = title_layout.size(); - let node = if let Some(controls) = &self.controls { - let controls_layout = controls.full.as_widget().layout( + let node = if let Some(controls) = &mut self.controls { + let controls_layout = controls.full.as_widget_mut().layout( &mut tree.children[1], renderer, &layout::Limits::new(Size::ZERO, max_size), @@ -300,8 +300,8 @@ where if title_layout.bounds().width + controls_layout.bounds().width > max_size.width { - if let Some(compact) = controls.compact.as_ref() { - let compact_layout = compact.as_widget().layout( + if let Some(compact) = controls.compact.as_mut() { + let compact_layout = compact.as_widget_mut().layout( &mut tree.children[2], renderer, &layout::Limits::new(Size::ZERO, max_size), @@ -369,7 +369,7 @@ where } pub(crate) fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -382,16 +382,16 @@ where let title_layout = children.next().unwrap(); let mut show_title = true; - if let Some(controls) = &self.controls { + if let Some(controls) = &mut self.controls { let controls_layout = children.next().unwrap(); if title_layout.bounds().width + controls_layout.bounds().width > padded.bounds().width { - if let Some(compact) = controls.compact.as_ref() { + if let Some(compact) = controls.compact.as_mut() { let compact_layout = children.next().unwrap(); - compact.as_widget().operate( + compact.as_widget_mut().operate( &mut tree.children[2], compact_layout, renderer, @@ -400,7 +400,7 @@ where } else { show_title = false; - controls.full.as_widget().operate( + controls.full.as_widget_mut().operate( &mut tree.children[1], controls_layout, renderer, @@ -408,7 +408,7 @@ where ); } } else { - controls.full.as_widget().operate( + controls.full.as_widget_mut().operate( &mut tree.children[1], controls_layout, renderer, @@ -418,7 +418,7 @@ where }; if show_title { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], title_layout, renderer, diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 4076d7f7..86dbdaf6 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -348,7 +348,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/pin.rs b/widget/src/pin.rs index 539bf2c1..04ed0324 100644 --- a/widget/src/pin.rs +++ b/widget/src/pin.rs @@ -139,7 +139,7 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, @@ -151,7 +151,7 @@ where let node = self .content - .as_widget() + .as_widget_mut() .layout(tree, renderer, &layout::Limits::new(Size::ZERO, available)) .move_to(self.position); @@ -160,13 +160,13 @@ where } fn operate( - &self, + &mut self, tree: &mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( tree, layout.children().next().unwrap(), renderer, diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs index a9b4eb65..d364f5df 100644 --- a/widget/src/progress_bar.rs +++ b/widget/src/progress_bar.rs @@ -157,7 +157,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs index 07bb0c50..e8a278fd 100644 --- a/widget/src/qr_code.rs +++ b/widget/src/qr_code.rs @@ -136,7 +136,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, _limits: &layout::Limits, diff --git a/widget/src/radio.rs b/widget/src/radio.rs index fab29f56..3bb2e806 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -290,7 +290,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/row.rs b/widget/src/row.rs index 101e51d8..d70dee7f 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -208,7 +208,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -222,13 +222,13 @@ where self.padding, self.spacing, self.align, - &self.children, + &mut self.children, &mut tree.children, ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -236,12 +236,12 @@ where ) { operation.container(None, layout.bounds(), &mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); @@ -407,7 +407,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -449,8 +449,8 @@ where } }; - for (i, child) in self.row.children.iter().enumerate() { - let node = child.as_widget().layout( + for (i, child) in self.row.children.iter_mut().enumerate() { + let node = child.as_widget_mut().layout( &mut tree.children[i], renderer, &limits, @@ -528,7 +528,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, diff --git a/widget/src/rule.rs b/widget/src/rule.rs index d70109bc..163d2757 100644 --- a/widget/src/rule.rs +++ b/widget/src/rule.rs @@ -112,7 +112,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index baff578e..909a14d9 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -421,7 +421,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -453,7 +453,7 @@ where ), ); - self.content.as_widget().layout( + self.content.as_widget_mut().layout( &mut tree.children[0], renderer, &child_limits, @@ -527,7 +527,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -553,7 +553,7 @@ where self.id.as_ref().map(|id| &id.0), bounds, &mut |operation| { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout.children().next().unwrap(), renderer, diff --git a/widget/src/sensor.rs b/widget/src/sensor.rs index ff5e6fd6..e0122b2e 100644 --- a/widget/src/sensor.rs +++ b/widget/src/sensor.rs @@ -287,14 +287,16 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.content.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) } fn draw( @@ -319,13 +321,13 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: core::Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout, renderer, diff --git a/widget/src/shader.rs b/widget/src/shader.rs index 6d532e59..cf165e59 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -77,7 +77,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/slider.rs b/widget/src/slider.rs index 74225436..db6bc175 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -234,7 +234,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/space.rs b/widget/src/space.rs index 35bb30c4..949ea3ce 100644 --- a/widget/src/space.rs +++ b/widget/src/space.rs @@ -65,7 +65,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/stack.rs b/widget/src/stack.rs index 16e82678..c0fb3147 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -158,7 +158,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -173,7 +173,7 @@ where )); } - let base = self.children[0].as_widget().layout( + let base = self.children[0].as_widget_mut().layout( &mut tree.children[0], renderer, &limits, @@ -183,18 +183,21 @@ where let limits = layout::Limits::new(Size::ZERO, size); let nodes = std::iter::once(base) - .chain(self.children[1..].iter().zip(&mut tree.children[1..]).map( - |(layer, tree)| { - layer.as_widget().layout(tree, renderer, &limits) - }, - )) + .chain( + self.children[1..] + .iter_mut() + .zip(&mut tree.children[1..]) + .map(|(layer, tree)| { + layer.as_widget_mut().layout(tree, renderer, &limits) + }), + ) .collect(); layout::Node::with_children(size, nodes) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -202,12 +205,12 @@ where ) { operation.container(None, layout.bounds(), &mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/widget/src/svg.rs b/widget/src/svg.rs index 72ead4f9..ab69b2dc 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -162,7 +162,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/table.rs b/widget/src/table.rs index 03537f9a..06b47124 100644 --- a/widget/src/table.rs +++ b/widget/src/table.rs @@ -232,7 +232,7 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, @@ -265,7 +265,7 @@ where let mut y = self.padding_y; for (i, (cell, state)) in - self.cells.iter().zip(&mut tree.children).enumerate() + self.cells.iter_mut().zip(&mut tree.children).enumerate() { let row = i / columns; let column = i % columns; @@ -306,7 +306,7 @@ where ) .width(width); - let layout = cell.as_widget().layout(state, renderer, &limits); + let layout = cell.as_widget_mut().layout(state, renderer, &limits); let size = limits.resolve(width, Length::Shrink, layout.size()); metrics.columns[column] = metrics.columns[column].max(size.width); @@ -344,7 +344,7 @@ where let mut y = self.padding_y; for (i, (cell, state)) in - self.cells.iter().zip(&mut tree.children).enumerate() + self.cells.iter_mut().zip(&mut tree.children).enumerate() { let row = i / columns; let column = i % columns; @@ -396,7 +396,7 @@ where ) .width(width); - let layout = cell.as_widget().layout(state, renderer, &limits); + let layout = cell.as_widget_mut().layout(state, renderer, &limits); let size = limits.resolve( if let Length::Fixed(_) = width { width @@ -581,7 +581,7 @@ where } fn operate( - &self, + &mut self, tree: &mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, @@ -589,11 +589,12 @@ where ) { for ((cell, state), layout) in self .cells - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) { - cell.as_widget().operate(state, layout, renderer, operation); + cell.as_widget_mut() + .operate(state, layout, renderer, operation); } } diff --git a/widget/src/text/rich.rs b/widget/src/text/rich.rs index 9e7eaddf..e11f406b 100644 --- a/widget/src/text/rich.rs +++ b/widget/src/text/rich.rs @@ -225,7 +225,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 3657c817..33666935 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -597,7 +597,7 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, @@ -1051,7 +1051,7 @@ where } fn operate( - &self, + &mut self, tree: &mut widget::Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index fa3dd770..c385b34b 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -297,7 +297,7 @@ where /// /// [`Renderer`]: text::Renderer pub fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -671,7 +671,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -680,7 +680,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/widget/src/themer.rs b/widget/src/themer.rs index 693b8486..f335cd01 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -90,23 +90,23 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout(tree, renderer, limits) + self.content.as_widget_mut().layout(tree, renderer, limits) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { self.content - .as_widget() + .as_widget_mut() .operate(tree, layout, renderer, operation); } diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index 13385317..0ad2d22c 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -270,7 +270,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index f3dea455..4c2c1a2e 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -179,14 +179,16 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.content.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) } fn update( @@ -294,7 +296,7 @@ where let tooltip = if let State::Hovered { cursor_position } = *state { Some(overlay::Element::new(Box::new(Overlay { position: layout.position() + translation, - tooltip: &self.tooltip, + tooltip: &mut self.tooltip, state: children.next().unwrap(), cursor_position, content_bounds: layout.bounds(), @@ -366,7 +368,7 @@ where Renderer: text::Renderer, { position: Point, - tooltip: &'b Element<'a, Message, Theme, Renderer>, + tooltip: &'b mut Element<'a, Message, Theme, Renderer>, state: &'b mut widget::Tree, cursor_position: Point, content_bounds: Rectangle, @@ -386,7 +388,7 @@ where fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node { let viewport = Rectangle::with_size(bounds); - let tooltip_layout = self.tooltip.as_widget().layout( + let tooltip_layout = self.tooltip.as_widget_mut().layout( self.state, renderer, &layout::Limits::new( diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index c262eb8e..8e8ca1c0 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -238,7 +238,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, From 497ebcd0c36c45d6203fc84005abb81f95cf34ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Aug 2025 23:14:23 +0200 Subject: [PATCH 21/34] Make `Widget::diff` mutable --- core/src/element.rs | 37 ++++++++++++++++++++++++++++--- core/src/widget.rs | 2 +- core/src/widget/tree.rs | 26 ++++++++++++---------- examples/loupe/src/main.rs | 4 ++-- examples/toast/src/main.rs | 6 ++--- runtime/src/user_interface.rs | 2 +- widget/src/button.rs | 4 ++-- widget/src/column.rs | 4 ++-- widget/src/combo_box.rs | 2 +- widget/src/container.rs | 4 ++-- widget/src/float.rs | 4 ++-- widget/src/grid.rs | 4 ++-- widget/src/helpers.rs | 8 +++---- widget/src/keyed/column.rs | 6 ++--- widget/src/lazy.rs | 6 ++--- widget/src/lazy/component.rs | 6 ++--- widget/src/lazy/responsive.rs | 2 +- widget/src/mouse_area.rs | 4 ++-- widget/src/overlay/menu.rs | 4 ++-- widget/src/pane_grid.rs | 4 ++-- widget/src/pane_grid/content.rs | 6 ++--- widget/src/pane_grid/title_bar.rs | 10 ++++----- widget/src/pin.rs | 4 ++-- widget/src/row.rs | 6 ++--- widget/src/scrollable.rs | 4 ++-- widget/src/sensor.rs | 4 ++-- widget/src/stack.rs | 4 ++-- widget/src/table.rs | 4 ++-- widget/src/text_input.rs | 2 +- widget/src/themer.rs | 4 ++-- widget/src/tooltip.rs | 8 +++---- 31 files changed, 114 insertions(+), 81 deletions(-) diff --git a/core/src/element.rs b/core/src/element.rs index 6a71296c..349aecb6 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -9,7 +9,7 @@ use crate::{ Vector, Widget, }; -use std::borrow::Borrow; +use std::borrow::{Borrow, BorrowMut}; /// A generic [`Widget`]. /// @@ -239,6 +239,37 @@ impl<'a, Message, Theme, Renderer> } } +impl<'a, Message, Theme, Renderer> + Borrow + 'a> + for &mut Element<'a, Message, Theme, Renderer> +{ + fn borrow(&self) -> &(dyn Widget + 'a) { + self.widget.borrow() + } +} + +impl<'a, Message, Theme, Renderer> + BorrowMut + 'a> + for Element<'a, Message, Theme, Renderer> +{ + fn borrow_mut( + &mut self, + ) -> &mut (dyn Widget + 'a) { + self.widget.borrow_mut() + } +} + +impl<'a, Message, Theme, Renderer> + BorrowMut + 'a> + for &mut Element<'a, Message, Theme, Renderer> +{ + fn borrow_mut( + &mut self, + ) -> &mut (dyn Widget + 'a) { + self.widget.borrow_mut() + } +} + struct Map<'a, A, B, Theme, Renderer> { widget: Box + 'a>, mapper: Box B + 'a>, @@ -278,7 +309,7 @@ where self.widget.children() } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { self.widget.diff(tree); } @@ -421,7 +452,7 @@ where self.element.widget.children() } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { self.element.widget.diff(tree); } diff --git a/core/src/widget.rs b/core/src/widget.rs index 3b39ffcc..9316dba1 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -96,7 +96,7 @@ where } /// Reconciles the [`Widget`] with the provided [`Tree`]. - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { tree.children.clear(); } diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs index 2600cfc6..20b55360 100644 --- a/core/src/widget/tree.rs +++ b/core/src/widget/tree.rs @@ -2,7 +2,7 @@ use crate::Widget; use std::any::{self, Any}; -use std::borrow::Borrow; +use std::borrow::{Borrow, BorrowMut}; use std::fmt; /// A persistent state widget tree. @@ -56,12 +56,12 @@ impl Tree { /// [`Widget::diff`]: crate::Widget::diff pub fn diff<'a, Message, Theme, Renderer>( &mut self, - new: impl Borrow + 'a>, + mut new: impl BorrowMut + 'a>, ) where Renderer: crate::Renderer, { - if self.tag == new.borrow().tag() { - new.borrow().diff(self); + if self.tag == new.borrow_mut().tag() { + new.borrow_mut().diff(self); } else { *self = Self::new(new); } @@ -70,13 +70,15 @@ impl Tree { /// Reconciles the children of the tree with the provided list of widgets. pub fn diff_children<'a, Message, Theme, Renderer>( &mut self, - new_children: &[impl Borrow + 'a>], + new_children: &mut [impl BorrowMut< + dyn Widget + 'a, + >], ) where Renderer: crate::Renderer, { self.diff_children_custom( new_children, - |tree, widget| tree.diff(widget.borrow()), + |tree, widget| tree.diff(widget.borrow_mut()), |widget| Self::new(widget.borrow()), ); } @@ -85,8 +87,8 @@ impl Tree { /// logic both for diffing and creating new widget state. pub fn diff_children_custom( &mut self, - new_children: &[T], - diff: impl Fn(&mut Tree, &T), + new_children: &mut [T], + diff: impl Fn(&mut Tree, &mut T), new_state: impl Fn(&T) -> Self, ) { if self.children.len() > new_children.len() { @@ -94,7 +96,7 @@ impl Tree { } for (child_state, new) in - self.children.iter_mut().zip(new_children.iter()) + self.children.iter_mut().zip(new_children.iter_mut()) { diff(child_state, new); } @@ -114,8 +116,8 @@ impl Tree { /// `maybe_changed` closure. pub fn diff_children_custom_with_search( current_children: &mut Vec, - new_children: &[T], - diff: impl Fn(&mut Tree, &T), + new_children: &mut [T], + diff: impl Fn(&mut Tree, &mut T), maybe_changed: impl Fn(usize) -> bool, new_state: impl Fn(&T) -> Tree, ) { @@ -183,7 +185,7 @@ pub fn diff_children_custom_with_search( // TODO: Merge loop with extend logic (?) for (child_state, new) in - current_children.iter_mut().zip(new_children.iter()) + current_children.iter_mut().zip(new_children.iter_mut()) { diff(child_state, new); } diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs index 89bc82da..ebd7a44f 100644 --- a/examples/loupe/src/main.rs +++ b/examples/loupe/src/main.rs @@ -87,8 +87,8 @@ mod loupe { self.content.as_widget().children() } - fn diff(&self, tree: &mut widget::Tree) { - self.content.as_widget().diff(tree); + fn diff(&mut self, tree: &mut widget::Tree) { + self.content.as_widget_mut().diff(tree); } fn size(&self) -> Size { diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index dc8c2593..d1be0c09 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -314,7 +314,7 @@ mod toast { .collect() } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { let instants = tree.state.downcast_mut::>>(); // Invalidating removed instants to None allows us to remove @@ -336,8 +336,8 @@ mod toast { } tree.diff_children( - &std::iter::once(&self.content) - .chain(self.toasts.iter()) + &mut std::iter::once(&mut self.content) + .chain(self.toasts.iter_mut()) .collect::>(), ); } diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 26c96201..6e32d10f 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -100,7 +100,7 @@ where let mut root = root.into(); let Cache { mut state } = cache; - state.diff(root.as_widget()); + state.diff(root.as_widget_mut()); let base = root.as_widget_mut().layout( &mut state, diff --git a/widget/src/button.rs b/widget/src/button.rs index 6ae8d3e5..2ae1508b 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -223,8 +223,8 @@ where vec![Tree::new(&self.content)] } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(std::slice::from_mut(&mut self.content)); } fn size(&self) -> Size { diff --git a/widget/src/column.rs b/widget/src/column.rs index 39507a78..bcbbe49e 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -194,8 +194,8 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.children); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(&mut self.children); } fn size(&self) -> Size { diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index f94705d8..86533c3a 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -509,7 +509,7 @@ where vec![widget::Tree::new(&self.text_input as &dyn Widget<_, _, _>)] } - fn diff(&self, _tree: &mut widget::Tree) { + fn diff(&mut self, _tree: &mut widget::Tree) { // do nothing so the children don't get cleared } diff --git a/widget/src/container.rs b/widget/src/container.rs index 286c5a63..97502b17 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -247,8 +247,8 @@ where self.content.as_widget().children() } - fn diff(&self, tree: &mut Tree) { - self.content.as_widget().diff(tree); + fn diff(&mut self, tree: &mut Tree) { + self.content.as_widget_mut().diff(tree); } fn size(&self) -> Size { diff --git a/widget/src/float.rs b/widget/src/float.rs index 1ff6ab10..a7eaf5bb 100644 --- a/widget/src/float.rs +++ b/widget/src/float.rs @@ -103,8 +103,8 @@ where self.content.as_widget().children() } - fn diff(&self, tree: &mut widget::Tree) { - self.content.as_widget().diff(tree); + fn diff(&mut self, tree: &mut widget::Tree) { + self.content.as_widget_mut().diff(tree); } fn size(&self) -> Size { diff --git a/widget/src/grid.rs b/widget/src/grid.rs index 25e3ebe0..5bf2bf6b 100644 --- a/widget/src/grid.rs +++ b/widget/src/grid.rs @@ -158,8 +158,8 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.children); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(&mut self.children); } fn size(&self) -> Size { diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 9e01fa99..a926d2ef 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -596,8 +596,8 @@ where self.content.as_widget().children() } - fn diff(&self, tree: &mut Tree) { - self.content.as_widget().diff(tree); + fn diff(&mut self, tree: &mut Tree) { + self.content.as_widget_mut().diff(tree); } fn size(&self) -> Size { @@ -759,8 +759,8 @@ where vec![Tree::new(&self.base), Tree::new(&self.top)] } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&[&self.base, &self.top]); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(&mut [&mut self.base, &mut self.top]); } fn size(&self) -> Size { diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 6ca6f485..907ae479 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -222,7 +222,7 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { let Tree { state, children, .. } = tree; @@ -231,8 +231,8 @@ where tree::diff_children_custom_with_search( children, - &self.children, - |tree, child| child.as_widget().diff(tree), + &mut self.children, + |tree, child| child.as_widget_mut().diff(tree), |index| { self.keys.get(index).or_else(|| self.keys.last()).copied() != Some(state.keys[index]) diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 0d56269b..36edce79 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -127,7 +127,7 @@ where self.with_element(|element| vec![Tree::new(element.as_widget())]) } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { let current = tree .state .downcast_mut::>(); @@ -146,8 +146,8 @@ where current.element = Rc::new(RefCell::new(Some(element))); (*self.element.borrow_mut()) = Some(current.element.clone()); - self.with_element(|element| { - tree.diff_children(std::slice::from_ref(&element.as_widget())); + self.with_element_mut(|element| { + tree.diff_children(std::slice::from_mut(element)); }); } else { (*self.element.borrow_mut()) = Some(current.element.clone()); diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index 1b582dee..c40be4d1 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -147,13 +147,13 @@ where Renderer: renderer::Renderer, { fn diff_self(&self) { - self.with_element(|element| { + self.with_element_mut(|element| { self.tree .borrow_mut() .borrow_mut() .as_mut() .unwrap() - .diff_children(std::slice::from_ref(&element)); + .diff_children(std::slice::from_mut(element)); }); } @@ -279,7 +279,7 @@ where vec![] } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { let tree = tree.state.downcast_ref::>>>(); *self.tree.borrow_mut() = tree.clone(); self.rebuild_element_if_necessary(); diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index f72250c6..18451a41 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -82,7 +82,7 @@ where let size = limits.max(); self.content = (self.view)(size); - state.tree.diff(&self.content); + state.tree.diff(&mut self.content); self.content.as_widget_mut().layout( &mut state.tree, diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index f4a76d3b..e7ce47b5 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -181,8 +181,8 @@ where vec![Tree::new(&self.content)] } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(std::slice::from_mut(&mut self.content)); } fn size(&self) -> Size { diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index c4c5b521..e84ae858 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -205,7 +205,7 @@ where class, } = menu; - let list = Scrollable::new(List { + let mut list = Scrollable::new(List { options, hovered_option, on_selected, @@ -218,7 +218,7 @@ where class, }); - state.tree.diff(&list as &dyn Widget<_, _, _>); + state.tree.diff(&mut list as &mut dyn Widget<_, _, _>); Self { position, diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 8c63fe63..3dd0c941 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -378,7 +378,7 @@ where self.contents.iter().map(Content::state).collect() } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { let Memory { order, .. } = tree.state.downcast_ref(); // `Pane` always increments and is iterated by Ord so new @@ -401,7 +401,7 @@ where }); tree.diff_children_custom( - &self.contents, + &mut self.contents, |state, content| content.diff(state), Content::state, ); diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index 6592694b..00f0d060 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -91,13 +91,13 @@ where } } - pub(super) fn diff(&self, tree: &mut Tree) { + pub(super) fn diff(&mut self, tree: &mut Tree) { if tree.children.len() == 2 { - if let Some(title_bar) = self.title_bar.as_ref() { + if let Some(title_bar) = self.title_bar.as_mut() { title_bar.diff(&mut tree.children[1]); } - tree.children[0].diff(&self.body); + tree.children[0].diff(&mut self.body); } else { *tree = self.state(); } diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index 7d15bf80..598cbcf5 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -128,17 +128,17 @@ where } } - pub(super) fn diff(&self, tree: &mut Tree) { + pub(super) fn diff(&mut self, tree: &mut Tree) { if tree.children.len() == 3 { - if let Some(controls) = self.controls.as_ref() { - if let Some(compact) = controls.compact.as_ref() { + if let Some(controls) = self.controls.as_mut() { + if let Some(compact) = controls.compact.as_mut() { tree.children[2].diff(compact); } - tree.children[1].diff(&controls.full); + tree.children[1].diff(&mut controls.full); } - tree.children[0].diff(&self.content); + tree.children[0].diff(&mut self.content); } else { *tree = self.state(); } diff --git a/widget/src/pin.rs b/widget/src/pin.rs index 04ed0324..f4a1c667 100644 --- a/widget/src/pin.rs +++ b/widget/src/pin.rs @@ -127,8 +127,8 @@ where self.content.as_widget().children() } - fn diff(&self, tree: &mut widget::Tree) { - self.content.as_widget().diff(tree); + fn diff(&mut self, tree: &mut widget::Tree) { + self.content.as_widget_mut().diff(tree); } fn size(&self) -> Size { diff --git a/widget/src/row.rs b/widget/src/row.rs index d70dee7f..2ee3d345 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -196,8 +196,8 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.children); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(&mut self.children); } fn size(&self) -> Size { @@ -398,7 +398,7 @@ where self.row.children() } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { self.row.diff(tree); } diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 909a14d9..0b0b6f9b 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -409,8 +409,8 @@ where vec![Tree::new(&self.content)] } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(std::slice::from_mut(&mut self.content)); } fn size(&self) -> Size { diff --git a/widget/src/sensor.rs b/widget/src/sensor.rs index e0122b2e..fd7a3510 100644 --- a/widget/src/sensor.rs +++ b/widget/src/sensor.rs @@ -184,8 +184,8 @@ where vec![Tree::new(&self.content)] } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&[&self.content]); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(std::slice::from_mut(&mut self.content)); } fn update( diff --git a/widget/src/stack.rs b/widget/src/stack.rs index c0fb3147..63a6b97e 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -146,8 +146,8 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.children); + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(&mut self.children); } fn size(&self) -> Size { diff --git a/widget/src/table.rs b/widget/src/table.rs index 06b47124..97083835 100644 --- a/widget/src/table.rs +++ b/widget/src/table.rs @@ -227,8 +227,8 @@ where .collect() } - fn diff(&self, state: &mut widget::Tree) { - state.diff_children(&self.cells); + fn diff(&mut self, state: &mut widget::Tree) { + state.diff_children(&mut self.cells); } fn layout( diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index c385b34b..1a4f8a99 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -654,7 +654,7 @@ where tree::State::new(State::::new()) } - fn diff(&self, tree: &mut Tree) { + fn diff(&mut self, tree: &mut Tree) { let state = tree.state.downcast_mut::>(); // Stop pasting if input becomes disabled diff --git a/widget/src/themer.rs b/widget/src/themer.rs index f335cd01..a089d1d5 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -81,8 +81,8 @@ where self.content.as_widget().children() } - fn diff(&self, tree: &mut Tree) { - self.content.as_widget().diff(tree); + fn diff(&mut self, tree: &mut Tree) { + self.content.as_widget_mut().diff(tree); } fn size(&self) -> Size { diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 4c2c1a2e..782258da 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -155,10 +155,10 @@ where ] } - fn diff(&self, tree: &mut widget::Tree) { - tree.diff_children(&[ - self.content.as_widget(), - self.tooltip.as_widget(), + fn diff(&mut self, tree: &mut widget::Tree) { + tree.diff_children(&mut [ + self.content.as_widget_mut(), + self.tooltip.as_widget_mut(), ]); } From 6f72ac4650037518847e9f1d03b0b37de72babbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Aug 2025 23:31:35 +0200 Subject: [PATCH 22/34] Remove internal `State` from `responsive` --- widget/src/lazy/responsive.rs | 38 ++++++++++------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index 18451a41..a0b18761 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -46,23 +46,18 @@ where } } -struct State { - tree: Tree, -} - impl Widget for Responsive<'_, Message, Theme, Renderer> where Renderer: core::Renderer, { fn tag(&self) -> tree::Tag { - tree::Tag::of::() + struct Marker; + tree::Tag::of::() } - fn state(&self) -> tree::State { - tree::State::new(State { - tree: Tree::empty(), - }) + fn diff(&mut self, _tree: &mut Tree) { + // Diff is deferred to layout } fn size(&self) -> Size { @@ -78,14 +73,13 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let state = tree.state.downcast_mut::(); let size = limits.max(); self.content = (self.view)(size); - state.tree.diff(&mut self.content); + tree.diff_children(std::slice::from_mut(&mut self.content)); self.content.as_widget_mut().layout( - &mut state.tree, + &mut tree.children[0], renderer, &limits.loose(), ) @@ -102,10 +96,8 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { - let state = tree.state.downcast_mut::(); - self.content.as_widget_mut().update( - &mut state.tree, + &mut tree.children[0], event, layout, cursor, @@ -126,10 +118,8 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - let state = tree.state.downcast_ref::(); - self.content.as_widget().draw( - &state.tree, + &tree.children[0], renderer, theme, style, @@ -147,10 +137,8 @@ where viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - let state = tree.state.downcast_ref::(); - self.content.as_widget().mouse_interaction( - &state.tree, + &tree.children[0], layout, cursor, viewport, @@ -165,10 +153,8 @@ where renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - let state = tree.state.downcast_mut::(); - self.content.as_widget_mut().operate( - &mut state.tree, + &mut tree.children[0], layout, renderer, operation, @@ -183,10 +169,8 @@ where viewport: &Rectangle, translation: Vector, ) -> Option> { - let state = tree.state.downcast_mut::(); - self.content.as_widget_mut().overlay( - &mut state.tree, + &mut tree.children[0], layout, renderer, viewport, From 199a18951504ce43686138130be0cbab9748503e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Aug 2025 00:07:04 +0200 Subject: [PATCH 23/34] Ungate `responsive` from the `lazy` feature flag --- examples/pane_grid/Cargo.toml | 2 +- widget/src/helpers.rs | 21 +++++++++++++++++++-- widget/src/lazy.rs | 2 -- widget/src/lazy/helpers.rs | 20 ++------------------ widget/src/lib.rs | 3 +++ widget/src/{lazy => }/responsive.rs | 1 - 6 files changed, 25 insertions(+), 24 deletions(-) rename widget/src/{lazy => }/responsive.rs (99%) diff --git a/examples/pane_grid/Cargo.toml b/examples/pane_grid/Cargo.toml index fd3e133c..c1756a65 100644 --- a/examples/pane_grid/Cargo.toml +++ b/examples/pane_grid/Cargo.toml @@ -7,4 +7,4 @@ publish = false [dependencies] iced.workspace = true -iced.features = ["debug", "lazy"] +iced.features = ["debug"] diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index a926d2ef..256a9d32 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -6,7 +6,7 @@ use crate::container::{self, Container}; use crate::core; use crate::core::widget::operation::{self, Operation}; use crate::core::window; -use crate::core::{Element, Length, Pixels, Widget}; +use crate::core::{Element, Length, Pixels, Size, Widget}; use crate::float::{self, Float}; use crate::keyed; use crate::overlay; @@ -25,7 +25,9 @@ use crate::text_input::{self, TextInput}; use crate::toggler::{self, Toggler}; use crate::tooltip::{self, Tooltip}; use crate::vertical_slider::{self, VerticalSlider}; -use crate::{Column, Grid, MouseArea, Pin, Row, Sensor, Space, Stack, Themer}; +use crate::{ + Column, Grid, MouseArea, Pin, Responsive, Row, Sensor, Space, Stack, Themer, +}; use std::borrow::Borrow; use std::ops::RangeInclusive; @@ -2140,3 +2142,18 @@ where { Float::new(content) } + +/// Creates a new [`Responsive`] widget with a closure that produces its +/// contents. +/// +/// The `view` closure will be provided with the current [`Size`] of +/// the [`Responsive`] widget and, therefore, can be used to build the +/// contents of the widget in a responsive way. +pub fn responsive<'a, Message, Theme, Renderer>( + f: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, +) -> Responsive<'a, Message, Theme, Renderer> +where + Renderer: core::Renderer, +{ + Responsive::new(f) +} diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 36edce79..b67ba56c 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -2,11 +2,9 @@ pub(crate) mod helpers; pub mod component; -pub mod responsive; #[allow(deprecated)] pub use component::Component; -pub use responsive::Responsive; mod cache; diff --git a/widget/src/lazy/helpers.rs b/widget/src/lazy/helpers.rs index 52e690ff..7e7601cb 100644 --- a/widget/src/lazy/helpers.rs +++ b/widget/src/lazy/helpers.rs @@ -1,10 +1,10 @@ -use crate::core::{self, Element, Size}; +use crate::core::{self, Element}; use crate::lazy::component; use std::hash::Hash; #[allow(deprecated)] -pub use crate::lazy::{Component, Lazy, Responsive}; +pub use crate::lazy::{Component, Lazy}; /// Creates a new [`Lazy`] widget with the given data `Dependency` and a /// closure that can turn this data into a widget tree. @@ -41,19 +41,3 @@ where { component::view(component) } - -/// Creates a new [`Responsive`] widget with a closure that produces its -/// contents. -/// -/// The `view` closure will be provided with the current [`Size`] of -/// the [`Responsive`] widget and, therefore, can be used to build the -/// contents of the widget in a responsive way. -#[cfg(feature = "lazy")] -pub fn responsive<'a, Message, Theme, Renderer>( - f: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, -) -> Responsive<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - Responsive::new(f) -} diff --git a/widget/src/lib.rs b/widget/src/lib.rs index d08a92f9..3818c92f 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -12,6 +12,7 @@ mod action; mod column; mod mouse_area; mod pin; +mod responsive; mod space; mod stack; mod themer; @@ -78,6 +79,8 @@ pub use progress_bar::ProgressBar; #[doc(no_inline)] pub use radio::Radio; #[doc(no_inline)] +pub use responsive::Responsive; +#[doc(no_inline)] pub use row::Row; #[doc(no_inline)] pub use rule::Rule; diff --git a/widget/src/lazy/responsive.rs b/widget/src/responsive.rs similarity index 99% rename from widget/src/lazy/responsive.rs rename to widget/src/responsive.rs index a0b18761..5bae852f 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/responsive.rs @@ -14,7 +14,6 @@ use crate::horizontal_space; /// /// A [`Responsive`] widget will always try to fill all the available space of /// its parent. -#[cfg(feature = "lazy")] #[allow(missing_debug_implementations)] pub struct Responsive< 'a, From 7a7d562b03597de79dd6d5b049051f8e1635846a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Aug 2025 02:26:10 +0200 Subject: [PATCH 24/34] Make `Shrink` take priority over nested `Fill` --- core/src/layout/limits.rs | 41 +++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/core/src/layout/limits.rs b/core/src/layout/limits.rs index 1aaed526..2bff8a55 100644 --- a/core/src/layout/limits.rs +++ b/core/src/layout/limits.rs @@ -6,6 +6,7 @@ use crate::{Length, Size}; pub struct Limits { min: Size, max: Size, + compress: Size, } impl Limits { @@ -13,11 +14,16 @@ impl Limits { pub const NONE: Limits = Limits { min: Size::ZERO, max: Size::INFINITE, + compress: Size::new(false, false), }; /// Creates new [`Limits`] with the given minimum and maximum [`Size`]. pub const fn new(min: Size, max: Size) -> Limits { - Limits { min, max } + Limits { + min, + max, + compress: Size::new(false, false), + } } /// Returns the minimum [`Size`] of the [`Limits`]. @@ -33,13 +39,17 @@ impl Limits { /// Applies a width constraint to the current [`Limits`]. pub fn width(mut self, width: impl Into) -> Limits { match width.into() { - Length::Shrink | Length::Fill | Length::FillPortion(_) => {} + Length::Shrink => { + self.compress.width = true; + } Length::Fixed(amount) => { let new_width = amount.min(self.max.width).max(self.min.width); self.min.width = new_width; self.max.width = new_width; + self.compress.width = false; } + Length::Fill | Length::FillPortion(_) => {} } self @@ -48,14 +58,18 @@ impl Limits { /// Applies a height constraint to the current [`Limits`]. pub fn height(mut self, height: impl Into) -> Limits { match height.into() { - Length::Shrink | Length::Fill | Length::FillPortion(_) => {} + Length::Shrink => { + self.compress.height = true; + } Length::Fixed(amount) => { let new_height = amount.min(self.max.height).max(self.min.height); self.min.height = new_height; self.max.height = new_height; + self.compress.height = false; } + Length::Fill | Length::FillPortion(_) => {} } self @@ -103,7 +117,11 @@ impl Limits { (self.max().height - size.height).max(0.0), ); - Limits { min, max } + Limits { + min, + max, + compress: self.compress, + } } /// Removes the minimum width constraint for the current [`Limits`]. @@ -111,6 +129,7 @@ impl Limits { Limits { min: Size::ZERO, max: self.max, + compress: self.compress, } } @@ -124,21 +143,23 @@ impl Limits { intrinsic_size: Size, ) -> Size { let width = match width.into() { - Length::Fill | Length::FillPortion(_) => self.max.width, + Length::Fill | Length::FillPortion(_) if !self.compress.width => { + self.max.width + } Length::Fixed(amount) => { amount.min(self.max.width).max(self.min.width) } - Length::Shrink => { - intrinsic_size.width.min(self.max.width).max(self.min.width) - } + _ => intrinsic_size.width.min(self.max.width).max(self.min.width), }; let height = match height.into() { - Length::Fill | Length::FillPortion(_) => self.max.height, + Length::Fill | Length::FillPortion(_) if !self.compress.height => { + self.max.height + } Length::Fixed(amount) => { amount.min(self.max.height).max(self.min.height) } - Length::Shrink => intrinsic_size + _ => intrinsic_size .height .min(self.max.height) .max(self.min.height), From 5050fcc7ac2c4b01e62ff0982cb616348ea142fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Aug 2025 03:30:13 +0200 Subject: [PATCH 25/34] Implement `width` and `height` for `responsive` --- widget/src/helpers.rs | 6 +++--- widget/src/responsive.rs | 45 +++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 256a9d32..b1d29127 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -2146,9 +2146,9 @@ where /// Creates a new [`Responsive`] widget with a closure that produces its /// contents. /// -/// The `view` closure will be provided with the current [`Size`] of -/// the [`Responsive`] widget and, therefore, can be used to build the -/// contents of the widget in a responsive way. +/// The `view` closure will receive the maximum available space for +/// the [`Responsive`] during layout. You can use this [`Size`] to +/// conditionally build the contents. pub fn responsive<'a, Message, Theme, Renderer>( f: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, ) -> Responsive<'a, Message, Theme, Renderer> diff --git a/widget/src/responsive.rs b/widget/src/responsive.rs index 5bae852f..6bb86a12 100644 --- a/widget/src/responsive.rs +++ b/widget/src/responsive.rs @@ -22,6 +22,8 @@ pub struct Responsive< Renderer = crate::Renderer, > { view: Box Element<'a, Message, Theme, Renderer> + 'a>, + width: Length, + height: Length, content: Element<'a, Message, Theme, Renderer>, } @@ -32,17 +34,31 @@ where /// Creates a new [`Responsive`] widget with a closure that produces its /// contents. /// - /// The `view` closure will be provided with the current [`Size`] of - /// the [`Responsive`] widget and, therefore, can be used to build the - /// contents of the widget in a responsive way. + /// The `view` closure will receive the maximum available space for + /// the [`Responsive`] during layout. You can use this [`Size`] to + /// conditionally build the contents. pub fn new( view: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, ) -> Self { Self { view: Box::new(view), + width: Length::Fill, + height: Length::Fill, content: Element::new(horizontal_space().width(0)), } } + + /// Sets the width of the [`Responsive`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Sets the height of the [`Responsive`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } } impl Widget @@ -61,8 +77,8 @@ where fn size(&self) -> Size { Size { - width: Length::Fill, - height: Length::Fill, + width: self.width, + height: self.height, } } @@ -72,16 +88,21 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); let size = limits.max(); self.content = (self.view)(size); tree.diff_children(std::slice::from_mut(&mut self.content)); - self.content.as_widget_mut().layout( + let node = self.content.as_widget_mut().layout( &mut tree.children[0], renderer, &limits.loose(), - ) + ); + + let size = limits.resolve(self.width, self.height, node.size()); + + layout::Node::with_children(size, vec![node]) } fn update( @@ -98,7 +119,7 @@ where self.content.as_widget_mut().update( &mut tree.children[0], event, - layout, + layout.children().next().unwrap(), cursor, renderer, clipboard, @@ -122,7 +143,7 @@ where renderer, theme, style, - layout, + layout.children().next().unwrap(), cursor, viewport, ); @@ -138,7 +159,7 @@ where ) -> mouse::Interaction { self.content.as_widget().mouse_interaction( &tree.children[0], - layout, + layout.children().next().unwrap(), cursor, viewport, renderer, @@ -154,7 +175,7 @@ where ) { self.content.as_widget_mut().operate( &mut tree.children[0], - layout, + layout.children().next().unwrap(), renderer, operation, ); @@ -170,7 +191,7 @@ where ) -> Option> { self.content.as_widget_mut().overlay( &mut tree.children[0], - layout, + layout.children().next().unwrap(), renderer, viewport, translation, From 04639a419458dcb421534461c69d5f953bf829b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Aug 2025 03:52:01 +0200 Subject: [PATCH 26/34] Update dependencies in `Cargo.lock` --- Cargo.lock | 453 +++++++++++++++++++++++++++-------------------------- 1 file changed, 233 insertions(+), 220 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0248b85e..4b7bec86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "addr2line" @@ -77,7 +77,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.1", + "bitflags 2.9.2", "cc", "cesu8", "jni", @@ -126,9 +126,9 @@ checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "approx" @@ -141,9 +141,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "arc" @@ -214,7 +214,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -303,11 +303,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "pin-project-lite", ] @@ -336,7 +336,7 @@ dependencies = [ "async-task", "blocking", "cfg-if", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-lite", "rustix 1.0.8", ] @@ -372,9 +372,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", @@ -404,9 +404,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -470,9 +470,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" +checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" dependencies = [ "arrayvec", ] @@ -569,9 +569,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" dependencies = [ "serde", ] @@ -645,18 +645,18 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", @@ -703,7 +703,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "log", "polling", "rustix 0.38.44", @@ -742,9 +742,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.30" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" dependencies = [ "jobserver", "libc", @@ -769,9 +769,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -843,18 +843,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.41" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.41" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstyle", "clap_lex", @@ -1030,7 +1030,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -1054,7 +1054,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.10.1", "libc", ] @@ -1065,7 +1065,7 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "fontdb 0.16.2", "log", "rangemap", @@ -1360,7 +1360,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1513,9 +1513,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -1528,7 +1528,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "pin-project-lite", ] @@ -1771,9 +1771,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -2048,7 +2048,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "gpu-alloc-types", ] @@ -2058,7 +2058,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -2079,7 +2079,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "gpu-descriptor-types", "hashbrown", ] @@ -2090,7 +2090,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -2150,9 +2150,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -2180,9 +2180,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash", ] @@ -2326,7 +2326,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2335,19 +2335,21 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", - "h2 0.4.11", + "futures-core", + "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -2360,9 +2362,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", - "rustls 0.23.29", + "rustls 0.23.31", "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", @@ -2377,7 +2379,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "native-tls", "tokio", @@ -2387,9 +2389,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", @@ -2398,12 +2400,12 @@ dependencies = [ "futures-util", "http 1.3.1", "http-body 1.0.1", - "hyper 1.6.0", + "hyper 1.7.0", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -2472,7 +2474,7 @@ dependencies = [ name = "iced_core" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytes", "dark-light", "glam", @@ -2525,7 +2527,7 @@ dependencies = [ name = "iced_graphics" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytemuck", "cosmic-text", "half", @@ -2612,7 +2614,7 @@ dependencies = [ name = "iced_wgpu" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytemuck", "cryoglyph", "futures", @@ -2853,11 +2855,11 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "libc", ] @@ -3064,9 +3066,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libfuzzer-sys" @@ -3085,7 +3087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -3096,13 +3098,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "libc", - "redox_syscall 0.5.15", + "redox_syscall 0.5.17", ] [[package]] @@ -3146,9 +3148,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "loading_spinners" @@ -3329,7 +3331,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -3424,9 +3426,9 @@ dependencies = [ [[package]] name = "mutate_once" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" +checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" @@ -3436,7 +3438,7 @@ checksum = "916cbc7cb27db60be930a4e2da243cf4bc39569195f22fd8ee419cd31d5b662c" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -3450,7 +3452,7 @@ dependencies = [ "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.12", + "thiserror 2.0.16", "unicode-ident", ] @@ -3477,7 +3479,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "jni-sys", "log", "ndk-sys", @@ -3513,7 +3515,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "cfg_aliases", "libc", @@ -3682,9 +3684,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" dependencies = [ "objc2-encode", ] @@ -3695,7 +3697,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "libc", "objc2 0.5.2", @@ -3711,7 +3713,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "objc2 0.5.2", "objc2-core-location", @@ -3735,7 +3737,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3777,7 +3779,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "dispatch", "libc", @@ -3790,8 +3792,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.2", + "objc2 0.6.2", ] [[package]] @@ -3812,7 +3814,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3824,7 +3826,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3847,7 +3849,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "objc2 0.5.2", "objc2-cloud-kit", @@ -3879,7 +3881,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "objc2 0.5.2", "objc2-core-location", @@ -3916,7 +3918,7 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "libc", "once_cell", "onig_sys", @@ -3955,7 +3957,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "foreign-types 0.3.2", "libc", @@ -4059,9 +4061,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owned_ttf_parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ "ttf-parser 0.25.1", ] @@ -4133,7 +4135,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.15", + "redox_syscall 0.5.17", "smallvec", "windows-targets 0.52.6", ] @@ -4268,7 +4270,7 @@ checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", "indexmap", - "quick-xml 0.38.0", + "quick-xml 0.38.2", "serde", "time", ] @@ -4328,9 +4330,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" dependencies = [ "cfg-if", "concurrent-queue", @@ -4396,9 +4398,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -4448,7 +4450,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "getopts", "memchr", "pulldown-cmark-escape", @@ -4500,9 +4502,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b" +checksum = "d200a41a7797e6461bd04e4e95c3347053a731c32c87f066f2f0dda22dbdbba8" dependencies = [ "memchr", ] @@ -4589,9 +4591,9 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" [[package]] name = "rangemap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" +checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" [[package]] name = "rav1e" @@ -4657,9 +4659,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -4667,9 +4669,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -4696,22 +4698,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.15" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] @@ -4751,20 +4753,20 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.4.11", + "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-rustls", "hyper-tls", "hyper-util", @@ -4862,9 +4864,9 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -4884,7 +4886,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4897,7 +4899,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys 0.9.4", @@ -4920,9 +4922,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.29" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "once_cell", "rustls-pki-types", @@ -4964,9 +4966,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rustybuzz" @@ -4974,7 +4976,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytemuck", "libm", "smallvec", @@ -5057,7 +5059,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5111,9 +5113,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -5200,9 +5202,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -5259,9 +5261,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slider" @@ -5291,7 +5293,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "calloop", "calloop-wayland-source", "cursor-icon", @@ -5357,6 +5359,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "softbuffer" version = "0.4.6" @@ -5377,7 +5389,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core", "raw-window-handle 0.6.2", - "redox_syscall 0.5.15", + "redox_syscall 0.5.17", "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", @@ -5410,7 +5422,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -5452,9 +5464,9 @@ dependencies = [ [[package]] name = "subsecond" -version = "0.7.0-alpha.3" +version = "0.7.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9884adf2ac9e1f7ee7924be9130e000619fb10ecaa108ba39f20ba773e9ab4" +checksum = "b14ed4d86ab065ffbfdb994fd3e44daf5244b02cb643bd52949d74b703f36605" dependencies = [ "js-sys", "libc", @@ -5463,7 +5475,7 @@ dependencies = [ "memmap2", "serde", "subsecond-types", - "thiserror 2.0.12", + "thiserror 2.0.16", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -5471,9 +5483,9 @@ dependencies = [ [[package]] name = "subsecond-types" -version = "0.7.0-alpha.3" +version = "0.7.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cacd233c591f4f27a52b436d38156ce13e666d3da74cf26bf44777a330475f4" +checksum = "275920a8a5634e47e12253971db85946798795bbe4d9dfc1debf23533d823983" dependencies = [ "serde", ] @@ -5520,9 +5532,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -5600,7 +5612,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5651,15 +5663,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix 1.0.8", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5691,11 +5703,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.16", ] [[package]] @@ -5711,9 +5723,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -5832,9 +5844,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -5870,9 +5882,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.46.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -5882,9 +5894,9 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "slab", - "socket2", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5925,7 +5937,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.29", + "rustls 0.23.31", "tokio", ] @@ -5943,9 +5955,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -6027,7 +6039,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytes", "futures-util", "http 1.3.1", @@ -6309,9 +6321,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -6553,13 +6565,13 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.0.8", "scoped-tls", "smallvec", "wayland-sys", @@ -6567,12 +6579,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.1", - "rustix 0.38.44", + "bitflags 2.9.2", + "rustix 1.0.8", "wayland-backend", "wayland-scanner", ] @@ -6583,29 +6595,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 0.38.44", + "rustix 1.0.8", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.32.8" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6613,11 +6625,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" +checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6626,11 +6638,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6639,9 +6651,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", "quick-xml 0.37.5", @@ -6650,9 +6662,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ "dlib", "log", @@ -6690,7 +6702,7 @@ dependencies = [ "jni", "log", "ndk-context", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", "url", "web-sys", @@ -6737,7 +6749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70b6ff82bbf6e9206828e1a3178e851f8c20f1c9028e74dd3a8090741ccd5798" dependencies = [ "arrayvec", - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "cfg_aliases", "document-features", @@ -6768,7 +6780,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg_aliases", "document-features", "hashbrown", @@ -6782,7 +6794,7 @@ dependencies = [ "raw-window-handle 0.6.2", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.16", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-wasm", @@ -6829,15 +6841,15 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "26.0.0" +version = "26.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2971a6c8b903aa9951cf3f3e4d8060904f8c8e905c11f7f5b228edf7faddb719" +checksum = "7df2c64ac282a91ad7662c90bc4a77d4a2135bc0b2a2da5a4d4e267afc034b9e" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", - "bitflags 2.9.1", + "bitflags 2.9.2", "block", "bytemuck", "cfg-if", @@ -6867,7 +6879,7 @@ dependencies = [ "raw-window-handle 0.6.2", "renderdoc-sys", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.16", "wasm-bindgen", "web-sys", "wgpu-types", @@ -6881,11 +6893,11 @@ version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytemuck", "js-sys", "log", - "thiserror 2.0.12", + "thiserror 2.0.16", "web-sys", ] @@ -6907,11 +6919,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -7163,7 +7175,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -7214,10 +7226,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -7416,7 +7429,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.1", + "bitflags 2.9.2", "block2", "bytemuck", "calloop", @@ -7484,7 +7497,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -7537,7 +7550,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "dlib", "log", "once_cell", @@ -7623,7 +7636,7 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-core", "futures-lite", "hex", @@ -7733,9 +7746,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -7770,9 +7783,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" +checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" dependencies = [ "zune-core", ] From 7711b49f6b11d45f14338c4ba81a9089b1d07828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Aug 2025 06:22:22 +0200 Subject: [PATCH 27/34] Propagate `Limits::compression` in `flex` layout --- core/src/layout/flex.rs | 50 ++++++++++++++++++++------------------- core/src/layout/limits.rs | 41 +++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index 3f28f0d9..bec80427 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -78,13 +78,13 @@ where let total_spacing = spacing * items.len().saturating_sub(1) as f32; let max_cross = axis.cross(limits.max()); + let compression = limits.compression(); + let (main_compress, cross_compress) = + axis.pack(compression.width, compression.height); + let mut fill_main_sum = 0; let mut some_fill_cross = false; - let (mut cross, cross_compress) = match axis { - Axis::Vertical if width == Length::Shrink => (0.0, true), - Axis::Horizontal if height == Length::Shrink => (0.0, true), - _ => (max_cross, false), - }; + let mut cross = if cross_compress { 0.0 } else { max_cross }; let mut available = axis.main(limits.max()) - total_spacing; @@ -103,7 +103,8 @@ where axis.pack(size.width.fill_factor(), size.height.fill_factor()) }; - if fill_main_factor == 0 && (!cross_compress || fill_cross_factor == 0) + if (main_compress || fill_main_factor == 0) + && (!cross_compress || fill_cross_factor == 0) { let (max_width, max_height) = axis.pack( available, @@ -114,8 +115,11 @@ where }, ); - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let child_limits = Limits::with_compression( + Size::ZERO, + Size::new(max_width, max_height), + compression, + ); let layout = child.as_widget_mut().layout(tree, renderer, &child_limits); @@ -159,8 +163,11 @@ where let (max_width, max_height) = axis.pack(available, cross); - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let child_limits = Limits::with_compression( + Size::ZERO, + Size::new(max_width, max_height), + compression, + ); let layout = child.as_widget_mut().layout(tree, renderer, &child_limits); @@ -174,16 +181,7 @@ where } } - let remaining = match axis { - Axis::Horizontal => match width { - Length::Shrink => 0.0, - _ => available.max(0.0), - }, - Axis::Vertical => match height { - Length::Shrink => 0.0, - _ => available.max(0.0), - }, - }; + let remaining = available.max(0.0); // THIRD PASS // We lay out the elements that are fluid in the main axis. @@ -196,7 +194,7 @@ where axis.pack(size.width.fill_factor(), size.height.fill_factor()) }; - if fill_main_factor != 0 { + if !main_compress && fill_main_factor != 0 { let max_main = remaining * fill_main_factor as f32 / fill_main_sum as f32; @@ -222,9 +220,10 @@ where }, ); - let child_limits = Limits::new( + let child_limits = Limits::with_compression( Size::new(min_width, min_height), Size::new(max_width, max_height), + compression, ); let layout = @@ -254,8 +253,11 @@ where let (max_width, max_height) = axis.pack(main, cross); - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let child_limits = Limits::with_compression( + Size::ZERO, + Size::new(max_width, max_height), + compression, + ); let layout = child.as_widget_mut().layout(tree, renderer, &child_limits); diff --git a/core/src/layout/limits.rs b/core/src/layout/limits.rs index 2bff8a55..888bc3fd 100644 --- a/core/src/layout/limits.rs +++ b/core/src/layout/limits.rs @@ -6,7 +6,7 @@ use crate::{Length, Size}; pub struct Limits { min: Size, max: Size, - compress: Size, + compression: Size, } impl Limits { @@ -14,15 +14,25 @@ impl Limits { pub const NONE: Limits = Limits { min: Size::ZERO, max: Size::INFINITE, - compress: Size::new(false, false), + compression: Size::new(false, false), }; /// Creates new [`Limits`] with the given minimum and maximum [`Size`]. pub const fn new(min: Size, max: Size) -> Limits { + Limits::with_compression(min, max, Size::new(false, false)) + } + + /// Creates new [`Limits`] with the given minimun and maximum [`Size`], and + /// whether fluid lengths should be compressed to intrinsic dimensions. + pub const fn with_compression( + min: Size, + max: Size, + compress: Size, + ) -> Self { Limits { min, max, - compress: Size::new(false, false), + compression: compress, } } @@ -36,18 +46,23 @@ impl Limits { self.max } + /// Returns the compression of the [`Limits`]. + pub fn compression(&self) -> Size { + self.compression + } + /// Applies a width constraint to the current [`Limits`]. pub fn width(mut self, width: impl Into) -> Limits { match width.into() { Length::Shrink => { - self.compress.width = true; + self.compression.width = true; } Length::Fixed(amount) => { let new_width = amount.min(self.max.width).max(self.min.width); self.min.width = new_width; self.max.width = new_width; - self.compress.width = false; + self.compression.width = false; } Length::Fill | Length::FillPortion(_) => {} } @@ -59,7 +74,7 @@ impl Limits { pub fn height(mut self, height: impl Into) -> Limits { match height.into() { Length::Shrink => { - self.compress.height = true; + self.compression.height = true; } Length::Fixed(amount) => { let new_height = @@ -67,7 +82,7 @@ impl Limits { self.min.height = new_height; self.max.height = new_height; - self.compress.height = false; + self.compression.height = false; } Length::Fill | Length::FillPortion(_) => {} } @@ -120,7 +135,7 @@ impl Limits { Limits { min, max, - compress: self.compress, + compression: self.compression, } } @@ -129,7 +144,7 @@ impl Limits { Limits { min: Size::ZERO, max: self.max, - compress: self.compress, + compression: self.compression, } } @@ -143,7 +158,9 @@ impl Limits { intrinsic_size: Size, ) -> Size { let width = match width.into() { - Length::Fill | Length::FillPortion(_) if !self.compress.width => { + Length::Fill | Length::FillPortion(_) + if !self.compression.width => + { self.max.width } Length::Fixed(amount) => { @@ -153,7 +170,9 @@ impl Limits { }; let height = match height.into() { - Length::Fill | Length::FillPortion(_) if !self.compress.height => { + Length::Fill | Length::FillPortion(_) + if !self.compression.height => + { self.max.height } Length::Fixed(amount) => { From 49a300ab38a86d548b5a607113827ac9efdcf2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Aug 2025 16:24:41 +0200 Subject: [PATCH 28/34] Fix `window::Icon` verbose `Debug` implementation --- core/src/window/icon.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/window/icon.rs b/core/src/window/icon.rs index 5ef0eed7..fb34ca32 100644 --- a/core/src/window/icon.rs +++ b/core/src/window/icon.rs @@ -35,7 +35,7 @@ pub fn from_rgba( } /// An window icon normally used for the titlebar or taskbar. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Icon { rgba: Vec, size: Size, @@ -48,6 +48,15 @@ impl Icon { } } +impl std::fmt::Debug for Icon { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Icon") + .field("rgba", &format!("{} pixels", self.rgba.len() / 4)) + .field("size", &self.size) + .finish() + } +} + #[derive(Debug, thiserror::Error)] /// An error produced when using [`from_rgba`] with invalid arguments. pub enum Error { From 3ad578e24823bf4612c3573efb1ad481bb16134e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Aug 2025 18:16:46 +0200 Subject: [PATCH 29/34] Skip third `flex` pass entirely if main axis is compressed --- core/src/layout/flex.rs | 90 +++++++++++++++++++------------------ examples/layout/src/main.rs | 8 ++-- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index bec80427..0afc6e1b 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -155,7 +155,9 @@ where axis.pack(size.width, size.height) }; - if main_size.fill_factor() == 0 && cross_size.fill_factor() != 0 { + if (main_compress || main_size.fill_factor() == 0) + && cross_size.fill_factor() != 0 + { if let Length::Fixed(main) = main_size { available -= main; continue; @@ -183,54 +185,57 @@ where let remaining = available.max(0.0); - // THIRD PASS + // THIRD PASS (conditional) // We lay out the elements that are fluid in the main axis. // We use the remaining space to evenly allocate space based on fill factors. - for (i, (child, tree)) in items.iter_mut().zip(trees.iter_mut()).enumerate() - { - let (fill_main_factor, fill_cross_factor) = { - let size = child.as_widget().size(); + if !main_compress { + for (i, (child, tree)) in + items.iter_mut().zip(trees.iter_mut()).enumerate() + { + let (fill_main_factor, fill_cross_factor) = { + let size = child.as_widget().size(); - axis.pack(size.width.fill_factor(), size.height.fill_factor()) - }; - - if !main_compress && fill_main_factor != 0 { - let max_main = - remaining * fill_main_factor as f32 / fill_main_sum as f32; - - let max_main = if max_main.is_nan() { - f32::INFINITY - } else { - max_main + axis.pack(size.width.fill_factor(), size.height.fill_factor()) }; - let min_main = if max_main.is_infinite() { - 0.0 - } else { - max_main - }; + if fill_main_factor != 0 { + let max_main = + remaining * fill_main_factor as f32 / fill_main_sum as f32; - let (min_width, min_height) = axis.pack(min_main, 0.0); - let (max_width, max_height) = axis.pack( - max_main, - if fill_cross_factor == 0 { - max_cross + let max_main = if max_main.is_nan() { + f32::INFINITY } else { - cross - }, - ); + max_main + }; - let child_limits = Limits::with_compression( - Size::new(min_width, min_height), - Size::new(max_width, max_height), - compression, - ); + let min_main = if max_main.is_infinite() { + 0.0 + } else { + max_main + }; - let layout = - child.as_widget_mut().layout(tree, renderer, &child_limits); - cross = cross.max(axis.cross(layout.size())); + let (min_width, min_height) = axis.pack(min_main, 0.0); + let (max_width, max_height) = axis.pack( + max_main, + if fill_cross_factor == 0 { + max_cross + } else { + cross + }, + ); - nodes[i] = layout; + let child_limits = Limits::with_compression( + Size::new(min_width, min_height), + Size::new(max_width, max_height), + compression, + ); + + let layout = + child.as_widget_mut().layout(tree, renderer, &child_limits); + cross = cross.max(axis.cross(layout.size())); + + nodes[i] = layout; + } } } @@ -253,11 +258,8 @@ where let (max_width, max_height) = axis.pack(main, cross); - let child_limits = Limits::with_compression( - Size::ZERO, - Size::new(max_width, max_height), - compression, - ); + let child_limits = + Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = child.as_widget_mut().layout(tree, renderer, &child_limits); diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs index f477c406..c15a0bfc 100644 --- a/examples/layout/src/main.rs +++ b/examples/layout/src/main.rs @@ -94,14 +94,14 @@ impl Layout { let controls = row([ (!self.example.is_first()).then_some( - button("← Previous") + button(text("← Previous").shaping(text::Shaping::Advanced)) .padding([5, 10]) .on_press(Message::Previous) .into(), ), Some(horizontal_space().into()), (!self.example.is_last()).then_some( - button("Next →") + button(text("Next →").shaping(text::Shaping::Advanced)) .padding([5, 10]) .on_press(Message::Next) .into(), @@ -294,7 +294,7 @@ fn quotes<'a>() -> Element<'a, Message> { fn quote<'a>( content: impl Into>, ) -> Element<'a, Message> { - row![vertical_rule(2), content.into()] + row![vertical_rule(1), content.into()] .spacing(10) .height(Shrink) .into() @@ -313,7 +313,7 @@ fn quotes<'a>() -> Element<'a, Message> { "This is another reply", ), horizontal_rule(1), - "A separator ↑", + text("A separator ↑").shaping(text::Shaping::Advanced), ] .width(Shrink) .spacing(10) From b476ab277a03b1fa87221defcd9489fba43ed89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Aug 2025 23:06:07 +0200 Subject: [PATCH 30/34] Remove `debug_assert!` from `scrollable` by enforcing compression --- widget/src/scrollable.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 0b0b6f9b..9c4748ea 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -111,22 +111,12 @@ where class: Theme::default(), last_status: None, } - .validate() + .enclose() } - fn validate(mut self) -> Self { + fn enclose(mut self) -> Self { let size_hint = self.content.as_widget().size_hint(); - debug_assert!( - self.direction.vertical().is_none() || !size_hint.height.is_fill(), - "scrollable content must not fill its vertical scrolling axis" - ); - - debug_assert!( - self.direction.horizontal().is_none() || !size_hint.width.is_fill(), - "scrollable content must not fill its horizontal scrolling axis" - ); - if self.direction.horizontal().is_none() { self.width = self.width.enclose(size_hint.width); } @@ -146,7 +136,7 @@ where /// Sets the [`Direction`] of the [`Scrollable`]. pub fn direction(mut self, direction: impl Into) -> Self { self.direction = direction.into(); - self.validate() + self.enclose() } /// Sets the [`Id`] of the [`Scrollable`]. @@ -437,20 +427,24 @@ where ..Padding::ZERO }, |limits| { - let child_limits = layout::Limits::new( + let is_horizontal = self.direction.horizontal().is_some(); + let is_vertical = self.direction.vertical().is_some(); + + let child_limits = layout::Limits::with_compression( limits.min(), Size::new( - if self.direction.horizontal().is_some() { + if is_horizontal { f32::INFINITY } else { limits.max().width }, - if self.direction.vertical().is_some() { + if is_vertical { f32::INFINITY } else { limits.max().height }, ), + Size::new(is_horizontal, is_vertical), ); self.content.as_widget_mut().layout( From d5521f4230b15b69fd2f21c9c1eedf451972958d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 22 Aug 2025 05:29:49 +0200 Subject: [PATCH 31/34] Loosen `Limits` for children of `row::Wrapping` --- widget/src/row.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widget/src/row.rs b/widget/src/row.rs index 2ee3d345..6667438b 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -417,6 +417,7 @@ where .height(self.row.height) .shrink(self.row.padding); + let child_limits = limits.loose(); let spacing = self.row.spacing; let vertical_spacing = self.vertical_spacing.unwrap_or(spacing); let max_width = limits.max().width; @@ -453,7 +454,7 @@ where let node = child.as_widget_mut().layout( &mut tree.children[i], renderer, - &limits, + &child_limits, ); let child_size = node.size(); From 7d72b9135d339d0dd63f4f6421b1354872383604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 23 Aug 2025 20:48:10 +0200 Subject: [PATCH 32/34] Remove alpha interpolation in `oklab` shader --- wgpu/src/shader/color/oklab.wgsl | 3 --- 1 file changed, 3 deletions(-) diff --git a/wgpu/src/shader/color/oklab.wgsl b/wgpu/src/shader/color/oklab.wgsl index 0dc37ba6..91bceca7 100644 --- a/wgpu/src/shader/color/oklab.wgsl +++ b/wgpu/src/shader/color/oklab.wgsl @@ -19,8 +19,5 @@ fn interpolate_color(from_: vec4, to_: vec4, factor: f32) -> vec4 // Back to linear RGB var color = to_rgb * (mixed * mixed * mixed); - // Alpha interpolation - color.a = mix(from_.a, to_.a, factor); - return color; } From f9a6a3051d0a5e1c9f47ebe4071daf5621f8d9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 23 Aug 2025 21:13:54 +0200 Subject: [PATCH 33/34] Remove Oklab gradient support --- wgpu/src/quad/gradient.rs | 46 ++++++++------------------------ wgpu/src/shader/color/oklab.wgsl | 23 ---------------- wgpu/src/triangle.rs | 39 +++++++-------------------- 3 files changed, 20 insertions(+), 88 deletions(-) delete mode 100644 wgpu/src/shader/color/oklab.wgsl diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs index cd99c8ca..6a5eed8d 100644 --- a/wgpu/src/quad/gradient.rs +++ b/wgpu/src/quad/gradient.rs @@ -72,8 +72,6 @@ impl Pipeline { ) -> Self { #[cfg(not(target_arch = "wasm32"))] { - use crate::graphics::color; - let layout = device.create_pipeline_layout( &wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu.quad.gradient.pipeline"), @@ -86,39 +84,17 @@ impl Pipeline { device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("iced_wgpu.quad.gradient.shader"), source: wgpu::ShaderSource::Wgsl( - std::borrow::Cow::Borrowed( - if color::GAMMA_CORRECTION { - concat!( - include_str!("../shader/quad.wgsl"), - "\n", - include_str!("../shader/vertex.wgsl"), - "\n", - include_str!( - "../shader/quad/gradient.wgsl" - ), - "\n", - include_str!("../shader/color.wgsl"), - "\n", - include_str!("../shader/color/oklab.wgsl") - ) - } else { - concat!( - include_str!("../shader/quad.wgsl"), - "\n", - include_str!("../shader/vertex.wgsl"), - "\n", - include_str!( - "../shader/quad/gradient.wgsl" - ), - "\n", - include_str!("../shader/color.wgsl"), - "\n", - include_str!( - "../shader/color/linear_rgb.wgsl" - ) - ) - }, - ), + std::borrow::Cow::Borrowed(concat!( + include_str!("../shader/quad.wgsl"), + "\n", + include_str!("../shader/vertex.wgsl"), + "\n", + include_str!("../shader/quad/gradient.wgsl"), + "\n", + include_str!("../shader/color.wgsl"), + "\n", + include_str!("../shader/color/linear_rgb.wgsl") + )), ), }); diff --git a/wgpu/src/shader/color/oklab.wgsl b/wgpu/src/shader/color/oklab.wgsl deleted file mode 100644 index 91bceca7..00000000 --- a/wgpu/src/shader/color/oklab.wgsl +++ /dev/null @@ -1,23 +0,0 @@ -const to_lms = mat3x4( - vec4(0.4121656120, 0.2118591070, 0.0883097947, 0.0), - vec4(0.5362752080, 0.6807189584, 0.2818474174, 0.0), - vec4(0.0514575653, 0.1074065790, 0.6302613616, 0.0), -); - -const to_rgb = mat3x4( - vec4( 4.0767245293, -3.3072168827, 0.2307590544, 0.0), - vec4(-1.2681437731, 2.6093323231, -0.3411344290, 0.0), - vec4(-0.0041119885, -0.7034763098, 1.7068625689, 0.0), -); - -fn interpolate_color(from_: vec4, to_: vec4, factor: f32) -> vec4 { - // To Oklab - let lms_a = pow(from_ * to_lms, vec3(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0)); - let lms_b = pow(to_ * to_lms, vec3(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0)); - let mixed = mix(lms_a, lms_b, factor); - - // Back to linear RGB - var color = to_rgb * (mixed * mixed * mixed); - - return color; -} diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 2c900f54..682f1966 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -827,7 +827,6 @@ mod solid { mod gradient { use crate::Buffer; use crate::graphics::Antialiasing; - use crate::graphics::color; use crate::graphics::mesh; use crate::triangle; @@ -922,35 +921,15 @@ mod gradient { device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("iced_wgpu.triangle.gradient.shader"), source: wgpu::ShaderSource::Wgsl( - std::borrow::Cow::Borrowed( - if color::GAMMA_CORRECTION { - concat!( - include_str!("shader/triangle.wgsl"), - "\n", - include_str!( - "shader/triangle/gradient.wgsl" - ), - "\n", - include_str!("shader/color.wgsl"), - "\n", - include_str!("shader/color/oklab.wgsl") - ) - } else { - concat!( - include_str!("shader/triangle.wgsl"), - "\n", - include_str!( - "shader/triangle/gradient.wgsl" - ), - "\n", - include_str!("shader/color.wgsl"), - "\n", - include_str!( - "shader/color/linear_rgb.wgsl" - ) - ) - }, - ), + std::borrow::Cow::Borrowed(concat!( + include_str!("shader/triangle.wgsl"), + "\n", + include_str!("shader/triangle/gradient.wgsl"), + "\n", + include_str!("shader/color.wgsl"), + "\n", + include_str!("shader/color/linear_rgb.wgsl") + )), ), }); From 6be707267e733fa9d3600826a1e0981d80875f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 29 Aug 2025 04:21:43 +0200 Subject: [PATCH 34/34] Revert "Make `Widget::diff` mutable" This reverts commit 497ebcd0c36c45d6203fc84005abb81f95cf34ee. --- core/src/element.rs | 37 +++---------------------------- core/src/widget.rs | 2 +- core/src/widget/tree.rs | 26 ++++++++++------------ examples/loupe/src/main.rs | 4 ++-- examples/toast/src/main.rs | 6 ++--- runtime/src/user_interface.rs | 2 +- widget/src/button.rs | 4 ++-- widget/src/column.rs | 4 ++-- widget/src/combo_box.rs | 2 +- widget/src/container.rs | 4 ++-- widget/src/float.rs | 4 ++-- widget/src/grid.rs | 4 ++-- widget/src/helpers.rs | 8 +++---- widget/src/keyed/column.rs | 6 ++--- widget/src/lazy.rs | 6 ++--- widget/src/lazy/component.rs | 6 ++--- widget/src/mouse_area.rs | 4 ++-- widget/src/overlay/menu.rs | 4 ++-- widget/src/pane_grid.rs | 4 ++-- widget/src/pane_grid/content.rs | 6 ++--- widget/src/pane_grid/title_bar.rs | 10 ++++----- widget/src/pin.rs | 4 ++-- widget/src/responsive.rs | 11 +++------ widget/src/row.rs | 6 ++--- widget/src/scrollable.rs | 4 ++-- widget/src/sensor.rs | 4 ++-- widget/src/stack.rs | 4 ++-- widget/src/table.rs | 4 ++-- widget/src/text_input.rs | 2 +- widget/src/themer.rs | 4 ++-- widget/src/tooltip.rs | 8 +++---- 31 files changed, 83 insertions(+), 121 deletions(-) diff --git a/core/src/element.rs b/core/src/element.rs index 349aecb6..6a71296c 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -9,7 +9,7 @@ use crate::{ Vector, Widget, }; -use std::borrow::{Borrow, BorrowMut}; +use std::borrow::Borrow; /// A generic [`Widget`]. /// @@ -239,37 +239,6 @@ impl<'a, Message, Theme, Renderer> } } -impl<'a, Message, Theme, Renderer> - Borrow + 'a> - for &mut Element<'a, Message, Theme, Renderer> -{ - fn borrow(&self) -> &(dyn Widget + 'a) { - self.widget.borrow() - } -} - -impl<'a, Message, Theme, Renderer> - BorrowMut + 'a> - for Element<'a, Message, Theme, Renderer> -{ - fn borrow_mut( - &mut self, - ) -> &mut (dyn Widget + 'a) { - self.widget.borrow_mut() - } -} - -impl<'a, Message, Theme, Renderer> - BorrowMut + 'a> - for &mut Element<'a, Message, Theme, Renderer> -{ - fn borrow_mut( - &mut self, - ) -> &mut (dyn Widget + 'a) { - self.widget.borrow_mut() - } -} - struct Map<'a, A, B, Theme, Renderer> { widget: Box + 'a>, mapper: Box B + 'a>, @@ -309,7 +278,7 @@ where self.widget.children() } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { self.widget.diff(tree); } @@ -452,7 +421,7 @@ where self.element.widget.children() } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { self.element.widget.diff(tree); } diff --git a/core/src/widget.rs b/core/src/widget.rs index 9316dba1..3b39ffcc 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -96,7 +96,7 @@ where } /// Reconciles the [`Widget`] with the provided [`Tree`]. - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { tree.children.clear(); } diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs index 20b55360..2600cfc6 100644 --- a/core/src/widget/tree.rs +++ b/core/src/widget/tree.rs @@ -2,7 +2,7 @@ use crate::Widget; use std::any::{self, Any}; -use std::borrow::{Borrow, BorrowMut}; +use std::borrow::Borrow; use std::fmt; /// A persistent state widget tree. @@ -56,12 +56,12 @@ impl Tree { /// [`Widget::diff`]: crate::Widget::diff pub fn diff<'a, Message, Theme, Renderer>( &mut self, - mut new: impl BorrowMut + 'a>, + new: impl Borrow + 'a>, ) where Renderer: crate::Renderer, { - if self.tag == new.borrow_mut().tag() { - new.borrow_mut().diff(self); + if self.tag == new.borrow().tag() { + new.borrow().diff(self); } else { *self = Self::new(new); } @@ -70,15 +70,13 @@ impl Tree { /// Reconciles the children of the tree with the provided list of widgets. pub fn diff_children<'a, Message, Theme, Renderer>( &mut self, - new_children: &mut [impl BorrowMut< - dyn Widget + 'a, - >], + new_children: &[impl Borrow + 'a>], ) where Renderer: crate::Renderer, { self.diff_children_custom( new_children, - |tree, widget| tree.diff(widget.borrow_mut()), + |tree, widget| tree.diff(widget.borrow()), |widget| Self::new(widget.borrow()), ); } @@ -87,8 +85,8 @@ impl Tree { /// logic both for diffing and creating new widget state. pub fn diff_children_custom( &mut self, - new_children: &mut [T], - diff: impl Fn(&mut Tree, &mut T), + new_children: &[T], + diff: impl Fn(&mut Tree, &T), new_state: impl Fn(&T) -> Self, ) { if self.children.len() > new_children.len() { @@ -96,7 +94,7 @@ impl Tree { } for (child_state, new) in - self.children.iter_mut().zip(new_children.iter_mut()) + self.children.iter_mut().zip(new_children.iter()) { diff(child_state, new); } @@ -116,8 +114,8 @@ impl Tree { /// `maybe_changed` closure. pub fn diff_children_custom_with_search( current_children: &mut Vec, - new_children: &mut [T], - diff: impl Fn(&mut Tree, &mut T), + new_children: &[T], + diff: impl Fn(&mut Tree, &T), maybe_changed: impl Fn(usize) -> bool, new_state: impl Fn(&T) -> Tree, ) { @@ -185,7 +183,7 @@ pub fn diff_children_custom_with_search( // TODO: Merge loop with extend logic (?) for (child_state, new) in - current_children.iter_mut().zip(new_children.iter_mut()) + current_children.iter_mut().zip(new_children.iter()) { diff(child_state, new); } diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs index ebd7a44f..89bc82da 100644 --- a/examples/loupe/src/main.rs +++ b/examples/loupe/src/main.rs @@ -87,8 +87,8 @@ mod loupe { self.content.as_widget().children() } - fn diff(&mut self, tree: &mut widget::Tree) { - self.content.as_widget_mut().diff(tree); + fn diff(&self, tree: &mut widget::Tree) { + self.content.as_widget().diff(tree); } fn size(&self) -> Size { diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index d1be0c09..dc8c2593 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -314,7 +314,7 @@ mod toast { .collect() } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { let instants = tree.state.downcast_mut::>>(); // Invalidating removed instants to None allows us to remove @@ -336,8 +336,8 @@ mod toast { } tree.diff_children( - &mut std::iter::once(&mut self.content) - .chain(self.toasts.iter_mut()) + &std::iter::once(&self.content) + .chain(self.toasts.iter()) .collect::>(), ); } diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 6e32d10f..26c96201 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -100,7 +100,7 @@ where let mut root = root.into(); let Cache { mut state } = cache; - state.diff(root.as_widget_mut()); + state.diff(root.as_widget()); let base = root.as_widget_mut().layout( &mut state, diff --git a/widget/src/button.rs b/widget/src/button.rs index 2ae1508b..6ae8d3e5 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -223,8 +223,8 @@ where vec![Tree::new(&self.content)] } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(std::slice::from_mut(&mut self.content)); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(std::slice::from_ref(&self.content)); } fn size(&self) -> Size { diff --git a/widget/src/column.rs b/widget/src/column.rs index bcbbe49e..39507a78 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -194,8 +194,8 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(&mut self.children); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&self.children); } fn size(&self) -> Size { diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index 86533c3a..f94705d8 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -509,7 +509,7 @@ where vec![widget::Tree::new(&self.text_input as &dyn Widget<_, _, _>)] } - fn diff(&mut self, _tree: &mut widget::Tree) { + fn diff(&self, _tree: &mut widget::Tree) { // do nothing so the children don't get cleared } diff --git a/widget/src/container.rs b/widget/src/container.rs index 97502b17..286c5a63 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -247,8 +247,8 @@ where self.content.as_widget().children() } - fn diff(&mut self, tree: &mut Tree) { - self.content.as_widget_mut().diff(tree); + fn diff(&self, tree: &mut Tree) { + self.content.as_widget().diff(tree); } fn size(&self) -> Size { diff --git a/widget/src/float.rs b/widget/src/float.rs index a7eaf5bb..1ff6ab10 100644 --- a/widget/src/float.rs +++ b/widget/src/float.rs @@ -103,8 +103,8 @@ where self.content.as_widget().children() } - fn diff(&mut self, tree: &mut widget::Tree) { - self.content.as_widget_mut().diff(tree); + fn diff(&self, tree: &mut widget::Tree) { + self.content.as_widget().diff(tree); } fn size(&self) -> Size { diff --git a/widget/src/grid.rs b/widget/src/grid.rs index 5bf2bf6b..25e3ebe0 100644 --- a/widget/src/grid.rs +++ b/widget/src/grid.rs @@ -158,8 +158,8 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(&mut self.children); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&self.children); } fn size(&self) -> Size { diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index b1d29127..3b1d0bbe 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -598,8 +598,8 @@ where self.content.as_widget().children() } - fn diff(&mut self, tree: &mut Tree) { - self.content.as_widget_mut().diff(tree); + fn diff(&self, tree: &mut Tree) { + self.content.as_widget().diff(tree); } fn size(&self) -> Size { @@ -761,8 +761,8 @@ where vec![Tree::new(&self.base), Tree::new(&self.top)] } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(&mut [&mut self.base, &mut self.top]); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&[&self.base, &self.top]); } fn size(&self) -> Size { diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 907ae479..6ca6f485 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -222,7 +222,7 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { let Tree { state, children, .. } = tree; @@ -231,8 +231,8 @@ where tree::diff_children_custom_with_search( children, - &mut self.children, - |tree, child| child.as_widget_mut().diff(tree), + &self.children, + |tree, child| child.as_widget().diff(tree), |index| { self.keys.get(index).or_else(|| self.keys.last()).copied() != Some(state.keys[index]) diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index b67ba56c..ec3c9604 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -125,7 +125,7 @@ where self.with_element(|element| vec![Tree::new(element.as_widget())]) } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { let current = tree .state .downcast_mut::>(); @@ -144,8 +144,8 @@ where current.element = Rc::new(RefCell::new(Some(element))); (*self.element.borrow_mut()) = Some(current.element.clone()); - self.with_element_mut(|element| { - tree.diff_children(std::slice::from_mut(element)); + self.with_element(|element| { + tree.diff_children(std::slice::from_ref(&element.as_widget())); }); } else { (*self.element.borrow_mut()) = Some(current.element.clone()); diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index c40be4d1..1b582dee 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -147,13 +147,13 @@ where Renderer: renderer::Renderer, { fn diff_self(&self) { - self.with_element_mut(|element| { + self.with_element(|element| { self.tree .borrow_mut() .borrow_mut() .as_mut() .unwrap() - .diff_children(std::slice::from_mut(element)); + .diff_children(std::slice::from_ref(&element)); }); } @@ -279,7 +279,7 @@ where vec![] } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { let tree = tree.state.downcast_ref::>>>(); *self.tree.borrow_mut() = tree.clone(); self.rebuild_element_if_necessary(); diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index e7ce47b5..f4a76d3b 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -181,8 +181,8 @@ where vec![Tree::new(&self.content)] } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(std::slice::from_mut(&mut self.content)); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(std::slice::from_ref(&self.content)); } fn size(&self) -> Size { diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index e84ae858..c4c5b521 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -205,7 +205,7 @@ where class, } = menu; - let mut list = Scrollable::new(List { + let list = Scrollable::new(List { options, hovered_option, on_selected, @@ -218,7 +218,7 @@ where class, }); - state.tree.diff(&mut list as &mut dyn Widget<_, _, _>); + state.tree.diff(&list as &dyn Widget<_, _, _>); Self { position, diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 3dd0c941..8c63fe63 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -378,7 +378,7 @@ where self.contents.iter().map(Content::state).collect() } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { let Memory { order, .. } = tree.state.downcast_ref(); // `Pane` always increments and is iterated by Ord so new @@ -401,7 +401,7 @@ where }); tree.diff_children_custom( - &mut self.contents, + &self.contents, |state, content| content.diff(state), Content::state, ); diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index 00f0d060..6592694b 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -91,13 +91,13 @@ where } } - pub(super) fn diff(&mut self, tree: &mut Tree) { + pub(super) fn diff(&self, tree: &mut Tree) { if tree.children.len() == 2 { - if let Some(title_bar) = self.title_bar.as_mut() { + if let Some(title_bar) = self.title_bar.as_ref() { title_bar.diff(&mut tree.children[1]); } - tree.children[0].diff(&mut self.body); + tree.children[0].diff(&self.body); } else { *tree = self.state(); } diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index 598cbcf5..7d15bf80 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -128,17 +128,17 @@ where } } - pub(super) fn diff(&mut self, tree: &mut Tree) { + pub(super) fn diff(&self, tree: &mut Tree) { if tree.children.len() == 3 { - if let Some(controls) = self.controls.as_mut() { - if let Some(compact) = controls.compact.as_mut() { + if let Some(controls) = self.controls.as_ref() { + if let Some(compact) = controls.compact.as_ref() { tree.children[2].diff(compact); } - tree.children[1].diff(&mut controls.full); + tree.children[1].diff(&controls.full); } - tree.children[0].diff(&mut self.content); + tree.children[0].diff(&self.content); } else { *tree = self.state(); } diff --git a/widget/src/pin.rs b/widget/src/pin.rs index f4a1c667..04ed0324 100644 --- a/widget/src/pin.rs +++ b/widget/src/pin.rs @@ -127,8 +127,8 @@ where self.content.as_widget().children() } - fn diff(&mut self, tree: &mut widget::Tree) { - self.content.as_widget_mut().diff(tree); + fn diff(&self, tree: &mut widget::Tree) { + self.content.as_widget().diff(tree); } fn size(&self) -> Size { diff --git a/widget/src/responsive.rs b/widget/src/responsive.rs index 6bb86a12..a987a430 100644 --- a/widget/src/responsive.rs +++ b/widget/src/responsive.rs @@ -3,7 +3,7 @@ use crate::core::mouse; use crate::core::overlay; use crate::core::renderer; use crate::core::widget; -use crate::core::widget::tree::{self, Tree}; +use crate::core::widget::Tree; use crate::core::{ self, Clipboard, Element, Event, Length, Rectangle, Shell, Size, Vector, Widget, @@ -66,12 +66,7 @@ impl Widget where Renderer: core::Renderer, { - fn tag(&self) -> tree::Tag { - struct Marker; - tree::Tag::of::() - } - - fn diff(&mut self, _tree: &mut Tree) { + fn diff(&self, _tree: &mut Tree) { // Diff is deferred to layout } @@ -92,7 +87,7 @@ where let size = limits.max(); self.content = (self.view)(size); - tree.diff_children(std::slice::from_mut(&mut self.content)); + tree.diff_children(std::slice::from_ref(&self.content)); let node = self.content.as_widget_mut().layout( &mut tree.children[0], diff --git a/widget/src/row.rs b/widget/src/row.rs index 6667438b..af1f490c 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -196,8 +196,8 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(&mut self.children); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&self.children); } fn size(&self) -> Size { @@ -398,7 +398,7 @@ where self.row.children() } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { self.row.diff(tree); } diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 9c4748ea..c70c3cf2 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -399,8 +399,8 @@ where vec![Tree::new(&self.content)] } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(std::slice::from_mut(&mut self.content)); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(std::slice::from_ref(&self.content)); } fn size(&self) -> Size { diff --git a/widget/src/sensor.rs b/widget/src/sensor.rs index fd7a3510..e0122b2e 100644 --- a/widget/src/sensor.rs +++ b/widget/src/sensor.rs @@ -184,8 +184,8 @@ where vec![Tree::new(&self.content)] } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(std::slice::from_mut(&mut self.content)); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&[&self.content]); } fn update( diff --git a/widget/src/stack.rs b/widget/src/stack.rs index 63a6b97e..c0fb3147 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -146,8 +146,8 @@ where self.children.iter().map(Tree::new).collect() } - fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(&mut self.children); + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&self.children); } fn size(&self) -> Size { diff --git a/widget/src/table.rs b/widget/src/table.rs index 97083835..06b47124 100644 --- a/widget/src/table.rs +++ b/widget/src/table.rs @@ -227,8 +227,8 @@ where .collect() } - fn diff(&mut self, state: &mut widget::Tree) { - state.diff_children(&mut self.cells); + fn diff(&self, state: &mut widget::Tree) { + state.diff_children(&self.cells); } fn layout( diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 1a4f8a99..c385b34b 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -654,7 +654,7 @@ where tree::State::new(State::::new()) } - fn diff(&mut self, tree: &mut Tree) { + fn diff(&self, tree: &mut Tree) { let state = tree.state.downcast_mut::>(); // Stop pasting if input becomes disabled diff --git a/widget/src/themer.rs b/widget/src/themer.rs index a089d1d5..f335cd01 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -81,8 +81,8 @@ where self.content.as_widget().children() } - fn diff(&mut self, tree: &mut Tree) { - self.content.as_widget_mut().diff(tree); + fn diff(&self, tree: &mut Tree) { + self.content.as_widget().diff(tree); } fn size(&self) -> Size { diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 782258da..4c2c1a2e 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -155,10 +155,10 @@ where ] } - fn diff(&mut self, tree: &mut widget::Tree) { - tree.diff_children(&mut [ - self.content.as_widget_mut(), - self.tooltip.as_widget_mut(), + fn diff(&self, tree: &mut widget::Tree) { + tree.diff_children(&[ + self.content.as_widget(), + self.tooltip.as_widget(), ]); }