Exemple #1
0
        /// <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));
            }
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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));
        }
Exemple #5
0
        /// <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
        }
Exemple #8
0
        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);
            }
        }
Exemple #14
0
        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++;
            }
        }
Exemple #15
0
        public static float GetPaddingSpace(this ReactShadowNode node, int spacingType)
        {
            var padding = node.GetPaddingValue(spacingType);

            return(YogaConstants.IsUndefined(padding)
                ? 0.0f
                : padding);
        }
Exemple #16
0
            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();
     }
 }
Exemple #18
0
            protected override object[] GetShadowNodeArgs(ReactShadowNode shadowNode, JObject props)
            {
                var args = s_args.Value;

                args[0] = shadowNode;
                args[1] = ExtractProp(props);
                return(args);
            }
Exemple #19
0
        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));
        }
Exemple #21
0
 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);
 }
Exemple #22
0
        /// <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);
     }
 }
Exemple #25
0
        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);
        }
Exemple #28
0
        /// <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);
        }
Exemple #30
0
        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));
        }