You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Now that CSS Grid support is nearing completion, I thought I'd take a look at what it would take to support Morphorm layout in Taffy (CC: @geom3trik). My findings are summarised below:
pubenumUnits{Pixels(f32),// = Dimension::PointsPercentage(f32),// = Dimension::PercentAuto,// = Dimension::AutoStretch(f32),// Fill available space in proportion to argument value. No equivalent in Taffy!}
Style Properties
Using these types (mostly Units), Morphorm has the following style properties. Note:
All properties are optional (their type is wrapped in Option). But I have omitted that below because it was noisy.
Not all properties that use Units actually support all variants, for example border (and I think also min_size and max_size) only really supports "LengthPercentage" (Auto and Stretch resolve to zero).
Property
Type
Taffy Equiv.
Description
Layout Mode
layout_mode
LayoutType
flex_direction
Row vs. Column vs. Grid
position_type
Position
position
SelfDirected (absolute) vs. ParentDirected (in-flow) position
Item size
size
Size<Units>
size
The preferred height and width of item
min_size
Size<Units>
min_size
The minimum height and width of the item
max_size
Size<Units>
max_size
The maximum height and width of the item
Border
border
Rect<Units>
border
How large should the border be on each side?
Morphorm Container
child_spacing
Rect<Units>
padding
Sets the default "spacing" (~margin) on each side of child nodes
row_between
Units
gap.height
Sets the default vertical "spacing" (~margin) between child nodes
col_between
Units
gap.width
Sets the default horizontal "spacing" (~margin) between child nodes
grid_rows
Vec<Units>
grid_template_rows
(Grid Container) Row definitions with a size for each row
grid_cols
Vec<Units>
grid_tempalte_columns
(Grid Container) Column definitions with a size for each column
Morphorm Item
spacing
Rect<Units>
margin
The preferred spacing on each side of the item
min_spacing
Rect<Units>
-
The minimum spacing on each side of the item
max_spacing
Rect<Units>
-
The maximum spacing on each side of the item
row_index
usize
grid_row.start
(Grid Item) Zero-based index for the start row of the item
col_index
usize
grid_column.start
(Grid Item) Zero-based index for the start column of the item
row_span
usize
grid_row.end
(Grid Item) The number of rows the item spans
col_span
usize
grid_column.end
(Grid Item) The number of columns the item spans
Analysis
Nearly everything in Morphorm is simplified version of either Flexbox or CSS Grid. There are really three things that aren't:
The child_spacing and *_between properties. CSS does not allow you to specify child margins on the parent like this. It does have the align-items and justify-items properties, but those are not as powerful.
The min_spacing and max_spacing properties. CSS does not allow you to specify min or max values for margins.
The Stretch variant of Units. Flexbox/CSS Grid do not allow you to express "fill available space" as a size like this. To achieve this with Flexbox/CSS Grid you need to use special properties that are specified separately.
The only really tricky thing here is the width/height properties. They are common to all algorithms, and furthermore by both parent and child nodes access this property. This means that the type of these properties really needs to be a single unified type. I thought this might be a blocker, however I have discovered that an upcoming CSS standard (css-sizing-4) actually does define this functionality and in fact even calls it stretch. The CSS version doesn't have the weighting parameter (although I have opened an issue proposing that it does - no idea if that's the right place to do that though) so it is roughly equivalent to Stretch(1.0), but that would a pretty straightforward extension.
Algorithm and Code Structure Differences
Morphorm is relative small and simple compared to Taffy's layout algorithms (yay!). Morphorm's layout.rs which contains the core implementation for both it's Grid and Row/Column algorithms is just over 1000 LoC. For comparison, Taffy's Flexbox implementation alone is around ~1700 LoC, and the CSS Grid implementation almost double that.
Morphorm uses a 3 phased approach to layout:
It computes the first and last child of every node in the tree (Note: I'm not quite sure why is necessary yet)
Working bottom-to-top, it computes the content size of each node
Working top-to-bottom it works down the tree and does final sizing, alignment and positioning for each node. This last step differs based on whether the node is a Grid node or a Row/Column node.
In contrast, Taffy uses a single phase approach which essentially corresponds to Phase 3 of Morphorm's approach. It also does things equivalent to Morphorm's Phase 2 (probably Phase 1 as well), but it does this lazily on demand.
Morphorm has fairly large/extensive persistent Cache object (See the trait and the reference implementation) which I believe it uses to store both intermediate computations and the output of it's layout. In contrast, Taffy stores it's intermediate computations locally within the computation function(s) and throws them away once it's done computing the size for a node. I have a feeling that the easiest way to get Morphorm to work with Taffy might be to make it work more like Taffy's other algorithms do and keep more of it's intermediate results internal: IMO keeping API surface small is going to be key to making this project feasible.
Morphorm's layout algorithms themselves obviously have differences (that being the whole point of supporting it!), but I don't think any of that (other than the already discussed Stretch variant of Units) is particularly relevant from an integration point of view. Taffy is already setup to let each container node have free reign over their children, so it should be straightforward to slip Morphorm's algorithms into that model.
Implementation Proposal
My suggestion based on looking into this is that we don't attempt to use the morphorm crate as a dependency, and instead port the Morphorm code/algorithm into Taffy directly (but I'd definitely be interested to hear @geom3trik's opinion on this). This is based on the following:
The Morphorm algorithm is fairly small/simple, and we have an existing standalone implementation (in Rust!) to copy, so porting it should be relatively straight forward.
I think details of the existing implementation will make it difficult to depend on use it as a dependency: it has a different style system, it has a component-per-style system for it's storage, it requires a large interface for it's cache storage, it has different traits for implementors, etc.
I think the algorithm code will require fairly extensive refactoring (making it run in a single pass, lazily sizing children) to integrate with other layout algorithms anyway.
More specifically, I would propose the following implementation plan:
Add a Stretch(f32) variant to Taffy's existing style system. Making it available on the width/height properties and implementing this variant for Flexbox/CSS Grid according to https://www.w3.org/TR/css-sizing-4/#stretch-fit-sizing (except with added support for weighting), and maybe on the margin and/or position properties (the latter two could be done later though).
Create a morphorm feature flag.
Add Morphorm style properties to Style (behind the feature flag). This will also involve working how to alter the Display type (e.g. do we add MorphormRow/MorphormColumn/MorphormGrid separately, or do we have a single Morphorm variant and sub-differentiate separately?)
Implement the Morphorm layout algorithms themselves, refactoring them to fit Taffy's structure as they are ported.
Consider improving Taffy's extension trait system (i.e. LayoutTree and MeasureFunc) with inspiration from Morphorm's traits (Node, Hierarchy, Cache). Possibly using the approach of "just having a Node trait" as proposed here Support multiple layout algorithms #28 (comment)
Implement a fracturing of the Style into smaller pieces similar to the one Bevy UI is about to implement.
I'd love to know others' thoughts, but I'm quite encourage by what I'm seen: this actually looks quite achievable to me!
P.S. I also did some preliminary research on Flutter and Swift UI's layout systems, but I think that to a large degree their systems amount to being layout-agnostic, with each Component being able to implement arbitrary layout algorithms. So I think support for those systems will look more like "an extension point that allows custom algorithm" than an implementation of specific algorithm(s).
Now that CSS Grid support is nearing completion, I thought I'd take a look at what it would take to support Morphorm layout in Taffy (CC: @geom3trik). My findings are summarised below:
Morphorm Styles
Unique Types
LayoutType(corresponds toDisplayin Taffy):PositionType(corresponds toPositionin Taffy):Units(corresponds toDimensionin Taffy):Style Properties
Using these types (mostly
Units), Morphorm has the following style properties. Note:Unitsactually support all variants, for exampleborder(and I think alsomin_sizeandmax_size) only really supports "LengthPercentage" (Auto and Stretch resolve to zero).layout_modeLayoutTypeflex_directionposition_typePositionpositionsizeSize<Units>sizemin_sizeSize<Units>min_sizemax_sizeSize<Units>max_sizeborderRect<Units>borderchild_spacingRect<Units>paddingrow_betweenUnitsgap.heightcol_betweenUnitsgap.widthgrid_rowsVec<Units>grid_template_rowsgrid_colsVec<Units>grid_tempalte_columnsspacingRect<Units>marginmin_spacingRect<Units>max_spacingRect<Units>row_indexusizegrid_row.startcol_indexusizegrid_column.startrow_spanusizegrid_row.endcol_spanusizegrid_column.endAnalysis
Nearly everything in Morphorm is simplified version of either Flexbox or CSS Grid. There are really three things that aren't:
child_spacingand*_betweenproperties. CSS does not allow you to specify child margins on the parent like this. It does have thealign-itemsandjustify-itemsproperties, but those are not as powerful.min_spacingandmax_spacingproperties. CSS does not allow you to specify min or max values for margins.Stretchvariant ofUnits. Flexbox/CSS Grid do not allow you to express "fill available space" as a size like this. To achieve this with Flexbox/CSS Grid you need to use special properties that are specified separately.The only really tricky thing here is the
width/heightproperties. They are common to all algorithms, and furthermore by both parent and child nodes access this property. This means that the type of these properties really needs to be a single unified type. I thought this might be a blocker, however I have discovered that an upcoming CSS standard (css-sizing-4) actually does define this functionality and in fact even calls itstretch. The CSS version doesn't have the weighting parameter (although I have opened an issue proposing that it does - no idea if that's the right place to do that though) so it is roughly equivalent toStretch(1.0), but that would a pretty straightforward extension.Algorithm and Code Structure Differences
Morphorm is relative small and simple compared to Taffy's layout algorithms (yay!). Morphorm's
layout.rswhich contains the core implementation for both it's Grid and Row/Column algorithms is just over 1000 LoC. For comparison, Taffy's Flexbox implementation alone is around ~1700 LoC, and the CSS Grid implementation almost double that.Morphorm uses a 3 phased approach to layout:
In contrast, Taffy uses a single phase approach which essentially corresponds to Phase 3 of Morphorm's approach. It also does things equivalent to Morphorm's Phase 2 (probably Phase 1 as well), but it does this lazily on demand.
Morphorm has fairly large/extensive persistent
Cacheobject (See the trait and the reference implementation) which I believe it uses to store both intermediate computations and the output of it's layout. In contrast, Taffy stores it's intermediate computations locally within the computation function(s) and throws them away once it's done computing the size for a node. I have a feeling that the easiest way to get Morphorm to work with Taffy might be to make it work more like Taffy's other algorithms do and keep more of it's intermediate results internal: IMO keeping API surface small is going to be key to making this project feasible.Morphorm's layout algorithms themselves obviously have differences (that being the whole point of supporting it!), but I don't think any of that (other than the already discussed
Stretchvariant ofUnits) is particularly relevant from an integration point of view. Taffy is already setup to let each container node have free reign over their children, so it should be straightforward to slip Morphorm's algorithms into that model.Implementation Proposal
My suggestion based on looking into this is that we don't attempt to use the
morphormcrate as a dependency, and instead port the Morphorm code/algorithm into Taffy directly (but I'd definitely be interested to hear @geom3trik's opinion on this). This is based on the following:More specifically, I would propose the following implementation plan:
Add a
Stretch(f32)variant to Taffy's existing style system. Making it available on thewidth/heightproperties and implementing this variant for Flexbox/CSS Grid according to https://www.w3.org/TR/css-sizing-4/#stretch-fit-sizing (except with added support for weighting), and maybe on themarginand/orpositionproperties (the latter two could be done later though).Create a
morphormfeature flag.Add Morphorm style properties to
Style(behind the feature flag). This will also involve working how to alter theDisplaytype (e.g. do we addMorphormRow/MorphormColumn/MorphormGridseparately, or do we have a singleMorphormvariant and sub-differentiate separately?)Implement the Morphorm layout algorithms themselves, refactoring them to fit Taffy's structure as they are ported.
Consider improving Taffy's extension trait system (i.e.
LayoutTreeandMeasureFunc) with inspiration from Morphorm's traits (Node,Hierarchy,Cache). Possibly using the approach of "just having a Node trait" as proposed here Support multiple layout algorithms #28 (comment)Implement a fracturing of the
Styleinto smaller pieces similar to the one Bevy UI is about to implement.I'd love to know others' thoughts, but I'm quite encourage by what I'm seen: this actually looks quite achievable to me!
P.S. I also did some preliminary research on Flutter and Swift UI's layout systems, but I think that to a large degree their systems amount to being layout-agnostic, with each Component being able to implement arbitrary layout algorithms. So I think support for those systems will look more like "an extension point that allows custom algorithm" than an implementation of specific algorithm(s).