Skip to content

Commit

Permalink
Adds proper layout measurement for the ReactTextViewManager.
Browse files Browse the repository at this point in the history
In order to leverage the CSSNode class to its fullest, various view managers need to override the measurement algorithm to determine the proper width and height needed for a view.

For now, we needed to move the measurement of FrameworkElements to the dispatcher thread, as WinRT does not have any text formatter or other hooks for determining the size of text without mimicking the element itself.  Issue #106 covers this.
  • Loading branch information
rozele committed May 25, 2016
1 parent a403032 commit 27b6f06
Show file tree
Hide file tree
Showing 17 changed files with 279 additions and 154 deletions.
8 changes: 4 additions & 4 deletions ReactNative/ReactNative.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,13 @@
<Compile Include="UIManager\UIViewOperationQueue.cs" />
<Compile Include="UIManager\ViewAtIndex.cs" />
<Compile Include="UIManager\ViewGroupManager.cs" />
<Compile Include="UIManager\ViewGroupManager.Generic.cs" />
<Compile Include="UIManager\PanelViewGroupManager.cs" />
<Compile Include="UIManager\ViewManager.cs" />
<Compile Include="UIManager\ViewManager.Generic.cs" />
<Compile Include="UIManager\ViewManagerRegistry.cs" />
<Compile Include="Views\Scroll\ReactScrollViewManager.cs" />
<Compile Include="Views\ViewManagersPropertyCache.cs" />
<Compile Include="UIManager\ViewProperties.cs" />
<Compile Include="Views\Text\InlineManager.cs" />
<Compile Include="Views\Text\ReactRawTextManager.cs" />
<Compile Include="Views\Text\ReactTextShadowNode.cs" />
<Compile Include="Views\Text\ReactTextViewManager.cs" />
Expand All @@ -262,8 +262,8 @@
<Compile Include="Views\View\ReactPanel.cs" />
<Compile Include="Views\View\ReactViewManager.cs" />
<Compile Include="Views\TextInput\ReactTextInputManager.cs" />
<Compile Include="Modules\Toast\ToastHelper.cs" />
<Compile Include="Modules\Toast\ToastModule.cs" />
<Compile Include="Modules\Toast\ToastHelper.cs" />
<Compile Include="Modules\Toast\ToastModule.cs" />
<EmbeddedResource Include="Properties\ReactNative.rd.xml" />
</ItemGroup>
<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion ReactNative/Shell/MainReactPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using ReactNative.Modules.Core;
using ReactNative.Modules.WebSocket;
using ReactNative.UIManager;
using ReactNative.Views.Scroll;
using ReactNative.Views.Text;
using ReactNative.Views.TextInput;
using ReactNative.Views.View;
Expand Down Expand Up @@ -39,7 +40,7 @@ public IReadOnlyList<ViewManager> CreateViewManagers(
//new ReactProgressBarViewManager(),
new ReactRawTextManager(),
//new RecyclerViewBackedScrollViewManager(),
//new ReactScrollViewManager(),
new ReactScrollViewManager(),
//new ReactSwitchManager(),
new ReactTextInputManager(),
new ReactTextViewManager(),
Expand Down
29 changes: 26 additions & 3 deletions ReactNative/UIManager/NativeViewHierarchyManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,23 @@ public void UpdateLayout(int parentTag, int tag, int x, int y, int width, int he
.With("tag", tag))
{
var viewToUpdate = ResolveView(tag);
// TODO: call viewToUpdate.Measure()
//throw new NotImplementedException();

var parentViewManager = default(ViewManager);
var parentViewGroupManager = default(ViewGroupManager);
if (!_tagsToViewManagers.TryGetValue(parentTag, out parentViewManager) ||
(parentViewGroupManager = parentViewManager as ViewGroupManager) == null)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"Trying to use view with tag '{0}' as a parent, but its manager doesn't extend ViewGroupManager.",
tag));
}

if (!parentViewGroupManager.NeedsCustomLayoutForChildren)
{
UpdateLayout(viewToUpdate, x, y, width, height);
}
}
}

Expand Down Expand Up @@ -189,7 +204,7 @@ public void ManageChildren(int tag, int[] indicesToRemove, ViewAtIndex[] viewsTo
}

var viewGroupManager = (ViewGroupManager)viewManager;
var viewToManage = (Panel)_tagsToViews[tag];
var viewToManage = _tagsToViews[tag];

var lastIndexToRemove = viewGroupManager.GetChildCount(viewToManage);
if (indicesToRemove != null)
Expand Down Expand Up @@ -513,5 +528,13 @@ private void DropView(FrameworkElement view)
_tagsToViews.Remove(tag);
_tagsToViewManagers.Remove(tag);
}

private void UpdateLayout(FrameworkElement viewToUpdate, int x, int y, int width, int height)
{
viewToUpdate.Width = width;
viewToUpdate.Height = height;
Canvas.SetLeft(viewToUpdate, x);
Canvas.SetTop(viewToUpdate, y);
}
}
}
4 changes: 2 additions & 2 deletions ReactNative/UIManager/NativeViewHierarchyOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public void HandleCreateView(
_uiViewOperationQueue.EnqueueCreateView(
themedContext,
node.ReactTag,
node.ViewClassName,
node.ViewClass,
initialProperties);
#else
var isLayoutOnly = node.ViewClass == ViewProperties.ViewClassName
Expand Down Expand Up @@ -376,7 +376,7 @@ private void ApplyLayoutRecursive(ReactShadowNode node, int x, int y)
if (!node.IsLayoutOnly && node.NativeParent != null)
{
_uiViewOperationQueue.EnqueueUpdateLayout(
node.Parent.ReactTag,
node.NativeParent.ReactTag,
node.ReactTag,
x,
y,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace ReactNative.UIManager
/// extending <see cref="Panel"/>.
/// </summary>
/// <typeparam name="TPanel">Type of panel.</typeparam>
public abstract class ViewGroupManager<TPanel> : ViewGroupManager
public abstract class PanelViewGroupManager<TPanel> : ViewGroupManager
where TPanel : Panel
{
/// <summary>
Expand Down Expand Up @@ -53,6 +53,57 @@ public sealed override void UpdateExtraData(FrameworkElement root, object extraD
UpdateExtraData((TPanel)root, extraData);
}

/// <summary>
/// Gets the number of children in the view group.
/// </summary>
/// <param name="parent">The view group.</param>
/// <returns>The number of children.</returns>
public sealed override int GetChildCount(FrameworkElement parent)
{
return GetChildCount((TPanel)parent);
}

/// <summary>
/// Gets the child at the given index.
/// </summary>
/// <param name="parent">The parent view.</param>
/// <param name="index">The index.</param>
/// <returns>The child view.</returns>
public sealed override FrameworkElement GetChildAt(FrameworkElement parent, int index)
{
return GetChildAt((TPanel)parent, index);
}

/// <summary>
/// Adds a child at the given index.
/// </summary>
/// <param name="parent">The parent view.</param>
/// <param name="child">The child view.</param>
/// <param name="index">The index.</param>
public sealed override void AddView(FrameworkElement parent, FrameworkElement child, int index)
{
AddView((TPanel)parent, child, index);
}

/// <summary>
/// Removes the child at the given index.
/// </summary>
/// <param name="parent">The view group.</param>
/// <param name="index">The index.</param>
public override void RemoveChildAt(FrameworkElement parent, int index)
{
RemoveChildAt((TPanel)parent, index);
}

/// <summary>
/// Removes all children from the view group.
/// </summary>
/// <param name="parent">The view group.</param>
public override void RemoveAllChildren(FrameworkElement parent)
{
RemoveAllChildren((TPanel)parent);
}

/// <summary>
/// Subclasses can override this method to install custom event
/// emitters on the given view.
Expand Down Expand Up @@ -172,5 +223,56 @@ protected virtual void ReceiveCommand(TPanel view, int commandId, JArray args)
protected virtual void UpdateExtraData(TPanel root, object extraData)
{
}

/// <summary>
/// Gets the number of children in the view group.
/// </summary>
/// <param name="parent">The view group.</param>
/// <returns>The number of children.</returns>
protected virtual int GetChildCount(TPanel parent)
{
return parent.Children.Count;
}

/// <summary>
/// Gets the child at the given index.
/// </summary>
/// <param name="parent">The parent view.</param>
/// <param name="index">The index.</param>
/// <returns>The child view.</returns>
protected virtual FrameworkElement GetChildAt(TPanel parent, int index)
{
return (FrameworkElement)parent.Children[index];
}

/// <summary>
/// Adds a child at the given index.
/// </summary>
/// <param name="parent">The parent view.</param>
/// <param name="child">The child view.</param>
/// <param name="index">The index.</param>
protected virtual void AddView(TPanel parent, FrameworkElement child, int index)
{
parent.Children.Insert(index, child);
}

/// <summary>
/// Removes the child at the given index.
/// </summary>
/// <param name="parent">The view group.</param>
/// <param name="index">The index.</param>
protected virtual void RemoveChildAt(TPanel parent, int index)
{
parent.Children.RemoveAt(index);
}

/// <summary>
/// Removes all children from the view group.
/// </summary>
/// <param name="parent">The view group.</param>
protected virtual void RemoveAllChildren(TPanel parent)
{
parent.Children.Clear();
}
}
}
8 changes: 3 additions & 5 deletions ReactNative/UIManager/RootViewManager.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using Windows.UI.Xaml;

namespace ReactNative.UIManager
namespace ReactNative.UIManager
{
/// <summary>
/// View manager for react root view components.
/// </summary>
public class RootViewManager : ViewGroupManager
public class RootViewManager : PanelViewGroupManager<SizeMonitoringPanel>
{
/// <summary>
/// The name of the react root view.
Expand All @@ -23,7 +21,7 @@ public override string Name
/// </summary>
/// <param name="reactContext">The react context.</param>
/// <returns>The view instance.</returns>
protected override FrameworkElement CreateViewInstance(ThemedReactContext reactContext)
protected override SizeMonitoringPanel CreateViewInstanceCore(ThemedReactContext reactContext)
{
return new SizeMonitoringPanel();
}
Expand Down
4 changes: 3 additions & 1 deletion ReactNative/UIManager/UIImplementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ public void MeasureLayoutRelativeToParent(int tag, ICallback errorCallback, ICal
/// <param name="batchId">The batch identifier.</param>
public void DispatchViewUpdates(EventDispatcher eventDispatcher, int batchId)
{
DispatcherHelpers.AssertOnDispatcher();

foreach (var tag in _shadowNodeRegistry.RootNodeTags)
{
var cssRoot = _shadowNodeRegistry.GetNode(tag);
Expand All @@ -472,7 +474,7 @@ public void DispatchViewUpdates(EventDispatcher eventDispatcher, int batchId)
}

_nativeViewHierarchyOptimizer.OnBatchComplete();
_operationsQueue.DispatchViewUpdates(batchId);
_operationsQueue.ExecuteOperations(batchId);
}

/// <summary>
Expand Down
15 changes: 9 additions & 6 deletions ReactNative/UIManager/UIManagerModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,13 +387,16 @@ public void OnBatchComplete()
{
var batchId = _batchId++;

using (Tracer.Trace(Tracer.TRACE_TAG_REACT_BRIDGE, "onBatchCompleteUI")
.With("BatchId", batchId))
Context.RunOnDispatcherQueueThread(() =>
{
_uiImplementation.DispatchViewUpdates(_eventDispatcher, batchId);
}

// TODO: coordinate with UI operations?
using (Tracer.Trace(Tracer.TRACE_TAG_REACT_BRIDGE, "onBatchCompleteUI")
.With("BatchId", batchId))
{
_uiImplementation.DispatchViewUpdates(_eventDispatcher, batchId);
}
});

// TODO: coordinate with UI operations a la choreographer?
_eventDispatcher.OnBatchComplete();
}

Expand Down
19 changes: 9 additions & 10 deletions ReactNative/UIManager/UIViewOperationQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,10 @@ public void EnqueueFindTargetForTouch(
/// Dispatches the view updates.
/// </summary>
/// <param name="batchId">The batch identifier.</param>
internal void DispatchViewUpdates(int batchId)
internal void ExecuteOperations(int batchId)
{
DispatcherHelpers.AssertOnDispatcher();

var operations = default(IList<Action>);
lock (_operationsLock)
{
Expand All @@ -324,20 +326,17 @@ internal void DispatchViewUpdates(int batchId)
}
}

_reactContext.RunOnDispatcherQueueThread(() =>
using (Tracer.Trace(Tracer.TRACE_TAG_REACT_BRIDGE, "DispatchUI")
.With("BatchId", batchId))
{
using (Tracer.Trace(Tracer.TRACE_TAG_REACT_BRIDGE, "DispatchUI")
.With("BatchId", batchId))
if (operations != null)
{
if (operations != null)
foreach (var operation in operations)
{
foreach (var operation in operations)
{
operation();
}
operation();
}
}
});
}
}

private void EnqueueOperation(Action action)
Expand Down
Loading

0 comments on commit 27b6f06

Please sign in to comment.