From 915ce027f9842058be3f0ad0eee9743783cffcdc Mon Sep 17 00:00:00 2001 From: kuuuube Date: Thu, 2 Oct 2025 21:30:32 -0400 Subject: [PATCH 01/10] Add triple click to select all in TextBox --- .../Graphics/UserInterface/TextBox.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index a646d38c78..2f9b10d28e 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -65,6 +65,11 @@ public abstract partial class TextBox : TabbableContainer, IHasCurrentValue InputProperties.Type.IsPassword(); + /// + /// The maximum time between a double-click and a subsequent click for a triple-click to be considered. + /// + public virtual float TripleClickTime => 250; + /// /// Represents the left/right selection coordinates of the word double clicked on when dragging. /// @@ -1349,8 +1354,12 @@ protected override void OnDrag(DragEvent e) onTextSelectionChanged(doubleClickWord != null ? TextSelectionType.Word : TextSelectionType.Character, lastSelectionBounds); } + private double? lastDoubleClickTime = null; + protected override bool OnDoubleClick(DoubleClickEvent e) { + lastDoubleClickTime = Time.Current; + FinalizeImeComposition(true); var lastSelectionBounds = getTextSelectionBounds(); @@ -1405,6 +1414,17 @@ protected override bool OnMouseDown(MouseDownEvent e) var lastSelectionBounds = getTextSelectionBounds(); + if (lastDoubleClickTime != null && Time.Current - lastDoubleClickTime < TripleClickTime) + { + lastDoubleClickTime = null; + + SelectAll(); + + onTextSelectionChanged(TextSelectionType.All, lastSelectionBounds); + + return false; + } + selectionStart = selectionEnd = getCharacterClosestTo(e.MousePosition); cursorAndLayout.Invalidate(); From f71f914fb5d587af4da3ec53f96c37e34ab0b203 Mon Sep 17 00:00:00 2001 From: kuuuube Date: Thu, 2 Oct 2025 21:55:09 -0400 Subject: [PATCH 02/10] Remove unnecessary initialization --- osu.Framework/Graphics/UserInterface/TextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index 2f9b10d28e..84a2ff1833 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -1354,7 +1354,7 @@ protected override void OnDrag(DragEvent e) onTextSelectionChanged(doubleClickWord != null ? TextSelectionType.Word : TextSelectionType.Character, lastSelectionBounds); } - private double? lastDoubleClickTime = null; + private double? lastDoubleClickTime; protected override bool OnDoubleClick(DoubleClickEvent e) { From 440ae3982b9f35e633caba37fed70fdb4194f111 Mon Sep 17 00:00:00 2001 From: kuuuube Date: Fri, 3 Oct 2025 10:02:35 -0400 Subject: [PATCH 03/10] Add test for triple clicking TextBox --- .../Visual/UserInterface/TestSceneTextBox.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs index c8e121376e..7e5e76db7a 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs @@ -917,6 +917,40 @@ public void TestTextChangedDuringDoubleClickDrag() AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left)); } + [Test] + public void TestTripleClickSelectAll() + { + InsertableTextBox textBox = null; + + AddStep("add textbox", () => + { + textBoxes.Add(textBox = new InsertableTextBox + { + Size = new Vector2(300, 40), + Text = "multiple words so a double click cant select it all", + }); + }); + + AddStep("move to textbox", () => InputManager.MoveMouseTo(textBox)); + + AddStep("triple click", () => + { + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + }); + AddAssert("all text selected", () => textBox.SelectedText, () => Is.EqualTo(textBox.Text)); + + AddStep("double click", () => + { + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + }); + AddWaitStep("wait to fail triple click", 2); + AddStep("third click", () => InputManager.Click(MouseButton.Left)); + AddAssert("no text selected", () => textBox.SelectedText, () => Is.EqualTo(string.Empty)); + } + [Test] public void TestSelectAll() { From aa298008ebdb1b47b91dcca4d723a7f8f01c62b4 Mon Sep 17 00:00:00 2001 From: kuuuube Date: Tue, 7 Oct 2025 10:13:46 -0400 Subject: [PATCH 04/10] Fetch tripleClickTime from containing input manager DoubleClickTime --- osu.Framework/Graphics/UserInterface/TextBox.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index 84a2ff1833..3ccd49056f 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -65,11 +65,6 @@ public abstract partial class TextBox : TabbableContainer, IHasCurrentValue InputProperties.Type.IsPassword(); - /// - /// The maximum time between a double-click and a subsequent click for a triple-click to be considered. - /// - public virtual float TripleClickTime => 250; - /// /// Represents the left/right selection coordinates of the word double clicked on when dragging. /// @@ -1405,6 +1400,8 @@ private static int findSeparatorIndex(string input, int searchPos, int direction return -1; } + private float tripleClickTime; + protected override bool OnMouseDown(MouseDownEvent e) { if (ReadOnly) @@ -1414,7 +1411,9 @@ protected override bool OnMouseDown(MouseDownEvent e) var lastSelectionBounds = getTextSelectionBounds(); - if (lastDoubleClickTime != null && Time.Current - lastDoubleClickTime < TripleClickTime) + tripleClickTime = GetContainingInputManager().AsNonNull().GetButtonEventManagerFor(e.Button).DoubleClickTime; + + if (lastDoubleClickTime != null && Time.Current - lastDoubleClickTime < tripleClickTime) { lastDoubleClickTime = null; From 0a35ef72a21088ea8cb3246df86ed53136011f57 Mon Sep 17 00:00:00 2001 From: kuuuube Date: Tue, 7 Oct 2025 11:02:26 -0400 Subject: [PATCH 05/10] Return true in OnMouseDown after triple click is handled --- osu.Framework/Graphics/UserInterface/TextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index 3ccd49056f..d78fbcdd3a 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -1421,7 +1421,7 @@ protected override bool OnMouseDown(MouseDownEvent e) onTextSelectionChanged(TextSelectionType.All, lastSelectionBounds); - return false; + return true; } selectionStart = selectionEnd = getCharacterClosestTo(e.MousePosition); From 95b39dca7076787b06fcc69a302cd5f20857fa1d Mon Sep 17 00:00:00 2001 From: kuuuube Date: Tue, 7 Oct 2025 11:31:25 -0400 Subject: [PATCH 06/10] Disallow editing selection from dragging while a triple click is ongoing --- osu.Framework/Graphics/UserInterface/TextBox.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index d78fbcdd3a..5baa922c57 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -868,6 +868,7 @@ private string removeCharacters(int number = 1) selectionStart = selectionEnd = removeStart; doubleClickWord = null; + tripleClickOngoing = false; endTextChange(beganChange); cursorAndLayout.Invalidate(); @@ -1308,7 +1309,7 @@ protected override void OnDrag(DragEvent e) FinalizeImeComposition(true); - if (ignoreOngoingDragSelection) + if (ignoreOngoingDragSelection || tripleClickOngoing) return; var lastSelectionBounds = getTextSelectionBounds(); @@ -1402,6 +1403,8 @@ private static int findSeparatorIndex(string input, int searchPos, int direction private float tripleClickTime; + private bool tripleClickOngoing; + protected override bool OnMouseDown(MouseDownEvent e) { if (ReadOnly) @@ -1421,6 +1424,8 @@ protected override bool OnMouseDown(MouseDownEvent e) onTextSelectionChanged(TextSelectionType.All, lastSelectionBounds); + tripleClickOngoing = true; + return true; } @@ -1436,6 +1441,7 @@ protected override bool OnMouseDown(MouseDownEvent e) protected override void OnMouseUp(MouseUpEvent e) { doubleClickWord = null; + tripleClickOngoing = false; } protected override void OnFocusLost(FocusLostEvent e) From 0a6a23aa48b2b6569cfea3fd3ad08518449d009e Mon Sep 17 00:00:00 2001 From: kuuuube Date: Wed, 8 Oct 2025 09:20:18 -0400 Subject: [PATCH 07/10] Add test for triple click drag not changing text selection --- .../Visual/UserInterface/TestSceneTextBox.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs index 7e5e76db7a..0921acd480 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs @@ -949,6 +949,16 @@ public void TestTripleClickSelectAll() AddWaitStep("wait to fail triple click", 2); AddStep("third click", () => InputManager.Click(MouseButton.Left)); AddAssert("no text selected", () => textBox.SelectedText, () => Is.EqualTo(string.Empty)); + + AddStep("triple click drag", () => + { + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + InputManager.PressButton(MouseButton.Left); + }); + AddStep("start drag", () => InputManager.MoveMouseTo(textBox.ScreenSpaceDrawQuad.TopLeft - new Vector2(200f, 0f))); + AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left)); + AddAssert("all text selected", () => textBox.SelectedText, () => Is.EqualTo(textBox.Text)); } [Test] From dcf59b17aaa633bad899372150d280f98caad28f Mon Sep 17 00:00:00 2001 From: kuuuube Date: Thu, 9 Oct 2025 10:07:36 -0400 Subject: [PATCH 08/10] Remove unnecessary class field --- osu.Framework/Graphics/UserInterface/TextBox.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index 5baa922c57..bcfc7407c9 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -1401,8 +1401,6 @@ private static int findSeparatorIndex(string input, int searchPos, int direction return -1; } - private float tripleClickTime; - private bool tripleClickOngoing; protected override bool OnMouseDown(MouseDownEvent e) @@ -1414,7 +1412,7 @@ protected override bool OnMouseDown(MouseDownEvent e) var lastSelectionBounds = getTextSelectionBounds(); - tripleClickTime = GetContainingInputManager().AsNonNull().GetButtonEventManagerFor(e.Button).DoubleClickTime; + var tripleClickTime = GetContainingInputManager().AsNonNull().GetButtonEventManagerFor(e.Button).DoubleClickTime; if (lastDoubleClickTime != null && Time.Current - lastDoubleClickTime < tripleClickTime) { From 85718c3f15d9801505697fc6d7985d66a8f1af55 Mon Sep 17 00:00:00 2001 From: kuuuube Date: Thu, 9 Oct 2025 10:17:04 -0400 Subject: [PATCH 09/10] Remove useless tripleClickOngoing reset on removeCharacters --- osu.Framework/Graphics/UserInterface/TextBox.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index bcfc7407c9..ec16ce6a8c 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -868,7 +868,6 @@ private string removeCharacters(int number = 1) selectionStart = selectionEnd = removeStart; doubleClickWord = null; - tripleClickOngoing = false; endTextChange(beganChange); cursorAndLayout.Invalidate(); From 599bef233fbdb4f1d18524b091086e3d91808fd1 Mon Sep 17 00:00:00 2001 From: kuuuube Date: Thu, 9 Oct 2025 10:23:37 -0400 Subject: [PATCH 10/10] Fix type --- osu.Framework/Graphics/UserInterface/TextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs index ec16ce6a8c..2701f6dc6b 100644 --- a/osu.Framework/Graphics/UserInterface/TextBox.cs +++ b/osu.Framework/Graphics/UserInterface/TextBox.cs @@ -1411,7 +1411,7 @@ protected override bool OnMouseDown(MouseDownEvent e) var lastSelectionBounds = getTextSelectionBounds(); - var tripleClickTime = GetContainingInputManager().AsNonNull().GetButtonEventManagerFor(e.Button).DoubleClickTime; + float tripleClickTime = GetContainingInputManager().AsNonNull().GetButtonEventManagerFor(e.Button).DoubleClickTime; if (lastDoubleClickTime != null && Time.Current - lastDoubleClickTime < tripleClickTime) {