Add method to invoke nested Yoga layout#10237
Conversation
|
I think in order to use this API, your native VM must implement a panel so that you can override layout, is that right? |
|
@asklar - indeed - there's not really any alternative for this. I'll work up a sample at some point. |
|
We're considering other options for handling re-entrant / nested layout. Converting to draft for now. |
Core VMs can override `SetLayoutProps` to get a callback just before native layout values are applied from Yoga. There is no option to observe these changes from Yoga in ABI VMs. This change introduces a new interface to receive a callback when computed Yoga layout values are being applied to a native view. We have a use case for this interface that is also dependent on microsoft#10422 (ensuring parent SetLayoutProps occur after children), microsoft#10237 (adding an API to invoke nested Yoga layout for a given React tag), and microsoft#10393 (custom measure func). Specifically, the following sequence occurs: 1. Prop updates cause Yoga layout to be applied on a root node 2. SetLayoutProps is invoked on children of custom VM, Top/Left/Width/Height get set 3. SetLayoutProps/OnLayout is called on the custom VM 4. Custom VM runs logic to re-caclulate dimensions of each child in OnLayout 5. Custom VM invokes XamlUIService::ApplyYogaLayout with computed dimensions on each child. 6. Custom VM applies Top/Left values to children. The custom measure function is used to prevent children from having Yoga layout computed twice.
|
Please note, this change is also going to be useful should we ever support nested Views in Text with RichTextBlock, as we'll need an option to invoke Yoga layout on a specific sub-tree from TextViewManager. @asklar - this is the best example I can think of without adding a custom VM to playground. We don't have any such examples yet, so not planning on setting that up for this PR. The sequence of events would look something like the following to implement nested Views in Text.
Another good thing to note is that this type of thing is already supported on the public surface for Android and iOS view managers / shadow nodes, so strictly speaking it's a bug / gap that we don't support this in RNW. |
Core VMs can override `SetLayoutProps` to get a callback just before native layout values are applied from Yoga. There is no option to observe these changes from Yoga in ABI VMs. This change introduces a new interface to receive a callback when computed Yoga layout values are being applied to a native view. We have a use case for this interface that is also dependent on microsoft#10422 (ensuring parent SetLayoutProps occur after children), microsoft#10237 (adding an API to invoke nested Yoga layout for a given React tag), and microsoft#10393 (custom measure func). Specifically, the following sequence occurs: 1. Prop updates cause Yoga layout to be applied on a root node 2. SetLayoutProps is invoked on children of custom VM, Top/Left/Width/Height get set 3. SetLayoutProps/OnLayout is called on the custom VM 4. Custom VM runs logic to re-caclulate dimensions of each child in OnLayout 5. Custom VM invokes XamlUIService::ApplyYogaLayout with computed dimensions on each child. 6. Custom VM applies Top/Left values to children. The custom measure function is used in this example to prevent children from having Yoga layout computed twice.
Core VMs can override `SetLayoutProps` to get a callback just before native layout values are applied from Yoga. There is no option to observe these changes from Yoga in ABI VMs. This change introduces a new interface to receive a callback when computed Yoga layout values are being applied to a native view. We have a use case for this interface that is also dependent on microsoft#10422 (ensuring parent SetLayoutProps occur after children), microsoft#10237 (adding an API to invoke nested Yoga layout for a given React tag), and microsoft#10393 (custom measure func). Specifically, the following sequence occurs: 1. Prop updates cause Yoga layout to be applied on a root node 2. SetLayoutProps is invoked on children of custom VM, Top/Left/Width/Height get set 3. SetLayoutProps/OnLayout is called on the custom VM 4. Custom VM runs logic to re-caclulate dimensions of each child in OnLayout 5. Custom VM invokes XamlUIService::ApplyYogaLayout with computed dimensions on each child. 6. Custom VM applies Top/Left values to children. The custom measure function is used in this example to prevent children from having Yoga layout computed twice.
Core VMs can override `SetLayoutProps` to get a callback just before native layout values are applied from Yoga. There is no option to observe these changes from Yoga in ABI VMs. This change introduces a new interface to receive a callback when computed Yoga layout values are being applied to a native view. We have a use case for this interface that is also dependent on microsoft#10422 (ensuring parent SetLayoutProps occur after children), microsoft#10237 (adding an API to invoke nested Yoga layout for a given React tag), and microsoft#10393 (custom measure func). Specifically, the following sequence occurs: 1. Prop updates cause Yoga layout to be applied on a root node 2. SetLayoutProps is invoked on children of custom VM, Top/Left/Width/Height get set 3. SetLayoutProps/OnLayout is called on the custom VM 4. Custom VM runs logic to re-caclulate dimensions of each child in OnLayout 5. Custom VM invokes XamlUIService::ApplyYogaLayout with computed dimensions on each child. 6. Custom VM applies Top/Left values to children. The custom measure function is used in this example to prevent children from having Yoga layout computed twice.
21aae46 to
eb4f768
Compare
| "Invokes Yoga layout for a specific React tag. " | ||
| "This method is useful for views that use @IViewManagerRequiresNativeLayout " | ||
| "but have descendant views that require Yoga layout.") | ||
| void ApplyYogaLayout(Int64 tag, Single width, Single height); |
There was a problem hiding this comment.
why is this in XamlUIService though? Seems more appropriate for the react context ?
There was a problem hiding this comment.
Fair enough. Wasn't sure where to put it.
There was a problem hiding this comment.
Perhaps a better approach would be to create a new IDL for UIManager module and expose that from ReactContext. We could then add things like GetReactRootView, ApplyYogaLayout, a callback for batch completion for native modules, etc.
There was a problem hiding this comment.
Another good question - why is DispatchEvent in XamlUIService? cc @acoates-ms. It looks to me like XamlUIService has become a dumping ground for APIs that we don't know where else to put :). I'll file a new issue to clean up XamlUIService (deprecate the API and move everything to APIs that are a bit more uniform in usage scenario).
|
Based on conversation with @asklar - moving to draft to confirm this does not break rn-xaml. |
* Adds IViewManagerWithOnLayout interface for ABI VMs Core VMs can override `SetLayoutProps` to get a callback just before native layout values are applied from Yoga. There is no option to observe these changes from Yoga in ABI VMs. This change introduces a new interface to receive a callback when computed Yoga layout values are being applied to a native view. We have a use case for this interface that is also dependent on #10422 (ensuring parent SetLayoutProps occur after children), #10237 (adding an API to invoke nested Yoga layout for a given React tag), and #10393 (custom measure func). Specifically, the following sequence occurs: 1. Prop updates cause Yoga layout to be applied on a root node 2. SetLayoutProps is invoked on children of custom VM, Top/Left/Width/Height get set 3. SetLayoutProps/OnLayout is called on the custom VM 4. Custom VM runs logic to re-caclulate dimensions of each child in OnLayout 5. Custom VM invokes XamlUIService::ApplyYogaLayout with computed dimensions on each child. 6. Custom VM applies Top/Left values to children. The custom measure function is used in this example to prevent children from having Yoga layout computed twice. * Change files * Ensure we set the IViewManagerWithOnLayout instance to ABIViewManager * yarn format
1548210 to
b2c24d1
Compare
cb264e0 to
37fc6c5
Compare
|
ping @chrisglein |
| const [hover1, setHover1] = useState(false); | ||
| const [hover2, setHover2] = useState(false); | ||
| const [hover3, setHover3] = useState(false); | ||
| const [hover4, setHover4] = useState(false); | ||
|
|
||
| const getHoverProps = (setter: (value: boolean) => void) => { | ||
| return { | ||
| onMouseEnter: () => setter(true), | ||
| onMouseLeave: () => setter(false), | ||
| }; | ||
| }; |
There was a problem hiding this comment.
Took a moment to realize why you were doing this hover text changing. The reason I take it is that you have a XAML item (GridViewItem) with a Yoga-driven child that's changing layout size (due to the font change).
Adding a comment to describe the why of this would help with the maintainability of the code.
| m_viewManagerWithChildren{viewManager.try_as<IViewManagerWithChildren>()}, | ||
| m_viewManagerWithPointerEvents{viewManager.try_as<IViewManagerWithPointerEvents>()}, | ||
| m_viewManagerWithDropViewInstance{viewManager.try_as<IViewManagerWithDropViewInstance>()}, | ||
| m_viewManagerWithOnLayout{viewManager.try_as<IViewManagerWithOnLayout>()} { |
There was a problem hiding this comment.
I didn't see m_viewManagerWithOnLayout being removed, but it's no longer being set here. Is that a problem?
There was a problem hiding this comment.
Only use is in this same file:
if (m_viewManagerWithOnLayout) {
m_viewManagerWithOnLayout.OnLayout(viewToUpdate.as<xaml::FrameworkElement>(), left, top, width, height);|
Change makes sense to me in that your handing off the responsibilities to the ViewManager to deal with this whole business. The one code example you have here just has one child and calls I'm prone to accept this change, but want to set up the right guidance for folks to write |
In the Messenger example, it's a bit simpler. It doesn't implicate XAML layout at all. There's a set of self-measuring leaf nodes in our call window example, where the grid layout for the call window is some cross-platform C++ algorithm. Each of these self-measuring grids has nested React content in it, so we use this to allow re-entrancy for Yoga layout.
It's a good question. I would think that in react-native-xaml, we might do something like the following:
This breaks down pretty quickly if anyone wanted to add a module that interoperates with react-native-xaml though. So it might be better for this custom DependencyProperty to be part of React Native, and get automatically assigned to any IViewManagerRequiresNativeLayout ABIViewManager component. Alternatively, we could do this on behalf of all possible XAML layout interoperable modules (via SizeChanged + ApplyLayout) so react-native-xaml gets this "for free" at the expense of tearing. The reason we went with the more flexible approach of asking the user to invoke ApplyLayout directly was so that we could decide the layout constraints passed to Yoga, rather than assuming that all available width/height gets passed to Yoga (which we would need to do if we wanted to create a baseline functionality for this). It's not a deal breaker, but one option is certainly more "flexible" than the other (at the expense of more code). |
It's a good question. I don't think any of the IViewManager interfaces are very well documented today. Our internal documentation is basically "go look at some other module that is already using the interface" :( This is the closest thing you have today: https://microsoft.github.io/react-native-windows/docs/view-managers This needs to be broken down by interface, giving an example of how it's implemented and what it's useful for. Also, IMO, there's no better way to document something than to give an example of it's use, but discoverability is certainly an issue. |
|
@chrisglein The more important consideration here though is how this will work with Fabric. React Native Windows already has a nice abstraction for this (i.e., LayoutService) where we can route calls to either Paper or Fabric depending on which UIManager is managing the node. So, LayoutService::ApplyLayout could still work with Fabric, but we'll need to eventually update LayoutService to trigger Yoga layout on Fabric node sub-trees. |
|
@chrisglein can you follow up on @rozele 's answers and approve if this is ready? |
chrisglein
left a comment
There was a problem hiding this comment.
High level questions for this did get answered in the comment thread. But there are some (small) edits suggested in the code itself that should still be addressed before merging.
Description
Type of Change
Erase all that don't apply.
Why
Yoga does not recurse layout beyond a node that provides a self-measurement function. If you want to build a custom control that allows for XAML layout with descendants from React that still use Yoga layout, you need to kick off a new layout calculation from each "inner root".
Resolves #7601
What
This change adds a hook to self-measuring shadow nodes to conditionally measure any nested children that may require Yoga layout, as well as an ABIViewManager interface to participate in these events.
Microsoft Reviewers: Open in CodeFlow