Skip to content

Commit

Permalink
Add ZStack widget and view
Browse files Browse the repository at this point in the history
  • Loading branch information
viktorstrate committed Nov 10, 2024
1 parent 6258856 commit cabd848
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 0 deletions.
2 changes: 2 additions & 0 deletions masonry/src/widget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod split;
mod textbox;
mod variable_label;
mod widget_arena;
mod zstack;

pub use self::image::Image;
pub use align::Align;
Expand All @@ -52,6 +53,7 @@ pub use variable_label::VariableLabel;
pub use widget_mut::WidgetMut;
pub use widget_pod::WidgetPod;
pub use widget_ref::WidgetRef;
pub use zstack::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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: masonry/src/widget/zstack.rs
expression: harness.root_widget()
---
ZStack(
Button(
Label<Button>,
),
Label<Label>,
)
135 changes: 135 additions & 0 deletions masonry/src/widget/zstack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use crate::{
vello::Scene, widget::WidgetMut, AccessCtx, BoxConstraints, LayoutCtx, PaintCtx, Point,
RegisterCtx, Size, Widget, WidgetId, WidgetPod,
};
use accesskit::{NodeBuilder, Role};
use smallvec::SmallVec;
use tracing::trace_span;

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

pub struct ZStack {
children: Vec<Child>,
}

// --- MARK: IMPL ZSTACK ---
impl ZStack {
pub fn new() -> Self {
ZStack {
children: Vec::new(),
}
}

pub fn with_child(self, child: impl Widget) -> Self {
self.with_child_pod(WidgetPod::new(Box::new(child)))
}

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_pod(mut self, child: WidgetPod<Box<dyn Widget>>) -> Self {
let child = Child { widget: child };
self.children.push(child);
self
}
}

// --- MARK: WIDGETMUT---
impl ZStack {
pub fn add_child(this: &mut WidgetMut<'_, Self>, child: impl Widget) {
let child_pod: WidgetPod<Box<dyn Widget>> = WidgetPod::new(Box::new(child));
Self::insert_child_pod(this, child_pod);
}

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

pub fn insert_child_pod(this: &mut WidgetMut<'_, Self>, widget: WidgetPod<Box<dyn Widget>>) {
let child = Child { widget };
this.widget.children.push(child);
this.ctx.children_changed();
this.ctx.request_layout();
}

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();
}

pub fn child_mut<'t>(
this: &'t mut WidgetMut<'_, Self>,
idx: usize,
) -> Option<WidgetMut<'t, Box<dyn Widget>>> {
let child = &mut this.widget.children[idx].widget;
Some(this.ctx.get_mut(child))
}
}

// --- MARK: IMPL WIDGET---
impl Widget for ZStack {
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
let total_size = bc.max();

for child in &mut self.children {
let _child_size = ctx.run_layout(&mut child.widget, bc);
ctx.place_child(&mut child.widget, Point::ZERO);
}

total_size
}

fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut Scene) {}

fn register_children(&mut self, ctx: &mut RegisterCtx) {
for child in self.children.iter_mut().map(|x| &mut x.widget) {
ctx.register_child(child);
}
}

fn children_ids(&self) -> SmallVec<[WidgetId; 16]> {
self.children
.iter()
.map(|child| &child.widget)
.map(|widget_pod| widget_pod.id())
.collect()
}

fn accessibility_role(&self) -> Role {
Role::GenericContainer
}

fn accessibility(&mut self, _ctx: &mut AccessCtx, _node: &mut NodeBuilder) {}

fn make_trace_span(&self) -> tracing::Span {
trace_span!("ZStack")
}
}

// --- MARK: TESTS ---
#[cfg(test)]
mod tests {
use insta::assert_debug_snapshot;

use super::*;
use crate::assert_render_snapshot;
use crate::testing::TestHarness;
use crate::widget::{Button, Label};

#[test]
fn zstack_with_button_and_label() {
let widget = ZStack::new()
.with_child(Button::new("Button"))
.with_child(Label::new("Label"));

let mut harness = TestHarness::create(widget);

assert_debug_snapshot!(harness.root_widget());
assert_render_snapshot!(harness, "zstack_with_button_and_label");
}
}
3 changes: 3 additions & 0 deletions xilem/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ pub use textbox::*;

mod portal;
pub use portal::*;

mod zstack;
pub use zstack::*;
200 changes: 200 additions & 0 deletions xilem/src/view/zstack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
use crate::{
core::{
AppendVec, DynMessage, ElementSplice, Mut, SuperElement, View, ViewElement, ViewMarker,
ViewSequence,
},
Pod, ViewCtx,
};
use masonry::{
widget::{self, WidgetMut},
Widget,
};

pub fn zstack<State, Action, Seq: ZStackSequence<State, Action>>(sequence: Seq) -> Stack<Seq> {
Stack { sequence }
}

pub struct Stack<Seq> {
sequence: Seq,
}

impl<Seq> ViewMarker for Stack<Seq> {}
impl<State, Action, Seq> View<State, Action, ViewCtx> for Stack<Seq>
where
State: 'static,
Action: 'static,
Seq: ZStackSequence<State, Action>,
{
type Element = Pod<widget::ZStack>;

type ViewState = Seq::SeqState;

fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let mut elements = AppendVec::default();
let mut widget = widget::ZStack::new();
let seq_state = self.sequence.seq_build(ctx, &mut elements);
for child in elements.into_inner() {
widget = widget.with_child_pod(child.0.inner);
}
(ctx.new_pod(widget), seq_state)
}

fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
element: crate::core::Mut<Self::Element>,
) {
let mut splice = StackSplice::new(element);
self.sequence
.seq_rebuild(&prev.sequence, view_state, ctx, &mut splice);
debug_assert!(splice.scratch.is_empty());
}

fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
element: crate::core::Mut<Self::Element>,
) {
let mut splice = StackSplice::new(element);
self.sequence.seq_teardown(view_state, ctx, &mut splice);
debug_assert!(splice.scratch.into_inner().is_empty());
}

fn message(
&self,
view_state: &mut Self::ViewState,
id_path: &[crate::core::ViewId],
message: DynMessage,
app_state: &mut State,
) -> crate::core::MessageResult<Action, DynMessage> {
self.sequence
.seq_message(view_state, id_path, message, app_state)
}
}

// MARK: ZStackElement
pub struct ZStackElement(Pod<Box<dyn Widget>>);

pub struct ZStackElementMut<'w> {
parent: WidgetMut<'w, widget::ZStack>,
idx: usize,
}

impl ViewElement for ZStackElement {
type Mut<'a> = ZStackElementMut<'a>;
}

impl SuperElement<ZStackElement, ViewCtx> for ZStackElement {
fn upcast(ctx: &mut ViewCtx, child: ZStackElement) -> Self {
child
}

fn with_downcast_val<R>(
mut this: crate::core::Mut<Self>,
f: impl FnOnce(crate::core::Mut<ZStackElement>) -> R,
) -> (Self::Mut<'_>, R) {
let r = {
let parent = this.parent.reborrow_mut();
let reborrow = ZStackElementMut {
idx: this.idx,
parent,
};
f(reborrow)
};
(this, r)
}
}

impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for ZStackElement {
fn upcast(ctx: &mut ViewCtx, child: Pod<W>) -> Self {
ZStackElement(ctx.boxed_pod(child))
}

fn with_downcast_val<R>(
mut this: crate::core::Mut<Self>,
f: impl FnOnce(crate::core::Mut<Pod<W>>) -> R,
) -> (Self::Mut<'_>, R) {
let ret = {
let mut child = widget::ZStack::child_mut(&mut this.parent, this.idx)
.expect("This is supposed to be a widget");
let downcast = child.downcast();
f(downcast)
};

(this, ret)
}
}

// MARK: Sequence
pub trait ZStackSequence<State, Action = ()>:
ViewSequence<State, Action, ViewCtx, ZStackElement>
{
}

impl<Seq, State, Action> ZStackSequence<State, Action> for Seq where
Seq: ViewSequence<State, Action, ViewCtx, ZStackElement>
{
}

// MARK: Splice

pub struct StackSplice<'w> {
idx: usize,
element: WidgetMut<'w, widget::ZStack>,
scratch: AppendVec<ZStackElement>,
}

impl<'w> StackSplice<'w> {
fn new(element: WidgetMut<'w, widget::ZStack>) -> Self {
Self {
idx: 0,
element,
scratch: AppendVec::default(),
}
}
}

impl ElementSplice<ZStackElement> for StackSplice<'_> {
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() {
widget::ZStack::insert_child_pod(&mut self.element, element.0.inner);
self.idx += 1;
}
ret
}

fn insert(&mut self, element: ZStackElement) {
widget::ZStack::insert_child_pod(&mut self.element, element.0.inner);
self.idx += 1;
}

fn mutate<R>(&mut self, f: impl FnOnce(Mut<ZStackElement>) -> R) -> R {
let child = ZStackElementMut {
parent: self.element.reborrow_mut(),
idx: self.idx,
};
let ret = f(child);
self.idx += 1;
ret
}

fn skip(&mut self, n: usize) {
self.idx += n;
}

fn delete<R>(&mut self, f: impl FnOnce(Mut<ZStackElement>) -> R) -> R {
let ret = {
let child = ZStackElementMut {
parent: self.element.reborrow_mut(),
idx: self.idx,
};
f(child)
};
widget::ZStack::remove_child(&mut self.element, self.idx);
ret
}
}

0 comments on commit cabd848

Please sign in to comment.