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(); }
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); }
/// <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); }
/// <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); } }
/// <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); }
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); }
/// <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 }
/// <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; }