From c0cd31c85f2ac948bf9a1415904185d078e65bbd Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Mon, 4 Nov 2024 02:47:48 -0500 Subject: [PATCH 1/9] Extend text input properties to include type of text input --- .../UserInterface/BasicPasswordTextBox.cs | 20 ------ .../UserInterface/DropdownSearchBar.cs | 12 ++-- .../Graphics/UserInterface/TextBox.cs | 49 ++++++++----- .../Input/ISuppressKeyEventLogging.cs | 8 ++- osu.Framework/Input/InputManager.cs | 5 +- osu.Framework/Input/SDLWindowTextInput.cs | 8 +-- osu.Framework/Input/TextInputProperties.cs | 21 ++++++ osu.Framework/Input/TextInputSource.cs | 28 ++++---- osu.Framework/Input/TextInputType.cs | 72 +++++++++++++++++++ osu.Framework/Platform/ISDLWindow.cs | 2 +- .../Platform/SDL2/SDL2Window_Input.cs | 2 +- osu.Framework/Platform/SDL3/SDL3Extensions.cs | 28 ++++++++ .../Platform/SDL3/SDL3Window_Input.cs | 8 ++- .../Platform/Windows/SDL2WindowsWindow.cs | 7 +- .../Platform/Windows/SDL3WindowsWindow.cs | 7 +- .../Testing/Input/ManualTextInputSource.cs | 16 ++--- 16 files changed, 210 insertions(+), 83 deletions(-) delete mode 100644 osu.Framework/Graphics/UserInterface/BasicPasswordTextBox.cs create mode 100644 osu.Framework/Input/TextInputProperties.cs create mode 100644 osu.Framework/Input/TextInputType.cs diff --git a/osu.Framework/Graphics/UserInterface/BasicPasswordTextBox.cs b/osu.Framework/Graphics/UserInterface/BasicPasswordTextBox.cs deleted file mode 100644 index b0791317b8..0000000000 --- a/osu.Framework/Graphics/UserInterface/BasicPasswordTextBox.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Input; - -namespace osu.Framework.Graphics.UserInterface -{ - public partial class BasicPasswordTextBox : BasicTextBox, ISuppressKeyEventLogging - { - protected virtual char MaskCharacter => '*'; - - protected override bool AllowClipboardExport => false; - - protected override bool AllowWordNavigation => false; - - protected override bool AllowIme => false; - - protected override Drawable AddCharacterToFlow(char c) => base.AddCharacterToFlow(MaskCharacter); - } -} diff --git a/osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs b/osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs index c0879d6ac5..2e1b33c995 100644 --- a/osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs +++ b/osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs @@ -197,20 +197,20 @@ public DropdownTextInputSource(TextInputSource platformSource, GameHost host) platformSource.OnImeResult += TriggerImeResult; } - protected override void ActivateTextInput(bool allowIme) + protected override void ActivateTextInput(TextInputProperties properties) { - base.ActivateTextInput(allowIme); + base.ActivateTextInput(properties); if (allowTextInput) - platformSource.Activate(allowIme, imeRectangle ?? RectangleF.Empty); + platformSource.Activate(properties, imeRectangle ?? RectangleF.Empty); } - protected override void EnsureTextInputActivated(bool allowIme) + protected override void EnsureTextInputActivated(TextInputProperties properties) { - base.EnsureTextInputActivated(allowIme); + base.EnsureTextInputActivated(properties); if (allowTextInput) - platformSource.EnsureActivated(allowIme, imeRectangle); + platformSource.EnsureActivated(properties, imeRectangle); } protected override void DeactivateTextInput() diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index e825299cd2..810e2f89db 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -29,13 +29,18 @@ namespace osu.Framework.Graphics.UserInterface { - public abstract partial class TextBox : TabbableContainer, IHasCurrentValue, IKeyBindingHandler + public abstract partial class TextBox : TabbableContainer, IHasCurrentValue, IKeyBindingHandler, ICanSuppressKeyEventLogging { protected FillFlowContainer TextFlow { get; private set; } protected Container TextContainer { get; private set; } public override bool HandleNonPositionalInput => HasFocus; + /// + /// A character displayed whenever the type of text input set by is hidden. + /// + protected virtual char MaskCharacter => '*'; + /// /// Padding to be used within the TextContainer. Requires special handling due to the sideways scrolling of text content. /// @@ -50,12 +55,14 @@ public abstract partial class TextBox : TabbableContainer, IHasCurrentValue /// Whether clipboard copying functionality is allowed. /// - protected virtual bool AllowClipboardExport => true; + protected virtual bool AllowClipboardExport => !InputProperties.Type.IsPassword(); /// /// Whether seeking to word boundaries is allowed. /// - protected virtual bool AllowWordNavigation => true; + protected virtual bool AllowWordNavigation => !InputProperties.Type.IsPassword(); + + bool ICanSuppressKeyEventLogging.SuppressKeyEventLogging => InputProperties.Type.IsPassword(); /// /// Represents the left/right selection coordinates of the word double clicked on when dragging. @@ -67,17 +74,13 @@ public abstract partial class TextBox : TabbableContainer, IHasCurrentValue public virtual bool HandleLeftRightArrows => true; + [Obsolete($"Use {nameof(InputProperties)} instead.")] // can be removed 20250506 + protected virtual bool AllowIme => true; + /// - /// Whether to allow IME input when this text box has input focus. + /// A set of properties to consider when interacting with this . /// - /// - /// This is just a hint to the native implementation, some might respect this, - /// while others will ignore and always have the IME (dis)allowed. - /// - /// - /// Useful for situations where IME input is not wanted, such as for passwords, numbers, or romanised text. - /// - protected virtual bool AllowIme => true; + public TextInputProperties InputProperties { get; init; } /// /// Check if a character can be added to this TextBox. @@ -87,9 +90,14 @@ public abstract partial class TextBox : TabbableContainer, IHasCurrentValue true; /// - /// Private helper for , additionally requiring that the character is not a control character. + /// Private helper for , additionally requiring that the character is not a control character and obeys . /// - private bool canAddCharacter(char character) => !char.IsControl(character) && CanAddCharacter(character); + private bool canAddCharacter(char character) + { + return !char.IsControl(character) + && (!InputProperties.Type.IsNumerical() || char.IsAsciiDigit(character)) + && CanAddCharacter(character); + } private bool readOnly; @@ -158,6 +166,8 @@ public bool ReadOnly protected TextBox() { + InputProperties = new TextInputProperties(TextInputType.Text, true); + Masking = true; Children = new Drawable[] @@ -789,6 +799,9 @@ private string removeCharacters(int number = 1) protected virtual Drawable AddCharacterToFlow(char c) { + if (InputProperties.Type.IsPassword()) + c = MaskCharacter; + // Remove all characters to the right and store them in a local list, // such that their depth can be updated. List charsRight = new List(); @@ -1339,7 +1352,7 @@ protected override void OnFocusLost(FocusLostEvent e) protected override bool OnClick(ClickEvent e) { if (!ReadOnly && textInputBound) - textInput.EnsureActivated(AllowIme); + textInput.EnsureActivated(InputProperties); return !ReadOnly; } @@ -1366,7 +1379,7 @@ private void bindInput([CanBeNull] TextBox previous) if (textInputBound) { - textInput.EnsureActivated(AllowIme); + textInput.EnsureActivated(InputProperties); return; } @@ -1374,9 +1387,9 @@ private void bindInput([CanBeNull] TextBox previous) // We don't deactivate and activate, but instead keep text input active during the focus handoff, so that virtual keyboards on phones don't flicker. if (previous?.textInput == textInput) - textInput.EnsureActivated(AllowIme, ScreenSpaceDrawQuad.AABBFloat); + textInput.EnsureActivated(InputProperties, ScreenSpaceDrawQuad.AABBFloat); else - textInput.Activate(AllowIme, ScreenSpaceDrawQuad.AABBFloat); + textInput.Activate(InputProperties, ScreenSpaceDrawQuad.AABBFloat); textInput.OnTextInput += handleTextInput; textInput.OnImeComposition += handleImeComposition; diff --git a/osu.Framework/Input/ISuppressKeyEventLogging.cs b/osu.Framework/Input/ISuppressKeyEventLogging.cs index 686ab71a97..02f6f47644 100644 --- a/osu.Framework/Input/ISuppressKeyEventLogging.cs +++ b/osu.Framework/Input/ISuppressKeyEventLogging.cs @@ -4,10 +4,14 @@ namespace osu.Framework.Input { /// - /// Marker interface which suppresses logging of keyboard input events. + /// An interface which suppresses logging of keyboard input events. /// Useful for password fields, where user input should not be logged. /// - public interface ISuppressKeyEventLogging + public interface ICanSuppressKeyEventLogging { + /// + /// Whether key event logging should be suppressed for this drawable. + /// + bool SuppressKeyEventLogging { get; } } } diff --git a/osu.Framework/Input/InputManager.cs b/osu.Framework/Input/InputManager.cs index debc7cef14..4235dc2ad0 100644 --- a/osu.Framework/Input/InputManager.cs +++ b/osu.Framework/Input/InputManager.cs @@ -1003,7 +1003,10 @@ protected virtual bool PropagateBlockableEvent(SlimReadOnlyListWrapper if (shouldLog(e)) { - string detail = d is ISuppressKeyEventLogging ? e.GetType().ReadableName() : e.ToString(); + string detail = d is ICanSuppressKeyEventLogging kd && kd.SuppressKeyEventLogging + ? e.GetType().ReadableName() + : e.ToString(); + Logger.Log($"{detail} handled by {d}.", LoggingTarget.Runtime, LogLevel.Debug); } diff --git a/osu.Framework/Input/SDLWindowTextInput.cs b/osu.Framework/Input/SDLWindowTextInput.cs index c0fd539f44..79fa1d848f 100644 --- a/osu.Framework/Input/SDLWindowTextInput.cs +++ b/osu.Framework/Input/SDLWindowTextInput.cs @@ -37,16 +37,16 @@ private void handleTextEditing(string? text, int selectionStart, int selectionLe TriggerImeComposition(text, selectionStart, selectionLength); } - protected override void ActivateTextInput(bool allowIme) + protected override void ActivateTextInput(TextInputProperties properties) { window.TextInput += handleTextInput; window.TextEditing += handleTextEditing; - window.StartTextInput(allowIme); + window.StartTextInput(properties); } - protected override void EnsureTextInputActivated(bool allowIme) + protected override void EnsureTextInputActivated(TextInputProperties properties) { - window.StartTextInput(allowIme); + window.StartTextInput(properties); } protected override void DeactivateTextInput() diff --git a/osu.Framework/Input/TextInputProperties.cs b/osu.Framework/Input/TextInputProperties.cs new file mode 100644 index 0000000000..66d7f6638e --- /dev/null +++ b/osu.Framework/Input/TextInputProperties.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Framework.Input +{ + /// + /// Represents a number of properties to consider during a text input session. + /// + /// The type of text being inputted. + /// + /// + /// Whether IME should be allowed during this text input session. + /// Useful for situations where IME input is not wanted, such as for passwords, numbers, or romanised text. + /// + /// + /// Note that this is just a hint to the native implementation, some might respect this, + /// while others will ignore and always have the IME (dis)allowed. + /// + /// + public record struct TextInputProperties(TextInputType Type, bool AllowIme); +} diff --git a/osu.Framework/Input/TextInputSource.cs b/osu.Framework/Input/TextInputSource.cs index 237d17f36d..9b77568e30 100644 --- a/osu.Framework/Input/TextInputSource.cs +++ b/osu.Framework/Input/TextInputSource.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Threading; using osu.Framework.Graphics.Primitives; @@ -29,7 +27,7 @@ public class TextInputSource /// Activates this . /// User text input can be acquired through , and . /// - /// Whether input using IME should be allowed. + /// A set of properties to consider during this text input session. /// /// Rough location of where the text will be input, so the native implementation /// can adjust virtual keyboards and IME popups. @@ -37,36 +35,36 @@ public class TextInputSource /// /// Each must be followed by a . /// - public void Activate(bool allowIme, RectangleF imeRectangle) + public void Activate(TextInputProperties properties, RectangleF imeRectangle) { if (Interlocked.Increment(ref activationCounter) == 1) { SetImeRectangle(imeRectangle); - ActivateTextInput(allowIme); + ActivateTextInput(properties); } else // the latest consumer that activated should always take precedence in (dis)allowing IME. - EnsureActivated(allowIme, imeRectangle); + EnsureActivated(properties, imeRectangle); } /// /// Ensures that the native implementation that retrieves user text input is activated /// and that the user can start entering text. /// - /// Whether input using IME should be allowed. + /// A set of properties to consider during this text input session. /// /// Rough location of where the text will be input, so the native implementation /// can adjust virtual keyboards and IME popups. Can be null to avoid changing /// the IME rectangle. /// - public void EnsureActivated(bool allowIme, RectangleF? imeRectangle = null) + public void EnsureActivated(TextInputProperties properties, RectangleF? imeRectangle = null) { if (activationCounter >= 1) { if (imeRectangle.HasValue) SetImeRectangle(imeRectangle.Value); - EnsureTextInputActivated(allowIme); + EnsureTextInputActivated(properties); } } @@ -103,29 +101,29 @@ public virtual void ResetIme() /// /// Invoked on text input. /// - public event Action OnTextInput; + public event Action? OnTextInput; /// /// Invoked when IME composition starts or changes. /// /// Empty string for text means that the composition has been cancelled. - public event ImeCompositionDelegate OnImeComposition; + public event ImeCompositionDelegate? OnImeComposition; /// /// Invoked when IME composition successfully completes. /// - public event Action OnImeResult; + public event Action? OnImeResult; /// /// Activates the native implementation that provides text input. /// Should be overriden per-platform. /// - /// Whether input using IME should be allowed. + /// A set of properties to consider during this text input session. /// /// An active native implementation should call on new text input /// and forward IME composition events through and . /// - protected virtual void ActivateTextInput(bool allowIme) + protected virtual void ActivateTextInput(TextInputProperties properties) { } @@ -134,7 +132,7 @@ protected virtual void ActivateTextInput(bool allowIme) /// /// Only called if the native implementation has been activated with . /// - protected virtual void EnsureTextInputActivated(bool allowIme) + protected virtual void EnsureTextInputActivated(TextInputProperties properties) { } diff --git a/osu.Framework/Input/TextInputType.cs b/osu.Framework/Input/TextInputType.cs new file mode 100644 index 0000000000..42da0afffe --- /dev/null +++ b/osu.Framework/Input/TextInputType.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Framework.Input +{ + public enum TextInputType + { + /// + /// Plain text, default type of text input. + /// + Text, + + /// + /// The text input is a person's name. + /// + Name, + + /// + /// The text input is an email address. + /// + EmailAddress, + + /// + /// The text input is a username. + /// + Username, + + /// + /// The text input is numerical. + /// + Number, + + /// + /// The text input is a password hidden from the user. + /// + Password, + + /// + /// The text input is a numerical password hidden from the user. + /// + NumericalPassword, + } + + public static class TextInputTypeExtensions + { + public static bool IsPassword(this TextInputType type) + { + switch (type) + { + case TextInputType.Password: + case TextInputType.NumericalPassword: + return true; + + default: + return false; + } + } + + public static bool IsNumerical(this TextInputType type) + { + switch (type) + { + case TextInputType.Number: + case TextInputType.NumericalPassword: + return true; + + default: + return false; + } + } + } +} diff --git a/osu.Framework/Platform/ISDLWindow.cs b/osu.Framework/Platform/ISDLWindow.cs index 66f627e28e..fde3545b25 100644 --- a/osu.Framework/Platform/ISDLWindow.cs +++ b/osu.Framework/Platform/ISDLWindow.cs @@ -42,7 +42,7 @@ internal interface ISDLWindow : IWindow void UpdateMousePosition(Vector2 position); - void StartTextInput(bool allowIme); + void StartTextInput(TextInputProperties properties); void StopTextInput(); void SetTextInputRect(RectangleF rectangle); void ResetIme(); diff --git a/osu.Framework/Platform/SDL2/SDL2Window_Input.cs b/osu.Framework/Platform/SDL2/SDL2Window_Input.cs index 43a62dd8ba..5c0088392d 100644 --- a/osu.Framework/Platform/SDL2/SDL2Window_Input.cs +++ b/osu.Framework/Platform/SDL2/SDL2Window_Input.cs @@ -172,7 +172,7 @@ private void pollMouse() } } - public virtual void StartTextInput(bool allowIme) => ScheduleCommand(SDL_StartTextInput); + public virtual void StartTextInput(TextInputProperties properties) => ScheduleCommand(SDL_StartTextInput); public void StopTextInput() => ScheduleCommand(SDL_StopTextInput); diff --git a/osu.Framework/Platform/SDL3/SDL3Extensions.cs b/osu.Framework/Platform/SDL3/SDL3Extensions.cs index 711d57fe1b..880407b7eb 100644 --- a/osu.Framework/Platform/SDL3/SDL3Extensions.cs +++ b/osu.Framework/Platform/SDL3/SDL3Extensions.cs @@ -1010,6 +1010,34 @@ public static SDL_Rect ToSDLRect(this RectangleI rectangle) => w = rectangle.Width, }; + public static SDL_TextInputType ToSDLTextInputType(this TextInputType type) + { + switch (type) + { + default: + case TextInputType.Text: + return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT; + + case TextInputType.Name: + return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT_NAME; + + case TextInputType.EmailAddress: + return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT_EMAIL; + + case TextInputType.Username: + return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT_USERNAME; + + case TextInputType.Number: + return SDL_TextInputType.SDL_TEXTINPUT_TYPE_NUMBER; + + case TextInputType.Password: + return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN; + + case TextInputType.NumericalPassword: + return SDL_TextInputType.SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN; + } + } + public static unsafe DisplayMode ToDisplayMode(this SDL_DisplayMode mode, int displayIndex) { int bpp; diff --git a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs index 5c340f8e37..130d3dc50d 100644 --- a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs +++ b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs @@ -187,7 +187,13 @@ private void pollMouse() } } - public virtual void StartTextInput(bool allowIme) => ScheduleCommand(() => SDL_StartTextInput(SDLWindowHandle)); + public virtual void StartTextInput(TextInputProperties properties) => ScheduleCommand(() => + { + var props = SDL_CreateProperties(); + SDL_SetNumberProperty(props, SDL_PROP_TEXTINPUT_TYPE_NUMBER, (long)properties.Type.ToSDLTextInputType()); + SDL_StartTextInputWithProperties(SDLWindowHandle, props); + SDL_DestroyProperties(props); + }); public void StopTextInput() => ScheduleCommand(() => SDL_StopTextInput(SDLWindowHandle)); diff --git a/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs b/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs index 4284cbf0e1..f04bcd4c75 100644 --- a/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs +++ b/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs @@ -5,6 +5,7 @@ using System.Drawing; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using osu.Framework.Input; using osu.Framework.Input.Handlers.Mouse; using osu.Framework.Platform.SDL2; using osu.Framework.Platform.Windows.Native; @@ -137,10 +138,10 @@ private void warpCursorFromFocusLoss() #region IME handling - public override void StartTextInput(bool allowIme) + public override void StartTextInput(TextInputProperties properties) { - base.StartTextInput(allowIme); - ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, allowIme)); + base.StartTextInput(properties); + ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, properties.AllowIme)); } public override void ResetIme() => ScheduleCommand(() => Imm.CancelComposition(WindowHandle)); diff --git a/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs b/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs index 7c065b98f2..e431ef4f83 100644 --- a/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs +++ b/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using osu.Framework.Allocation; +using osu.Framework.Input; using osu.Framework.Input.Handlers.Mouse; using osu.Framework.Platform.SDL3; using osu.Framework.Platform.Windows.Native; @@ -127,10 +128,10 @@ private void warpCursorFromFocusLoss() #region IME handling - public override void StartTextInput(bool allowIme) + public override void StartTextInput(TextInputProperties properties) { - base.StartTextInput(allowIme); - ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, allowIme)); + base.StartTextInput(properties); + ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, properties.AllowIme)); } public override void ResetIme() => ScheduleCommand(() => Imm.CancelComposition(WindowHandle)); diff --git a/osu.Framework/Testing/Input/ManualTextInputSource.cs b/osu.Framework/Testing/Input/ManualTextInputSource.cs index def56cb23b..94b722da5e 100644 --- a/osu.Framework/Testing/Input/ManualTextInputSource.cs +++ b/osu.Framework/Testing/Input/ManualTextInputSource.cs @@ -8,8 +8,8 @@ namespace osu.Framework.Testing.Input { public class ManualTextInputSource : TextInputSource { - public readonly Queue ActivationQueue = new Queue(); - public readonly Queue EnsureActivatedQueue = new Queue(); + public readonly Queue ActivationQueue = new Queue(); + public readonly Queue EnsureActivatedQueue = new Queue(); public readonly Queue DeactivationQueue = new Queue(); public void Text(string text) => TriggerTextInput(text); @@ -32,16 +32,16 @@ public override void ResetIme() base.TriggerImeComposition(string.Empty, 0, 0); } - protected override void ActivateTextInput(bool allowIme) + protected override void ActivateTextInput(TextInputProperties properties) { - base.ActivateTextInput(allowIme); - ActivationQueue.Enqueue(allowIme); + base.ActivateTextInput(properties); + ActivationQueue.Enqueue(properties); } - protected override void EnsureTextInputActivated(bool allowIme) + protected override void EnsureTextInputActivated(TextInputProperties properties) { - base.EnsureTextInputActivated(allowIme); - EnsureActivatedQueue.Enqueue(allowIme); + base.EnsureTextInputActivated(properties); + EnsureActivatedQueue.Enqueue(properties); } protected override void DeactivateTextInput() From d7ce29d5d18bd4548b615037de8b06e788bec196 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Mon, 4 Nov 2024 04:34:04 -0500 Subject: [PATCH 2/9] Update test code --- .../Visual/UserInterface/TestSceneTextBox.cs | 3 ++- .../Visual/UserInterface/TestSceneTextBoxEvents.cs | 12 ++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs index 1242f984f3..3e82f94c72 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs @@ -122,8 +122,9 @@ public void VariousTextBoxes() TabbableContentContainer = otherTextBoxes }); - otherTextBoxes.Add(new BasicPasswordTextBox + otherTextBoxes.Add(new BasicTextBox { + InputProperties = new TextInputProperties(TextInputType.Password, false), PlaceholderText = @"Password textbox", Text = "Secret ;)", Size = new Vector2(500, 30), diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBoxEvents.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBoxEvents.cs index 643924de69..d3211bfd6a 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBoxEvents.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBoxEvents.cs @@ -206,7 +206,7 @@ public void TestMovingOrExpandingSelectionInvokesEvent() AddAssert("text input not deactivated", () => textInput.DeactivationQueue.Count == 0); AddAssert("text input not activated again", () => textInput.ActivationQueue.Count == 0); - AddAssert("text input ensure activated", () => textInput.EnsureActivatedQueue.Dequeue() && textInput.EnsureActivatedQueue.Count == 0); + AddAssert("text input ensure activated", () => textInput.EnsureActivatedQueue.Dequeue() != default && textInput.EnsureActivatedQueue.Count == 0); AddStep("click deselection", () => { @@ -217,7 +217,7 @@ public void TestMovingOrExpandingSelectionInvokesEvent() AddAssert("text input not deactivated", () => textInput.DeactivationQueue.Count == 0); AddAssert("text input not activated again", () => textInput.ActivationQueue.Count == 0); - AddAssert("text input ensure activated", () => textInput.EnsureActivatedQueue.Dequeue() && textInput.EnsureActivatedQueue.Count == 0); + AddAssert("text input ensure activated", () => textInput.EnsureActivatedQueue.Dequeue() != default && textInput.EnsureActivatedQueue.Count == 0); AddStep("click-drag selection", () => { @@ -500,7 +500,7 @@ public void TestChangingFocusDoesNotReactivate(bool allowIme) AddStep("add second textbox", () => textInputContainer.Add(secondTextBox = new EventQueuesTextBox { - ImeAllowed = allowIme, + InputProperties = new TextInputProperties(TextInputType.Text, allowIme), Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, CommitOnFocusLost = true, @@ -517,7 +517,7 @@ public void TestChangingFocusDoesNotReactivate(bool allowIme) AddAssert("text input not deactivated", () => textInput.DeactivationQueue.Count == 0); AddAssert("text input not activated again", () => textInput.ActivationQueue.Count == 0); - AddAssert($"text input ensure activated {(allowIme ? "with" : "without")} IME", () => textInput.EnsureActivatedQueue.Dequeue() == allowIme && textInput.EnsureActivatedQueue.Count == 0); + AddAssert($"text input ensure activated {(allowIme ? "with" : "without")} IME", () => textInput.EnsureActivatedQueue.Dequeue().AllowIme == allowIme && textInput.EnsureActivatedQueue.Count == 0); AddStep("commit text", () => InputManager.Key(Key.Enter)); AddAssert("text input deactivated", () => textInput.DeactivationQueue.Dequeue()); @@ -574,10 +574,6 @@ private void testNormalTextInput() public partial class EventQueuesTextBox : TestSceneTextBox.InsertableTextBox { - public bool ImeAllowed { get; set; } = true; - - protected override bool AllowIme => ImeAllowed; - public readonly Queue InputErrorQueue = new Queue(); public readonly Queue UserConsumedTextQueue = new Queue(); public readonly Queue UserRemovedTextQueue = new Queue(); From 5d4c3566cf4f3389f7ee9fd16512a52e40cf2d5e Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Mon, 4 Nov 2024 04:34:12 -0500 Subject: [PATCH 3/9] Use obsolete `AllowIme` until it's removed --- osu.Framework/Graphics/UserInterface/TextBox.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index 810e2f89db..fb1ca73423 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -166,7 +166,9 @@ public bool ReadOnly protected TextBox() { - InputProperties = new TextInputProperties(TextInputType.Text, true); +#pragma warning disable CS0618 // Type or member is obsolete + InputProperties = new TextInputProperties(TextInputType.Text, AllowIme); +#pragma warning restore CS0618 // Type or member is obsolete Masking = true; From 5a34404f6f2c99ece4781edf57303df5aff02592 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Mon, 4 Nov 2024 04:45:00 -0500 Subject: [PATCH 4/9] Please fix --- .../Visual/UserInterface/TestSceneTextBox.cs | 12 +++--------- ...ventLogging.cs => ICanSuppressKeyEventLogging.cs} | 0 2 files changed, 3 insertions(+), 9 deletions(-) rename osu.Framework/Input/{ISuppressKeyEventLogging.cs => ICanSuppressKeyEventLogging.cs} (100%) diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs index 3e82f94c72..005177b63b 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs @@ -170,12 +170,13 @@ public void VariousTextBoxes() [Test] public void TestNumbersOnly() { - NumberTextBox numbers = null; + BasicTextBox numbers = null; AddStep("add number textbox", () => { - textBoxes.Add(numbers = new NumberTextBox + textBoxes.Add(numbers = new BasicTextBox { + InputProperties = new TextInputProperties(TextInputType.Number, false), PlaceholderText = @"Only numbers", Size = new Vector2(500, 30), TabbableContentContainer = textBoxes @@ -1043,13 +1044,6 @@ public partial class InsertableTextBox : BasicTextBox public new void InsertString(string text) => base.InsertString(text); } - private partial class NumberTextBox : BasicTextBox - { - protected override bool CanAddCharacter(char character) => char.IsAsciiDigit(character); - - protected override bool AllowIme => false; - } - private partial class CustomTextBox : BasicTextBox { protected override Drawable GetDrawableCharacter(char c) => new ScalingText(c, FontSize); diff --git a/osu.Framework/Input/ISuppressKeyEventLogging.cs b/osu.Framework/Input/ICanSuppressKeyEventLogging.cs similarity index 100% rename from osu.Framework/Input/ISuppressKeyEventLogging.cs rename to osu.Framework/Input/ICanSuppressKeyEventLogging.cs From 06371f47754436cb30ee1e280c9da5d983ed93af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Nov 2024 11:03:02 +0100 Subject: [PATCH 5/9] Fix grammar Co-authored-by: Dan Balasescu --- osu.Framework/Input/TextInputProperties.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Framework/Input/TextInputProperties.cs b/osu.Framework/Input/TextInputProperties.cs index 66d7f6638e..e45d389556 100644 --- a/osu.Framework/Input/TextInputProperties.cs +++ b/osu.Framework/Input/TextInputProperties.cs @@ -6,7 +6,7 @@ namespace osu.Framework.Input /// /// Represents a number of properties to consider during a text input session. /// - /// The type of text being inputted. + /// The type of text being input. /// /// /// Whether IME should be allowed during this text input session. From 9b7683b59f7704e8064998c33794717f25c63af0 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sat, 9 Nov 2024 04:31:50 -0500 Subject: [PATCH 6/9] Fix key event logging suppression --- osu.Framework/Input/ButtonEventManager.cs | 11 +++++++- osu.Framework/Input/InputManager.cs | 32 ++--------------------- osu.Framework/Input/KeyEventManager.cs | 2 ++ 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/osu.Framework/Input/ButtonEventManager.cs b/osu.Framework/Input/ButtonEventManager.cs index b10ef23a26..ab8b5e4923 100644 --- a/osu.Framework/Input/ButtonEventManager.cs +++ b/osu.Framework/Input/ButtonEventManager.cs @@ -133,9 +133,18 @@ private void handleButtonUp(InputState state) } if (handledBy != null) - Logger.Log($"{e} handled by {handledBy}.", LoggingTarget.Runtime, LogLevel.Debug); + { + Logger.Log(SuppressLoggingEventInformation(handledBy) + ? $"{e.GetType().Name} handled by {handledBy}." + : $"{e} handled by {handledBy}.", LoggingTarget.Runtime, LogLevel.Debug); + } return handledBy; } + + /// + /// Whether information about the event should be suppressed from logging for the given drawable. + /// + protected virtual bool SuppressLoggingEventInformation(Drawable drawable) => false; } } diff --git a/osu.Framework/Input/InputManager.cs b/osu.Framework/Input/InputManager.cs index 4235dc2ad0..934837af72 100644 --- a/osu.Framework/Input/InputManager.cs +++ b/osu.Framework/Input/InputManager.cs @@ -10,7 +10,6 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.ListExtensions; -using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -999,40 +998,13 @@ protected virtual bool PropagateBlockableEvent(SlimReadOnlyListWrapper { foreach (var d in drawables) { - if (!d.TriggerEvent(e)) continue; - - if (shouldLog(e)) - { - string detail = d is ICanSuppressKeyEventLogging kd && kd.SuppressKeyEventLogging - ? e.GetType().ReadableName() - : e.ToString(); - - Logger.Log($"{detail} handled by {d}.", LoggingTarget.Runtime, LogLevel.Debug); - } - - return true; + if (d.TriggerEvent(e)) + return true; } return false; } - private bool shouldLog(UIEvent eventType) - { - switch (eventType) - { - case KeyDownEvent k: - return !k.Repeat; - - case DragEvent: - case ScrollEvent: - case MouseMoveEvent: - return false; - - default: - return true; - } - } - /// /// Unfocus the current focused drawable if it is no longer in a valid state. /// diff --git a/osu.Framework/Input/KeyEventManager.cs b/osu.Framework/Input/KeyEventManager.cs index b716e7b67c..d1434a3bf4 100644 --- a/osu.Framework/Input/KeyEventManager.cs +++ b/osu.Framework/Input/KeyEventManager.cs @@ -33,5 +33,7 @@ public void HandleRepeat(InputState state) protected override void HandleButtonUp(InputState state, List targets) => PropagateButtonEvent(targets, new KeyUpEvent(state, Button)); + + protected override bool SuppressLoggingEventInformation(Drawable drawable) => drawable is ICanSuppressKeyEventLogging canSuppress && canSuppress.SuppressKeyEventLogging; } } From fb364b143d07c1c5d6551b40218b746998ba9f9d Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sat, 9 Nov 2024 04:36:14 -0500 Subject: [PATCH 7/9] Set `AllowIme` to true by default and give sanity --- osu.Framework/Input/TextInputProperties.cs | 5 ++--- osu.Framework/Input/TextInputType.cs | 2 ++ osu.Framework/Platform/Windows/SDL2WindowsWindow.cs | 2 +- osu.Framework/Platform/Windows/SDL3WindowsWindow.cs | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Framework/Input/TextInputProperties.cs b/osu.Framework/Input/TextInputProperties.cs index e45d389556..e44bb6dbd2 100644 --- a/osu.Framework/Input/TextInputProperties.cs +++ b/osu.Framework/Input/TextInputProperties.cs @@ -9,13 +9,12 @@ namespace osu.Framework.Input /// The type of text being input. /// /// - /// Whether IME should be allowed during this text input session. - /// Useful for situations where IME input is not wanted, such as for passwords, numbers, or romanised text. + /// Whether IME should be allowed during this text input session, if supported by the given text input type. /// /// /// Note that this is just a hint to the native implementation, some might respect this, /// while others will ignore and always have the IME (dis)allowed. /// /// - public record struct TextInputProperties(TextInputType Type, bool AllowIme); + public record struct TextInputProperties(TextInputType Type, bool AllowIme = true); } diff --git a/osu.Framework/Input/TextInputType.cs b/osu.Framework/Input/TextInputType.cs index 42da0afffe..2a1645875f 100644 --- a/osu.Framework/Input/TextInputType.cs +++ b/osu.Framework/Input/TextInputType.cs @@ -68,5 +68,7 @@ public static bool IsNumerical(this TextInputType type) return false; } } + + public static bool SupportsIme(this TextInputType type) => type == TextInputType.Name || type == TextInputType.Text; } } diff --git a/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs b/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs index f04bcd4c75..4a1d9dafee 100644 --- a/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs +++ b/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs @@ -141,7 +141,7 @@ private void warpCursorFromFocusLoss() public override void StartTextInput(TextInputProperties properties) { base.StartTextInput(properties); - ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, properties.AllowIme)); + ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, properties.Type.SupportsIme() && properties.AllowIme)); } public override void ResetIme() => ScheduleCommand(() => Imm.CancelComposition(WindowHandle)); diff --git a/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs b/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs index e431ef4f83..66c6f1654b 100644 --- a/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs +++ b/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs @@ -131,7 +131,7 @@ private void warpCursorFromFocusLoss() public override void StartTextInput(TextInputProperties properties) { base.StartTextInput(properties); - ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, properties.AllowIme)); + ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, properties.Type.SupportsIme() && properties.AllowIme)); } public override void ResetIme() => ScheduleCommand(() => Imm.CancelComposition(WindowHandle)); From 6a7a92a32ee8b601d71e58bb7e1ce4c2a75b9f47 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sat, 9 Nov 2024 04:58:41 -0500 Subject: [PATCH 8/9] Remove unnecessary specifications --- osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs index 005177b63b..e209afa3aa 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs @@ -124,7 +124,7 @@ public void VariousTextBoxes() otherTextBoxes.Add(new BasicTextBox { - InputProperties = new TextInputProperties(TextInputType.Password, false), + InputProperties = new TextInputProperties(TextInputType.Password), PlaceholderText = @"Password textbox", Text = "Secret ;)", Size = new Vector2(500, 30), @@ -176,7 +176,7 @@ public void TestNumbersOnly() { textBoxes.Add(numbers = new BasicTextBox { - InputProperties = new TextInputProperties(TextInputType.Number, false), + InputProperties = new TextInputProperties(TextInputType.Number), PlaceholderText = @"Only numbers", Size = new Vector2(500, 30), TabbableContentContainer = textBoxes From a699201327f94b66cb1883cada9bfe36af3f577b Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Mon, 23 Dec 2024 17:32:08 -0500 Subject: [PATCH 9/9] Fix `ResetIme` not starting text input with properties --- osu.Framework/Platform/SDL3/SDL3Window_Input.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs index 9de1ff9d05..f2f1870886 100644 --- a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs +++ b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs @@ -187,12 +187,15 @@ private void pollMouse() } } + private SDL_PropertiesID? currentTextInputProperties; + public virtual void StartTextInput(TextInputProperties properties) => ScheduleCommand(() => { - var props = SDL_CreateProperties(); + currentTextInputProperties ??= SDL_CreateProperties(); + + var props = currentTextInputProperties.Value; SDL_SetNumberProperty(props, SDL_PROP_TEXTINPUT_TYPE_NUMBER, (long)properties.Type.ToSDLTextInputType()); SDL_StartTextInputWithProperties(SDLWindowHandle, props); - SDL_DestroyProperties(props); }); public void StopTextInput() => ScheduleCommand(() => SDL_StopTextInput(SDLWindowHandle)); @@ -204,7 +207,11 @@ public virtual void StartTextInput(TextInputProperties properties) => ScheduleCo public virtual void ResetIme() => ScheduleCommand(() => { SDL_StopTextInput(SDLWindowHandle); - SDL_StartTextInput(SDLWindowHandle); + + if (currentTextInputProperties is SDL_PropertiesID props) + SDL_StartTextInputWithProperties(SDLWindowHandle, props); + else + SDL_StartTextInput(SDLWindowHandle); }); public void SetTextInputRect(RectangleF rect) => ScheduleCommand(() =>