diff options
author | 2024-08-06 18:06:42 +0200 | |
---|---|---|
committer | 2024-08-06 18:06:42 +0200 | |
commit | d5ffe98ce939990e6c8ef6f40895cd1f9633fe18 (patch) | |
tree | ce165aa5f79750b1fe4d05f7380a92759014cef6 | |
parent | ff0da4dc819a0cb3037502fd2ee82609f25f2fe9 (diff) | |
parent | 6fbbc30f5cb1f699c61e064ed54fe428d96be7d3 (diff) | |
download | iced-d5ffe98ce939990e6c8ef6f40895cd1f9633fe18.tar.gz iced-d5ffe98ce939990e6c8ef6f40895cd1f9633fe18.tar.bz2 iced-d5ffe98ce939990e6c8ef6f40895cd1f9633fe18.zip |
Merge pull request #2539 from iced-rs/feature/row-wrapping
Implement `row::Wrapping` widget
-rw-r--r-- | core/src/layout/node.rs | 13 | ||||
-rw-r--r-- | examples/layout/src/main.rs | 7 | ||||
-rw-r--r-- | widget/src/row.rs | 200 |
3 files changed, 211 insertions, 9 deletions
diff --git a/core/src/layout/node.rs b/core/src/layout/node.rs index 5743a9bd..0c0f90fb 100644 --- a/core/src/layout/node.rs +++ b/core/src/layout/node.rs @@ -103,12 +103,13 @@ impl Node { } /// Translates the [`Node`] by the given translation. - pub fn translate(self, translation: impl Into<Vector>) -> Self { - let translation = translation.into(); + pub fn translate(mut self, translation: impl Into<Vector>) -> Self { + self.translate_mut(translation); + self + } - Self { - bounds: self.bounds + translation, - ..self - } + /// Translates the [`Node`] by the given translation. + pub fn translate_mut(&mut self, translation: impl Into<Vector>) { + self.bounds = self.bounds + translation.into(); } } diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs index f39e24f9..cb33369b 100644 --- a/examples/layout/src/main.rs +++ b/examples/layout/src/main.rs @@ -258,9 +258,10 @@ fn application<'a>() -> Element<'a, Message> { scrollable( column![ "Content!", - square(400), - square(200), - square(400), + row((1..10).map(|i| square(if i % 2 == 0 { 80 } else { 160 }))) + .spacing(20) + .align_y(Center) + .wrap(), "The end" ] .spacing(40) diff --git a/widget/src/row.rs b/widget/src/row.rs index 3feeaa7e..fee2218a 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -142,6 +142,13 @@ where ) -> Self { children.into_iter().fold(self, Self::push) } + + /// Turns the [`Row`] into a [`Wrapping`] row. + /// + /// The original alignment of the [`Row`] is preserved per row wrapped. + pub fn wrap(self) -> Wrapping<'a, Message, Theme, Renderer> { + Wrapping { row: self } + } } impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer> @@ -339,3 +346,196 @@ where Self::new(row) } } + +/// A [`Row`] that wraps its contents. +/// +/// Create a [`Row`] first, and then call [`Row::wrap`] to +/// obtain a [`Row`] that wraps its contents. +/// +/// The original alignment of the [`Row`] is preserved per row wrapped. +#[allow(missing_debug_implementations)] +pub struct Wrapping< + 'a, + Message, + Theme = crate::Theme, + Renderer = crate::Renderer, +> { + row: Row<'a, Message, Theme, Renderer>, +} + +impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer> + for Wrapping<'a, Message, Theme, Renderer> +where + Renderer: crate::core::Renderer, +{ + fn children(&self) -> Vec<Tree> { + self.row.children() + } + + fn diff(&self, tree: &mut Tree) { + self.row.diff(tree); + } + + fn size(&self) -> Size<Length> { + self.row.size() + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits + .width(self.row.width) + .height(self.row.height) + .shrink(self.row.padding); + + let spacing = self.row.spacing; + let max_width = limits.max().width; + + let mut children: Vec<layout::Node> = Vec::new(); + let mut intrinsic_size = Size::ZERO; + let mut row_start = 0; + let mut row_height = 0.0; + let mut x = 0.0; + let mut y = 0.0; + + let align_factor = match self.row.align { + Alignment::Start => 0.0, + Alignment::Center => 2.0, + Alignment::End => 1.0, + }; + + let align = |row_start: std::ops::Range<usize>, + row_height: f32, + children: &mut Vec<layout::Node>| { + if align_factor != 0.0 { + for node in &mut children[row_start] { + let height = node.size().height; + + node.translate_mut(Vector::new( + 0.0, + (row_height - height) / align_factor, + )); + } + } + }; + + for (i, child) in self.row.children.iter().enumerate() { + let node = child.as_widget().layout( + &mut tree.children[i], + renderer, + &limits, + ); + + let child_size = node.size(); + + if x != 0.0 && x + child_size.width > max_width { + intrinsic_size.width = intrinsic_size.width.max(x - spacing); + + align(row_start..i, row_height, &mut children); + + y += row_height + spacing; + x = 0.0; + row_start = i; + row_height = 0.0; + } + + row_height = row_height.max(child_size.height); + + children.push(node.move_to(( + x + self.row.padding.left, + y + self.row.padding.top, + ))); + + x += child_size.width + spacing; + } + + if x != 0.0 { + intrinsic_size.width = intrinsic_size.width.max(x - spacing); + } + + intrinsic_size.height = (y - spacing).max(0.0) + row_height; + align(row_start..children.len(), row_height, &mut children); + + let size = + limits.resolve(self.row.width, self.row.height, intrinsic_size); + + layout::Node::with_children(size.expand(self.row.padding), children) + } + + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation<()>, + ) { + self.row.operate(tree, layout, renderer, operation); + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + self.row.on_event( + tree, event, layout, cursor, renderer, clipboard, shell, viewport, + ) + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.row + .mouse_interaction(tree, layout, cursor, viewport, renderer) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.row + .draw(tree, renderer, theme, style, layout, cursor, viewport); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + translation: Vector, + ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { + self.row.overlay(tree, layout, renderer, translation) + } +} + +impl<'a, Message, Theme, Renderer> From<Wrapping<'a, Message, Theme, Renderer>> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: crate::core::Renderer + 'a, +{ + fn from(row: Wrapping<'a, Message, Theme, Renderer>) -> Self { + Self::new(row) + } +} |