Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[Tizen] Fix the text selection issue of Entry #12468

Merged
merged 1 commit into from
Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Xamarin.Forms.Platform.Tizen/Extensions/EntryExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using ElmSharp;
using System;
using System.Runtime.InteropServices;
using ElmSharp;
using EEntry = ElmSharp.Entry;

namespace Xamarin.Forms.Platform.Tizen
{
Expand All @@ -24,6 +27,12 @@ internal static InputPanelReturnKeyType ToInputPanelReturnKeyType(this ReturnTyp
throw new System.NotImplementedException($"ReturnType {returnType} not supported");
}
}
public static void GetSelectRegion(this EEntry entry, out int start, out int end)
{
elm_entry_select_region_get(entry.RealHandle, out start, out end);
}

[DllImport("libelementary.so.1")]
static extern void elm_entry_select_region_get(IntPtr obj, out int start, out int end);
}
}
97 changes: 63 additions & 34 deletions Xamarin.Forms.Platform.Tizen/Renderers/EntryRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using ElmSharp;
using EEntry = ElmSharp.Entry;
using IEntry = Xamarin.Forms.Platform.Tizen.Native.IEntry;
using Specific = Xamarin.Forms.PlatformConfiguration.TizenSpecific.Entry;
Expand All @@ -7,6 +8,9 @@ namespace Xamarin.Forms.Platform.Tizen
{
public class EntryRenderer : ViewRenderer<Entry, EEntry>
{
SmartEvent _selectionCleared;
bool _nativeSelectionIsUpdating;

public EntryRenderer()
{
RegisterPropertyHandler(Entry.IsPasswordProperty, UpdateIsPassword);
Expand Down Expand Up @@ -39,12 +43,25 @@ protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
entry.Activated += OnCompleted;
entry.CursorChanged += OnCursorChanged;


// In order to know when the selection is cleared, "selecton,cleared" event has been used.
// Because CursorChanged event is still invoked with the selected text when an user clears selection. It is an known issue in EFL.
_selectionCleared = new SmartEvent(entry, entry.RealHandle, ThemeConstants.Entry.Signals.SelectionCleared);
_selectionCleared.On += OnSelectionCleared;

if (entry is IEntry ie)
{
ie.TextChanged += OnTextChanged;
}
entry.PrependMarkUpFilter(MaxLengthFilter);
SetNativeControl(entry);

// An initial CursorPosition is set after layouting to avoid timing issue when the EditField entry is initialized.
Device.BeginInvokeOnMainThread(() =>
{
UpdateSelectionLength(false);
});

}
base.OnElementChanged(e);
}
Expand Down Expand Up @@ -225,42 +242,42 @@ void UpdateReturnType()
Control.SetInputPanelReturnKeyType(Element.ReturnType.ToInputPanelReturnKeyType());
}

void UpdateSelectionLength()
void UpdateSelectionLength(bool initialize)
{
int start = GetSelectionStart();
int end = GetSelectionEnd(start);
if (initialize || _nativeSelectionIsUpdating )
return;

var start = GetSelectionStart();
var end = GetSelectionEnd(start);
var selectionLength = end - start;

if (selectionLength != Element.SelectionLength)
SetSelectionLengthFromRenderer(selectionLength);

if (start != end)
if (selectionLength > 0)
{
Control.SetSelectionRegion(start, end);
}
else
{
Control.CursorPosition = start;
Control.SelectNone();
Control.SetFocus(true);
Control.CursorPosition = Element.CursorPosition;
}
}

int GetSelectionEnd(int start)
{
int end = start;
int selectionLength = Element.SelectionLength;
var end = start;

if (Element.IsSet(Entry.SelectionLengthProperty))
end = Math.Max(start, Math.Min(Control.Text.Length, start + selectionLength));

int newSelectionLength = Math.Max(0, end - start);
if (newSelectionLength != selectionLength)
SetSelectionLengthFromRenderer(newSelectionLength);
end = Math.Min((start + Element.SelectionLength), Element.Text?.Length ?? 0);

return end;
}

int GetSelectionStart()
{
int start = Element.Text?.Length ?? 0;
int cursorPosition = Element.CursorPosition;
var start = Element.Text?.Length ?? 0;
var cursorPosition = Element.CursorPosition;

if (Element.IsSet(Entry.CursorPositionProperty))
start = Math.Min(start, cursorPosition);
Expand All @@ -271,51 +288,63 @@ int GetSelectionStart()
return start;
}

void OnSelectionCleared(object sender, EventArgs e)
{
if (Control.IsFocused)
{
SetSelectionLengthFromRenderer(0);
SetCursorPositionFromRenderer(Control.CursorPosition);
}
}

void OnCursorChanged(object sender, EventArgs e)
{
int cursorPosition = Element.CursorPosition;
int selectionStart = GetCursorPosition();
var position = Control.CursorPosition;

if (selectionStart != cursorPosition)
SetCursorPositionFromRenderer(selectionStart);
Control.GetSelectRegion(out int start, out int end);

SetSelectionLengthFromRenderer(Control.GetSelection()?.Length ?? 0);
if (start > -1)
{
position = (start < end) ? start : end;
var selectionLength = Math.Abs(end - start);
SetSelectionLengthFromRenderer(selectionLength);
}

SetCursorPositionFromRenderer(position);
}

void SetCursorPositionFromRenderer(int start)
void SetCursorPositionFromRenderer(int position)
{
try
{
Element?.SetValueFromRenderer(Entry.CursorPositionProperty, start);
_nativeSelectionIsUpdating = true;
Element?.SetValueFromRenderer(Entry.CursorPositionProperty, position);
}
catch (Exception ex)
{
Internals.Log.Warning("Entry", $"Failed to set CursorPosition from renderer: {ex}");
Log.Error($"Failed to set CursorPosition from renderer: {ex}");
}
finally
{
_nativeSelectionIsUpdating = false;
}
}

void SetSelectionLengthFromRenderer(int selectionLength)
{
try
{
_nativeSelectionIsUpdating = true;
Element?.SetValueFromRenderer(Entry.SelectionLengthProperty, selectionLength);
}
catch (Exception ex)
{
Internals.Log.Warning("Entry", $"Failed to set SelectionLength from renderer: {ex}");
Log.Error($"Failed to set SelectionLength from renderer: {ex}");
}
}

int GetCursorPosition()
{
var selection = Control.GetSelection();
if (string.IsNullOrEmpty(selection))
finally
{
return Control.CursorPosition;
_nativeSelectionIsUpdating = false;
}

return Element.Text.IndexOf(selection, Math.Max(Control.CursorPosition - selection.Length, 0));
}

void UpdateIsReadOnly()
Expand Down
6 changes: 6 additions & 0 deletions Xamarin.Forms.Platform.Tizen/ThemeConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ public class Parts
{
public const string PlaceHolderText = "elm.guide";
}

public class Signals
{
public const string SelectionChanged = "selection,changed";
public const string SelectionCleared = "selection,cleared";
}
}
#endregion

Expand Down