Conversation
Swift bindings
Implement js bindings
| fn style(&self, node: Node) -> &FlexboxLayout; | ||
|
|
||
| /// Get the node's output "Final Layout" | ||
| fn layout(&self, node: Node) -> &Layout; |
There was a problem hiding this comment.
Unsolicited thoughts.
I was thinking about the change and would love to share some thoughts:
- If I understand correctly this approach aims to solve the fundamental decision with Taffy: who owns the notion of visual hierarchy? From my experience, you kinda have to have your own notion of visual hierarchy and then somehow keep your hierarchy and Taffy's in sync, this approach might help with that.
- As a side benefit of this approach you can have dynamic
FlexboxLayoutbased on some external context (like in CSS). Although I'm curious if you can mark only the root node withmark_dirtyor go through the entire subtree and callmark_dirtyon each node recursively if you want to "cascade" new font size through the entire hierarchy (example:body {font-size: 14 -> 16}). - Assuming that you want to implement your own
LayoutTree, I wonder what kind of restrictions need to be applied toNode? It feels likeSlotMapis very restrictive, but feels "right" for this use case, so I'm curious what a custom implementation might look like in regards to generatingNodes - It feels to me that the methods in this trait can be roughly split into two categories: functions that deal with things that created outside of the library, and functions that deal with things that created inside of the library. Let me elaborate a bit, parent -> child relationships and
FlexboxLayoutare created and managed in user's code, but caching andLayoutseem to be originated and used inside the library itself. So I wonder if this trait perhaps should be split in two: layout hierarchy and layout computation, note that "layout computation" can potentially be in the "core" Taffy and not a trait. - As a general thought: this approach seems to introduce more cognitive complexity and I wonder if the benefits out-weight the cost for the broader audience. For example: for Dioxus use-case this trait saves X amount of memory and makes it Y% faster, but will these benefits to be as pronounced for the broader audience, if yes, what would it take to unleash the full potential?
Thanks for reading!
There was a problem hiding this comment.
Consider thoughts of this nature solicited by default! This is great feedback.
| // for reference, CSS cascades require context, and storing a full flexbox layout for each node could be inefficient | ||
| // | ||
| /// Get the [`FlexboxLayout`] for this Node. | ||
| fn style(&self, node: Node) -> &FlexboxLayout; |
There was a problem hiding this comment.
I would like to suggest that FlexboxLayout is renamed to FlexboxStyle: the idea being to establish a naming convention that *Style is input and *Layout is output. FlexboxLayout is already in a file called style.rs and returned from a method called style, so this seems to improve naming consistency.
There was a problem hiding this comment.
This makes a lot of sense, it is what it should have been all along.
There was a problem hiding this comment.
I'm on board with that. IMO we should do it in a small seperate PR though.
There was a problem hiding this comment.
I have thought a bit about this.
- CSS cascade is a bit on the complex side, but when you need it, you need it. That means that there shouldn't be 2 implementations of it: one for the Taffy properties (the flexbox data) and one for the rest of the UI properties (color, font, whatever). The former should be derived from the latter.
- A proper CSS implementation probably shouldn't store the full properties for each node but some kind of diff to its parent, to let the whole cascade (with selectors, etc.) work properly and not to take huge amount of memory.
So I like this style() method and I approve the FlexboxStyle name.
There was a problem hiding this comment.
and one for the rest of the UI properties (color, font, whatever).
This is out of scope for this library :) I'd like to keep this focused on pure layout, and let consumers (like bevy_ui or Dioxus) define their own tools for working with these sort of non-layout properties.
There was a problem hiding this comment.
Yes it is, but my point is that the user will have to implement it anyway, so it probably should be reused; so having a style() method to ask the user how it resolved a particular Node's properties looks good to me.
|
EDIT: I've been working with this code more closely, and I've realised that this PR more or less already implements this. |
|
On the subject of supporting multiple layout algorithms, since I know that Morphorm is being looked at as a candidate, this isn't too far away from what Morphorm does with its |
Agreed. We should start with an MVP where we can't mix styles in the same tree. Then once that's built and we have a couple candidates, we can start looking to Flutter to figure out if there's a good interface by which we can let them talk. |
| /// Taffy caches results of computations for nodes to not need re-caculating nodes it already knows | ||
| /// | ||
| /// When a node does not have a cache, Taffy will layout that node appropriately. | ||
| fn primary_cache(&mut self, node: Node) -> &mut Option<Cache>; |
There was a problem hiding this comment.
I do not understand how this cache works (when is it invalidated, etc.).
Nevermind I get it; all nodes have to contain the space for 2 Cache structs.
| /// remains the same between re-layouts. | ||
| pub trait LayoutTree { | ||
| /// Get the list of children IDs for the given node | ||
| fn children(&self, node: Node) -> &[Node]; |
There was a problem hiding this comment.
Maybe here you restrict too much the type of the underlying children set (which now has to be a Vec or something like that) and it's be better to have something like
fn children(&self, node: Node) -> impl Iterator<Item=Node>;(which also has a trivial implementation). Now if the LayoutTree implementor uses e.g. a BTreeMap as the underlying storage it still can trivially implement children().
There was a problem hiding this comment.
Definitely agree with this :) This sort of flexibility is really nice for public traits.
We should probably use IntoIterator not Iterator though; as that's the trait that's implemented directly on collections.
There was a problem hiding this comment.
Wouldn't IntoIterator consume the collection (into_iter() takes self by value) ? I think we just want using iter() here.
You made me have a look at it. It's really cool (even if the API is a bit clunky at times) but in my small test it doesn't implement wrapping; wouldn't that be required for Taffy to use it ? |
|
Note for later: |
I also looked into this recently, and it does seem cool. I think Taffy would use it as another alternative algorithm (e.g. a
This makes sense. |
|
@jkelleyrtp What's the status of this PR? I've been basing my work off of it, and I'm concerned that it's now drifting further and further from (side note: I believe that the recent force-push to |
This is my position as well :) I'd like to avoid long-lived branches, especially for foundational data structures. |
Somehow missed this before. You're right that Morphorm doesn't currently support wrapping but it's something I'm planning to add for a rewrite that I'm working on at the moment (there's already a working but not very well tested implementation). Once I've finished this rewrite then I think Morphorm will be in a better position to be integrated with taffy in some way if that's still desired. |
It is! |
|
Closing as this work was merged via a rebased version of this branch (#238) |
This PR makes the flexbox algorithm generic over a LayoutTree trait.
At the time of filing this PR, the LayoutTree trait mostly abstracts over the broadest set of functions that the
Taffystruct satisfies. I don't know or think this is the best and most general solution to flexboxing: Taffy internally does caching of calculations that a user's LayoutTree shouldn't really be concerned about. Perhaps some of the state - like caching - could remain in a Taffy struct, but this level of specificity intrinsically ties a user's UI system to Taffy due to dependent data structures.Generally, since Taffy uses its own types, most implementations will be tied to Taffy anyways, so perhaps this linking of user code to Taffy is not a big deal.
Also - this trait supplies a method that returns a FlexboxLayout (not the final layout, despite the confusing name) which might not be exactly a trait implementation would return the "current state" of the DOM Node. For instance, in Dioxus, we are using the CSS Cascade to supply the "current state" of the DOM node's style. I think before publishing out these changes, it might be worth further abstracting the trait to supply single-level Taffy attributes. Perhaps a "TaffyAttribute" enum could be supplied so use cases like Dioxus' cascading CSS could be implemented efficiently.
Closes #182