[Yoga] Initial commit for supporting Yoga-powered layout calculation.#2982
[Yoga] Initial commit for supporting Yoga-powered layout calculation.#2982Adlai-Holler merged 11 commits intomasterfrom
Conversation
…ayout calculation. Because this results in ASLayout objects, it preserve support for automaticallyManagesSubnodes as well as the animated transition system. More work remains to vet performance of the new mode, and it will remain in +Beta for the forseeable future. I'm not sure that this should ever be used as the primary ASDK layout system, but it should remain an option for some apps to experiment with if they require an implementation that more strictly mirrors W3C standard Flexbox.
| @property (nonatomic, assign, readwrite) ASDimension maxWidth; | ||
|
|
||
| #if YOGA | ||
| @property (nonatomic, assign, readwrite) ASStackLayoutDirection direction; |
There was a problem hiding this comment.
@maicki I decided to use compile-gated extensions in the base style object for a few reasons:
- They are compiled out by default
- Apps that use them will find them to be very performance-sensitive due to very widespread use
- They aren't particularly feature-specific, but rather standard expressions of CSS Flexbox
- The Extensibility PR had not landed by the time I started this, and isn't merging (also see my comment about ASEnvironment).
All that said, I would like to find a path to landing this diff soon that is "acceptable" and then I can actively improve it from there, so that the time-consuming merge conflicts we've all been dealing with are reduced. For example, we could create a new style object like .yogaStyle, or a category .h / .mm for these new properties.
|
Is there any way we can separate this out so that it can be cleanly included / excluded? I.e. all separate files with non-yoga specific changes in the core classes which would support a generally pluggable system? |
|
@garrettmoon unless I missed one, every single change is gated by just one compiler flag, which is off by default (#if YOGA). Because of the high rate of change recently, I think it would be better to split out into separate files when ASDisplayNode is broken up (IIRC this is on the docket for very soon?). For an example of this pain, recently there was a ~3,100 line change to ASDisplayNode that took almost a day for me to fix past :(. |
|
OK, I think this is technically merge-conflict-free for the moment. However we should definitely strategize how to better separate this out. If we don't want to land it until then, I'm open to suggestions — let's just agree on how to separate it and try to coordinate on changing these files too much until that point? |
|
@appleguy yeah, hopefully we have fewer changes like that in the future; they're specifically attempting to prevent it :D Sounds good, we can try and hold off on landing big changes until we've made some decisions around this one. |
|
@garrettmoon - awesome, thanks! Since we're continuing to change this, I want to try to stay in sync as much as possible (even with this PR there's a risk of internal divergence). The best way to do that would be to land in the next few days, so I'm willing to put effort in over the weekend to gate it to whatever degree is needed to be a good citizen when landing something experimental like this. Any thoughts on how to compartmentalize it further than the compile-out switch? Can I use the +Beta.h headers, and then you'd prefer to see +Yoga.mm implementation files -- or did you have a different setup in mind? |
31b244e to
c47a173
Compare
|
My impression is that you, @Adlai-Holler and @maicki came up with a direction in your meeting, will defer to them :) |
|
|
||
| #if ASEVENTLOG_ENABLE | ||
| #define ASDisplayNodeLogEvent(node, ...) [node.eventLog logEventWithBacktrace:(AS_SAVE_EVENT_BACKTRACES ? [NSThread callStackSymbols] : nil) format:__VA_ARGS__] | ||
| #define ASDisplayNodeLogEvent(node, ...) [node.eventLog logEventWithBacktrace:(AS_SAVE_EVENT_BACKTRACES ? [NSThread callStackSymbols] : nil) format:__VA_ARGS__] |
There was a problem hiding this comment.
I have seen this indentation style used in a few places in ASDK, and I think it increases readability quite a bit. So only in the files I touched, I made sure the #defines matched.
|
|
||
| @interface ASLayoutElementStyle (Yoga) | ||
|
|
||
| @property (nonatomic, assign, readwrite) ASStackLayoutDirection direction; |
There was a problem hiding this comment.
@maicki I decided to put these on the style object directly for a few reasons:
- They are able to use std::atomic, which is great for perf.
- This is the only way to achieve thread safety (extensibility API is new, and doesn't yet have a solution for locking)
- They can be nicely hidden in +Beta
- The impact in the .mm is very small
I'm believe these changes won't add any work during refactoring of the layout system, as they are tucked into the style object — without business logic integrations.
| /** | ||
| * Declare <ASInterfaceState> methods as requiring super calls (this can't be required in the protocol). | ||
| * For descriptions, see <ASInterfaceState> definition. | ||
| * Declare <ASInterfaceStateDelegate> methods as requiring super calls (this can't be required in the protocol). |
There was a problem hiding this comment.
This was a spot that hadn't been renamed after there was a request to put Delegate in the name.
|
|
||
| - (ASDisplayNode *)yogaParent | ||
| { | ||
| return _yogaParent; |
There was a problem hiding this comment.
I realize these methods don't currently lock. This is something I have in mind and will follow up on. For now, there is a risk of deadlocking if I add locks, and so I would like to approach this with a careful analysis of the correct locking before dropping them in.
|
|
||
| // Apply the constrainedSize as a base, known frame of reference. | ||
| // If the root node also has style.*Size set, these will be overridden below. | ||
| YGNodeStyleSetMinWidth (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.width)); |
There was a problem hiding this comment.
I have planned changes to how this copying of style information should work. However, I'd like to get the initial version in before changing this much more. The reason is that I can't test against a large Yoga layout without doing a repository sync, and syncing is tough right now because of divergence from master. In any case, consider it my responsibility to make sure this logic becomes cleaner, even though it is hidden in the +Yoga file.
| _subnodes = nil; | ||
|
|
||
| #if YOGA | ||
| if (_yogaNode != NULL) { |
There was a problem hiding this comment.
For the three changes in ASDisplayNode.mm, I couldn't figure out a reasonable way to move them out. However, I thought pretty carefully about each of these, and don't think they will create any complexity problems during refactoring.
| relativeToParentSize:(CGSize)parentSize | ||
| { | ||
| const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, ASLayoutElementSizeResolve(self.style.size, parentSize)); | ||
| ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize); |
There was a problem hiding this comment.
This is here because I found it is very useful during debugging to have this intermediate variable.
| ASDN::MutexLocker l(__instanceLock__); | ||
|
|
||
| #if YOGA /* YOGA */ | ||
| if (ASHierarchyStateIncludesYogaLayoutEnabled(_hierarchyState) == YES && |
There was a problem hiding this comment.
This integration in the layout method is just an early-bail condition that has no other ties into the behavior of the method. So, total refactoring of the rest of the method should be OK.
If any issues are encountered when refactoring, I am very supportive of deleting this integration entirely, and just ask me to figure out how to re-add it with the new code. The intention is not to add more officially-supported paths.
| ASDimension left; | ||
| ASDimension bottom; | ||
| ASDimension right; | ||
| } ASEdgeInsets; |
There was a problem hiding this comment.
I think this type could be generally useful for many things, so proposing we keep it in ASDimension. I can move it if needed.
|
|
||
| #if YOGA | ||
|
|
||
| - (ASStackLayoutDirection)direction { return _direction.load(); } |
There was a problem hiding this comment.
These and especially the ivars had to be in the main class instead of a category. I think the footprint is minimized enough and not linked into the other code that it should be OK. This integration is very beneficial due to the std::atomic performance in very key code (these values are read much more frequently even than the ASLayoutSpec style properties, because of how the YGNodeRef style copying works currently).
| Any layout calculated during this state should not be applied immediately, but pending until later. */ | ||
| ASHierarchyStateLayoutPending = 1 << 3, | ||
| ASHierarchyStateVisualizeLayout = 1 << 4 | ||
| ASHierarchyStateYogaLayoutEnabled = 1 << 4, |
There was a problem hiding this comment.
Likewise, I could #define these values and "Illegally" OR them into the state value. However since it doesn't cause tangling, it seemed best to add these directly.
…ng code. Created new file ASDisplayNode+Yoga.mm, reduced size and number of integration points in core code.
25c39f1 to
a7daf50
Compare
…llocating _yogaNode in property accessor, and changing all accesses to use the property.
Adlai-Holler
left a comment
There was a problem hiding this comment.
I've given this a complete review and I'm glad to approve it. I'm excited to see this up and running sometime. Thanks for taking the time to minimize impact while this bold project gets developed. High hopes!
Because this results in ASLayout objects, it preserve support for automaticallyManagesSubnodes as well as the animated transition system. More work remains to vet performance of the new mode,
and it will remain in +Beta for the forseeable future.
I'm not sure that this should ever be used as the primary ASDK layout system, but it should remain an option for some apps to experiment with if they require an implementation that more strictly mirrors W3C standard Flexbox.