/// <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
        }
Пример #2
0
        protected sealed override Inline Make(ReactShadowNode node, IList <Inline> children)
        {
            var textNode = node as ReactInlineShadowNode;

            if (textNode != null)
            {
                return(textNode.MakeInline(children));
            }

            if (node.StyleWidth.Unit != YogaUnit.Point || node.StyleHeight.Unit != YogaUnit.Point)
            {
                throw new InvalidOperationException("Inline views embedded in text must have absolute dimensions.");
            }

            var rectangle = new Rectangle
            {
                Width  = node.StyleWidth.Value,
                Height = node.StyleHeight.Value,
            };

            return(new InlineUIContainer
            {
                Child = rectangle,
            });
        }
        public async Task ShadowRegistryNode_AddRootNode_WhileEnumerating()
        {
            var n1 = new ReactShadowNode {
                ReactTag = 1
            };
            var n2 = new ReactShadowNode {
                ReactTag = 2
            };

            var enter = new AutoResetEvent(false);
            var exit  = new AutoResetEvent(false);

            var registry = new ShadowNodeRegistry();

            var task = Task.Run(() =>
            {
                enter.WaitOne();
                registry.AddRootNode(n2);
                exit.Set();
            });

            registry.AddRootNode(n1);

            foreach (var tag in registry.RootNodeTags)
            {
                enter.Set();
                exit.WaitOne();
            }

            await task;

            Assert.AreEqual(2, registry.RootNodeTags.Count);
        }
Пример #4
0
        private static void buildSpannedFromTextCSSNode(ReactTextShadowNode textShadowNode, ref string sb)
        {
            if (textShadowNode._textProperty != null)
            {
                sb += textShadowNode._textProperty;
            }
            if (textShadowNode._text != null)
            {
                sb += textShadowNode._text;
            }

            for (int i = 0, length = textShadowNode.TotalNativeChildren; i < length; i++)
            {
                ReactShadowNode child = textShadowNode.GetChildAt(i);
                if (child is ReactTextShadowNode)
                {
                    buildSpannedFromTextCSSNode((ReactTextShadowNode)child, ref sb);
                }
                child.MarkUpdateSeen();
            }
            if (textShadowNode._textProperty != null)
            {
                sb += _textPropertyTail;
            }
        }
        /// <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;
        }
        /// <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;
        }
Пример #7
0
        protected override Inline VisitCore(ReactShadowNode node)
        {
            var textNode = node as ReactInlineShadowNode;

            if (textNode != null)
            {
                return(base.VisitCore(node));
            }

            return(Make(node, new List <Inline>()));
        }
Пример #8
0
        /// <summary>
        /// Insert a child at the given index.
        /// </summary>
        /// <param name="child">The child.</param>
        /// <param name="index">The index.</param>
        public override void AddChildAt(ReactShadowNode child, int index)
        {
            base.AddChildAt(child, index);

            // Fixes a issue on Windows phone where rotating from horizontal to
            // vertical would leave a gap between the modal bottom and the screen edge.
            // This is set to the proper screen height when loading the modal in
            // ReactModalHostView.SetContentSize. The actual screen height cannot be
            // set here due to it being run by the layout manager thread which has no
            // active window and only mimics the main view bounds.
            child.StyleHeight = int.MaxValue;
        }
        protected sealed override Inline Make(ReactShadowNode node, IList <Inline> children)
        {
            var textNode = node as ReactInlineShadowNode;

            if (textNode != null)
            {
                return(textNode.MakeInline(children));
            }
            else
            {
                var rectangle = new Rectangle();
                rectangle.Width  = node.StyleWidth;
                rectangle.Height = node.StyleHeight;
                return(new InlineUIContainer
                {
                    Child = rectangle,
                });
            }
        }
        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 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 RemoveShadowNode(ReactShadowNode nodeToRemove)
        {
            _nativeViewHierarchyOptimizer.HandleRemoveNode(nodeToRemove);
            _shadowNodeRegistry.RemoveNode(nodeToRemove.ReactTag);
            for (var i = nodeToRemove.ChildCount - 1; i >= 0; --i)
            {
                RemoveShadowNode(nodeToRemove.GetChildAt(i));
            }

            nodeToRemove.RemoveAllChildren();
        }
        private void RemoveNodeFromParent(ReactShadowNode nodeToRemove, bool shouldDelete)
        {
            var nativeNodeToRemoveFrom = nodeToRemove.NativeParent;

            if (nativeNodeToRemoveFrom != null)
            {
                var index = nativeNodeToRemoveFrom.GetIndexOfNativeChild(nodeToRemove);
                nativeNodeToRemoveFrom.RemoveNativeChildAt(index);
                _uiViewOperationQueue.EnqueueManageChildren(
                    nativeNodeToRemoveFrom.ReactTag,
                    new int[] { index },
                    null,
                    shouldDelete ? new int[] { nodeToRemove.ReactTag } : null);
            }
            else
            {
                for (var i = nodeToRemove.ChildCount - 1; i >= 0; --i)
                {
                    RemoveNodeFromParent(nodeToRemove.GetChildAt(i), shouldDelete);
                }
            }
        }
        /// <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 !ENABLED
            _uiViewOperationQueue.EnqueueManageChildren(
                nodeToManage.ReactTag,
                indicesToRemove,
                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>
        /// Handles a setChildren call. This is a simplification of <see cref="HandleManageChildren(ReactShadowNode, int[], int[], ViewAtIndex[], int[])"/>
        /// that only adds children in index order of the <paramref name="childrenTags"/>
        /// array.
        /// </summary>
        /// <param name="nodeToManage">The node to manage.</param>
        /// <param name="childrenTags">The children tags.</param>
        public void HandleSetChildren(ReactShadowNode nodeToManage, int[] childrenTags)
        {
#if !ENABLED
            _uiViewOperationQueue.EnqueueSetChildren(
                nodeToManage.ReactTag,
                childrenTags);
#else
            for (var i = 0; i < childrenTags.Length; ++i)
            {
                var nodeToAdd = _shadowNodeRegistry.GetNode(childrenTags[i]);
                AddNodeToNode(nodeToManage, nodeToAdd, i);
            }
#endif
        }
        private void ApplyUpdatesRecursive(
            ReactShadowNode cssNode,
            EventDispatcher eventDispatcher)
        {
            if (!cssNode.HasUpdates)
            {
                return;
            }

            if (!cssNode.IsVirtualAnchor)
            {
                for (var i = 0; i < cssNode.ChildCount; ++i)
                {
                    ApplyUpdatesRecursive(
                        cssNode.GetChildAt(i),
                        eventDispatcher);
                }
            }

            var tag = cssNode.ReactTag;
            if (!_shadowNodeRegistry.IsRootNode(tag))
            {
                cssNode.DispatchUpdates(
                    _operationsQueue,
                    _nativeViewHierarchyOptimizer);

                if (cssNode.ShouldNotifyOnLayout)
                {
                    eventDispatcher.DispatchEvent(
                        OnLayoutEvent.Obtain(
                            tag,
                            cssNode.LayoutX,
                            cssNode.LayoutY,
                            cssNode.LayoutWidth,
                            cssNode.LayoutHeight));
                }
            }

            cssNode.MarkUpdateSeen();
        }
 /// <summary>
 /// Handles native children cleanup when the shadow node is removed
 /// from the hierarchy.
 /// </summary>
 /// <param name="node">The node to cleanup.</param>
 public void HandleRemoveNode(ReactShadowNode node)
 {
     node.RemoveAllNativeChildren();
 }
        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);

            // 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.
            ApplyLayoutBase(node);
            for (var i = 0; i < node.ChildCount; ++i)
            {
                ApplyLayoutBase(node.GetChildAt(i));
            }

            _tagsWithLayoutVisited.Clear();
        }
        private void ApplyLayoutRecursive(ReactShadowNode node, double x, double y)
        {
            if (!node.IsLayoutOnly && node.NativeParent != null)
            {
                _uiViewOperationQueue.EnqueueUpdateLayout(
                    node.NativeParent.ReactTag,
                    node.ReactTag,
                    new Dimensions
                    {
                        X = x,
                        Y = y,
                        Width = node.LayoutWidth,
                        Height = node.LayoutHeight,
                    });

                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 = (double)child.LayoutX;
                var childY = (double)child.LayoutY;

                childX += x;
                childY += y;

                ApplyLayoutRecursive(child, childX, childY);
            }
        }
        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;

            while (parent != null && parent.IsLayoutOnly)
            {
                x += parent.LayoutX;
                y += 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);
        }
 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);
 }
 private void AddLayoutOnlyNodeToNonLayoutOnlyNode(ReactShadowNode parent, ReactShadowNode child, int index)
 {
     var currentIndex = index;
     for (var i = 0; i < child.ChildCount; ++i)
     {
         var childToAdd = child.GetChildAt(i);
         if (childToAdd.IsLayoutOnly)
         {
             var childCountBefore = parent.NativeChildCount;
             AddLayoutOnlyNodeToNonLayoutOnlyNode(
                 parent,
                 childToAdd,
                 currentIndex);
             var childCountAfter = parent.NativeChildCount;
             currentIndex += childCountAfter - childCountBefore;
         }
         else
         {
             AddNonLayoutOnlyNodeToNonLayoutOnlyNode(parent, childToAdd, currentIndex++);
         }
     }
 }
        private void AddNonLayoutOnlyNodeToLayoutOnlyNode(ReactShadowNode parent, ReactShadowNode child, int index)
        {
            var parentParent = parent.Parent;

            // If the parent hasn't been attached to its parent yet, don't
            // issue commands to the native hierarchy. This will occur when the
            // parent node actually gets attached somewhere.
            if (parentParent == null)
            {
                return;
            }

            var transformedIndex = index + parentParent.GetNativeOffsetForChild(parent);
            if (parentParent.IsLayoutOnly)
            {
                AddNonLayoutOnlyNodeToLayoutOnlyNode(parentParent, child, transformedIndex);
            }
            else
            {
                AddNonLayoutOnlyNodeToNonLayoutOnlyNode(parentParent, child, transformedIndex);
            }
        }
        private void NotifyBeforeOnLayoutRecursive(ReactShadowNode cssNode)
        {
            if (!cssNode.HasUpdates)
            {
                return;
            }

            for (var i = 0; i < cssNode.ChildCount; ++i)
            {
                NotifyBeforeOnLayoutRecursive(cssNode.GetChildAt(i));
            }

            cssNode.OnBeforeLayout();
        }
 private void CalculateRootLayout(ReactShadowNode cssRoot)
 {
     using (Tracer.Trace(Tracer.TRACE_TAG_REACT_BRIDGE, "ReactShadowNode.CalculateLayout")
         .With("RootTag", cssRoot.ReactTag)
         .Start())
     {
         cssRoot.CalculateLayout();
     }
 }
        /// <summary>
        /// Handles a call to <see cref="UIManagerModule.updateView(int, string, Newtonsoft.Json.Linq.JObject)"/>.
        /// If a view transitions from being layout-only to not (or vice versa)
        /// this could result in some number of additional create view or
        /// manage children calls. If the view is layout only, no update view
        /// call will be dispatched to the native hierarchy.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="className">The class name.</param>
        /// <param name="props">The properties.</param>
        public void HandleUpdateView(ReactShadowNode node, string className, ReactStylesDiffMap props)
        {
#if !ENABLED
            _uiViewOperationQueue.EnqueueUpdateProperties(node.ReactTag, className, props);
#else
            var needsToLeaveLayoutOnly = node.IsLayoutOnly && !IsLayoutOnlyAndCollapsible(props);
            if (needsToLeaveLayoutOnly)
            {
                TransitionLayoutOnlyViewToNativeView(node, props);
            }
            else if (!node.IsLayoutOnly)
            {
                _uiViewOperationQueue.EnqueueUpdateProperties(node.ReactTag, className, props);
            }
#endif
        }
        /// <summary>
        /// Handles an update layout call. All update layout calls are 
        /// collected and dispatched at the end of a batch because update
        /// layout calls to layout-only nodes can necessitate multiple update
        /// layout calls for all its children.
        /// </summary>
        /// <param name="node">The node.</param>
        public void HandleUpdateLayout(ReactShadowNode node)
        {
#if !ENABLED
            _uiViewOperationQueue.EnqueueUpdateLayout(
                node.Parent.ReactTag,
                node.ReactTag,
                new Dimensions
                {
                    X = node.LayoutX,
                    Y = node.LayoutY,
                    Width = node.LayoutWidth,
                    Height = node.LayoutHeight
                });
#else
            ApplyLayoutBase(node);
#endif
        }
 private void HandleCreateView(ReactShadowNode cssNode, int rootViewTag, ReactStylesDiffMap styles)
 {
     if (!cssNode.IsVirtual)
     {
         _nativeViewHierarchyOptimizer.HandleCreateView(cssNode, cssNode.ThemedContext, styles);
     }
 }
Пример #29
0
 public static Inline Apply(ReactShadowNode node)
 {
     return(s_instance.Visit(node));
 }
 private void HandleUpdateView(
     ReactShadowNode cssNode,
     string className,
     ReactStylesDiffMap styles)
 {
     if (!cssNode.IsVirtual)
     {
         _nativeViewHierarchyOptimizer.HandleUpdateView(cssNode, className, styles);
     }
 }
        private ReactShadowNode CreateRootShadowNode()
        {
            var rootCssNode = new ReactShadowNode();
            if (I18NUtil.IsRightToLeft)
            {
                rootCssNode.Direction = CSSDirection.RTL;
            }

            rootCssNode.ViewClass = "Root";
            return rootCssNode;
        }
        private void AddNodeToNode(ReactShadowNode parent, ReactShadowNode child, int index)
        {
            var indexInNativeChildren = parent.GetNativeOffsetForChild(parent.GetChildAt(index));

            if (!parent.IsLayoutOnly && !child.IsLayoutOnly)
            {
                AddNonLayoutOnlyNodeToNonLayoutOnlyNode(parent, child, indexInNativeChildren);
            }
            else if (!child.IsLayoutOnly)
            {
                AddNonLayoutOnlyNodeToLayoutOnlyNode(parent, child, indexInNativeChildren);
            }
            else if (!parent.IsLayoutOnly)
            {
                AddLayoutOnlyNodeToNonLayoutOnlyNode(parent, child, indexInNativeChildren);
            }
            else
            {
                AddLayoutOnlyNodeToLayoutOnlyNode(parent, child, indexInNativeChildren);
            }
        }