예제 #1
0
        private static MeasureOutput MeasureTextInput(CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode)
        {
            var textInputNode = (ReactPasswordBoxShadowNode)node;

            textInputNode._computedPadding = textInputNode.GetComputedPadding();

            var borderLeftWidth  = textInputNode.GetBorder(CSSSpacingType.Left);
            var borderRightWidth = textInputNode.GetBorder(CSSSpacingType.Right);

            var normalizedWidth = Math.Max(0,
                                           (CSSConstants.IsUndefined(width) ? double.PositiveInfinity : width)
                                           - textInputNode._computedPadding[0]
                                           - textInputNode._computedPadding[2]
                                           - (CSSConstants.IsUndefined(borderLeftWidth) ? 0 : borderLeftWidth)
                                           - (CSSConstants.IsUndefined(borderRightWidth) ? 0 : borderRightWidth));
            var normalizedHeight = Math.Max(0, CSSConstants.IsUndefined(height) ? double.PositiveInfinity : height);

            // This is not a terribly efficient way of projecting the height of
            // the text elements. It requires that we have access to the
            // dispatcher in order to do measurement, which, for obvious
            // reasons, can cause perceived performance issues as it will block
            // the UI thread from handling other work.
            //
            // TODO: determine another way to measure text elements.
            var task = DispatcherHelpers.CallOnDispatcher(() =>
            {
                var textNode = (ReactPasswordBoxShadowNode)node;

                var textBlock = new TextBlock
                {
                    TextWrapping = TextWrapping.Wrap,
                };

                var passwordChar   = GetDefaultPasswordChar();
                var normalizedText = !string.IsNullOrEmpty(textNode._text)
                    ? new string(passwordChar[0], textNode._text.Length)
                    : passwordChar;
                var inline = new Run {
                    Text = normalizedText
                };
                FormatTextElement(textNode, inline);
                textBlock.Inlines.Add(inline);

                textBlock.Measure(new Size(normalizedWidth, normalizedHeight));

                var borderTopWidth    = textInputNode.GetBorder(CSSSpacingType.Top);
                var borderBottomWidth = textInputNode.GetBorder(CSSSpacingType.Bottom);

                var finalizedHeight = (float)textBlock.DesiredSize.Height;
                finalizedHeight    += textInputNode._computedPadding[1];
                finalizedHeight    += textInputNode._computedPadding[3];
                finalizedHeight    += CSSConstants.IsUndefined(borderTopWidth) ? 0 : borderTopWidth;
                finalizedHeight    += CSSConstants.IsUndefined(borderBottomWidth) ? 0 : borderBottomWidth;

                return(new MeasureOutput(width, finalizedHeight));
            });

            return(task.Result);
        }
        private static YogaSize MeasureTextInput(ReactTextInputShadowNode textInputNode, YogaNode node, float width, YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode)
        {
            textInputNode._computedPadding = textInputNode.GetComputedPadding();

            var borderLeftWidth  = textInputNode.GetBorder(YogaEdge.Left);
            var borderRightWidth = textInputNode.GetBorder(YogaEdge.Right);

            var normalizedWidth = Math.Max(0,
                                           (YogaConstants.IsUndefined(width) ? double.PositiveInfinity : width)
                                           - textInputNode._computedPadding[0]
                                           - textInputNode._computedPadding[2]
                                           - (YogaConstants.IsUndefined(borderLeftWidth) ? 0 : borderLeftWidth)
                                           - (YogaConstants.IsUndefined(borderRightWidth) ? 0 : borderRightWidth));
            var normalizedHeight = Math.Max(0, YogaConstants.IsUndefined(height) ? double.PositiveInfinity : height);

            // This is not a terribly efficient way of projecting the height of
            // the text elements. It requires that we have access to the
            // dispatcher in order to do measurement, which, for obvious
            // reasons, can cause perceived performance issues as it will block
            // the UI thread from handling other work.
            //
            // TODO: determine another way to measure text elements.
            var task = DispatcherHelpers.CallOnDispatcher(() =>
            {
                /*
                 * var textBlock = new TextBlock
                 * {
                 *  TextWrapping = TextWrapping.Wrap,
                 * };
                 *
                 * var normalizedText = string.IsNullOrEmpty(textNode._text) ? " " : textNode._text;
                 * var inline = new Run { Text = normalizedText };
                 * FormatInline(textNode, inline);
                 *
                 * textBlock.Inlines.Add(inline);
                 *
                 * textBlock.Measure(new Size(normalizedWidth, normalizedHeight));
                 *
                 * var borderTopWidth = textInputNode.GetBorder(CSSSpacingType.Top);
                 * var borderBottomWidth = textInputNode.GetBorder(CSSSpacingType.Bottom);
                 *
                 * var finalizedHeight = (float)textBlock.DesiredSize.Height;
                 * finalizedHeight += textInputNode._computedPadding[1];
                 * finalizedHeight += textInputNode._computedPadding[3];
                 * finalizedHeight += CSSConstants.IsUndefined(borderTopWidth) ? 0 : borderTopWidth;
                 * finalizedHeight += CSSConstants.IsUndefined(borderBottomWidth) ? 0 : borderBottomWidth;
                 *
                 * return new MeasureOutput(width, finalizedHeight);
                 */

                float finalizedHeight = 1;
                return(MeasureOutput.Make(
                           (float)Math.Ceiling(width),
                           (float)Math.Ceiling(finalizedHeight)));
            });

            return(task.Result);
        }
        private static DisplayMetrics GetDefaultDisplayMetrics()
        {
            if (CoreApplication.MainView.CoreWindow != null)
            {
                // TODO: blocking call not ideal, but should be inlined in almost all cases
                return(DispatcherHelpers.CallOnDispatcher(DisplayMetrics.GetForCurrentView, true).Result);
            }

            return(DisplayMetrics.Empty);
        }
        /// <summary>
        /// Called once per batch of updates by the <see cref="UIManagerModule"/>
        /// if the text node is dirty.
        /// </summary>
        public override void OnBeforeLayout()
        {
            if (_isVirtual)
            {
                return;
            }

            _inline = DispatcherHelpers.CallOnDispatcher(() => ReactTextShadowNodeInlineVisitor.Apply(this)).Result;
            MarkUpdated();
        }
예제 #5
0
        public async static void Create_Return()
        {
            var thrown   = 0;
            var uiThread = await DispatcherHelpers.CallOnDispatcher(() => MessageQueueThread.Create(MessageQueueThreadSpec.DispatcherThreadSpec, ex => thrown++));

            var backgroundThread = MessageQueueThread.Create(MessageQueueThreadSpec.Create("background", MessageQueueThreadKind.BackgroundSingleThread), ex => thrown++);
            var taskPoolThread   = MessageQueueThread.Create(MessageQueueThreadSpec.Create("any", MessageQueueThreadKind.BackgroundAnyThread), ex => thrown++);

            Assert.NotNull(uiThread);
            Assert.NotNull(backgroundThread);
            Assert.NotNull(taskPoolThread);
        }
예제 #6
0
        /// <summary>
        /// Registers a new root view.
        /// </summary>
        /// <param name="rootView">The root view instance.</param>
        /// <returns>The root view tag.</returns>
        /// <remarks>
        /// JavaScript can use the returned tag with to add or remove children
        /// to this view through <see cref="manageChildren(int, int[], int[], int[], int[], int[])"/>.
        /// </remarks>
        public async Task <int> AddMeasuredRootViewAsync(ReactRootView rootView)
        {
            // Called on main dispatcher thread
            DispatcherHelpers.AssertOnDispatcher();

            var tag = _nextRootTag;

            _nextRootTag += RootViewTagIncrement;

            // Set tag early in case of concurrent DetachRootViewAsync
            rootView.SetTag(tag);

            var context = new ThemedReactContext(Context);

            await DispatcherHelpers.CallOnDispatcher(rootView.Dispatcher, () =>
            {
                var width  = rootView.ActualWidth;
                var height = rootView.ActualHeight;

                _layoutActionQueue.Dispatch(() =>
                {
                    _uiImplementation.RegisterRootView(rootView, tag, width, height, context);
                });

                var resizeCount = 0;

                rootView.SetOnSizeChangedListener((sender, args) =>
                {
                    var currentCount = ++resizeCount;
                    var newWidth     = args.NewSize.Width;
                    var newHeight    = args.NewSize.Height;

                    _layoutActionQueue.Dispatch(() =>
                    {
                        if (currentCount == resizeCount)
                        {
                            _layoutActionQueue.AssertOnThread();
                            _uiImplementation.UpdateRootNodeSize(tag, newWidth, newHeight);
                        }
                    });
                });

                rootView.StartTouchHandling();

#if WINDOWS_UWP
                // Register view in DeviceInfoModule for tracking its dimensions
                Context.GetNativeModule <DeviceInfoModule>().RegisterRootView(rootView, tag);
#endif
                return(true);
            }, true); // Allow inlining

            return(tag);
        }
예제 #7
0
        /// <summary>
        /// Enqueues an operation to remove the root view.
        /// </summary>
        /// <param name="rootViewTag">The root view tag.</param>
        public Task RemoveRootViewAsync(int rootViewTag)
        {
            // Called on layout manager thread

            UIViewOperationQueueInstance queue = GetQueueByTag(rootViewTag);

            // Send forward
            queue.EnqueueRemoveRootView(rootViewTag);

            // Do some maintenance/cleanup if needed.
            // Find the queue info
            KeyValuePair <CoreDispatcher, QueueInstanceInfo> pair;

            lock (_lock)
            {
                pair = _dispatcherToOperationQueueInfo.First(p => p.Value.queueInstance == queue);
            }

            // Decrement number of root views
            pair.Value.rootViewCount--;

            if (queue != MainUIViewOperationQueue)
            {
                if (pair.Value.rootViewCount == 0)
                {
                    lock (_lock)
                    {
                        // We can remove this queue and then destroy
                        _dispatcherToOperationQueueInfo.Remove(pair.Key);
                    }

                    // Simulate an OnDestroy from the correct dispatcher thread
                    // (OnResume/OnSuspend/OnDestroy have this thread affinity, all other methods do enqueuings in a thread safe manner)
                    return(DispatcherHelpers.CallOnDispatcher(pair.Key, () =>
                    {
                        queue.OnDestroy();

                        return true;
                    }));
                }
                else
                {
                    return(Task.CompletedTask);
                }
            }
            else
            {
                return(Task.CompletedTask);
            }
        }
예제 #8
0
        /// <summary>
        /// Detaches a root view from the size monitoring hooks in preparation for the unmount
        /// </summary>
        /// <param name="rootView">The root view instance.</param>
        /// <returns>A task to await the cleanup of the root view.</returns>
        public async Task <Task> DetachRootViewAsync(ReactRootView rootView)
        {
            // Called on main dispatcher thread
            DispatcherHelpers.AssertOnDispatcher();

            await DispatcherHelpers.CallOnDispatcher(rootView.Dispatcher, () =>
            {
                rootView.RemoveSizeChanged();
                return(true);
            }, true); // allow inlining

            var rootTag = rootView.GetTag();
            var taskCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            _rootViewCleanupTasks.AddOrUpdate(rootTag, taskCompletionSource, (k, v) => throw new InvalidOperationException("Duplicate root view removal"));
            return(taskCompletionSource.Task);
        }
        private static MeasureOutput MeasureText(CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode)
        {
            // This is not a terribly efficient way of projecting the height of
            // the text elements. It requires that we have access to the
            // dispatcher in order to do measurement, which, for obvious
            // reasons, can cause perceived performance issues as it will block
            // the UI thread from handling other work.
            //
            // TODO: determine another way to measure text elements.
            var task = DispatcherHelpers.CallOnDispatcher(() =>
            {
                var textBlock = new TextBlock
                {
                    TextWrapping  = TextWrapping.Wrap,
                    TextAlignment = TextAlignment.DetectFromContent,
                    TextTrimming  = TextTrimming.CharacterEllipsis,
                };

                var textNode = (ReactTextShadowNode)node;

                textBlock.CharacterSpacing = textNode._letterSpacing;
                textBlock.LineHeight       = textNode._lineHeight;
                textBlock.MaxLines         = textNode._numberOfLines;
                textBlock.TextAlignment    = textNode._textAlignment;

                textBlock.Inlines.Add(ReactTextShadowNodeInlineVisitor.Apply(node));

                try
                {
                    var normalizedWidth  = CSSConstants.IsUndefined(width) ? double.PositiveInfinity : width;
                    var normalizedHeight = CSSConstants.IsUndefined(height) ? double.PositiveInfinity : height;
                    textBlock.Measure(new Size(normalizedWidth, normalizedHeight));
                    return(new MeasureOutput(
                               (float)Math.Ceiling(textBlock.DesiredSize.Width),
                               (float)Math.Ceiling(textBlock.DesiredSize.Height)));
                }
                finally
                {
                    textBlock.Inlines.Clear();
                }
            });

            return(task.Result);
        }
예제 #10
0
        private static YogaSize MeasureText(ReactTextShadowNode textNode, YogaNode node, float width,
                                            YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode)
        {
            Log.Info(ReactConstants.Tag, "MeasureText node=" + textNode.ReactTag);
            // This is not a terribly efficient way of projecting the height of
            // the text elements. It requires that we have access to the
            // dispatcher in order to do measurement, which, for obvious
            // reasons, can cause perceived performance issues as it will block
            // the UI thread from handling other work.
            //
            // TODO: determine another way to measure text elements.

            var task = DispatcherHelpers.CallOnDispatcher(() =>
            {
                if (textView == null)
                {
                    textView = new ReactTextView(ReactProgram.RctWindow);
                    textView.LineWrapType = WrapType.Mixed;
                }

                var normalizedWidth  = YogaConstants.IsUndefined(width) ? ReactProgram.RctWindow.ScreenSize.Width : width;
                var normalizedHeight = YogaConstants.IsUndefined(height) ? ReactProgram.RctWindow.ScreenSize.Height : height;
                textView.Resize((int)normalizedWidth, (int)normalizedHeight);

                textView.Text      = textNode._preparedSpannableText;
                var textPartObject = textView.EdjeObject["elm.text"];
                if (textPartObject == null)
                {
                    throw new Exception("Invalid ReactTextView.EdjeObject[\"elm.text\"]");
                }
                Size size = textPartObject.TextBlockFormattedSize;
                Log.Info(ReactConstants.Tag, "MeasureText result : width: " + size.Width + " height:" + size.Height);

                //textView.Unrealize();

                return(MeasureOutput.Make(
                           (float)size.Width,
                           (float)size.Height));
            });

            return(task.Result);
        }
        private YogaSize MeasureButton(ReactButtonShadowNode textNode, YogaNode node, float width,
                                       YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode)
        {
            Log.Info(ReactConstants.Tag, "[1] MeasureButton node=" + textNode.ReactTag + " with=" + width + " height=" + height + " content=" + _preparedSpannableText);
            // This is not a terribly efficient way of projecting the height of
            // the text elements. It requires that we have access to the
            // dispatcher in order to do measurement, which, for obvious
            // reasons, can cause perceived performance issues as it will block
            // the UI thread from handling other work.
            //
            // TODO: determine another way to measure text elements.

            var task = DispatcherHelpers.CallOnDispatcher(() =>
            {
                var btnView = new Button(ReactProgram.RctWindow);

                var normalizedWidth  = YogaConstants.IsUndefined(width) ? double.PositiveInfinity : width;
                var normalizedHeight = YogaConstants.IsUndefined(height) ? double.PositiveInfinity : height;

                btnView.Resize((int)normalizedWidth, (int)normalizedHeight);
                btnView.Text = _preparedSpannableText;

                var btnPartObject = btnView.EdjeObject["elm.text"];
                if (btnPartObject == null)
                {
                    throw new Exception("Invalid Button.EdjeObject[\"elm.text\"]");
                }
                Size size = btnPartObject.TextBlockFormattedSize;
                Log.Info(ReactConstants.Tag, "[2] EDC conf info ={ width: " + size.Width + " height:" + size.Height + "}");
                btnView.Unrealize();

                return(MeasureOutput.Make(
                           (float)(size.Width + 80),
                           (float)(size.Height < 80 ? 80 : size.Height)));
            });

            return(task.Result);
        }
        private static MeasureOutput MeasureText(CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode)
        {
            // This is not a terribly efficient way of projecting the height of
            // the text elements. It requires that we have access to the
            // dispatcher in order to do measurement, which, for obvious
            // reasons, can cause perceived performance issues as it will block
            // the UI thread from handling other work.
            //
            // TODO: determine another way to measure text elements.
            var task = DispatcherHelpers.CallOnDispatcher(() =>
            {
                var textBlock = new RichTextBlock
                {
                    TextWrapping  = TextWrapping.Wrap,
                    TextAlignment = TextAlignment.DetectFromContent,
                    TextTrimming  = TextTrimming.CharacterEllipsis,
                };

                var textNode = (ReactTextShadowNode)node;
                textNode.UpdateTextBlockCore(textBlock, true);

                var block = new Paragraph();
                foreach (var child in textNode.Children)
                {
                    block.Inlines.Add(ReactInlineShadowNodeVisitor.Apply(child));
                }
                textBlock.Blocks.Add(block);

                var normalizedWidth  = CSSConstants.IsUndefined(width) ? double.PositiveInfinity : width;
                var normalizedHeight = CSSConstants.IsUndefined(height) ? double.PositiveInfinity : height;
                textBlock.Measure(new Size(normalizedWidth, normalizedHeight));
                return(new MeasureOutput(
                           (float)textBlock.DesiredSize.Width,
                           (float)textBlock.DesiredSize.Height));
            });

            return(task.Result);
        }
예제 #13
0
        /// <summary>
        /// Detach given <paramref name="rootView"/> from the current react
        /// instance. This method is idempotent and can be called multiple
        /// times on the same <see cref="ReactRootView"/> instance.
        /// WARNING! Has to be called by the thread assocviated with the view.
        /// </summary>
        /// <param name="rootView">The root view.</param>
        public async Task DetachRootViewAsync(ReactRootView rootView)
        {
            if (rootView == null)
            {
                throw new ArgumentNullException(nameof(rootView));
            }

            DispatcherHelpers.AssertOnDispatcher(rootView);

            await DispatcherHelpers.CallOnDispatcher(() =>
            {
                if (_attachedRootViews.Remove(rootView))
                {
                    var currentReactContext = _currentReactContext;
                    if (currentReactContext != null && currentReactContext.HasActiveReactInstance)
                    {
                        DetachViewFromInstance(rootView, currentReactContext.ReactInstance);
                    }
                }

                return(true);
            }, true); // inlining allowed
        }
예제 #14
0
        /// <summary>
        /// Detaches a root view from the size monitoring hooks in preparation for the unmount
        /// </summary>
        /// <param name="rootView">The root view instance.</param>
        /// <returns>A task to await the cleanup of the root view.</returns>
        public async Task <Task> DetachRootViewAsync(ReactRootView rootView)
        {
            // Called on main dispatcher thread
            DispatcherHelpers.AssertOnDispatcher();

            await DispatcherHelpers.CallOnDispatcher(rootView.Dispatcher, () =>
            {
                rootView.RemoveSizeChanged();

                rootView.StopTouchHandling();
#if WINDOWS_UWP
                // Unregister view from DeviceInfoModule
                Context.GetNativeModule <DeviceInfoModule>().UnregisterRootView(rootView);
#endif
                return(true);
            }, true); // allow inlining

            var rootTag = rootView.GetTag();
            var taskCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            _rootViewCleanupTasks.AddOrUpdate(rootTag, taskCompletionSource, (k, v) => throw new InvalidOperationException("Duplicate root view removal"));
            return(taskCompletionSource.Task);
        }
        private static YogaSize MeasureText(ReactTextShadowNode textNode, YogaNode node, float width, YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode)
        {
            // This is not a terribly efficient way of projecting the height of
            // the text elements. It requires that we have access to the
            // dispatcher in order to do measurement, which, for obvious
            // reasons, can cause perceived performance issues as it will block
            // the UI thread from handling other work.
            //
            // TODO: determine another way to measure text elements.
            var task = DispatcherHelpers.CallOnDispatcher(() =>
            {
                var textBlock = new TextBlock
                {
                    TextAlignment = TextAlignment.Left,
                    TextWrapping  = TextWrapping.Wrap,
                    TextTrimming  = TextTrimming.CharacterEllipsis,
                };

                textNode.UpdateTextBlockCore(textBlock, true);

                for (var i = 0; i < textNode.ChildCount; ++i)
                {
                    var child = textNode.GetChildAt(i);
                    textBlock.Inlines.Add(ReactInlineShadowNodeVisitor.Apply(child));
                }

                var normalizedWidth  = YogaConstants.IsUndefined(width) ? double.PositiveInfinity : width;
                var normalizedHeight = YogaConstants.IsUndefined(height) ? double.PositiveInfinity : height;
                textBlock.Measure(new Size(normalizedWidth, normalizedHeight));
                return(MeasureOutput.Make(
                           (float)Math.Ceiling(textBlock.DesiredSize.Width),
                           (float)Math.Ceiling(textBlock.DesiredSize.Height)));
            });

            return(task.Result);
        }
        /// <summary>
        /// Detach given <paramref name="rootView"/> from the current react
        /// instance. This method is idempotent and can be called multiple
        /// times on the same <see cref="ReactRootView"/> instance.
        /// WARNING! Has to be called by the thread assocviated with the view.
        /// </summary>
        /// <param name="rootView">The root view.</param>
        public async Task DetachRootViewAsync(ReactRootView rootView)
        {
            if (rootView == null)
            {
                throw new ArgumentNullException(nameof(rootView));
            }

            DispatcherHelpers.AssertOnDispatcher(rootView);

            await DispatcherHelpers.CallOnDispatcher(() =>
            {
                if (_attachedRootViews.Remove(rootView))
                {
                    var currentReactContext = _currentReactContext;
                    if (currentReactContext != null && currentReactContext.HasActiveReactInstance)
                    {
                        return(DetachViewFromInstanceAsync(rootView, currentReactContext.ReactInstance));
                    }
                }

                // return Task.CompletedTask;
                return(Net46.Net45.Task.CompletedTask);
            }, true /* inlining allowed */).Unwrap(); // await inner task
        }
 public void UpdateDisplayMetrics()
 {
     this.CurrentDisplayMetrics = DispatcherHelpers.CallOnDispatcher(this.RootView.Dispatcher, () => DisplayMetrics.GetForDeviceView(this), true).Result;
 }