Skip to content

Commit

Permalink
Merge pull request #638 from DevFromDownUnder/#618
Browse files Browse the repository at this point in the history
#618 clear icon focus handling
  • Loading branch information
enisn committed Apr 3, 2024
2 parents 56d590e + af8e432 commit c41f20a
Show file tree
Hide file tree
Showing 7 changed files with 373 additions and 257 deletions.
12 changes: 12 additions & 0 deletions src/UraniumUI.Material/Controls/TextField.BindableProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,16 @@ public partial class TextField
{
EntryProperties.SetSelectionHighlightColor(bindable, (Color)newValue);
});

public bool DisallowClearButtonFocus { get => (bool)GetValue(DisallowClearButtonFocusProperty); set => SetValue(DisallowClearButtonFocusProperty, value); }

public static BindableProperty DisallowClearButtonFocusProperty = BindableProperty.Create(
nameof(DisallowClearButtonFocus),
typeof(bool),
typeof(TextField),
false,
propertyChanged: (bindable, oldValue, newValue) =>
{
(bindable as TextField).iconClear.IsFocusable = !(bool)newValue;
});
}
4 changes: 4 additions & 0 deletions src/UraniumUI.Material/Controls/TextField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public TextField()
EntryView.SetBinding(Entry.IsEnabledProperty, new Binding(nameof(IsEnabled), BindingMode.TwoWay, source: this));
EntryView.SetBinding(Entry.IsReadOnlyProperty, new Binding(nameof(IsReadOnly), BindingMode.TwoWay, source: this));

iconClear.SetBinding(StatefulContentView.IsFocusableProperty, new Binding(nameof(DisallowClearButtonFocus), source: this));

AfterConstructor();
}

Expand All @@ -63,6 +65,7 @@ public TextField()
protected override void OnHandlerChanged()
{
base.OnHandlerChanged();

if (Handler is null)
{
EntryView.TextChanged -= EntryView_TextChanged;
Expand All @@ -72,6 +75,7 @@ protected override void OnHandlerChanged()
{
EntryView.TextChanged += EntryView_TextChanged;
EntryView.Completed += EntryView_Completed;

ApplyAttachedProperties();
}
}
Expand Down
87 changes: 87 additions & 0 deletions src/UraniumUI/Handlers/StatefulContentViewHandler.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#if ANDROID
using Android.Views;
using Microsoft.Maui.Platform;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UraniumUI.Views;
using static Microsoft.Maui.Controls.VisualStateManager;

namespace UraniumUI.Handlers;
public partial class StatefulContentViewHandler
{
protected override ContentViewGroup CreatePlatformView()
{
var platformView = base.CreatePlatformView();

platformView.Touch += OnTouch;
platformView.Hover += NativeView_Hover;
platformView.Click += PlatformView_Click;
platformView.LongClick += PlatformView_LongClick;

return platformView;
}

protected override void DisconnectHandler(ContentViewGroup platformView)
{
platformView.Touch -= OnTouch;
platformView.Hover -= NativeView_Hover;
platformView.LongClick -= PlatformView_LongClick;
base.DisconnectHandler(platformView);
}

private void NativeView_Hover(object sender, Android.Views.View.HoverEventArgs e)
{
if (e.Event.Action == MotionEventActions.HoverEnter)
{
GoToState(StatefulView, CommonStates.PointerOver);
ExecuteCommandIfCan(StatefulView.HoverCommand);
StatefulView.InvokeHovered();
return;
}

if (e.Event.Action == MotionEventActions.HoverExit)
{
GoToState(StatefulView, CommonStates.Normal);
ExecuteCommandIfCan(StatefulView.HoverExitCommand);
StatefulView.InvokeHoverExited();
}
}

private void OnTouch(object sender, Android.Views.View.TouchEventArgs e)
{
if (e.Event.Action == MotionEventActions.Down)
{
GoToState(StatefulView, "Pressed");
ExecuteCommandIfCan(StatefulView.PressedCommand);
StatefulView.InvokePressed();
e.Handled = false;
}
else if (e.Event.Action == MotionEventActions.Up)
{
GoToState(StatefulView, CommonStates.Normal);
e.Handled = false;
}
}

private void PlatformView_Click(object sender, EventArgs e)
{
GoToState(StatefulView, CommonStates.Normal);
ExecuteCommandIfCan(StatefulView.TappedCommand);
StatefulView.InvokeTapped();
}

private void PlatformView_LongClick(object sender, Android.Views.View.LongClickEventArgs e)
{
ExecuteCommandIfCan(StatefulView.LongPressCommand);
StatefulView.InvokeLongPressed();
}

public static void MapIsFocusable(StatefulContentViewHandler handler, StatefulContentView view)
{
handler.StatefulView.IsFocusable = view.IsFocusable;
}
}
#endif
133 changes: 133 additions & 0 deletions src/UraniumUI/Handlers/StatefulContentViewHandler.Apple.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#if IOS || MACCATALYST
using Foundation;
using System.Diagnostics;
using UraniumUI.Views;
using UIKit;
using static Microsoft.Maui.Controls.VisualStateManager;

namespace UraniumUI.Handlers;
public partial class StatefulContentViewHandler
{
protected override Microsoft.Maui.Platform.ContentView CreatePlatformView()
{
var platformView = new StatefulUIContentView();
platformView.IsFocusable = StatefulView.IsFocusable;
return platformView;
}

protected override void ConnectHandler(Microsoft.Maui.Platform.ContentView platformView)
{
platformView.AddGestureRecognizer(new UIContinousGestureRecognizer(Tapped));
if (OperatingSystem.IsIOSVersionAtLeast(13))
{
platformView.AddGestureRecognizer(new UIHoverGestureRecognizer(OnHover));
}
platformView.AddGestureRecognizer(new UILongPressGestureRecognizer(OnLongPress));
base.ConnectHandler(platformView);
}

private void OnLongPress(UILongPressGestureRecognizer recognizer)
{
ExecuteCommandIfCan(StatefulView.LongPressCommand);
StatefulView.InvokeLongPressed();
}

private void OnHover(UIHoverGestureRecognizer recognizer)
{
switch (recognizer.State)
{
case UIGestureRecognizerState.Began:

GoToState(StatefulView, CommonStates.PointerOver);
ExecuteCommandIfCan(StatefulView.HoverCommand);
StatefulView.InvokeHovered();
break;
case UIGestureRecognizerState.Ended:
case UIGestureRecognizerState.Cancelled:
case UIGestureRecognizerState.Failed:
GoToState(StatefulView, CommonStates.Normal);
ExecuteCommandIfCan(StatefulView.HoverExitCommand);
StatefulView.InvokeHoverExited();
break;
}
}

private void Tapped(UIGestureRecognizer recognizer)
{
switch (recognizer.State)
{
case UIGestureRecognizerState.Began:
GoToState(StatefulView, "Pressed");
ExecuteCommandIfCan(StatefulView.PressedCommand);
StatefulView.InvokePressed();

break;
case UIGestureRecognizerState.Ended:
GoToState(StatefulView, CommonStates.Normal);
ExecuteCommandIfCan(StatefulView.TappedCommand);
StatefulView.InvokeTapped();

//// TODO: Fix working of native gesture recognizers of MAUI
foreach (var item in StatefulView.GestureRecognizers)
{
Debug.WriteLine(item.GetType().Name);
if (item is TapGestureRecognizer tgr)
{
tgr.Command?.Execute(StatefulView);
}
}

break;
}
}

// TODO: Move it to the different file
internal class UIContinousGestureRecognizer : UIGestureRecognizer
{
private readonly Action<UIGestureRecognizer> action;

public UIContinousGestureRecognizer(Action<UIGestureRecognizer> action)
{
this.action = action;
}

public override void TouchesBegan(NSSet touches, UIEvent evt)
{
State = UIGestureRecognizerState.Began;

action(this);

base.TouchesBegan(touches, evt);
}

public override void TouchesEnded(NSSet touches, UIEvent evt)
{
State = UIGestureRecognizerState.Ended;

action(this);

base.TouchesEnded(touches, evt);
}
}

public static void MapIsFocusable(StatefulContentViewHandler handler, StatefulContentView view)
{
if (handler.PlatformView is StatefulUIContentView uiView)
{
uiView.IsFocusable = view.IsFocusable;
}
}

internal void UpdateFocusable()
{

}

// TODO: Move it to the different file
public class StatefulUIContentView : Microsoft.Maui.Platform.ContentView
{
public bool IsFocusable { get; set; }
public override bool CanBecomeFocused => IsFocusable;
}
}
#endif
120 changes: 120 additions & 0 deletions src/UraniumUI/Handlers/StatefulContentViewHandler.Windows.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#if WINDOWS
using Microsoft.Maui.Platform;
using Microsoft.UI.Xaml.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UraniumUI.Views;
using static Microsoft.Maui.Controls.VisualStateManager;

namespace UraniumUI.Handlers;
public partial class StatefulContentViewHandler
{
protected override ContentPanel CreatePlatformView()
{
var platformView = base.CreatePlatformView();

platformView.AddHandler(
Microsoft.UI.Xaml.Controls.Button.PointerPressedEvent,
new PointerEventHandler(PlatformView_PointerPressed), true);

platformView.AddHandler(
Microsoft.UI.Xaml.Controls.Button.PointerReleasedEvent,
new PointerEventHandler(PlatformView_PointerReleased), true);

platformView.PointerEntered += PlatformView_PointerEntered;
platformView.PointerExited += PlatformView_PointerExited;

platformView.IsTabStop = true;
platformView.UseSystemFocusVisuals = true;
platformView.KeyDown += PlatformView_KeyDown;
platformView.KeyUp += PlatformView_KeyUp;

return platformView;
}

protected override void DisconnectHandler(ContentPanel platformView)
{
platformView.PointerEntered -= PlatformView_PointerEntered;
platformView.PointerExited -= PlatformView_PointerExited;
platformView.KeyDown -= PlatformView_KeyDown;
platformView.KeyUp -= PlatformView_KeyUp;
base.DisconnectHandler(platformView);
}

private void PlatformView_PointerExited(object sender, PointerRoutedEventArgs e)
{
GoToState(StatefulView, CommonStates.Normal);
ExecuteCommandIfCan(StatefulView.HoverExitCommand);
StatefulView.InvokeHoverExited();
}

private void PlatformView_PointerEntered(object sender, PointerRoutedEventArgs e)
{
GoToState(StatefulView, CommonStates.PointerOver);
ExecuteCommandIfCan(StatefulView.HoverCommand);
StatefulView.InvokeHovered();
}

long lastPressed;

private void PlatformView_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
lastPressed = DateTime.Now.Ticks;

GoToState(StatefulView, "Pressed");
ExecuteCommandIfCan(StatefulView.PressedCommand);
StatefulView.InvokePressed();
}

private void PlatformView_PointerReleased(object sender, PointerRoutedEventArgs e)
{
// TODO: Implement better.
if (DateTime.Now.Ticks - lastPressed >= TimeSpan.TicksPerMillisecond * 500)
{
ExecuteCommandIfCan(StatefulView.LongPressCommand);
}

GoToState(StatefulView, CommonStates.Normal);
ExecuteCommandIfCan(StatefulView.TappedCommand);
StatefulView.InvokeTapped();
}

private void PlatformView_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (IsActionKey(e.Key))
{
GoToState(StatefulView, "Pressed");
ExecuteCommandIfCan(StatefulView.PressedCommand);
StatefulView.InvokePressed();
}
}

private void PlatformView_KeyUp(object sender, KeyRoutedEventArgs e)
{
if (IsActionKey(e.Key))
{
ExecuteCommandIfCan(StatefulView.TappedCommand);
StatefulView.InvokeTapped();
GoToState(StatefulView, CommonStates.Normal);
}
}

private bool IsActionKey(Windows.System.VirtualKey key)
{
return key == Windows.System.VirtualKey.Enter || key == Windows.System.VirtualKey.Space;
}

internal void UpdateFocusable()
{
PlatformView.IsTabStop = StatefulView.IsFocusable;
}

public static void MapIsFocusable(StatefulContentViewHandler handler, StatefulContentView view)
{
handler.PlatformView.IsTabStop = view.IsFocusable;
}
}
#endif
Loading

0 comments on commit c41f20a

Please sign in to comment.