-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6258856
commit cabd848
Showing
6 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
...et/screenshots/masonry__widget__zstack__tests__zstack_with_button_and_label.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions
10
...ry/src/widget/snapshots/masonry__widget__zstack__tests__zstack_with_button_and_label.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,3 +47,6 @@ pub use textbox::*; | |
|
||
mod portal; | ||
pub use portal::*; | ||
|
||
mod zstack; | ||
pub use zstack::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |