Skip to content

Commit

Permalink
feat: Text input support in Uno Islands
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinZikmund committed May 13, 2022
1 parent ab9a883 commit c82f80e
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/SamplesApp/UnoIslands.Shared/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<TextBlock Text="{Binding Address}" />
<HyperlinkButton Content="{Binding Email}" NavigateUri="{Binding EmailUrl}" Grid.Row="1" />
<TextBlock Text="{Binding Phone}" />
<TextBox Text="{Binding Note, Mode=TwoWay}" PlaceholderText="Enter a note..." />
</StackPanel>
</Grid>
</Page>
2 changes: 2 additions & 0 deletions src/SamplesApp/UnoIslands.WPF/PersonViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class PersonViewModel

public string Country { get; set; }

public string Note { get; set; }

public string EmailUrl => "mailto:" + Email;

public string ImageUrl => $"https://www.gravatar.com/avatar/{Name.GetHashCode()}?s=128&d=identicon&r=PG";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Uno.UI.Xaml.Controls.Extensions;
using Point = Windows.Foundation.Point;
using WpfCanvas = System.Windows.Controls.Canvas;
using Uno.UI.Runtime.Skia.Wpf.Hosting;

namespace Uno.UI.Runtime.Skia.WPF.Extensions.UI.Xaml.Controls
{
Expand All @@ -23,7 +24,16 @@ public TextBoxViewExtension(TextBoxView owner)
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
}

private WpfCanvas? GetWindowTextInputLayer() => WpfHost.Current?.NativeOverlayLayer;
private WpfCanvas? GetWindowTextInputLayer()
{
if (_owner?.TextBox?.XamlRoot is not { } xamlRoot)
{
return null;
}

var host = XamlRootMap.GetHostForRoot(xamlRoot);
return host?.NativeOverlayLayer;
}

public void StartEntry()
{
Expand Down
3 changes: 3 additions & 0 deletions src/Uno.UI.Runtime.Skia.Wpf/IWpfHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

using Windows.Devices.Input;
using Windows.UI.Xaml;
using WpfCanvas = System.Windows.Controls.Canvas;

namespace Uno.UI.Runtime.Skia.Wpf
{
internal interface IWpfHost
{
XamlRoot? XamlRoot { get; }

WpfCanvas? NativeOverlayLayer { get;}

void ReleasePointerCapture(PointerIdentifier pointer);

void SetPointerCapture(PointerIdentifier pointer);
Expand Down
1 change: 1 addition & 0 deletions src/Uno.UI.Runtime.Skia.Wpf/Islands/UnoXamlHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public partial class UnoXamlHost : UnoXamlHostBase
{
public UnoXamlHost()
{
this.DefaultStyleKey = typeof(UnoXamlHost);
this.DataContextChanged += UnoXamlHost_DataContextChanged;
this.Loaded += OnLoaded;
}
Expand Down
10 changes: 7 additions & 3 deletions src/Uno.UI.Runtime.Skia.Wpf/Islands/UnoXamlHost.host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@
using Windows.Graphics.Display;
using WinUI = Windows.UI.Xaml;
using WpfControl = global::System.Windows.Controls.Control;
using WpfCanvas = global::System.Windows.Controls.Canvas;

namespace Uno.UI.XamlHost.Skia.Wpf
{
/// <summary>
/// UnoXamlHost control hosts UWP XAML content inside the Windows Presentation Foundation
/// </summary>
abstract partial class UnoXamlHostBase : WpfControl, WinUI.ISkiaHost, IWpfHost
partial class UnoXamlHostBase : WpfControl, WinUI.ISkiaHost, IWpfHost
{
private bool _designMode;
private DisplayInformation _displayInformation;
private bool _ignorePixelScaling;
private WriteableBitmap _bitmap;
private HostPointerHandler _hostPointerHandler;
private WpfCanvas _nativeOverlayLayer;

public bool IgnorePixelScaling
{
Expand All @@ -34,8 +36,6 @@ public bool IgnorePixelScaling
}
}

WinUI.XamlRoot? IWpfHost.XamlRoot => ChildInternal?.XamlRoot;

private void InitializeHost()
{
_designMode = DesignerProperties.GetIsInDesignMode(this);
Expand Down Expand Up @@ -123,5 +123,9 @@ protected override void OnRender(DrawingContext drawingContext)
void IWpfHost.ReleasePointerCapture(PointerIdentifier pointer) => CaptureMouse(); //TODO:MZ:This should capture the correct type of pointer (stylus/mouse/touch)

void IWpfHost.SetPointerCapture(PointerIdentifier pointer) => ReleaseMouseCapture();

WinUI.XamlRoot? IWpfHost.XamlRoot => ChildInternal?.XamlRoot;

System.Windows.Controls.Canvas? IWpfHost.NativeOverlayLayer => _nativeOverlayLayer;
}
}
6 changes: 5 additions & 1 deletion src/Uno.UI.Runtime.Skia.Wpf/Islands/UnoXamlHostBase.Focus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
using WF = Windows.Foundation;
using WUX = Windows.UI.Xaml;

//TODO:MZ: We need to make sure that when the UnoXamlHost loses focus, focus is changed in the XamlRoot as well,
//so that for active input fields the native overlay is closed and changes are committed to underlying TextBox text
//before potential data binding changes.

namespace Uno.UI.XamlHost.Skia.Wpf
{
/// <summary>
/// Focus and Keyboard handling for Focus integration with UWP XAML
/// </summary>
public partial class UnoXamlHostBase
partial class UnoXamlHostBase
{
/// <summary>
/// Dictionary that maps WPF (host framework) FocusNavigationDirection to UWP XAML XxamlSourceFocusNavigationReason
Expand Down
28 changes: 26 additions & 2 deletions src/Uno.UI.Runtime.Skia.Wpf/Islands/UnoXamlHostBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
using System;
using Uno.UI.Runtime.Skia.Wpf.Hosting;
using Uno.UI.Skia.Platform;
using Uno.UI.XamlHost;
using Windows.UI.Xaml;
using WUX = Windows.UI.Xaml;

namespace Uno.UI.XamlHost.Skia.Wpf
{
/// <summary>
/// UnoXamlHost control hosts UWP XAML content inside the Windows Presentation Foundation
/// </summary>
abstract partial class UnoXamlHostBase
public abstract partial class UnoXamlHostBase
{
/// <summary>
/// An instance of <seealso cref="IXamlMetadataContainer"/>. Required to
Expand Down Expand Up @@ -102,6 +102,14 @@ public UnoXamlHostBase(string typeName)
ChildInternal.SetWrapper(this);
}

// Uno specific: We need native overlay layer to show input for TextBoxes.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();

_nativeOverlayLayer = GetTemplateChild("NativeOverlayLayer") as System.Windows.Controls.Canvas;
}

/// <summary>
/// Gets the current instance of <seealso cref="XamlApplication"/>
/// </summary>
Expand Down Expand Up @@ -184,6 +192,16 @@ protected WUX.UIElement ChildInternal
}

_childInternal = value;

if (_childInternal.XamlRoot is not null)
{
XamlRootMap.Register(_childInternal.XamlRoot, this);
}
else if (_childInternal is FrameworkElement element)
{
element.Loading += OnChildLoading;
}

SetContent();

var frameworkElement = ChildInternal as WUX.FrameworkElement;
Expand All @@ -205,6 +223,12 @@ protected WUX.UIElement ChildInternal
}
}

private void OnChildLoading(FrameworkElement sender, object args)
{
// Ensure the XamlRoot is registered early.
XamlRootMap.Register(sender.XamlRoot, this);
}

private void XamlRoot_InvalidateRender()
{
//InvalidateOverlays();
Expand Down
3 changes: 3 additions & 0 deletions src/Uno.UI.Runtime.Skia.Wpf/Islands/WpfIslandsHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

namespace Uno.UI.Skia.Platform
{
// TODO:MZ: Get rid of this class and use the one UnoXamlHost directly.
[TemplatePart(Name = NativeOverlayLayerPart, Type = typeof(WpfCanvas))]
public class WpfIslandsHost : WpfControl, WinUI.ISkiaHost, IWpfHost
{
Expand Down Expand Up @@ -191,6 +192,8 @@ public bool IgnorePixelScaling

WinUI.XamlRoot? IWpfHost.XamlRoot => throw new NotImplementedException();

WpfCanvas? IWpfHost.NativeOverlayLayer => NativeOverlayLayer;

protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
Expand Down
15 changes: 15 additions & 0 deletions src/Uno.UI.Runtime.Skia.Wpf/Themes/Generic.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:platform="clr-namespace:Uno.UI.Skia.Platform"
xmlns:xamlHostWpf="clr-namespace:Uno.UI.XamlHost.Skia.Wpf"
xmlns:controls="clr-namespace:Uno.UI.Runtime.Skia.WPF.Controls">

<Style TargetType="{x:Type platform:WpfHost}">
Expand All @@ -18,6 +19,20 @@
</Setter>
</Style>

<Style TargetType="{x:Type xamlHostWpf:UnoXamlHost}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type xamlHostWpf:UnoXamlHost}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Canvas x:Name="NativeOverlayLayer" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style TargetType="{x:Type controls:WpfTextViewTextBox}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
Expand Down
7 changes: 5 additions & 2 deletions src/Uno.UI.Runtime.Skia.Wpf/WpfHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,6 @@ public bool IgnorePixelScaling
}
}

WinUI.XamlRoot? IWpfHost.XamlRoot => WinUI.Window.Current?.RootElement?.XamlRoot;

protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
Expand Down Expand Up @@ -340,5 +338,10 @@ private void InvalidateOverlays()
void IWpfHost.ReleasePointerCapture(PointerIdentifier pointer) => CaptureMouse(); //TODO:MZ:This should capture the correct type of pointer (stylus/mouse/touch)

void IWpfHost.SetPointerCapture(PointerIdentifier pointer) => ReleaseMouseCapture();

//TODO:MZ: This will need to be adjusted when multi-window support is added.
WinUI.XamlRoot? IWpfHost.XamlRoot => WinUI.Window.Current?.RootElement?.XamlRoot;

WpfCanvas? IWpfHost.NativeOverlayLayer => NativeOverlayLayer;
}
}

0 comments on commit c82f80e

Please sign in to comment.