Skip to content

Commit

Permalink
ZStack children can now individually be aligned
Browse files Browse the repository at this point in the history
Still needs documentation
  • Loading branch information
viktorstrate committed Dec 20, 2024
1 parent 6d5fa9d commit bbb1d5f
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 28 deletions.
2 changes: 1 addition & 1 deletion masonry/src/widget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub use variable_label::VariableLabel;
pub use widget_mut::WidgetMut;
pub use widget_pod::WidgetPod;
pub use widget_ref::WidgetRef;
pub use zstack::{Alignment, HorizontalAlignment, VerticalAlignment, ZStack};
pub use zstack::{Alignment, ChildAlignment, HorizontalAlignment, VerticalAlignment, ZStack};

pub(crate) use widget_arena::WidgetArena;
pub(crate) use widget_state::WidgetState;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
104 changes: 90 additions & 14 deletions masonry/src/widget/zstack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ use tracing::trace_span;

struct Child {
widget: WidgetPod<Box<dyn Widget>>,
alignment: ChildAlignment,
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ChildAlignment {
ParentAligned,
SelfAligned(Alignment),
}

/// A widget container that lays the child widgets on top of each other.
Expand Down Expand Up @@ -136,6 +143,22 @@ impl From<HorizontalAlignment> for Alignment {
}
}

impl From<Alignment> for ChildAlignment {
fn from(value: Alignment) -> Self {
ChildAlignment::SelfAligned(value)
}
}

impl Child {
fn new(widget: WidgetPod<Box<dyn Widget>>, alignment: ChildAlignment) -> Self {
Self { widget, alignment }
}

fn update_alignment(&mut self, alignment: ChildAlignment) {
self.alignment = alignment;
}
}

// --- MARK: IMPL ZSTACK ---
impl ZStack {
/// Constructs a new empty `ZStack` widget.
Expand All @@ -151,16 +174,25 @@ impl ZStack {

/// Appends a child widget to the `ZStack`.
/// The child are placed back to front, in the order they are added.
pub fn with_child(self, child: impl Widget) -> Self {
self.with_child_pod(WidgetPod::new(Box::new(child)))
pub fn with_child(self, child: impl Widget, alignment: impl Into<ChildAlignment>) -> Self {
self.with_child_pod(WidgetPod::new(Box::new(child)), alignment)
}

pub fn with_child_id(self, child: impl Widget, id: WidgetId) -> Self {
self.with_child_pod(WidgetPod::new_with_id(Box::new(child), id))
pub fn with_child_id(
self,
child: impl Widget,
id: WidgetId,
alignment: impl Into<ChildAlignment>,
) -> Self {
self.with_child_pod(WidgetPod::new_with_id(Box::new(child), id), alignment)
}

pub fn with_child_pod(mut self, child: WidgetPod<Box<dyn Widget>>) -> Self {
let child = Child { widget: child };
pub fn with_child_pod(
mut self,
child: WidgetPod<Box<dyn Widget>>,
alignment: impl Into<ChildAlignment>,
) -> Self {
let child = Child::new(child, alignment.into());
self.children.push(child);
self
}
Expand All @@ -172,19 +204,32 @@ impl ZStack {
/// The child are placed back to front, in the order they are added.
///
/// See also [`with_child`][Self::with_child].
pub fn add_child(this: &mut WidgetMut<'_, Self>, child: impl Widget) {
pub fn add_child(
this: &mut WidgetMut<'_, Self>,
child: impl Widget,
alignment: impl Into<ChildAlignment>,
) {
let child_pod: WidgetPod<Box<dyn Widget>> = WidgetPod::new(Box::new(child));
Self::insert_child_pod(this, child_pod);
Self::insert_child_pod(this, child_pod, alignment);
}

pub fn add_child_id(this: &mut WidgetMut<'_, Self>, child: impl Widget, id: WidgetId) {
pub fn add_child_id(
this: &mut WidgetMut<'_, Self>,
child: impl Widget,
id: WidgetId,
alignment: impl Into<ChildAlignment>,
) {
let child_pod: WidgetPod<Box<dyn Widget>> = WidgetPod::new_with_id(Box::new(child), id);
Self::insert_child_pod(this, child_pod);
Self::insert_child_pod(this, child_pod, alignment);
}

/// Add a child widget to the `ZStack`.
pub fn insert_child_pod(this: &mut WidgetMut<'_, Self>, widget: WidgetPod<Box<dyn Widget>>) {
let child = Child { widget };
pub fn insert_child_pod(
this: &mut WidgetMut<'_, Self>,
widget: WidgetPod<Box<dyn Widget>>,
alignment: impl Into<ChildAlignment>,
) {
let child = Child::new(widget, alignment.into());
this.widget.children.push(child);
this.ctx.children_changed();
this.ctx.request_layout();
Expand All @@ -211,6 +256,16 @@ impl ZStack {
this.widget.alignment = alignment.into();
this.ctx.request_layout();
}

pub fn update_child_alignment(
this: &mut WidgetMut<'_, Self>,
idx: usize,
alignment: impl Into<ChildAlignment>,
) {
let child = &mut this.widget.children[idx];
child.update_alignment(alignment.into());
this.ctx.request_layout();
}
}

// --- MARK: IMPL WIDGET---
Expand All @@ -236,7 +291,12 @@ impl Widget for ZStack {

let center = Point::new(end.x / 2., end.y / 2.);

let origin = match self.alignment {
let child_alignment = match child.alignment {
ChildAlignment::SelfAligned(alignment) => alignment,
ChildAlignment::ParentAligned => self.alignment,
};

let origin = match child_alignment {
Alignment::TopLeading => Point::ZERO,
Alignment::Top => Point::new(center.x, 0.),
Alignment::TopTrailing => Point::new(end.x, 0.),
Expand Down Expand Up @@ -292,19 +352,21 @@ mod tests {
use crate::widget::{Label, SizedBox};

#[test]
fn zstack_alignments() {
fn zstack_alignments_parent_aligned() {
let widget = ZStack::new()
.with_child(
SizedBox::new(Label::new("Background"))
.width(200.)
.height(100.)
.background(Color::BLUE)
.border(Color::TEAL, 2.),
ChildAlignment::ParentAligned,
)
.with_child(
SizedBox::new(Label::new("Foreground"))
.background(Color::RED)
.border(Color::PINK, 2.),
ChildAlignment::ParentAligned,
);

let mut harness = TestHarness::create(widget);
Expand Down Expand Up @@ -337,4 +399,18 @@ mod tests {
);
}
}

#[test]
fn zstack_alignments_self_aligned() {
let widget = ZStack::new()
.with_alignment(Alignment::Center)
.with_child(Label::new("ParentAligned"), ChildAlignment::ParentAligned)
.with_child(Label::new("TopLeading"), Alignment::TopLeading)
.with_child(Label::new("TopTrailing"), Alignment::TopTrailing)
.with_child(Label::new("BottomLeading"), Alignment::BottomLeading)
.with_child(Label::new("BottomTrailing"), Alignment::BottomTrailing);

let mut harness = TestHarness::create(widget);
assert_render_snapshot!(harness, "zstack_alignments_self_aligned");
}
}
8 changes: 4 additions & 4 deletions xilem/examples/http_cats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use xilem::core::fork;
use xilem::core::one_of::OneOf3;
use xilem::view::{
button, flex, image, inline_prose, portal, prose, sized_box, spinner, worker, zstack, Axis,
FlexExt, FlexSpacer, Padding,
FlexExt, FlexSpacer, Padding, ZStackExt,
};
use xilem::{Color, EventLoop, EventLoopBuilder, TextAlignment, WidgetView, Xilem};

Expand Down Expand Up @@ -210,9 +210,9 @@ impl Status {
.background(Color::BLACK.multiply_alpha(0.5)),
)
// HACK: Trailing padding workaround scrollbar covering content
.padding((30., 42., 0., 0.)),
))
.alignment(Alignment::TopTrailing),
.padding((30., 42., 0., 0.))
.alignment(Alignment::TopTrailing),
)),
))
.main_axis_alignment(xilem::view::MainAxisAlignment::Start)
}
Expand Down
Loading

0 comments on commit bbb1d5f

Please sign in to comment.