Browse Source

Merge branch 'master' into fix-x86-overflow

pull/9400/head
Max Katz 3 years ago
committed by GitHub
parent
commit
a0d2ab8034
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      Avalonia.sln
  2. 1
      samples/MobileSandbox/MainView.xaml
  3. 46
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  4. 92
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  5. 2
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs
  6. 1
      src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs
  7. 2
      src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
  8. 29
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  9. 2
      src/Avalonia.Controls/TextBox.cs

1
Avalonia.sln

@ -522,6 +522,7 @@ Global
{3B8519C1-2F51-4F12-A348-120AB91D4532}.Release|Any CPU.Build.0 = Release|Any CPU {3B8519C1-2F51-4F12-A348-120AB91D4532}.Release|Any CPU.Build.0 = Release|Any CPU
{C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.Build.0 = Debug|Any CPU {C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{C90FE60B-B01E-4F35-91D6-379D6966030F}.Release|Any CPU.ActiveCfg = Release|Any CPU {C90FE60B-B01E-4F35-91D6-379D6966030F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C90FE60B-B01E-4F35-91D6-379D6966030F}.Release|Any CPU.Build.0 = Release|Any CPU {C90FE60B-B01E-4F35-91D6-379D6966030F}.Release|Any CPU.Build.0 = Release|Any CPU
{FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

1
samples/MobileSandbox/MainView.xaml

@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Margin="100 50" Spacing="50"> <StackPanel Margin="100 50" Spacing="50">
<TextBlock Text="Login" Foreground="White" /> <TextBlock Text="Login" Foreground="White" />
<TextBox Watermark="Text" />
<TextBox Watermark="Username" TextInputOptions.ContentType="Email" AcceptsReturn="True" TextInputOptions.ReturnKeyType="Search" /> <TextBox Watermark="Username" TextInputOptions.ContentType="Email" AcceptsReturn="True" TextInputOptions.ReturnKeyType="Search" />
<TextBox Watermark="Password" PasswordChar="*" TextInputOptions.ContentType="Password" /> <TextBox Watermark="Password" PasswordChar="*" TextInputOptions.ContentType="Password" />
<TextBox Watermark="Pin" PasswordChar="*" TextInputOptions.ContentType="Digits" /> <TextBox Watermark="Pin" PasswordChar="*" TextInputOptions.ContentType="Digits" />

46
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -1,6 +1,7 @@
using System; using System;
using Android.Content; using Android.Content;
using Android.Runtime; using Android.Runtime;
using Android.Text;
using Android.Views; using Android.Views;
using Android.Views.InputMethods; using Android.Views.InputMethods;
using Avalonia.Android.Platform.SkiaPlatform; using Avalonia.Android.Platform.SkiaPlatform;
@ -62,22 +63,21 @@ namespace Avalonia.Android
public void Reset() public void Reset()
{ {
_imm.RestartInput(_host);
} }
public void SetClient(ITextInputMethodClient client) public void SetClient(ITextInputMethodClient client)
{ {
if(client is null)
{
_inputConnection?.SetComposingText("", 0);
}
if (_client != null) if (_client != null)
{ {
_client.SurroundingTextChanged -= SurroundingTextChanged; _client.SurroundingTextChanged -= SurroundingTextChanged;
} }
Reset(); if(_inputConnection != null)
{
_inputConnection.ComposingText = null;
_inputConnection.ComposingRegion = default;
}
_client = client; _client = client;
@ -87,23 +87,31 @@ namespace Avalonia.Android
_host.RequestFocus(); _host.RequestFocus();
_imm.RestartInput(View);
_imm.ShowSoftInput(_host, ShowFlags.Implicit); _imm.ShowSoftInput(_host, ShowFlags.Implicit);
}
else var surroundingText = Client.SurroundingText;
{
_imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None); _imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset);
} }
} }
private void SurroundingTextChanged(object sender, EventArgs e) private void SurroundingTextChanged(object sender, EventArgs e)
{ {
if (IsActive) if (IsActive && _inputConnection != null)
{ {
var surroundingText = Client.SurroundingText; var surroundingText = Client.SurroundingText;
_inputConnection.SurroundingText = surroundingText; _inputConnection.SurroundingText = surroundingText;
_imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset); _imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset);
if (_inputConnection.ComposingText != null && !_inputConnection.IsCommiting && surroundingText.AnchorOffset == surroundingText.CursorOffset)
{
_inputConnection.CommitText(_inputConnection.ComposingText, 0);
_inputConnection.SetSelection(surroundingText.AnchorOffset, surroundingText.CursorOffset);
}
} }
} }
@ -153,10 +161,20 @@ namespace Avalonia.Android
return _inputConnection; return _inputConnection;
}); });
} }
}
private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e) public readonly struct ComposingRegion
{
private readonly int _start = -1;
private readonly int _end = -1;
public ComposingRegion(int start, int end)
{ {
_imm.ShowSoftInput(_host, ShowFlags.Implicit); _start = start;
_end = end;
} }
public int Start => _start;
public int End => _end;
} }
} }

92
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -3,7 +3,9 @@ using System.Collections.Generic;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.Graphics; using Android.Graphics;
using Android.OS;
using Android.Runtime; using Android.Runtime;
using Android.Text;
using Android.Views; using Android.Views;
using Android.Views.InputMethods; using Android.Views.InputMethods;
using Avalonia.Android.OpenGL; using Avalonia.Android.OpenGL;
@ -23,6 +25,7 @@ using Avalonia.Platform.Storage;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using Java.Lang; using Java.Lang;
using Math = System.Math;
namespace Avalonia.Android.Platform.SkiaPlatform namespace Avalonia.Android.Platform.SkiaPlatform
{ {
@ -276,8 +279,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
internal class AvaloniaInputConnection : BaseInputConnection internal class AvaloniaInputConnection : BaseInputConnection
@ -293,11 +294,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public TextInputMethodSurroundingText SurroundingText { get; set; } public TextInputMethodSurroundingText SurroundingText { get; set; }
public string ComposingText { get; private set; } public string ComposingText { get; internal set; }
public ComposingRegion ComposingRegion { get; private set; } public ComposingRegion? ComposingRegion { get; internal set; }
public bool IsComposing { get; private set; } public bool IsComposing => !string.IsNullOrEmpty(ComposingText);
public bool IsCommiting { get; private set; }
public override bool SetComposingRegion(int start, int end) public override bool SetComposingRegion(int start, int end)
{ {
@ -314,8 +316,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
ComposingText = composingText; ComposingText = composingText;
IsComposing = true;
_inputMethod.Client?.SetPreeditText(ComposingText); _inputMethod.Client?.SetPreeditText(ComposingText);
return base.SetComposingText(text, newCursorPosition); return base.SetComposingText(text, newCursorPosition);
@ -323,20 +323,25 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public override bool FinishComposingText() public override bool FinishComposingText()
{ {
IsComposing = false; if (!string.IsNullOrEmpty(ComposingText))
{
ComposingRegion = new ComposingRegion(SurroundingText.CursorOffset, SurroundingText.CursorOffset); CommitText(ComposingText, ComposingText.Length);
}
else
{
ComposingRegion = new ComposingRegion(SurroundingText.CursorOffset, SurroundingText.CursorOffset);
}
return base.FinishComposingText(); return base.FinishComposingText();
} }
public override ICharSequence GetTextBeforeCursorFormatted(int length, [GeneratedEnum] GetTextFlags flags) public override ICharSequence GetTextBeforeCursorFormatted(int length, [GeneratedEnum] GetTextFlags flags)
{ {
if (!string.IsNullOrEmpty(SurroundingText.Text)) if (!string.IsNullOrEmpty(SurroundingText.Text) && length > 0)
{ {
var start = System.Math.Max(SurroundingText.CursorOffset - length, 0); var start = System.Math.Max(SurroundingText.CursorOffset - length, 0);
var end = System.Math.Min(start + length, SurroundingText.CursorOffset); var end = System.Math.Min(start + length - 1, SurroundingText.CursorOffset);
var text = SurroundingText.Text.Substring(start, end - start); var text = SurroundingText.Text.Substring(start, end - start);
@ -368,11 +373,31 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public override bool CommitText(ICharSequence text, int newCursorPosition) public override bool CommitText(ICharSequence text, int newCursorPosition)
{ {
IsCommiting = true;
var committedText = text.ToString(); var committedText = text.ToString();
_inputMethod.Client.SetPreeditText(null); _inputMethod.Client.SetPreeditText(null);
_inputMethod.Client.SelectInSurroundingText(ComposingRegion.Start, ComposingRegion.End); int? start, end;
if(SurroundingText.CursorOffset != SurroundingText.AnchorOffset)
{
start = Math.Min(SurroundingText.CursorOffset, SurroundingText.AnchorOffset);
end = Math.Max(SurroundingText.CursorOffset, SurroundingText.AnchorOffset);
}
else if (ComposingRegion != null)
{
start = ComposingRegion?.Start;
end = ComposingRegion?.End;
ComposingRegion = null;
}
else
{
start = end = _inputMethod.Client.SurroundingText.CursorOffset;
}
_inputMethod.Client.SelectInSurroundingText((int)start, (int)end);
var time = DateTime.Now.TimeOfDay; var time = DateTime.Now.TimeOfDay;
@ -380,15 +405,29 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_topLevel.Input(rawTextEvent); _topLevel.Input(rawTextEvent);
ComposingText = null;
ComposingRegion = new ComposingRegion(newCursorPosition, newCursorPosition);
return base.CommitText(text, newCursorPosition); return base.CommitText(text, newCursorPosition);
} }
public override bool DeleteSurroundingText(int beforeLength, int afterLength) public override bool DeleteSurroundingText(int beforeLength, int afterLength)
{ {
_inputMethod.Client.SelectInSurroundingText(beforeLength, afterLength); var surroundingText = _inputMethod.Client.SurroundingText;
var selectionStart = surroundingText.CursorOffset;
_inputMethod.Client.SelectInSurroundingText(selectionStart - beforeLength, selectionStart + afterLength);
_inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); _inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
surroundingText = _inputMethod.Client.SurroundingText;
selectionStart = surroundingText.CursorOffset;
ComposingRegion = new ComposingRegion(selectionStart, selectionStart);
return base.DeleteSurroundingText(beforeLength, afterLength); return base.DeleteSurroundingText(beforeLength, afterLength);
} }
@ -396,22 +435,23 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{ {
_inputMethod.Client.SelectInSurroundingText(start, end); _inputMethod.Client.SelectInSurroundingText(start, end);
ComposingRegion = new ComposingRegion(start, end);
return base.SetSelection(start, end); return base.SetSelection(start, end);
} }
}
public readonly struct ComposingRegion
{
private readonly int _start = -1;
private readonly int _end = -1;
public ComposingRegion(int start, int end) public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode)
{ {
_start = start; switch (actionCode)
_end = end; {
} case ImeAction.Done:
{
_inputMethod.IMM.HideSoftInputFromWindow(_inputMethod.View.WindowToken, HideSoftInputFlags.ImplicitOnly);
break;
}
}
public int Start => _start; return base.PerformEditorAction(actionCode);
public int End => _end; }
} }
} }

2
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@ -63,7 +63,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
{ {
var rawTextEvent = new RawTextInputEventArgs( var rawTextEvent = new RawTextInputEventArgs(
AndroidKeyboardDevice.Instance, AndroidKeyboardDevice.Instance,
Convert.ToUInt32(e.EventTime), Convert.ToUInt64(e.EventTime),
_view.InputRoot, _view.InputRoot,
unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString() unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString()
); );

1
src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs

@ -29,6 +29,7 @@ namespace Avalonia.Input.TextInput
/// Sets the non-committed input string /// Sets the non-committed input string
/// </summary> /// </summary>
void SetPreeditText(string? text); void SetPreeditText(string? text);
/// <summary> /// <summary>
/// Indicates if text input client is capable of providing the text around the cursor /// Indicates if text input client is capable of providing the text around the cursor
/// </summary> /// </summary>

2
src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs

@ -140,7 +140,7 @@ namespace Avalonia.Media.TextFormatting
} }
} }
return length; return Math.Min(length, text.Length);
} }
} }
} }

29
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -9,6 +9,7 @@ using Avalonia.VisualTree;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
using Avalonia.Controls.Documents; using Avalonia.Controls.Documents;
using Avalonia.Input.TextInput;
namespace Avalonia.Controls.Presenters namespace Avalonia.Controls.Presenters
{ {
@ -514,7 +515,12 @@ namespace Avalonia.Controls.Presenters
{ {
if (!string.IsNullOrEmpty(_preeditText)) if (!string.IsNullOrEmpty(_preeditText))
{ {
var text = _text?.Substring(0, _caretIndex) + _preeditText + _text?.Substring(_caretIndex); if (string.IsNullOrEmpty(_text) || _caretIndex > _text.Length)
{
return _preeditText;
}
var text = _text.Substring(0, _caretIndex) + _preeditText + _text.Substring(_caretIndex);
return text; return text;
} }
@ -868,28 +874,11 @@ namespace Avalonia.Controls.Presenters
if (string.IsNullOrEmpty(newValue)) if (string.IsNullOrEmpty(newValue))
{ {
if (!string.IsNullOrEmpty(oldValue)) UpdateCaret(_lastCharacterHit);
{
var textPosition = _compositionStartHit.FirstCharacterIndex + _compositionStartHit.TrailingLength + newValue?.Length ?? 0;
var characterHit = GetCharacterHitFromTextPosition(textPosition);
UpdateCaret(characterHit, true);
}
_compositionStartHit = new CharacterHit(-1);
} }
else else
{ {
if (_compositionStartHit.FirstCharacterIndex == -1) var textPosition = _caretIndex + newValue?.Length ?? 0;
{
_compositionStartHit = _lastCharacterHit;
}
}
if (_compositionStartHit.FirstCharacterIndex != -1)
{
var textPosition = _compositionStartHit.FirstCharacterIndex + _compositionStartHit.TrailingLength + newValue?.Length ?? 0;
var characterHit = GetCharacterHitFromTextPosition(textPosition); var characterHit = GetCharacterHitFromTextPosition(textPosition);

2
src/Avalonia.Controls/TextBox.cs

@ -1212,7 +1212,7 @@ namespace Avalonia.Controls
protected override void OnPointerPressed(PointerPressedEventArgs e) protected override void OnPointerPressed(PointerPressedEventArgs e)
{ {
if (_presenter == null || !string.IsNullOrEmpty(_presenter.PreeditText)) if (_presenter == null )
{ {
return; return;
} }

Loading…
Cancel
Save