-
Notifications
You must be signed in to change notification settings - Fork 111
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
WIP: Refactor the LayoutTree
trait
#326
Conversation
LayoutTree
traitLayoutTree
trait
ff082fb
to
2cc6cb3
Compare
LayoutTree
traitLayoutTree
trait
I've made a proof of concept integration with Iced based on this PR (see: https://github.com/nicoburns/iced_taffy). I had to change quite a few bits to get it to work, and it's still not perfect (but some of that needs to be fixed on the Iced side), but it's super nice to see the result of the layout in visual form. And it was also nice to confirm that Taffy's grid implementation is usable for real world UIs. |
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 do like the changes, it does feel like taffy is becoming more modular and opening up to more different ways of using it as an end user.
If I understand it correctly, so far there is no changes to the end user yet, in terms of how they interact with taffy as a library? As in you would still be running the layouting on the full tree at once and not node-by-node?
prelude::*, | ||
}; | ||
use core::fmt::Debug; | ||
|
||
/// Any item that implements the LayoutTree can be layed out using Taffy's algorithms. | ||
/// | ||
/// Generally, Taffy expects your Node tree to be indexable by stable indices. A "stable" index means that the Node's ID | ||
/// remains the same between re-layouts. | ||
pub trait LayoutTree { |
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 should have some examples showing how (and when) to use the various methods on the trait, especially if it will be up to the end user to run the layout on each of the node (as opposed to calling perform_layout on the tree as a whole)
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.
That may not need to be in this PR, but it would be useful when reviewing to see the impact of the refactor on user-facing code
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 should have some examples showing how (and when) to use the various methods on the trait, especially if it will be up to the end user to run the layout on each of the node
Agreed. Also, I've just noticed that this comment (about stable indexes) is no longer accurate. I want to remove a few more methods from the LayoutTree
trait first though!
src/compute/flexbox.rs
Outdated
constants.container_size | ||
// 8.5. Flex Container Baselines: calculate the flex container's first baseline | ||
// See https://www.w3.org/TR/css-flexbox-1/#flex-baselines | ||
let first_vertical_baseline = if flex_lines.is_empty() { |
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 assume horizontal baselines are only relevant when supporting vertical text directions?
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.
Ah, well I'd phrase it as "vertical baselines are only relevant when supporting vertical text directions", but I suspect that makes the short answer "yes".
That depends. There are effectively two ways to use taffy. Either:
|
3a7109e
to
2d4df87
Compare
2d4df87
to
0b18279
Compare
@alice-i-cecile @Weibye @geom3trik @jkelleyrtp @Demonthos @TimJentzsch I'm not currently intending for this to be merged until after a 0.3 release (so 0.3 would contain CSS Grid, and this change would go into a 0.4 release). However it is now at a point that I consider it ready for review, and as this represents a significant change to Taffy's API (albeit a hitherto little-used part of it), I'm keen to get as many eyes on it as possible and to give people advance warning so that there is plenty of time for feedback and revisions. If you are reviewing this:
|
745f41a
to
b391102
Compare
Other than needing more documentation around when certain functions should be called, I think the API here looks good. The performance penalty is unfortunate, but I think it is worth the tradeoff. Dioxus' native renderer uses a tree similar to Taffy's tree, so the child ids help the performance there as well. I think Leaving the caching up to the implementor makes sense. It gives them more flexibility to allow caching of different steps in whatever internal layout algorithms they implement. |
992086d
to
925f435
Compare
7085532
to
f9c9974
Compare
5cfe3c0
to
bb3bb23
Compare
bb3bb23
to
0831400
Compare
8cbbc8c
to
29570ca
Compare
Closing in favour of smaller separate PRs:
The new set of PRs differs from this PR in two ways:
|
Objective
Fix #28 by adding child measurement functions to the
LayoutTree
trait.measure_child_size
andperform_child_layout
methods to theLayoutTree
trait, and trampoline all calls to size children through the LayoutTree trait (fixes Support multiple layout algorithms #28)parent
method (unused - we only ever traverse down the tree)mark_dirty
method (unused - layout algorithms never need to mark nodes as dirty)layout
method (unused - we only ever uselayout_mut
)is_childless
method (no need to make implementors implement another method when we can just usechild_count() == 0
)impl LayoutTree
represents a single node rather an entire hierarchy. Addchild_style
andchild_layout_mut
methods.ChildId
anassociatedgeneric typeneeds_measure
andmeasure_child
methods (if you're implementing LayoutTree then you don't need measure_funcs or the Leaf layout more).Remaining Issues
Caching. Currently the
LayoutTree
trait still has acache_mut
method, but the standalone Flexbox and CSS Grid implementations don't use it (in the high-level API caching is implemented in the generic method that switches between the modes). There are a couple of options here:cache_mut
method from the trait. This simplifies the trait, but requires that implementors implement caching themselves. This is entirely possible with the current caching strategy, and we could provide an standalone cache type that encapsulates the storage and actual cache logic for people who want to use it. But it does preclude things like storing more of the internal state of the Flexbox/Grid algorithms in future (although we could of course just reintroduce a cache method to the trait at that point).Taffy
struct where we are in control of the whole tree it doesn't make sense for the flexbox implementation to cache the sizes of it's children, because those children can cache their own sizes and this avoids us doubling up on caches. I suppose perhaps we could have nodes only cache their children, but that relies on making sure we have stable node references (reordering children shouldn't end up with them getting each other's cached values!)I'm definitely leaning towards removing caching entirely for those implementing
LayoutTree
themselves for the time being (on the basis that it's better to have no abstraction than a leaky abstraction), but I'd value other perspectives on this.Performance. I'm currently seeing a 10%-16% decrease in performance from this PR. I'm not sure what's causing the slowdown, but I feel like I'd be willing to accept this for the increased flexibility (thoughts?). For the "deep tree" benchmarks we're still quite a bit faster than Taffy 0.2 due to earlier optimisations that have already been merged to main (for the "wide tree" benchmarks we seem to be a bit slower - I'm not sure why, but I'm not going to worry too much about this as a single node with 1000 direct (non-absolutely positioned) children doesn't seem like a very likely use case).
Children/Child methods. There are a few related issues here:
ChildIter
associated type andchildren
method aren't strictly necessary. We could make do withchild_count
andchild
. This would simplify the implementation of the trait, but would make things slightly less efficient. In some cases we're usingchild_count
andchild
anyway to get around borrow checking restrictions.ChildId
associated type andchild
methods are not strictly necessary. We could just always refer to children by index. Again, this would be less efficient (for some storage methods that actually have a meaningful child key - like Taffy's own storage), but would simplify the trait implementation.Reference Nesting. The layout algorithms currently take
&mut impl LayoutTree
like they did before this PR. However, prior to this PR theimpl LayoutTree
was an instance of theTaffy
struct (so there was only one level of indirection), but in the new implementationimpl LayoutTree
is an instance ofTaffyNodeRef<'a>
, which is a small struct which itself contains an&mut Taffy
and a node id (so there are now two levels of indirection). It's possible to remove the outer layer by making the layout algorithms takeimpl LayoutTree
instead of&mut LayoutTree
, and I've implemented this in https://github.com/nicoburns/taffy/commits/reborrow-layout-tree (see most recent 2 commits).In theory this should be more efficient (assuming that struct implementing
LayoutTree
are as small as the two I've created so far). However:LayoutTree
to implement an additional reborrow methodI'm currently leaning towards leaving this as it is.
Naming. The trait is called
LayoutTree
, and that makes sense in the current version of Taffy because it represents a whole tree. But with the changes in this PR it only represents a single node in the tree (and it's direct children). As such, I'm thinking it makes sense to rename the trait. Some options:LayoutNode
LayoutNodeRef
Node
NodeRef
The "ref" bit is because in both the implementations of the new
LayoutTree
I have written so far (the one in this PR, and the one iniced_taffy
), the type implementingLayoutTree
has ended up being a wrapper around a reference back to theContext
See #28 (comment)
Blocked on #325, which is blocked on #320.
New
LayoutTree
trait