From 2e0103a9db44a81f29f9b95abc2f1bfe35adfb46 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 16 Sep 2022 18:21:26 +0200 Subject: [PATCH] Move all text input to the InputHelperInterop --- .../Avalonia.Web.Blazor/AvaloniaView.razor | 2 +- .../Avalonia.Web.Blazor/AvaloniaView.razor.cs | 38 +++++++------ .../Interop/InputHelperInterop.cs | 55 +++++++++++++++---- .../webapp/modules/Avalonia/InputHelper.ts | 37 ++++++++++--- 4 files changed, 97 insertions(+), 35 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index 8bfa1d6014..1c88f35106 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -16,7 +16,7 @@
- _client != null; + public bool IsComposing { get; private set; } + internal INativeControlHostImpl GetNativeControlHostImpl() { return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet"); @@ -231,20 +234,6 @@ namespace Avalonia.Web.Blazor } } - private void OnInput(ChangeEventArgs e) - { - if (e.Value != null) - { - var inputData = e.Value.ToString(); - if (inputData != null) - { - _topLevelImpl.RawTextEvent(inputData); - } - } - - _inputHelper?.Clear(); - } - [Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary? AdditionalAttributes { get; set; } @@ -261,6 +250,7 @@ namespace Avalonia.Web.Blazor _inputHelper = new InputHelperInterop(_avaloniaModule, _inputElement); _inputHelper.CompositionEvent += InputHelperOnCompositionEvent; + _inputHelper.InputEvent += InputHelperOnInputEvent; HideIme(); _canvasHelper.SetCursor("default"); @@ -333,6 +323,16 @@ namespace Avalonia.Web.Blazor } } + private void InputHelperOnInputEvent(object? sender, WebInputEventArgs e) + { + if (IsComposing) + { + return; + } + + _topLevelImpl.RawTextEvent(e.Data); + } + private void InputHelperOnCompositionEvent(object? sender, WebCompositionEventArgs e) { if(_client == null) @@ -344,12 +344,15 @@ namespace Avalonia.Web.Blazor { case WebCompositionEventArgs.WebCompositionEventType.Start: _client.SetPreeditText(null); + IsComposing = true; break; case WebCompositionEventArgs.WebCompositionEventType.Update: _client?.SetPreeditText(e.Data); break; case WebCompositionEventArgs.WebCompositionEventType.End: + IsComposing = false; _client?.SetPreeditText(null); + _topLevelImpl.RawTextEvent(e.Data); break; } } @@ -462,9 +465,12 @@ namespace Avalonia.Web.Blazor private void SurroundingTextChanged(object? sender, EventArgs e) { - var surroundingText = _client.SurroundingText; + if(_client != null && IsComposing) + { + var surroundingText = _client.SurroundingText; - _inputHelper?.SetSurroundingText(surroundingText.Text, surroundingText.AnchorOffset, surroundingText.CursorOffset); + _inputHelper?.SetSurroundingText(surroundingText.Text, surroundingText.AnchorOffset, surroundingText.CursorOffset); + } } public void SetCursorRect(Rect rect) diff --git a/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs index bd4e3f92f6..2dbc2027ab 100644 --- a/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs +++ b/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; namespace Avalonia.Web.Blazor.Interop @@ -30,6 +29,19 @@ namespace Avalonia.Web.Blazor.Interop public string Data { get; } } + + internal class WebInputEventArgs + { + public WebInputEventArgs(string type, string data) + { + Type = type; + Data = data; + } + + public string Type { get; } + + public string Data { get; } + } internal class InputHelperInterop { @@ -39,23 +51,29 @@ namespace Avalonia.Web.Blazor.Interop private const string HideSymbol = "InputHelper.hide"; private const string ShowSymbol = "InputHelper.show"; private const string StartSymbol = "InputHelper.start"; + private const string SetSurroundingTextSymbol = "InputHelper.setSurroundingText"; private readonly AvaloniaModule _module; private readonly ElementReference _inputElement; - private readonly ActionHelper _actionHelper; - private DotNetObjectReference>? callbackReference; + private readonly ActionHelper _compositionAction; + private readonly ActionHelper _inputAction; + + private DotNetObjectReference>? compositionActionReference; + private DotNetObjectReference>? inputActionReference; public InputHelperInterop(AvaloniaModule module, ElementReference inputElement) { _module = module; _inputElement = inputElement; - _actionHelper = new ActionHelper(OnCompositionEvent); - + _compositionAction = new ActionHelper(OnCompositionEvent); + _inputAction = new ActionHelper(OnInputEvent); + Start(); } - public event EventHandler? CompositionEvent; + public event EventHandler? CompositionEvent; + public event EventHandler? InputEvent; private void OnCompositionEvent(string type, string data) { @@ -63,6 +81,12 @@ namespace Avalonia.Web.Blazor.Interop CompositionEvent?.Invoke(this, new WebCompositionEventArgs(type, data)); } + private void OnInputEvent(string type, string data) + { + Console.WriteLine($"InputEvent Handler Helper {InputEvent == null} "); + InputEvent?.Invoke(this, new WebInputEventArgs(type, data)); + } + public void Clear() => _module.Invoke(ClearSymbol, _inputElement); public void Focus() => _module.Invoke(FocusSymbol, _inputElement); @@ -75,12 +99,21 @@ namespace Avalonia.Web.Blazor.Interop private void Start() { - if(callbackReference != null) + if(compositionActionReference != null) + { return; - - callbackReference = DotNetObjectReference.Create(_actionHelper); + } + + compositionActionReference = DotNetObjectReference.Create(_compositionAction); - _module.Invoke(StartSymbol, _inputElement, callbackReference); + inputActionReference = DotNetObjectReference.Create(_inputAction); + + _module.Invoke(StartSymbol, _inputElement, compositionActionReference, inputActionReference); + } + + public void SetSurroundingText(string text, int start, int end) + { + _module.Invoke(SetSurroundingTextSymbol, _inputElement, text, start, end); } } } diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts index 409fdebae9..c38eb66e67 100644 --- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts +++ b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts @@ -1,13 +1,18 @@ export class InputHelper { - static callback?: DotNet.DotNetObject; + static inputCallback?: DotNet.DotNetObject; + static compositionCallback?: DotNet.DotNetObject - public static start(inputElement: HTMLInputElement, callback: DotNet.DotNetObject) - { - InputHelper.callback = callback; + public static start(inputElement: HTMLInputElement, compositionCallback: DotNet.DotNetObject, inputCallback: DotNet.DotNetObject) + { + InputHelper.compositionCallback = compositionCallback; inputElement.addEventListener('compositionstart', InputHelper.onCompositionEvent); inputElement.addEventListener('compositionupdate', InputHelper.onCompositionEvent); - inputElement.addEventListener('compositionend', InputHelper.onCompositionEvent); + inputElement.addEventListener('compositionend', InputHelper.onCompositionEvent); + + InputHelper.inputCallback = inputCallback; + + inputElement.addEventListener('input', InputHelper.onInputEvent); } public static clear(inputElement: HTMLInputElement) { @@ -30,9 +35,18 @@ inputElement.style.display = 'block'; } + public static setSurroundingText(inputElement: HTMLInputElement, text: string, start: number, end: number) { + if (!inputElement) { + return; + } + + inputElement.value = text; + inputElement.setSelectionRange(start, end); + } + private static onCompositionEvent(ev: CompositionEvent) { - if(!InputHelper.callback) + if(!InputHelper.compositionCallback) return; switch (ev.type) @@ -40,9 +54,18 @@ case "compositionstart": case "compositionupdate": case "compositionend": - InputHelper.callback.invokeMethod('Invoke', ev.type, ev.data); + InputHelper.compositionCallback.invokeMethod('Invoke', ev.type, ev.data); break; } } + + private static onInputEvent(ev: Event) { + if (!InputHelper.inputCallback) + return; + + var inputEvent = ev as InputEvent; + + InputHelper.inputCallback.invokeMethod('Invoke', ev.type, inputEvent.data); + } }