Skip to content

Commit

Permalink
Fix onPress handlers on nested Text components (#560)
Browse files Browse the repository at this point in the history
Currently, onPress handlers only work on outermost Text components.
This change enables you to place onPress handlers on nested Text
components. For example, this now works:

  <Text>
    <Text onPress={this._handlePress}>Press Me</Text>
  </Text>

In the fix, we need to manually do hit testing on outermost Text
components to see if any of the nested Text components got hit. This is
because the nested Text components are represented by Inlines rather than
by UIElements.
  • Loading branch information
rigdern authored and rozele committed Aug 7, 2016
1 parent 3de3cc3 commit a766d1a
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 1 deletion.
3 changes: 3 additions & 0 deletions ReactWindows/ReactNative/ReactNative.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,9 @@
<Compile Include="Shell\MainReactPackage.cs" />
<Compile Include="Touch\JavaScriptResponderHandler.cs" />
<Compile Include="UIManager\BaseViewManager.cs" />
<Compile Include="UIManager\ReactDefaultCompoundView.cs" />
<Compile Include="UIManager\Dimensions.cs" />
<Compile Include="UIManager\IReactCompoundView.cs" />
<Compile Include="UIManager\IViewManager.cs" />
<Compile Include="UIManager\IViewParentManager.cs" />
<Compile Include="UIManager\LayoutAnimation\BaseLayoutAnimation.cs" />
Expand Down Expand Up @@ -362,6 +364,7 @@
<Compile Include="Views\Text\ReactInlineViewManager.cs" />
<Compile Include="Views\Text\ReactSpanShadowNode.cs" />
<Compile Include="Views\Text\ReactInlineShadowNodeVisitor.cs" />
<Compile Include="Views\Text\ReactTextCompoundView.cs" />
<Compile Include="Views\ViewManagersPropertyCache.cs" />
<Compile Include="UIManager\ViewProps.cs" />
<Compile Include="Views\Text\ReactRunManager.cs" />
Expand Down
5 changes: 4 additions & 1 deletion ReactWindows/ReactNative/Touch/TouchHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@ private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
}

var reactView = GetReactViewFromView(e.OriginalSource as UIElement);
var reactTag = reactView.GetReactCompoundView().GetReactTagAtPoint(reactView,
e.GetCurrentPoint(reactView).Position);

if (reactView != null && _view.CapturePointer(e.Pointer))
{
var pointer = new ReactPointer();
pointer.Target = reactView.GetTag();
pointer.Target = reactTag;
pointer.PointerId = e.Pointer.PointerId;
pointer.Identifier = ++_pointerIDs;
pointer.ReactView = reactView;
Expand Down
43 changes: 43 additions & 0 deletions ReactWindows/ReactNative/UIManager/DependencyObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public static class DependencyObjectExtensions
{
private static readonly ConditionalWeakTable<DependencyObject, DependencyObjectData> s_properties =
new ConditionalWeakTable<DependencyObject, DependencyObjectData>();
private static readonly IReactCompoundView s_defaultCompoundView = new ReactDefaultCompoundView();

/// <summary>
/// Sets the pointer events for the view.
Expand Down Expand Up @@ -44,6 +45,46 @@ public static PointerEvents GetPointerEvents(this DependencyObject view)
return elementData.PointerEvents.Value;
}

/// <summary>
/// Associates an implementation of IReactCompoundView with the view.
/// </summary>
/// <param name="view">The view.</param>
/// <param name="compoundView">The implementation of IReactCompoundView.</param>
public static void SetReactCompoundView(this DependencyObject view, IReactCompoundView compoundView)
{
if (view == null)
throw new ArgumentNullException(nameof(view));

s_properties.GetOrCreateValue(view).CompoundView = compoundView;
}

/// <summary>
/// Gets the implementation of IReactCompoundView associated with the view.
/// </summary>
/// <param name="view">The view.</param>
/// <returns>
/// The implementation of IReactCompoundView associated with the view. Defaults to
/// an instance of ReactDefaultCompoundView when no other implementation has been
/// provided.
/// </returns>
public static IReactCompoundView GetReactCompoundView(this DependencyObject view)
{
if (view == null)
throw new ArgumentNullException(nameof(view));

var elementData = default(DependencyObjectData);
if (s_properties.TryGetValue(view, out elementData))
{
var compoundView = elementData.CompoundView;
if (compoundView != null)
{
return compoundView;
}
}

return s_defaultCompoundView;
}

internal static void SetTag(this DependencyObject view, int tag)
{
if (view == null)
Expand Down Expand Up @@ -115,6 +156,8 @@ class DependencyObjectData
public PointerEvents? PointerEvents { get; set; }

public int? Tag { get; set; }

public IReactCompoundView CompoundView { get; set; }
}
}
}
28 changes: 28 additions & 0 deletions ReactWindows/ReactNative/UIManager/IReactCompoundView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;

namespace ReactNative.UIManager
{
/// <summary>
/// Interface consisting of methods which are relevant to views which contain
/// visuals that have react tags but are not rendered using UIElements.
/// </summary>
public interface IReactCompoundView
{
/// <summary>
/// Returns the react tag rendered at point in reactView. The view
/// is not expected to do hit testing on its UIElement descendants. Rather,
/// this is useful for views which are composed of visuals that are associated
/// with react tags but the visuals are not UIElements.
/// </summary>
/// <param name="reactView">The react view to do hit testing within.</param>
/// <param name="point">The point to hit test in coordinates that are relative to the view.</param>
/// <returns>The react tag rendered at point in reactView.</returns>
int GetReactTagAtPoint(UIElement reactView, Point point);
}
}
18 changes: 18 additions & 0 deletions ReactWindows/ReactNative/UIManager/ReactDefaultCompoundView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;

namespace ReactNative.UIManager
{
class ReactDefaultCompoundView : IReactCompoundView
{
public int GetReactTagAtPoint(UIElement reactView, Point point)
{
return reactView.GetTag();
}
}
}
22 changes: 22 additions & 0 deletions ReactWindows/ReactNative/Views/Text/ReactTextCompoundView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using ReactNative.UIManager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace ReactNative.Views.Text
{
class ReactTextCompoundView : IReactCompoundView
{
public int GetReactTagAtPoint(UIElement reactView, Point point)
{
var richTextBlock = reactView.As<RichTextBlock>();
var textPointer = richTextBlock.GetPositionFromPoint(point);
return textPointer.Parent.GetTag();
}
}
}
2 changes: 2 additions & 0 deletions ReactWindows/ReactNative/Views/Text/ReactTextViewManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace ReactNative.Views.Text
/// </summary>
public class ReactTextViewManager : ViewParentManager<RichTextBlock, ReactTextShadowNode>
{
private static readonly IReactCompoundView s_compoundView = new ReactTextCompoundView();
private const double DefaultFontSize = 15;

/// <summary>
Expand Down Expand Up @@ -162,6 +163,7 @@ protected override RichTextBlock CreateViewInstance(ThemedReactContext reactCont
};

richTextBlock.Blocks.Add(new Paragraph());
richTextBlock.SetReactCompoundView(s_compoundView);

return richTextBlock;
}
Expand Down

0 comments on commit a766d1a

Please sign in to comment.