-
Notifications
You must be signed in to change notification settings - Fork 119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add zstack
view to compose views on top of each other
#737
base: main
Are you sure you want to change the base?
Conversation
zstack
view to compose views on top of each otherzstack
view to compose views on top of each other
See also #591, which goes a similar direction. I think to make a I like the general direction, i.e. unifying a potential xilem_svg API using a zstack (instead of separate context such as in the web). |
I don't think that arbitrary transforms should block this landing, though. I don't really have the bandwidth to look at this too closely at the moment, unfortunately. |
To clarify, I didn't mean this to be a requirement, it's orthogonal to this change (and a lot more work...). I'll take a look at the PR when it's out of draft. On a skim it seems reasonable so far. |
0730a16
to
7c21129
Compare
Maybe adding a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a good foundation, and it should be a useful view. Some review thoughts from a first pass.
One thing which does concern me is event handling; I suspect that the behaviour will be hard to reason about. Ideally, we'd warn if more than one layer is interactive, but we don't currently have a mechanism to do that.
use tracing::trace_span; | ||
|
||
struct Child { | ||
widget: WidgetPod<Box<dyn Widget>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason the children don't have an individual alignment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Xilem, I guess it could be done by making a generic extension for all Views that allows for specifying the alignment on each child individually, similar to how flex does it, but I'm concerned that it would add unnecessary complexity.
The same effect could be achieved by using nested ZStack
s or maybe using flex
with spacing.
I would like to hear what you think about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this would be the right way to expose that at the Xilem level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we implement this or keep it simple like it is now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can implement it, I don't think it will be much more complex? It's a little bit more code, but it would be consistent to the other container views, and may be more straight-forward on the user-facing-side.
I think it's a little bit awkward to compose 2 zstack
to achieve similar behavior.
(And the wrapper types (Child
, ZStackElement
) would actually make sense).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these types should have documentation. I recognise that this isn't the prevailing code style, but that doesn't mean we should continue that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've written a fair bit of documentation in 42473c8, is there anything obvious that I missed?
masonry/src/widget/zstack.rs
Outdated
// --- MARK: IMPL WIDGET--- | ||
impl Widget for ZStack { | ||
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size { | ||
let total_size = bc.max(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is suspect for a few reasons:
- An infinite bound makes this fundamentally broken.
- Even if all the children are small, this view takes up all available space.
I think the correct approach here might be a 2-pass solution, first to get the "overall" size, then to align widgets within that size. Allowing that is the reason that run_layout
and place_child
are different methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this in 78a901e, can you please take a look at it. I'm unsure if I did it right?
} | ||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub enum HorizontalAlignment { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to check whether we can merge or reuse a few of these enums for different container widgets. (no action required here though, I think that should be done in follow-up PRs)
Can you expand on that a little bit? |
Sure. If you have a child widget of the zstack which accepts events in only a small region, and wants to pass through the rest (for example, a circular button), I don't think that will work because of how Masonry works, that events can only bubble. I haven't thought too deeply about it, and it doesn't need to be addressed in this PR. |
Would it somehow be possible to pass the event to the front most child of which the cursor is within the bounding box? |
Hmm, I think we need a more significant rework of the pointer-intersection logic in masonry anyways, i.e. allow the widgets to customize the pointer intersection behavior (as right now only the bounds are checked). Possibly with a cheaper hierarchical bounds check (BVH) before. I have similar issues in #753. I'm thinking about tackling this, as this is relevant for kurbo shapes with pointer event listeners as well. Anyway, I think this PR (and the linked) just shows issues with the mentioned pointer/widget-intersection logic, which I think is orthogonal and should probably be handled in another PR.... |
to make it more readable and to demonstrate more use cases of sized_box.
I fixed a lot of the comments you all made. After rebasing the PR the copyright symbol in the I made a black semi-transparent background for the copyright text, to make it easier to read. There are still some open comments that are unresolved and that I'd like your input on to continue. |
FWIW I think this should be fixed with #753, as well as supporting custom pointer intersection shapes (like the circular button), when I understand you correctly. |
Still needs documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handful of small things, but otherwise this looks good.
@@ -0,0 +1,416 @@ | |||
// Copyright 2024 the Xilem Authors | |||
// SPDX-License-Identifier: Apache-2.0 | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have an #![warn(missing_docs)]
here
// 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let child_size = ctx.run_layout(&mut child.widget, &child_bc); | |
let child_size = ctx.child_size(&child.widget); |
Running layout twice for all the children is not the right thing to do; it can very easily introduce pathological behaviour.
// --- MARK: ZStackExt --- | ||
|
||
pub trait ZStackExt<State, Action>: WidgetView<State, Action> { | ||
fn alignment(self, alignment: impl Into<ChildAlignment>) -> ZStackItem<Self, State, Action> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have some documentation. In particular, it should specify that this alignment can only be used for a direct child of a zstack
.
.rounded(4.) | ||
.background(Color::BLACK.multiply_alpha(0.5)), | ||
) | ||
// HACK: Trailing padding workaround scrollbar covering content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this hack comment is entirely correct anymore.
Adds a new
zstack
Xilem view (along with an accompanied Masonry widget).The view is inspired by the ZStack view in SwiftUI, which composes the child view on top of each other.
This pull-request is still work-in-progress.
(top-left, top-right, bottom-left, bottom-right, center).
http_cat
to put copyright on top of image like seen below.