/// <summary> /// Enqueues an operation to remove the root view. /// </summary> /// <param name="rootViewTag">The root view tag.</param> public void EnqueueRemoveRootView(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 var pair = _dispatcherToOperationQueueInfo.First(p => p.Value.queueInstance == queue); // Decrement number of root views pair.Value.rootViewCount--; if (queue != MainUIViewOperationQueue) { if (pair.Value.rootViewCount == 0) { // 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) DispatcherHelpers.RunOnDispatcher(pair.Key, queue.OnDestroy); } } }
/// <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> /// Enqueues an operation to measure the view relative to the window. /// </summary> /// <param name="tag">The tag of the view to measure.</param> /// <param name="callback">The measurement result callback.</param> public void EnqueueMeasureInWindow(int tag, ICallback callback) { // Called on layout manager thread UIViewOperationQueueInstance queue = GetQueueByTag(tag, true); if (queue == null) { // This is called bypassing the optimizer, so we need to fake a result for layout only nodes. callback.Invoke(); return; } queue.EnqueueMeasureInWindow(tag, callback); }
/// <summary> /// Enqueues an operation to create a view. /// </summary> /// <param name="themedContext">The React context.</param> /// <param name="viewReactTag">The view React tag.</param> /// <param name="viewClassName">The view class name.</param> /// <param name="initialProps">The initial props.</param> /// <param name="rootViewTag">Root view tag.</param> public void EnqueueCreateView( ThemedReactContext themedContext, int viewReactTag, string viewClassName, JObject initialProps, int rootViewTag) { // Called on layout manager thread UIViewOperationQueueInstance queue = GetQueueByTag(rootViewTag); _reactTagToOperationQueue.Add(viewReactTag, queue); queue.EnqueueCreateView(themedContext, viewReactTag, viewClassName, initialProps); }
/// <summary> /// Enqueues an operation to find a touch target. /// </summary> /// <param name="tag">The parent view to search from.</param> /// <param name="targetX">The x-coordinate of the touch event.</param> /// <param name="targetY">The y-coordinate of the touch event.</param> /// <param name="callback">The callback.</param> public void EnqueueFindTargetForTouch( int tag, double targetX, double targetY, ICallback callback) { // Called on layout manager thread UIViewOperationQueueInstance queue = GetQueueByTag(tag, true); if (queue == null) { // This is called bypassing the optimizer, so we need to fake a result for layout only nodes. callback.Invoke(); return; } queue.EnqueueFindTargetForTouch(tag, targetX, targetY, callback); }
/// <summary> /// Used by the native animated module to bypass the process of /// updating the values through the shadow view hierarchy. This method /// will directly update the native views, which means that updates for /// layout-related props won't be handled properly. /// </summary> /// <param name="tag">The view tag.</param> /// <param name="props">The props.</param> /// <remarks> /// Make sure you know what you're doing before calling this method :) /// </remarks> public bool SynchronouslyUpdateViewOnDispatcherThread(int tag, JObject props) { // The native animations module is a single threaded implementation affinitized to the "main" dispatcher thread. // As a result all calls of this method are on main dispatcher thread. // Yet, for secondary views we have to dispatch to correct dispatcher as fast as possible DispatcherHelpers.AssertOnDispatcher(); UIViewOperationQueueInstance queue = GetQueueByTag(tag, true); if (queue == null) { // Returns false as per the caller's expectation return(false); } if (queue == MainUIViewOperationQueue) { // Main queue case. Just forward. if (!MainUIViewOperationQueue.NativeViewHierarchyManager.ViewExists(tag)) { return(false); } MainUIViewOperationQueue.NativeViewHierarchyManager.UpdateProps(tag, props); } else { // Dispatch to the correct thread. DispatcherHelpers.RunOnDispatcher(queue.Dispatcher, CoreDispatcherPriority.High, () => { if (queue.NativeViewHierarchyManager.ViewExists(tag)) { queue.NativeViewHierarchyManager.UpdateProps(tag, props); } else { Debug.WriteLine($"View with tag {tag} not found due to race condition"); } }); } return(true); }
/// <summary> /// Instantiates the <see cref="UIViewOperationQueue"/>. /// </summary> /// <param name="reactContext">The React context.</param> /// <param name="viewManagerRegistry"> /// The view manager registry. /// </param> public UIViewOperationQueue(ReactContext reactContext, ViewManagerRegistry viewManagerRegistry) { _reactContext = reactContext; _viewManagerRegistry = viewManagerRegistry; _active = false; // Corner case: UWP scenarios that start with no main window. // We create the UIViewOperationQueueInstance for main dispatcher thread ahead of time so animations // in secondary windows can work. var queueInfo = new QueueInstanceInfo() { queueInstance = new UIViewOperationQueueInstance( _reactContext, new NativeViewHierarchyManager(_viewManagerRegistry, DispatcherHelpers.MainDispatcher, OnViewsDropped), ReactChoreographer.Instance) }; _dispatcherToOperationQueueInfo.Add(DispatcherHelpers.MainDispatcher, queueInfo); _mainUiViewOperationsQueueInstance = queueInfo.queueInstance; }