/// <summary> /// Handles the creation of a view. /// </summary> /// <param name="node">The shadow node for the view.</param> /// <param name="rootViewTag">The react tag id of the root.</param> /// <param name="themedContext">The themed context.</param> /// <param name="initialProperties"> /// The initial properties for the view. /// </param> public void HandleCreateView( ReactShadowNode node, int rootViewTag, ThemedReactContext themedContext, ReactStylesDiffMap initialProperties) { #if DISABLE_NATIVE_VIEW_HIERARCHY_OPTIMIZER _uiViewOperationQueue.EnqueueCreateView( themedContext, node.ReactTag, node.ViewClass, initialProperties, rootViewTag); #else var isLayoutOnly = node.ViewClass == ViewProps.ViewClassName && IsLayoutOnlyAndCollapsible(initialProperties); node.IsLayoutOnly = isLayoutOnly; if (!isLayoutOnly) { _uiViewOperationQueue.EnqueueCreateView( themedContext, node.ReactTag, node.ViewClass, initialProperties, rootViewTag); } #endif }
private void AssertNodeDoesNotNeedCustomLayoutForChildren(ReactShadowNode node) { var viewManager = _viewManagers.Get(node.ViewClass); if (viewManager == null) { throw new InvalidOperationException( Invariant($"Could not find view class '{node.ViewClass}'.")); } var viewParentManager = viewManager as IViewParentManager; if (viewParentManager == null) { throw new InvalidOperationException( Invariant($"Trying to use view '{node.ViewClass}' as a parent but its manager is not a ViewParentManager.")); } if (viewParentManager.NeedsCustomLayoutForChildren) { throw new InvalidOperationException( string.Format( CultureInfo.InvariantCulture, "Trying to measure a view using measureLayout/measureLayoutRelativeToParent relative to " + "an ancestor that requires custom layout for it's children ('{0}'). Use measure instead.", node.ViewClass)); } }
private void ApplyLayoutBase(ReactShadowNode node) { var tag = node.ReactTag; if (_tagsWithLayoutVisited.TryGetValue(tag, out var visited) && visited) { return; } _tagsWithLayoutVisited.Add(tag, true); var parent = node.Parent; // We use ScreenX/ScreenY (which round to integer pixels) at each // node in the hierarchy to emulate what the layout would look like // if it were actually built with native views, which have integral // top/left/bottom/right values. var x = node.LayoutX; var y = node.LayoutY; while (parent != null && parent.IsLayoutOnly) { x += parent.LayoutX; y += parent.LayoutY; parent = parent.Parent; } ApplyLayoutRecursive(node, x, y); }
public static float GetPaddingValue(this ReactShadowNode node, int spacingType) { var padding = node.GetPadding(spacingType); if (!YogaConstants.IsUndefined(padding)) { return(padding); } if (spacingType == EdgeSpacing.Left || spacingType == EdgeSpacing.Right) { padding = node.GetPadding(EdgeSpacing.Horizontal); } if (!YogaConstants.IsUndefined(padding)) { return(padding); } if (spacingType == EdgeSpacing.Top || spacingType == EdgeSpacing.Bottom) { padding = node.GetPadding(EdgeSpacing.Vertical); } if (!YogaConstants.IsUndefined(padding)) { return(padding); } return(node.GetPadding(EdgeSpacing.All)); }
/// <summary> /// Handles a manage children call. This may translate into multiple /// manage children calls for multiple other views. /// </summary> /// <param name="nodeToManage">The node to manage.</param> /// <param name="indexesToRemove">The indices to remove.</param> /// <param name="tagsToRemove">The tags to remove.</param> /// <param name="viewsToAdd">The views to add.</param> /// <param name="tagsToDelete">The tags to delete.</param> /// <remarks> /// The assumption for calling this method is that all corresponding /// <see cref="ReactShadowNode"/>s have been updated, but /// <paramref name="tagsToDelete"/> have not been deleted yet. This is /// because we need to use the metadata from those nodes to figure out /// the correct commands to dispatch. This is unlike other calls on /// this class where we assume all operations on the shadow hierarchy /// have already completed by the time a corresponding method here is /// called. /// </remarks> public void HandleManageChildren(ReactShadowNode nodeToManage, int[] indexesToRemove, int[] tagsToRemove, ViewAtIndex[] viewsToAdd, int[] tagsToDelete) { #if DISABLE_NATIVE_VIEW_HIERARCHY_OPTIMIZER _uiViewOperationQueue.EnqueueManageChildren( nodeToManage.ReactTag, indexesToRemove, viewsToAdd, tagsToDelete); #else // We operate on tagsToRemove instead of indicesToDelete because by // the time this method is called, these views have already been // removed from the shadow hierarchy and the indices are no longer // useful to operate on. for (var i = 0; i < tagsToRemove.Length; ++i) { var tagToRemove = tagsToRemove[i]; var delete = tagsToDelete.Contains(tagToRemove); var nodeToRemove = _shadowNodeRegistry.GetNode(tagToRemove); RemoveNodeFromParent(nodeToRemove, delete); } for (var i = 0; i < viewsToAdd.Length; ++i) { var toAdd = viewsToAdd[i]; var nodeToAdd = _shadowNodeRegistry.GetNode(toAdd.Tag); AddNodeToNode(nodeToManage, nodeToAdd, toAdd.Index); } #endif }
/// <summary> /// Returns the offset within the native children owned by all layout- /// only nodes in the subtree rooted at this node for the given child. /// Put another way, this returns the number of native nodes (nodes not /// optimized out of the native tree) that are a) to the left (visited /// before by a depth-first search) of the given child in the subtree /// rooted at this node and b) do not have a native parent in this /// subtree (which means that the given child will be a sibling of /// theirs in the final native hierarchy since they'll get attached to /// the same native parent). /// /// Basically, a view might have children that have been optimized away /// by <see cref="NativeViewHierarchyOptimizer"/>. Since those children /// will then add their native children to this view, we now have /// ranges of native children that correspond to single unoptimized /// children. The purpose of this method is to return the index within /// the native children that corresponds to the start of the native /// children that belong to the given child. Also, note that all of the /// children of a view might be optimized away, so this could return /// the same value for multiple different children. /// </summary> /// <param name="child">The child.</param> /// <returns>The native offset.</returns> public int GetNativeOffsetForChild(ReactShadowNode child) { var index = 0; var found = false; for (var i = 0; i < ChildCount; ++i) { var current = GetChildAt(i); if (child == current) { found = true; break; } index += current.IsLayoutOnly ? current.TotalNativeChildren : 1; } if (!found) { throw new InvalidOperationException( Invariant($"Child '{child.ReactTag}' was not a child of '{ReactTag}'.")); } return(index); }
/// <summary> /// Handles the creation of a view. /// </summary> /// <param name="node">The shadow node for the view.</param> /// <param name="themedContext">The themed context.</param> /// <param name="initialProperties"> /// The initial properties for the view. /// </param> public void HandleCreateView( ReactShadowNode node, ThemedReactContext themedContext, ReactStylesDiffMap initialProperties) { #if !ENABLED _uiViewOperationQueue.EnqueueCreateView( themedContext, node.ReactTag, node.ViewClass, initialProperties); #else var isLayoutOnly = node.ViewClass == ViewProps.ViewClassName && IsLayoutOnlyAndCollapsible(initialProperties); node.IsLayoutOnly = isLayoutOnly; if (!isLayoutOnly) { _uiViewOperationQueue.EnqueueCreateView( themedContext, node.ReactTag, node.ViewClass, initialProperties); } #endif }
public T Visit(ReactShadowNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } var n = node.ChildCount; if (n == 0) { return(Make(node, Array.Empty <T>())); } else { var children = new List <T>(n); for (var i = 0; i < node.ChildCount; ++i) { var child = node.GetChildAt(i); children.Add(Visit(child)); } return(Make(node, children)); } }
public static float GetLeftBorderWidth(this ReactShadowNode node) { var width = node.GetBorder(EdgeSpacing.Left); if (!YogaConstants.IsUndefined(width)) { return(width); } if (!I18NUtil.IsRightToLeft) { width = node.GetBorder(YogaEdge.Start); if (!YogaConstants.IsUndefined(width)) { return(width); } } else { width = node.GetBorder(YogaEdge.End); if (!YogaConstants.IsUndefined(width)) { return(width); } } width = node.GetBorder(YogaEdge.All); if (!YogaConstants.IsUndefined(width)) { return(width); } return(0.0f); }
private ReactShadowNode CreateRootShadowNode() { var rootCssNode = new ReactShadowNode(); rootCssNode.ViewClass = "Root"; return(rootCssNode); }
private void HandleCreateView(ReactShadowNode cssNode, int rootViewTag, JObject styles) { if (!cssNode.IsVirtual) { _nativeViewHierarchyOptimizer.HandleCreateView(cssNode, rootViewTag, cssNode.ThemedContext, styles); } }
private void MeasureLayoutRelativeToVerifiedAncestor( ReactShadowNode node, ReactShadowNode ancestor, double[] outputBuffer) { var offsetX = 0.0; var offsetY = 0.0; if (node != ancestor) { offsetX = node.LayoutX; offsetY = node.LayoutY; var current = node.Parent; while (current != ancestor) { Debug.Assert(current != null); AssertNodeDoesNotNeedCustomLayoutForChildren(current); offsetX += current.LayoutX; offsetY += current.LayoutY; current = current.Parent; } AssertNodeDoesNotNeedCustomLayoutForChildren(ancestor); } outputBuffer[0] = offsetX; outputBuffer[1] = offsetY; outputBuffer[2] = node.LayoutWidth; outputBuffer[3] = node.LayoutHeight; }
private void ApplyLayoutRecursive(ReactShadowNode node, int x, int y) { if (!node.IsLayoutOnly && node.NativeParent != null) { _uiViewOperationQueue.EnqueueUpdateLayout( node.NativeParent.ReactTag, node.ReactTag, x, y, node.ScreenWidth, node.ScreenHeight); return; } for (var i = 0; i < node.ChildCount; ++i) { var child = node.GetChildAt(i); var visited = default(bool); if (_tagsWithLayoutVisited.TryGetValue(child.ReactTag, out visited) && visited) { continue; } _tagsWithLayoutVisited.Add(child.ReactTag, true); var childX = child.ScreenX; var childY = child.ScreenY; childX += x; childY += y; ApplyLayoutRecursive(child, childX, childY); } }
private void PrintNodeTree(string indent, ReactShadowNode node, bool last) { string str = ""; str += indent; if (last) { str += "\\-"; indent += " "; } else { str += "|-"; indent += "| "; } if (node.IsLayoutOnly) { str += "LayoutOnly" + "." + node.ReactTag + "\n"; } else { str += (node.ViewClass.Replace("RCT", "") + "." + node.ReactTag + "\n"); } Log.Info(ReactConstants.Tag, str, "", "", 0); int i = 0; var count = node.ChildCount; for (int j = 0; j < count; j++) { PrintNodeTree(indent, node.GetChildAt(j) as ReactShadowNode, i == node.ChildCount - 1); i++; } }
public static float GetPaddingSpace(this ReactShadowNode node, int spacingType) { var padding = node.GetPaddingValue(spacingType); return(YogaConstants.IsUndefined(padding) ? 0.0f : padding); }
protected override object[] GetShadowNodeArgs(ReactShadowNode shadowNode, ReactStylesDiffMap props) { var args = s_args.Value; args[0] = shadowNode; args[1] = ExtractProperty(props); return(args); }
private void CalculateRootLayout(ReactShadowNode cssRoot) { using (Tracer.Trace(Tracer.TRACE_TAG_REACT_BRIDGE, "ReactShadowNode.CalculateLayout") .With("RootTag", cssRoot.ReactTag)) { cssRoot.CalculateLayout(); } }
protected override object[] GetShadowNodeArgs(ReactShadowNode shadowNode, JObject props) { var args = s_args.Value; args[0] = shadowNode; args[1] = ExtractProp(props); return(args); }
private ReactShadowNode CreateRootShadowNode() { var rootCssNode = new ReactShadowNode(); // RTL/LTR is implemented through Xaml FlowDirection, so we keep Yoga oblivious of this. rootCssNode.ViewClass = "Root"; return(rootCssNode); }
public T Visit(ReactShadowNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } return(VisitCore(node)); }
private void AddNonLayoutOnlyNodeToNonLayoutOnlyNode(ReactShadowNode parent, ReactShadowNode child, int index) { parent.AddNativeChildAt(child, index); _uiViewOperationQueue.EnqueueManageChildren( parent.ReactTag, null, new[] { new ViewAtIndex(child.ReactTag, index) }, null); }
/// <summary> /// Add a React shadow node. /// </summary> /// <param name="node">The node to add.</param> public void AddNode(ReactShadowNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } _tagsToCssNodes[node.ReactTag] = node; }
private void HandleUpdateView( ReactShadowNode cssNode, string className, JObject props) { if (!cssNode.IsVirtual) { _nativeViewHierarchyOptimizer.HandleUpdateView(cssNode, className, props); } }
private void HandleUpdateView( ReactShadowNode cssNode, string className, ReactStylesDiffMap styles) { if (!cssNode.IsVirtual) { _nativeViewHierarchyOptimizer.HandleUpdateView(cssNode, className, styles); } }
private void TransitionLayoutOnlyViewToNativeView(ReactShadowNode node, ReactStylesDiffMap props) { var parent = node.Parent; if (parent == null) { node.IsLayoutOnly = false; return; } // First, remove the node from its parent. This causes the parent // to update its native children count. The call will cause all the // view's children to be detached from their native parent. var childIndex = parent.IndexOf(node); parent.RemoveChildAt(childIndex); RemoveNodeFromParent(node, false); node.IsLayoutOnly = false; // Create the view since it doesn't exist in the native hierarchy yet. _uiViewOperationQueue.EnqueueCreateView( node.RootNode.ThemedContext, node.ReactTag, node.ViewClass, props, node.RootNode.ReactTag); // Add the node and all its children as if adding new nodes. parent.AddChildAt(node, childIndex); AddNodeToNode(parent, node, childIndex); for (var i = 0; i < node.ChildCount; ++i) { AddNodeToNode(node, node.GetChildAt(i), i); } // Update layouts since the children of the node were offset by its // x/y position previously. (Warning: bit of a hack) We need to // update the layout of this node's children now that it's no // longer layout-only, but we may still receive more layout updates // at the end of this batch that we don't want to ignore. // Also <node>.DispatchUpdates optimizes the layout applying out // if screen position/sizes didn't change, yet in this particular case // we want to force the applying of those values since the native view // is a freshly created one with no history. ApplyLayoutBase(node); for (var i = 0; i < node.ChildCount; ++i) { ApplyLayoutBase(node.GetChildAt(i)); } _tagsWithLayoutVisited.Clear(); node.MarkForceLayout(); }
private void RemoveShadowNodeRecursive(ReactShadowNode nodeToRemove) { _nativeViewHierarchyOptimizer.HandleRemoveNode(nodeToRemove); _shadowNodeRegistry.RemoveNode(nodeToRemove.ReactTag); for (var i = nodeToRemove.ChildCount - 1; i >= 0; --i) { RemoveShadowNodeRecursive(nodeToRemove.GetChildAt(i)); } nodeToRemove.RemoveAndDisposeAllChildren(); }
private void ApplyLayoutBase(ReactShadowNode node) { var tag = node.ReactTag; var visited = default(bool); if (_tagsWithLayoutVisited.TryGetValue(tag, out visited) && visited) { return; } _tagsWithLayoutVisited.Add(tag, true); var parent = node.Parent; // We use ScreenX/ScreenY (which round to integer pixels) at each // node in the hierarchy to emulate what the layout would look like // if it were actually built with native views, which have integral // top/left/bottom/right values. var x = node.LayoutX; var y = node.LayoutY; //Log.Info(ReactConstants.Tag, "tag=" + tag + " node.LayoutX=" + node.LayoutX + ", node.LayoutY=" + node.LayoutY); while (parent != null && parent.IsLayoutOnly) { x += parent.LayoutX; y += parent.LayoutY; //Log.Info(ReactConstants.Tag, "tag=" + parent.ReactTag + " parent.LayoutX=" + parent.LayoutX + ", parent.LayoutY=" + parent.LayoutY); parent = parent.Parent; } // This is a hack that accomodates for the fact that borders are // wrapped around the canvases that contain the UI elements. It is // likely to prove brittle over time, and we should consider either // alternate ways of drawing borders, or different mechanisms to // set absolute positions of elements. var borderParent = node.Parent; if (borderParent != null && borderParent.IsLayoutOnly) { borderParent = node.NativeParent; } if (borderParent != null) { //x -= borderParent.GetLeftBorderWidth(); //y -= borderParent.GetTopBorderWidth(); } ApplyLayoutRecursive(node, x, y); }
/// <summary> /// Add a root shadow node. /// </summary> /// <param name="node">The node.</param> public void AddRootNode(ReactShadowNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } var tag = node.ReactTag; _tagsToCssNodes[tag] = node; _rootTags[tag] = true; }
private ReactShadowNode CreateRootShadowNode() { var rootCssNode = new ReactShadowNode(); if (I18NUtil.IsRightToLeft) { rootCssNode.LayoutDirection = YogaDirection.RTL; } rootCssNode.ViewClass = "Root"; return(rootCssNode); }
public void UpdateShadowNodeProp(ReactShadowNode shadowNode, JObject props) { if (shadowNode == null) { throw new ArgumentNullException(nameof(shadowNode)); } if (props == null) { throw new ArgumentNullException(nameof(props)); } Invoke(GetShadowNodeArgs(shadowNode, props)); }