Skip to content

Commit

Permalink
Write more documentation and fix run_layout for ZStack
Browse files Browse the repository at this point in the history
  • Loading branch information
viktorstrate committed Dec 20, 2024
1 parent ed23eee commit 5d399e0
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 14 deletions.
38 changes: 34 additions & 4 deletions masonry/src/widget/zstack.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

#![warn(missing_docs)]

use crate::{
vello::Scene, widget::WidgetMut, AccessCtx, BoxConstraints, LayoutCtx, PaintCtx, Point,
QueryCtx, RegisterCtx, Size, Widget, WidgetId, WidgetPod,
Expand All @@ -14,15 +16,19 @@ struct Child {
alignment: ChildAlignment,
}

/// An option specifying how a child widget is aligned within a [`ZStack`].
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ChildAlignment {
/// Specifies that the child should use the global alignment as specified by the parent [`ZStack`] widget.
ParentAligned,
/// Specifies that the child should override the global alignment specified by the parent [`ZStack`] widget.
SelfAligned(Alignment),
}

/// A widget container that lays the child widgets on top of each other.
///
/// The alignment of how the children are placed can be specified using [`with_alignment`][Self::with_alignment].
/// The alignment of how the children are placed can be specified globally using [`with_alignment`][Self::with_alignment].
/// Each child can additionally override the global alignment using [`ChildAlignment::SelfAligned`].
#[derive(Default)]
pub struct ZStack {
children: Vec<Child>,
Expand All @@ -34,35 +40,50 @@ pub struct ZStack {
/// See also [`VerticalAlignment`] and [`HorizontalAlignment`] for describing only a single axis.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum Alignment {
/// Align to the top leading corner.
TopLeading,
/// Align to the center of the top edge.
Top,
/// Align to the top trailing corner.
TopTrailing,
/// Align to the center of the leading edge.
Leading,
/// Align to the center.
#[default]
Center,
/// Align to the center of the trailing edge.
Trailing,
/// Align to the bottom leading corner.
BottomLeading,
/// Align to the center of the bottom edge.
Bottom,
/// Align to the bottom trailing corner.
BottomTrailing,
}

/// Describes the vertical position of a view laid on top of another view.
/// See also [Alignment].
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum VerticalAlignment {
/// Align to the top edge.
Top,
/// Align to the center.
#[default]
Center,
/// Align to the bottom edge.
Bottom,
}

/// Describes the horizontal position of a view laid on top of another view.
/// See also [Alignment].
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum HorizontalAlignment {
/// Align to the leading edge.
Leading,
#[default]
/// Align to the center.
Center,
/// Align to the trailing edge.
Trailing,
}

Expand Down Expand Up @@ -178,6 +199,7 @@ impl ZStack {
self.with_child_pod(WidgetPod::new(Box::new(child)), alignment)
}

/// Appends a child widget with a given `id` to the `ZStack`.
pub fn with_child_id(
self,
child: impl Widget,
Expand All @@ -187,6 +209,9 @@ impl ZStack {
self.with_child_pod(WidgetPod::new_with_id(Box::new(child), id), alignment)
}

/// Appends a child widget pod to the `ZStack`.
///
/// See also [`Self::with_child`] if the widget is not already wrapped in a [`WidgetPod`].
pub fn with_child_pod(
mut self,
child: WidgetPod<Box<dyn Widget>>,
Expand All @@ -213,6 +238,9 @@ impl ZStack {
Self::insert_child_pod(this, child_pod, alignment);
}

/// Add a child widget with a given `id` to the `ZStack`.
///
/// See [`Self::add_child`] for more details.
pub fn add_child_id(
this: &mut WidgetMut<'_, Self>,
child: impl Widget,
Expand All @@ -235,12 +263,14 @@ impl ZStack {
this.ctx.request_layout();
}

/// Remove a child from the `ZStack`.
pub fn remove_child(this: &mut WidgetMut<'_, Self>, idx: usize) {
let child = this.widget.children.remove(idx);
this.ctx.remove_child(child.widget);
this.ctx.request_layout();
}

/// Get a mutable reference to a child of the `ZStack`.
pub fn child_mut<'t>(
this: &'t mut WidgetMut<'_, Self>,
idx: usize,
Expand All @@ -249,14 +279,15 @@ impl ZStack {
Some(this.ctx.get_mut(child))
}

/// Changes the alignment of the widget.
/// Change the alignment of the `ZStack`.
///
/// See also [`with_alignment`][Self::with_alignment].
pub fn set_alignment(this: &mut WidgetMut<'_, Self>, alignment: impl Into<Alignment>) {
this.widget.alignment = alignment.into();
this.ctx.request_layout();
}

/// Change the alignment of a child of the `ZStack`.
pub fn update_child_alignment(
this: &mut WidgetMut<'_, Self>,
idx: usize,
Expand All @@ -282,9 +313,8 @@ impl Widget for ZStack {
}

// Second pass: place the children given the calculated max_size bounds.
let child_bc = BoxConstraints::new(Size::ZERO, max_size);
for child in &mut self.children {
let child_size = ctx.run_layout(&mut child.widget, &child_bc);
let child_size = ctx.child_size(&child.widget);

let end = max_size - child_size;
let end = Point::new(end.width, end.height);
Expand Down
1 change: 0 additions & 1 deletion xilem/examples/http_cats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ impl Status {
.rounded(4.)
.background(Color::BLACK.multiply_alpha(0.5)),
)
// HACK: Trailing padding workaround scrollbar covering content
.padding((30., 42., 0., 0.))
.alignment(Alignment::TopTrailing),
)),
Expand Down
34 changes: 25 additions & 9 deletions xilem/src/view/zstack.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

#![warn(missing_docs)]

use std::marker::PhantomData;

use crate::{
Expand All @@ -25,12 +27,12 @@ use xilem_core::{MessageResult, ViewId};
///
/// ```
/// use xilem::WidgetView;
/// use xilem::view::{zstack, label};
/// use xilem::view::{zstack, label, button};
///
/// fn view() -> impl WidgetView<()> {
/// zstack::<(), (), _>((
/// fn view<State: 'static>() -> impl WidgetView<State> {
/// zstack((
/// label("Background"),
/// label("Foreground")
/// button("Click me", |_| {})
/// ))
/// }
/// ```
Expand Down Expand Up @@ -90,7 +92,7 @@ where
widget::ZStack::set_alignment(&mut element, self.alignment);
}

let mut splice = StackSplice::new(element);
let mut splice = ZStackSplice::new(element);
self.sequence
.seq_rebuild(&prev.sequence, view_state, ctx, &mut splice);
debug_assert!(splice.scratch.is_empty());
Expand All @@ -102,7 +104,7 @@ where
ctx: &mut ViewCtx,
element: Mut<Self::Element>,
) {
let mut splice = StackSplice::new(element);
let mut splice = ZStackSplice::new(element);
self.sequence.seq_teardown(view_state, ctx, &mut splice);
debug_assert!(splice.scratch.into_inner().is_empty());
}
Expand All @@ -121,7 +123,11 @@ where

// --- MARK: ZStackExt ---

/// A trait that extends a [`WidgetView`] with methods to provide parameters for a parent [`ZStack`].
pub trait ZStackExt<State, Action>: WidgetView<State, Action> {
/// Applies [`ChildAlignment`] to this view.
/// This allows the view to override the default [`Alignment`] of the parent [`ZStack`].
/// This can only be used on views that are direct children of a [`ZStack`].
fn alignment(self, alignment: impl Into<ChildAlignment>) -> ZStackItem<Self, State, Action>
where
State: 'static,
Expand All @@ -134,12 +140,16 @@ pub trait ZStackExt<State, Action>: WidgetView<State, Action> {

impl<State, Action, V: WidgetView<State, Action>> ZStackExt<State, Action> for V {}

/// A wrapper around a [`WidgetView`], with a specified [`ChildAlignment`].
/// This struct is most often constructed indrectly using [`ZStackExt::alignment`].
pub struct ZStackItem<V, State, Action> {
view: V,
alignment: ChildAlignment,
phantom: PhantomData<fn() -> (State, Action)>,
}

/// Constructs a new `ZStackItem`.
/// See also [`ZStackExt::alignment`], for constructing a `ZStackItem` from an existing view.
pub fn zstack_item<V, State, Action>(
view: V,
alignment: impl Into<ChildAlignment>,
Expand Down Expand Up @@ -221,11 +231,14 @@ where
}

// --- MARK: ZStackElement ---

/// A struct implementing [`ViewElement`] for a `ZStack`.
pub struct ZStackElement {
widget: Pod<Box<dyn Widget>>,
alignment: ChildAlignment,
}

/// A mutable version of `ZStackElement`.
pub struct ZStackElementMut<'w> {
parent: WidgetMut<'w, widget::ZStack>,
idx: usize,
Expand Down Expand Up @@ -283,6 +296,8 @@ impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for ZStackElement {
}

// MARK: Sequence

/// A trait implementing `ViewSequence` for `ZStackElements`.
pub trait ZStackSequence<State, Action = ()>:
ViewSequence<State, Action, ViewCtx, ZStackElement>
{
Expand All @@ -295,13 +310,14 @@ impl<Seq, State, Action> ZStackSequence<State, Action> for Seq where

// MARK: Splice

pub struct StackSplice<'w> {
/// An implementation of [`ElementSplice`] for `ZStackElements`.
pub struct ZStackSplice<'w> {
idx: usize,
element: WidgetMut<'w, widget::ZStack>,
scratch: AppendVec<ZStackElement>,
}

impl<'w> StackSplice<'w> {
impl<'w> ZStackSplice<'w> {
fn new(element: WidgetMut<'w, widget::ZStack>) -> Self {
Self {
idx: 0,
Expand All @@ -311,7 +327,7 @@ impl<'w> StackSplice<'w> {
}
}

impl ElementSplice<ZStackElement> for StackSplice<'_> {
impl ElementSplice<ZStackElement> for ZStackSplice<'_> {
fn with_scratch<R>(&mut self, f: impl FnOnce(&mut AppendVec<ZStackElement>) -> R) -> R {
let ret = f(&mut self.scratch);
for element in self.scratch.drain() {
Expand Down

0 comments on commit 5d399e0

Please sign in to comment.