Skip to content

Commit 41e69b0

Browse files
authored
Merge pull request #2884 from edwloef/column-wrapping
Add `wrap` method for `column` widget
2 parents 711c0c6 + 0eb24ee commit 41e69b0

File tree

1 file changed

+262
-0
lines changed

1 file changed

+262
-0
lines changed

widget/src/column.rs

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,17 @@ where
160160
) -> Self {
161161
children.into_iter().fold(self, Self::push)
162162
}
163+
164+
/// Turns the [`Column`] into a [`Wrapping`] column.
165+
///
166+
/// The original alignment of the [`Column`] is preserved per column wrapped.
167+
pub fn wrap(self) -> Wrapping<'a, Message, Theme, Renderer> {
168+
Wrapping {
169+
column: self,
170+
horizontal_spacing: None,
171+
align_y: alignment::Vertical::Top,
172+
}
173+
}
163174
}
164175

165176
impl<Message, Renderer> Default for Column<'_, Message, Renderer>
@@ -353,3 +364,254 @@ where
353364
Self::new(column)
354365
}
355366
}
367+
368+
/// A [`Column`] that wraps its contents.
369+
///
370+
/// Create a [`Column`] first, and then call [`Column::wrap`] to
371+
/// obtain a [`Column`] that wraps its contents.
372+
///
373+
/// The original alignment of the [`Column`] is preserved per column wrapped.
374+
#[allow(missing_debug_implementations)]
375+
pub struct Wrapping<
376+
'a,
377+
Message,
378+
Theme = crate::Theme,
379+
Renderer = crate::Renderer,
380+
> {
381+
column: Column<'a, Message, Theme, Renderer>,
382+
horizontal_spacing: Option<f32>,
383+
align_y: alignment::Vertical,
384+
}
385+
386+
impl<Message, Theme, Renderer> Wrapping<'_, Message, Theme, Renderer> {
387+
/// Sets the horizontal spacing _between_ columns.
388+
pub fn horizontal_spacing(mut self, amount: impl Into<Pixels>) -> Self {
389+
self.horizontal_spacing = Some(amount.into().0);
390+
self
391+
}
392+
393+
/// Sets the vertical alignment of the wrapping [`Column`].
394+
pub fn align_x(mut self, align_y: impl Into<alignment::Vertical>) -> Self {
395+
self.align_y = align_y.into();
396+
self
397+
}
398+
}
399+
400+
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
401+
for Wrapping<'_, Message, Theme, Renderer>
402+
where
403+
Renderer: crate::core::Renderer,
404+
{
405+
fn children(&self) -> Vec<Tree> {
406+
self.column.children()
407+
}
408+
409+
fn diff(&self, tree: &mut Tree) {
410+
self.column.diff(tree);
411+
}
412+
413+
fn size(&self) -> Size<Length> {
414+
self.column.size()
415+
}
416+
417+
fn layout(
418+
&mut self,
419+
tree: &mut Tree,
420+
renderer: &Renderer,
421+
limits: &layout::Limits,
422+
) -> layout::Node {
423+
let limits = limits
424+
.width(self.column.width)
425+
.height(self.column.height)
426+
.shrink(self.column.padding);
427+
428+
let child_limits = limits.loose();
429+
let spacing = self.column.spacing;
430+
let horizontal_spacing = self.horizontal_spacing.unwrap_or(spacing);
431+
let max_height = limits.max().height;
432+
433+
let mut children: Vec<layout::Node> = Vec::new();
434+
let mut intrinsic_size = Size::ZERO;
435+
let mut column_start = 0;
436+
let mut column_width = 0.0;
437+
let mut x = 0.0;
438+
let mut y = 0.0;
439+
440+
let align_factor = match self.column.align {
441+
Alignment::Start => 0.0,
442+
Alignment::Center => 2.0,
443+
Alignment::End => 1.0,
444+
};
445+
446+
let align_x = |column_start: std::ops::Range<usize>,
447+
column_width: f32,
448+
children: &mut Vec<layout::Node>| {
449+
if align_factor != 0.0 {
450+
for node in &mut children[column_start] {
451+
let width = node.size().width;
452+
453+
node.translate_mut(Vector::new(
454+
(column_width - width) / align_factor,
455+
0.0,
456+
));
457+
}
458+
}
459+
};
460+
461+
for (i, child) in self.column.children.iter_mut().enumerate() {
462+
let node = child.as_widget_mut().layout(
463+
&mut tree.children[i],
464+
renderer,
465+
&child_limits,
466+
);
467+
468+
let child_size = node.size();
469+
470+
if y != 0.0 && y + child_size.height > max_height {
471+
intrinsic_size.height = intrinsic_size.height.max(y - spacing);
472+
473+
align_x(column_start..i, column_width, &mut children);
474+
475+
x += column_width + horizontal_spacing;
476+
y = 0.0;
477+
column_start = i;
478+
column_width = 0.0;
479+
}
480+
481+
column_width = column_width.max(child_size.width);
482+
483+
children.push(node.move_to((
484+
x + self.column.padding.left,
485+
y + self.column.padding.top,
486+
)));
487+
488+
y += child_size.height + spacing;
489+
}
490+
491+
if y != 0.0 {
492+
intrinsic_size.height = intrinsic_size.height.max(y - spacing);
493+
}
494+
495+
intrinsic_size.width = x + column_width;
496+
align_x(column_start..children.len(), column_width, &mut children);
497+
498+
let align_factor = match self.align_y {
499+
alignment::Vertical::Top => 0.0,
500+
alignment::Vertical::Center => 2.0,
501+
alignment::Vertical::Bottom => 1.0,
502+
};
503+
504+
if align_factor != 0.0 {
505+
let total_height = intrinsic_size.height;
506+
507+
let mut column_start = 0;
508+
509+
for i in 0..children.len() {
510+
let bounds = children[i].bounds();
511+
let column_height = bounds.y + bounds.height;
512+
513+
let next_y = children
514+
.get(i + 1)
515+
.map(|node| node.bounds().y)
516+
.unwrap_or_default();
517+
518+
if next_y == 0.0 {
519+
let translation = Vector::new(
520+
0.0,
521+
(total_height - column_height) / align_factor,
522+
);
523+
524+
for node in &mut children[column_start..=i] {
525+
node.translate_mut(translation);
526+
}
527+
528+
column_start = i + 1;
529+
}
530+
}
531+
}
532+
533+
let size = limits.resolve(
534+
self.column.width,
535+
self.column.height,
536+
intrinsic_size,
537+
);
538+
539+
layout::Node::with_children(size.expand(self.column.padding), children)
540+
}
541+
542+
fn operate(
543+
&mut self,
544+
tree: &mut Tree,
545+
layout: Layout<'_>,
546+
renderer: &Renderer,
547+
operation: &mut dyn Operation,
548+
) {
549+
self.column.operate(tree, layout, renderer, operation);
550+
}
551+
552+
fn update(
553+
&mut self,
554+
tree: &mut Tree,
555+
event: &Event,
556+
layout: Layout<'_>,
557+
cursor: mouse::Cursor,
558+
renderer: &Renderer,
559+
clipboard: &mut dyn Clipboard,
560+
shell: &mut Shell<'_, Message>,
561+
viewport: &Rectangle,
562+
) {
563+
self.column.update(
564+
tree, event, layout, cursor, renderer, clipboard, shell, viewport,
565+
);
566+
}
567+
568+
fn mouse_interaction(
569+
&self,
570+
tree: &Tree,
571+
layout: Layout<'_>,
572+
cursor: mouse::Cursor,
573+
viewport: &Rectangle,
574+
renderer: &Renderer,
575+
) -> mouse::Interaction {
576+
self.column
577+
.mouse_interaction(tree, layout, cursor, viewport, renderer)
578+
}
579+
580+
fn draw(
581+
&self,
582+
tree: &Tree,
583+
renderer: &mut Renderer,
584+
theme: &Theme,
585+
style: &renderer::Style,
586+
layout: Layout<'_>,
587+
cursor: mouse::Cursor,
588+
viewport: &Rectangle,
589+
) {
590+
self.column
591+
.draw(tree, renderer, theme, style, layout, cursor, viewport);
592+
}
593+
594+
fn overlay<'b>(
595+
&'b mut self,
596+
tree: &'b mut Tree,
597+
layout: Layout<'b>,
598+
renderer: &Renderer,
599+
viewport: &Rectangle,
600+
translation: Vector,
601+
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
602+
self.column
603+
.overlay(tree, layout, renderer, viewport, translation)
604+
}
605+
}
606+
607+
impl<'a, Message, Theme, Renderer> From<Wrapping<'a, Message, Theme, Renderer>>
608+
for Element<'a, Message, Theme, Renderer>
609+
where
610+
Message: 'a,
611+
Theme: 'a,
612+
Renderer: crate::core::Renderer + 'a,
613+
{
614+
fn from(column: Wrapping<'a, Message, Theme, Renderer>) -> Self {
615+
Self::new(column)
616+
}
617+
}

0 commit comments

Comments
 (0)