Skip to content

Commit

Permalink
[Android] Wire up text for SearchBar and Editor (#2454)
Browse files Browse the repository at this point in the history
* Wire up text for SearchBar and Editor

* - searchbar

* - fix searchbar text update

* - convert to MauiSearchBar

* Update EditorHandlerTests.cs

* Update EditorHandlerTests.cs

* Update EntryHandlerTests.cs

* Update SearchBarHandler.iOS.cs

* - fix Editor on iOS

* Update EditorHandler.iOS.cs
  • Loading branch information
PureWeen committed Sep 11, 2021
1 parent 23c91d9 commit 85b5699
Show file tree
Hide file tree
Showing 23 changed files with 300 additions and 144 deletions.
33 changes: 33 additions & 0 deletions src/Core/src/Core/Extensions/ITextInputExtensions.cs
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Maui
{
public static class ITextInputExtensions
{
public static void UpdateText(this ITextInput textInput, string? text)
{
// Even though <null> is technically different to "", it has no
// functional difference to apps. Thus, hide it.
var mauiText = textInput.Text ?? string.Empty;
var nativeText = text ?? string.Empty;
if (mauiText != nativeText)
textInput.Text = nativeText;
}

#if __ANDROID__
public static void UpdateText(this ITextInput textInput, Android.Text.TextChangedEventArgs e)
{
if (e.BeforeCount == 0 && e.AfterCount == 0)
return;

if (e.Text is Java.Lang.ICharSequence cs)
textInput.UpdateText(cs.ToString());
else if (e.Text != null)
textInput.UpdateText(String.Concat(e.Text));
else
textInput.UpdateText((string?)null);
}
#endif
}
}
7 changes: 5 additions & 2 deletions src/Core/src/Handlers/Editor/EditorHandler.Android.cs
Expand Up @@ -34,20 +34,23 @@ protected override void ConnectHandler(AppCompatEditText nativeView)
{
FocusChangeListener.Handler = this;

nativeView.TextChanged += OnTextChanged;
nativeView.OnFocusChangeListener = FocusChangeListener;
}

protected override void DisconnectHandler(AppCompatEditText nativeView)
{
nativeView.OnFocusChangeListener = null;

nativeView.TextChanged -= OnTextChanged;
FocusChangeListener.Handler = null;
}

void OnTextChanged(object? sender, Android.Text.TextChangedEventArgs e) =>
VirtualView.UpdateText(e);

void SetupDefaults(AppCompatEditText nativeView)
{


DefaultTextColors = nativeView.TextColors;
DefaultPlaceholderTextColors = nativeView.HintTextColors;
DefaultBackground = nativeView.Background;
Expand Down
16 changes: 9 additions & 7 deletions src/Core/src/Handlers/Editor/EditorHandler.iOS.cs
Expand Up @@ -25,6 +25,7 @@ protected override void ConnectHandler(MauiTextView nativeView)
nativeView.Changed += OnChanged;
nativeView.ShouldChangeText += OnShouldChangeText;
nativeView.Ended += OnEnded;
nativeView.TextPropertySet += OnTextPropertySet;
}

protected override void DisconnectHandler(MauiTextView nativeView)
Expand All @@ -34,6 +35,7 @@ protected override void DisconnectHandler(MauiTextView nativeView)
nativeView.Changed -= OnChanged;
nativeView.ShouldChangeText -= OnShouldChangeText;
nativeView.Ended -= OnEnded;
nativeView.TextPropertySet -= OnTextPropertySet;
}

public override Size GetDesiredSize(double widthConstraint, double heightConstraint) =>
Expand Down Expand Up @@ -128,19 +130,19 @@ bool OnShouldChangeText(UITextView textView, NSRange range, string replacementSt

void OnEnded(object? sender, EventArgs eventArgs)
{
if (VirtualView == null || NativeView == null)
return;

if (NativeView.Text != VirtualView.Text)
VirtualView.Text = NativeView.Text ?? string.Empty;

// TODO: Update IsFocused property
VirtualView.Completed();
}

private void OnTextPropertySet(object? sender, EventArgs e)
{
VirtualView.UpdateText(NativeView.Text);
}


public static void MapKeyboard(EditorHandler handler, IEditor editor)
{
handler.NativeView?.UpdateKeyboard(editor);
}
}
}
}
39 changes: 6 additions & 33 deletions src/Core/src/Handlers/Entry/EntryHandler.Android.cs
Expand Up @@ -14,7 +14,6 @@ namespace Microsoft.Maui.Handlers
{
public partial class EntryHandler : ViewHandler<IEntry, AppCompatEditText>
{
readonly TextWatcher _watcher = new();
readonly EntryTouchListener _touchListener = new();
readonly EntryFocusChangeListener _focusChangeListener = new();
readonly EditorActionListener _actionListener = new();
Expand All @@ -37,13 +36,12 @@ protected override AppCompatEditText CreateNativeView()

protected override void ConnectHandler(AppCompatEditText nativeView)
{
_watcher.Handler = this;
_touchListener.Handler = this;
_focusChangeListener.Handler = this;
_actionListener.Handler = this;

nativeView.TextChanged += OnTextChanged;
nativeView.OnFocusChangeListener = _focusChangeListener;
nativeView.AddTextChangedListener(_watcher);
nativeView.SetOnTouchListener(_touchListener);
nativeView.SetOnEditorActionListener(_actionListener);
}
Expand All @@ -52,17 +50,19 @@ protected override void DisconnectHandler(AppCompatEditText nativeView)
{
_clearButtonDrawable = null;

nativeView.RemoveTextChangedListener(_watcher);
nativeView.TextChanged -= OnTextChanged;
nativeView.SetOnTouchListener(null);
nativeView.OnFocusChangeListener = null;
nativeView.SetOnEditorActionListener(null);

_focusChangeListener.Handler = null;
_watcher.Handler = null;
_touchListener.Handler = null;
_actionListener.Handler = null;
}

void OnTextChanged(object? sender, Android.Text.TextChangedEventArgs e) =>
VirtualView.UpdateText(e);

// This is a Android-specific mapping
public static void MapBackground(EntryHandler handler, IEntry entry)
{
Expand Down Expand Up @@ -180,12 +180,7 @@ void OnTextChanged(string? text)
if (VirtualView == null || NativeView == null)
return;

// Even though <null> is technically different to "", it has no
// functional difference to apps. Thus, hide it.
var mauiText = VirtualView.Text ?? string.Empty;
var nativeText = text ?? string.Empty;
if (mauiText != nativeText)
VirtualView.Text = nativeText;
VirtualView.UpdateText(text);

// Text changed should trigger clear button visibility.
UpdateValue(nameof(VirtualView.ClearButtonVisibility));
Expand Down Expand Up @@ -235,28 +230,6 @@ bool HandleClearButtonTouched(MotionEvent? motionEvent)
return false;
}

class TextWatcher : Java.Lang.Object, ITextWatcher
{
public EntryHandler? Handler { get; set; }

void ITextWatcher.AfterTextChanged(IEditable? s)
{
}

void ITextWatcher.BeforeTextChanged(Java.Lang.ICharSequence? s, int start, int count, int after)
{
}

void ITextWatcher.OnTextChanged(Java.Lang.ICharSequence? s, int start, int before, int count)
{
// We are replacing 0 characters with 0 characters, so skip
if (before == 0 && count == 0)
return;

Handler?.OnTextChanged(s?.ToString());
}
}

// TODO: Maybe better to have generic version in INativeViewHandler?
class EntryTouchListener : Java.Lang.Object, IOnTouchListener
{
Expand Down
7 changes: 1 addition & 6 deletions src/Core/src/Handlers/Entry/EntryHandler.iOS.cs
Expand Up @@ -173,12 +173,7 @@ void OnTextChanged()
if (VirtualView == null || NativeView == null)
return;

// Even though <null> is technically different to "", it has no
// functional difference to apps. Thus, hide it.
var mauiText = VirtualView!.Text ?? string.Empty;
var nativeText = NativeView.Text ?? string.Empty;
if (mauiText != nativeText)
VirtualView.Text = nativeText;
VirtualView.UpdateText(NativeView.Text);
}

bool OnShouldChangeCharacters(UITextField textField, NSRange range, string replacementString)
Expand Down
37 changes: 14 additions & 23 deletions src/Core/src/Handlers/SearchBar/SearchBarHandler.Android.cs
Expand Up @@ -8,8 +8,6 @@ namespace Microsoft.Maui.Handlers
{
public partial class SearchBarHandler : ViewHandler<ISearchBar, SearchView>
{
QueryTextListener QueryListener { get; } = new QueryTextListener();

static Drawable? DefaultBackground;
static ColorStateList? DefaultPlaceholderTextColors { get; set; }

Expand All @@ -28,16 +26,14 @@ protected override SearchView CreateNativeView()

protected override void ConnectHandler(SearchView nativeView)
{
QueryListener.Handler = this;

nativeView.SetOnQueryTextListener(QueryListener);
nativeView.QueryTextChange += OnQueryTextChange;
nativeView.QueryTextSubmit += OnQueryTextSubmit;
}

protected override void DisconnectHandler(SearchView nativeView)
{
nativeView.SetOnQueryTextListener(null);

QueryListener.Handler = null;
nativeView.QueryTextChange -= OnQueryTextChange;
nativeView.QueryTextSubmit -= OnQueryTextSubmit;
}

void SetupDefaults(SearchView nativeView)
Expand Down Expand Up @@ -104,23 +100,18 @@ public static void MapCancelButtonColor(SearchBarHandler handler, ISearchBar sea
handler.NativeView?.UpdateCancelButtonColor(searchBar);
}

public class QueryTextListener : Java.Lang.Object, IOnQueryTextListener
{
public SearchBarHandler? Handler { get; set; }

public bool OnQueryTextChange(string newText)
{
return true;
}

public bool OnQueryTextSubmit(string newText)
{
Handler?.VirtualView?.SearchButtonPressed();

// TODO: Clear focus
void OnQueryTextSubmit(object? sender, QueryTextSubmitEventArgs e)
{
VirtualView.SearchButtonPressed();
// TODO: Clear focus
e.Handled = true;
}

return true;
}
void OnQueryTextChange(object? sender, QueryTextChangeEventArgs e)
{
VirtualView.UpdateText(e.NewText);
e.Handled = true;
}
}
}
27 changes: 14 additions & 13 deletions src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
Expand Up @@ -6,7 +6,7 @@

namespace Microsoft.Maui.Handlers
{
public partial class SearchBarHandler : ViewHandler<ISearchBar, UISearchBar>
public partial class SearchBarHandler : ViewHandler<ISearchBar, MauiSearchBar>
{
UIColor? _defaultTextColor;

Expand All @@ -18,32 +18,33 @@ public partial class SearchBarHandler : ViewHandler<ISearchBar, UISearchBar>

public UITextField? QueryEditor => _editor;

protected override UISearchBar CreateNativeView()
protected override MauiSearchBar CreateNativeView()
{
var searchBar = new UISearchBar(RectangleF.Empty) { ShowsCancelButton = true, BarStyle = UIBarStyle.Default };
var searchBar = new MauiSearchBar() { ShowsCancelButton = true, BarStyle = UIBarStyle.Default };

_editor = searchBar.FindDescendantView<UITextField>();
if (NativeVersion.IsAtLeast(13))
_editor = searchBar.SearchTextField;
else
_editor = searchBar.FindDescendantView<UITextField>();

return searchBar;
}

protected override void ConnectHandler(UISearchBar nativeView)
protected override void ConnectHandler(MauiSearchBar nativeView)
{
nativeView.CancelButtonClicked += OnCancelClicked;
nativeView.SearchButtonClicked += OnSearchButtonClicked;
nativeView.TextChanged += OnTextChanged;
nativeView.TextPropertySet += OnTextPropertySet;
nativeView.ShouldChangeTextInRange += ShouldChangeText;

base.ConnectHandler(nativeView);
}

protected override void DisconnectHandler(UISearchBar nativeView)
protected override void DisconnectHandler(MauiSearchBar nativeView)
{
nativeView.CancelButtonClicked -= OnCancelClicked;
nativeView.SearchButtonClicked -= OnSearchButtonClicked;
nativeView.TextChanged -= OnTextChanged;
nativeView.TextPropertySet -= OnTextPropertySet;
nativeView.ShouldChangeTextInRange -= ShouldChangeText;

base.DisconnectHandler(nativeView);
}

Expand Down Expand Up @@ -146,10 +147,10 @@ void OnSearchButtonClicked(object? sender, EventArgs e)
NativeView?.ResignFirstResponder();
}

void OnTextChanged(object? sender, UISearchBarTextChangedEventArgs a)
void OnTextPropertySet(object? sender, EventArgs e)
{
if (VirtualView != null)
VirtualView.Text = a.SearchText;
VirtualView.UpdateText(NativeView?.Text);
}

bool ShouldChangeText(UISearchBar searchBar, NSRange range, string text)
Expand All @@ -158,4 +159,4 @@ bool ShouldChangeText(UISearchBar searchBar, NSRange range, string text)
return newLength <= VirtualView?.MaxLength;
}
}
}
}
47 changes: 47 additions & 0 deletions src/Core/src/Platform/iOS/MauiSearchBar.cs
@@ -0,0 +1,47 @@
using System;
using System.Drawing;
using CoreGraphics;
using Foundation;
using UIKit;

namespace Microsoft.Maui.Handlers
{
public class MauiSearchBar : UISearchBar
{
public MauiSearchBar() : this(RectangleF.Empty)
{
}

public MauiSearchBar(NSCoder coder) : base(coder)
{
}

public MauiSearchBar(CGRect frame) : base(frame)
{
}

protected MauiSearchBar(NSObjectFlag t) : base(t)
{
}

protected internal MauiSearchBar(IntPtr handle) : base(handle)
{
}

public override string? Text
{
get => base.Text;
set
{
var old = base.Text;

base.Text = value;

if (old != value)
TextPropertySet?.Invoke(this, EventArgs.Empty);
}
}

public event EventHandler? TextPropertySet;
}
}

0 comments on commit 85b5699

Please sign in to comment.