From 529a5b92fcf2aea2d75e43af1c967f2ca5d37a54 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 6 Oct 2021 20:32:02 +0100 Subject: [PATCH 01/16] Fixes #1475. Selection ending with a white space error. --- Terminal.Gui/Views/TextField.cs | 10 ++++++-- Terminal.Gui/Views/TextView.cs | 43 +++++++++++++++++++++------------ UnitTests/TextFieldTests.cs | 34 ++++++++++++++++++++++++++ UnitTests/TextViewTests.cs | 41 +++++++++++++++++++++++++++++-- 4 files changed, 109 insertions(+), 19 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 61c2ab8b48..57012d72d7 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -613,6 +613,9 @@ int WordForward (int p) return text.Count; var ti = text [i]; + if (Rune.IsLetterOrDigit (ti) && Rune.IsWhiteSpace (text [p])) + return i; + if (Rune.IsPunctuation (ti) || Rune.IsSymbol (ti) || Rune.IsWhiteSpace (ti)) { for (; i < text.Count; i++) { if (Rune.IsLetterOrDigit (text [i])) @@ -624,7 +627,8 @@ int WordForward (int p) break; } for (; i < text.Count; i++) { - if (Rune.IsLetterOrDigit (text [i])) + if (Rune.IsLetterOrDigit (text [i]) || + (Rune.IsPunctuation (text [i]) && Rune.IsWhiteSpace (text [i - 1]))) break; } } @@ -751,7 +755,9 @@ public override bool MouseEvent (MouseEvent ev) } else if (ev.Flags == MouseFlags.Button1DoubleClicked) { int x = PositionCursor (ev); int sbw = x; - if (x > 0 && (char)Text [x - 1] != ' ') { + if (x == text.Count || (x > 0 && (char)Text [x - 1] != ' ' + || (x > 0 && (char)Text [x] == ' '))) { + sbw = WordBackward (x); } if (sbw != -1) { diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 448ab696f9..200616f0cf 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2885,10 +2885,13 @@ bool MoveNext (ref int col, ref int row, out Rune rune) if (col + 1 < line.Count) { col++; rune = line [col]; - if (col + 1 == line.Count) { + if (col + 1 == line.Count && !Rune.IsLetterOrDigit (rune) + && !Rune.IsWhiteSpace (line [col - 1])) { col++; } return true; + } else if (col + 1 == line.Count) { + col++; } while (row + 1 < model.Count) { col = 0; @@ -3060,7 +3063,8 @@ public override bool MouseEvent (MouseEvent ev) && !ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ButtonShift) && !ev.Flags.HasFlag (MouseFlags.WheeledDown) && !ev.Flags.HasFlag (MouseFlags.WheeledUp) && !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked) - && !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked | MouseFlags.ButtonShift)) { + && !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked | MouseFlags.ButtonShift) + && !ev.Flags.HasFlag (MouseFlags.Button1TripleClicked)) { return false; } @@ -3160,28 +3164,37 @@ public override bool MouseEvent (MouseEvent ev) StopSelecting (); } ProcessMouseClick (ev, out List line); - (int col, int row)? newPos = null; - if (currentColumn > 0 && line [currentColumn - 1] != ' ') { + (int col, int row)? newPos; + int sbw = currentColumn; + if (currentColumn == line.Count || (currentColumn > 0 && (line [currentColumn - 1] != ' ' + || line [currentColumn] == ' '))) { + newPos = WordBackward (currentColumn, currentRow); if (newPos.HasValue) { - currentColumn = newPos.Value.col; - currentRow = newPos.Value.row; + currentColumn = currentRow == newPos.Value.row ? newPos.Value.col : 0; + sbw = currentColumn; } } if (!selecting) { StartSelecting (); } - if (currentRow < selectionStartRow || currentRow == selectionStartRow && currentColumn < selectionStartColumn) { - if (currentColumn > 0 && line [currentColumn - 1] != ' ') { - newPos = WordBackward (currentColumn, currentRow); - } - } else { - newPos = WordForward (currentColumn, currentRow); + newPos = WordForward (currentColumn, currentRow); + if (newPos != null && newPos.HasValue && sbw != -1) { + currentColumn = currentRow == newPos.Value.row ? newPos.Value.col : line.Count; } - if (newPos != null && newPos.HasValue) { - currentColumn = newPos.Value.col; - currentRow = newPos.Value.row; + PositionCursor (); + lastWasKill = false; + columnTrack = currentColumn; + } else if (ev.Flags.HasFlag (MouseFlags.Button1TripleClicked)) { + if (selecting) { + StopSelecting (); } + ProcessMouseClick (ev, out List line); + currentColumn = 0; + if (!selecting) { + StartSelecting (); + } + currentColumn = line.Count; PositionCursor (); lastWasKill = false; columnTrack = currentColumn; diff --git a/UnitTests/TextFieldTests.cs b/UnitTests/TextFieldTests.cs index 95054eb2fd..6ff23ced36 100644 --- a/UnitTests/TextFieldTests.cs +++ b/UnitTests/TextFieldTests.cs @@ -457,6 +457,12 @@ public void WordForward_With_No_Selection_And_With_More_Than_Only_One_Whitespace Assert.Null (_textField.SelectedText); break; case 9: + Assert.Equal (54, _textField.CursorPosition); + Assert.Equal (-1, _textField.SelectedStart); + Assert.Equal (0, _textField.SelectedLength); + Assert.Null (_textField.SelectedText); + break; + case 10: Assert.Equal (55, _textField.CursorPosition); Assert.Equal (-1, _textField.SelectedStart); Assert.Equal (0, _textField.SelectedLength); @@ -785,5 +791,33 @@ public void Text_Replaces_Tabs_With_Empty_String () _textField.Paste (); Assert.Equal ("TAB to jump between text fields.", _textField.Text); } + + [Fact] + [InitShutdown] + public void TextField_SpaceHandling () + { + var tf = new TextField () { + Width = 10, + Text = " " + }; + + MouseEvent ev = new MouseEvent () { + X = 0, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked, + }; + + tf.MouseEvent (ev); + Assert.Equal (1, tf.SelectedLength); + + ev = new MouseEvent () { + X = 1, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked, + }; + + tf.MouseEvent (ev); + Assert.Equal (1, tf.SelectedLength); + } } } diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index ab06df48a9..ca8a12e585 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -573,6 +573,14 @@ public void WordForward_With_No_Selection_And_With_More_Than_Only_One_Whitespace Assert.Equal ("", _textView.SelectedText); break; case 9: + Assert.Equal (54, _textView.CursorPosition.X); + Assert.Equal (0, _textView.CursorPosition.Y); + Assert.Equal (0, _textView.SelectionStartColumn); + Assert.Equal (0, _textView.SelectionStartRow); + Assert.Equal (0, _textView.SelectedLength); + Assert.Equal ("", _textView.SelectedText); + break; + case 10: Assert.Equal (55, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); Assert.Equal (0, _textView.SelectionStartColumn); @@ -2009,8 +2017,8 @@ public void Internal_Tests () Assert.True (gaveFullTurn); Assert.Equal ((new Point (9, 1), true), tm.ReplaceAllText ("is", false, false, "really")); - Assert.Equal (TextModel.ToRunes ("Threally really first line."), tm.GetLine (0)); - Assert.Equal (TextModel.ToRunes ("Threally really last line."), tm.GetLine (1)); + Assert.Equal (TextModel.ToRunes ("Threally really first line."), tm.GetLine (0)); + Assert.Equal (TextModel.ToRunes ("Threally really last line."), tm.GetLine (1)); tm = new TextModel (); tm.AddLine (0, TextModel.ToRunes ("This is first line.")); tm.AddLine (1, TextModel.ToRunes ("This is last line.")); @@ -2078,5 +2086,34 @@ public void RightOffset_Sets_To_Zero_Adjust_leftColumn () Assert.Equal (3, tv.LeftColumn); Assert.Equal (0, tv.RightOffset); } + + [Fact] + [InitShutdown] + public void TextView_SpaceHandling () + { + var tv = new TextView () { + Width = 10, + Text = " " + }; + + MouseEvent ev = new MouseEvent () { + X = 0, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked, + }; + + tv.MouseEvent (ev); + Assert.Equal (1, tv.SelectedLength); + + ev = new MouseEvent () { + X = 1, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked, + }; + + tv.MouseEvent (ev); + Assert.Equal (1, tv.SelectedLength); + } + } } From c6a22c19eb47afef9a4a1c015ac548ad3990f0ed Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 6 Oct 2021 20:42:37 +0100 Subject: [PATCH 02/16] Prevents the mouse double click processing twice. --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 5976532670..2a1c5a1d55 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -953,7 +953,7 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) //System.Diagnostics.Debug.WriteLine ($"isButtonPressed: {isButtonPressed};buttonPressedCount: {buttonPressedCount};lastMouseButtonPressed: {lastMouseButtonPressed}"); //System.Diagnostics.Debug.WriteLine ($"isOneFingerDoubleClicked: {isOneFingerDoubleClicked}"); - if (buttonPressedCount == 1 && lastMouseButtonPressed != null + if (buttonPressedCount == 1 && lastMouseButtonPressed != null && p == point && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) { @@ -973,7 +973,7 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) } isOneFingerDoubleClicked = true; - } else if (buttonPressedCount == 3 && lastMouseButtonPressed != null && isOneFingerDoubleClicked + } else if (buttonPressedCount == 3 && lastMouseButtonPressed != null && isOneFingerDoubleClicked && p == point && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) { From 59959acfd15e6f9530582349d552b4fb9061a921 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 6 Oct 2021 21:12:55 +0100 Subject: [PATCH 03/16] Removing unnecessary variable. --- Terminal.Gui/Views/TextView.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 200616f0cf..9d400890df 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3165,21 +3165,19 @@ public override bool MouseEvent (MouseEvent ev) } ProcessMouseClick (ev, out List line); (int col, int row)? newPos; - int sbw = currentColumn; if (currentColumn == line.Count || (currentColumn > 0 && (line [currentColumn - 1] != ' ' || line [currentColumn] == ' '))) { newPos = WordBackward (currentColumn, currentRow); if (newPos.HasValue) { currentColumn = currentRow == newPos.Value.row ? newPos.Value.col : 0; - sbw = currentColumn; } } if (!selecting) { StartSelecting (); } newPos = WordForward (currentColumn, currentRow); - if (newPos != null && newPos.HasValue && sbw != -1) { + if (newPos != null && newPos.HasValue) { currentColumn = currentRow == newPos.Value.row ? newPos.Value.col : line.Count; } PositionCursor (); From abe9c3232f1fed59e81eb73ad16b5710b4f49ea5 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 10 Oct 2021 22:11:40 +0100 Subject: [PATCH 04/16] Sets ScrollViewBar CanFocus to false to ensure the host always focused. --- Terminal.Gui/Views/ScrollBarView.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs index 7760245eb3..4c8e53b794 100644 --- a/Terminal.Gui/Views/ScrollBarView.cs +++ b/Terminal.Gui/Views/ScrollBarView.cs @@ -85,10 +85,10 @@ public ScrollBarView (View host, bool isVertical, bool showBothScrollIndicator = X = isVertical ? Pos.Right (host) - 1 : Pos.Left (host); Y = isVertical ? Pos.Top (host) : Pos.Bottom (host) - 1; Host = host; - CanFocus = host.CanFocus; + CanFocus = false; Enabled = host.Enabled; Visible = host.Visible; - Host.CanFocusChanged += Host_CanFocusChanged; + //Host.CanFocusChanged += Host_CanFocusChanged; Host.EnabledChanged += Host_EnabledChanged; Host.VisibleChanged += Host_VisibleChanged; Host.SuperView.Add (this); @@ -97,7 +97,7 @@ public ScrollBarView (View host, bool isVertical, bool showBothScrollIndicator = OtherScrollBarView = new ScrollBarView (0, 0, !isVertical) { ColorScheme = host.ColorScheme, Host = host, - CanFocus = host.CanFocus, + CanFocus = false, Enabled = host.Enabled, Visible = host.Visible, OtherScrollBarView = this @@ -140,13 +140,13 @@ private void Host_EnabledChanged () contentBottomRightCorner.Enabled = Enabled; } - private void Host_CanFocusChanged () - { - CanFocus = Host.CanFocus; - if (otherScrollBarView != null) { - otherScrollBarView.CanFocus = CanFocus; - } - } + //private void Host_CanFocusChanged () + //{ + // CanFocus = Host.CanFocus; + // if (otherScrollBarView != null) { + // otherScrollBarView.CanFocus = CanFocus; + // } + //} void ContentBottomRightCorner_MouseClick (MouseEventArgs me) { From a143b4862a5ad9fd42682f19175466ce7822b880 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 10 Oct 2021 22:20:42 +0100 Subject: [PATCH 05/16] Only navigates through TabView if winDialog is not null and ensures TextView being focused. --- UICatalog/Scenarios/Editor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 566a170790..c528f170d9 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -171,14 +171,14 @@ public override void Init (Toplevel top, ColorScheme colorScheme) } else if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) { Quit (); e.Handled = true; - } else if (keys == (Key.Tab | Key.CtrlMask)) { + } else if (winDialog != null && keys == (Key.Tab | Key.CtrlMask)) { if (_tabView.SelectedTab == _tabView.Tabs.ElementAt (_tabView.Tabs.Count - 1)) { _tabView.SelectedTab = _tabView.Tabs.ElementAt (0); } else { _tabView.SwitchTabBy (1); } e.Handled = true; - } else if (keys == (Key.Tab | Key.CtrlMask | Key.ShiftMask)) { + } else if (winDialog != null && keys == (Key.Tab | Key.CtrlMask | Key.ShiftMask)) { if (_tabView.SelectedTab == _tabView.Tabs.ElementAt (0)) { _tabView.SelectedTab = _tabView.Tabs.ElementAt (_tabView.Tabs.Count - 1); } else { @@ -438,6 +438,7 @@ private void CloseFile () private void Quit () { if (!CanCloseFile ()) { + _textView.SetFocus (); return; } From 086a8a12eeebd74fa761070da39cb046472c9959 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 12 Oct 2021 19:24:59 +0100 Subject: [PATCH 06/16] Fixes both dynamic menu and status bar broken scenarios. --- UICatalog/Scenarios/DynamicMenuBar.cs | 7 ++++--- UICatalog/Scenarios/DynamicStatusBar.cs | 11 ++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/UICatalog/Scenarios/DynamicMenuBar.cs b/UICatalog/Scenarios/DynamicMenuBar.cs index 13918064da..4b0c1bfa8b 100644 --- a/UICatalog/Scenarios/DynamicMenuBar.cs +++ b/UICatalog/Scenarios/DynamicMenuBar.cs @@ -12,10 +12,11 @@ namespace UICatalog { [ScenarioMetadata (Name: "Dynamic MenuBar", Description: "Demonstrates how to add and remove a MenuBar, Menus and change titles dynamically.")] [ScenarioCategory ("Dynamic")] class DynamicMenuBar : Scenario { - public override void Run () + public override void Init (Toplevel top, ColorScheme colorScheme) { - Top.Add (new DynamicMenuBarSample (Win.Title)); - base.Run (); + Application.Init (); + Top = Application.Top; + Top.Add (new DynamicMenuBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}")); } } diff --git a/UICatalog/Scenarios/DynamicStatusBar.cs b/UICatalog/Scenarios/DynamicStatusBar.cs index be4f960af7..474f5fac03 100644 --- a/UICatalog/Scenarios/DynamicStatusBar.cs +++ b/UICatalog/Scenarios/DynamicStatusBar.cs @@ -9,10 +9,11 @@ namespace UICatalog { [ScenarioMetadata (Name: "Dynamic StatusBar", Description: "Demonstrates how to add and remove a StatusBar and change items dynamically.")] [ScenarioCategory ("Dynamic")] class DynamicStatusBar : Scenario { - public override void Run () + public override void Init (Toplevel top, ColorScheme colorScheme) { - Top.Add (new DynamicStatusBarSample (Win.Title)); - base.Run (); + Application.Init (); + Top = Application.Top; + Top.Add (new DynamicStatusBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}")); } } @@ -222,6 +223,7 @@ public DynamicStatusBarSample (ustring title) : base (title) _statusBar.AddItemAt (_currentSelectedStatusBar, newStatusItem); DataContext.Items.Add (new DynamicStatusItemList (newStatusItem.Title, newStatusItem)); _lstItems.MoveDown (); + SetFrameDetails (); }; _btnRemove.Clicked += () => { @@ -310,8 +312,7 @@ void SetListViewSource (StatusItem _currentStatusItem, bool fill = false) StatusItem CreateNewStatusBar (DynamicStatusItem item) { - StatusItem newStatusItem; - newStatusItem = new StatusItem (ShortcutHelper.GetShortcutFromTag ( + var newStatusItem = new StatusItem (ShortcutHelper.GetShortcutFromTag ( item.shortcut, StatusBar.ShortcutDelimiter), item.title, _frmStatusBarDetails.CreateAction (item)); From 99e1e35ce750d2bd5da04ecbe1a00996d32a9757 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 12 Oct 2021 19:30:07 +0100 Subject: [PATCH 07/16] Fix a bug where the subviews oldEnabled can be overridden, even the superview Enable property hasn't changed. --- Terminal.Gui/Core/View.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 7d0e7b6d2f..0a87f41825 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -2154,15 +2154,16 @@ public override bool Enabled { } OnEnabledChanged (); SetNeedsDisplay (); - } - if (subviews != null) { - foreach (var view in subviews) { - if (!value) { - view.oldEnabled = view.Enabled; - view.Enabled = value; - } else { - view.Enabled = view.oldEnabled; - view.addingView = false; + + if (subviews != null) { + foreach (var view in subviews) { + if (!value) { + view.oldEnabled = view.Enabled; + view.Enabled = value; + } else { + view.Enabled = view.oldEnabled; + view.addingView = false; + } } } } From 00fbcea077456ca01d0722269bb04da415adcd54 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 14 Oct 2021 23:34:36 +0100 Subject: [PATCH 08/16] Fixes CanFocus when set to false and HasFocus is true. --- Terminal.Gui/Core/View.cs | 98 +++++++++++++++++++++-------------- UnitTests/ApplicationTests.cs | 6 +++ 2 files changed, 66 insertions(+), 38 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 0a87f41825..a7b5a912c8 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -344,36 +344,45 @@ public override bool CanFocus { TabIndex = SuperView != null ? SuperView.tabIndexes.IndexOf (this) : -1; } TabStop = value; - } - if (subviews != null && IsInitialized) { - foreach (var view in subviews) { - if (view.CanFocus != value) { - if (!value) { - view.oldCanFocus = view.CanFocus; - view.oldTabIndex = view.tabIndex; - view.CanFocus = value; - view.tabIndex = -1; + + if (!value && Application.Top?.Focused == this) { + Application.Top.focused = null; + } + if (!value && HasFocus) { + SetHasFocus (false, this); + EnsureFocus (); + if (Focused == null) { + if (Application.Top.Focused == null) { + Application.Top.FocusNext (); } else { - if (addingView) { - view.addingView = true; - } - view.CanFocus = view.oldCanFocus; - view.tabIndex = view.oldTabIndex; - view.addingView = false; + var v = Application.Top.GetMostFocused (Application.Top.Focused); + v.SetHasFocus (true, null, true); } + Application.EnsuresTopOnFront (); } } - } - if (!value && HasFocus) { - SetHasFocus (false, this); - EnsureFocus (); - if (Focused == null) { - Application.Top.FocusNext (); - Application.EnsuresTopOnFront (); + if (subviews != null && IsInitialized) { + foreach (var view in subviews) { + if (view.CanFocus != value) { + if (!value) { + view.oldCanFocus = view.CanFocus; + view.oldTabIndex = view.tabIndex; + view.CanFocus = value; + view.tabIndex = -1; + } else { + if (addingView) { + view.addingView = true; + } + view.CanFocus = view.oldCanFocus; + view.tabIndex = view.oldTabIndex; + view.addingView = false; + } + } + } } + OnCanFocusChanged (); + SetNeedsDisplay (); } - OnCanFocusChanged (); - SetNeedsDisplay (); } } @@ -1195,9 +1204,9 @@ public override bool HasFocus { } } - void SetHasFocus (bool value, View view) + void SetHasFocus (bool value, View view, bool force = false) { - if (hasFocus != value) { + if (hasFocus != value || force) { hasFocus = value; if (value) { OnEnter (view); @@ -1789,6 +1798,19 @@ public bool FocusNext () return false; } + View GetMostFocused (View view) + { + if (view == null) { + return view; + } + + if (view.focused != null) { + return GetMostFocused (view.focused); + } else { + return view; + } + } + /// /// Sets the View's to the relative coordinates if its container, given the for its container. /// @@ -1875,19 +1897,19 @@ List TopologicalSort (HashSet nodes, HashSet<(View From, View To)> e } } - if (edges.Any ()) { - var (from, to) = edges.First (); - if (from != Application.Top) { - if (!ReferenceEquals (from, to)) { - throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {from} linked with {to}. Did you forget to add it to {this}?"); - } else { - throw new InvalidOperationException ("TopologicalSort encountered a recursive cycle in the relative Pos/Dim in the views of " + this); - } - } - } + if (edges.Any ()) { + var (from, to) = edges.First (); + if (from != Application.Top) { + if (!ReferenceEquals (from, to)) { + throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {from} linked with {to}. Did you forget to add it to {this}?"); + } else { + throw new InvalidOperationException ("TopologicalSort encountered a recursive cycle in the relative Pos/Dim in the views of " + this); + } + } + } - // return L (a topologically sorted order) - return result; + // return L (a topologically sorted order) + return result; } /// diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs index e5b28a67c7..f10338f973 100644 --- a/UnitTests/ApplicationTests.cs +++ b/UnitTests/ApplicationTests.cs @@ -1234,6 +1234,12 @@ public void EnsuresTopOnFront_CanFocus_False_By_Keyboard_And_Mouse () Application.Begin (top); + Assert.True (win.CanFocus); + Assert.True (win.HasFocus); + Assert.True (win2.CanFocus); + Assert.False (win2.HasFocus); + Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title); + win.CanFocus = false; Assert.False (win.CanFocus); Assert.False (win.HasFocus); From f103a309ccd535522e4a2d4e84412e54250b13a0 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 14 Oct 2021 23:36:28 +0100 Subject: [PATCH 09/16] Fixes the broken TextView DesiredCursorVisibility. --- Terminal.Gui/Views/TextView.cs | 11 ++-- UICatalog/Scenarios/Editor.cs | 98 +++++++++++++++++++++++++++------- 2 files changed, 82 insertions(+), 27 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 448ab696f9..6aa1be6e26 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -1272,14 +1272,9 @@ void SaveCursorVisibility () void ResetCursorVisibility () { - if (savedCursorVisibility == 0) { - savedCursorVisibility = desiredCursorVisibility; - } - if (savedCursorVisibility != desiredCursorVisibility && !HasFocus) { + if (savedCursorVisibility != 0) { DesiredCursorVisibility = savedCursorVisibility; - savedCursorVisibility = CursorVisibility.Default; - } else if (desiredCursorVisibility != CursorVisibility.Underline) { - DesiredCursorVisibility = CursorVisibility.Underline; + savedCursorVisibility = 0; } } @@ -1446,7 +1441,7 @@ public bool ReadOnly { public CursorVisibility DesiredCursorVisibility { get => desiredCursorVisibility; set { - if (desiredCursorVisibility != value && HasFocus) { + if (HasFocus) { Application.Driver.SetCursorVisibility (value); } diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index c528f170d9..bd4621a61b 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -83,19 +83,7 @@ public override void Init (Toplevel top, ColorScheme colorScheme) new MenuItem ("_Select All", "", () => SelectAll(),null,null, Key.CtrlMask | Key.T) }), new MenuBarItem ("_ScrollBarView", CreateKeepChecked ()), - new MenuBarItem ("_Cursor", new MenuItem [] { - new MenuItem ("_Invisible", "", () => SetCursor(CursorVisibility.Invisible)), - new MenuItem ("_Box", "", () => SetCursor(CursorVisibility.Box)), - new MenuItem ("_Underline", "", () => SetCursor(CursorVisibility.Underline)), - new MenuItem ("", "", () => {}, () => { return false; }), - new MenuItem ("xTerm :", "", () => {}, () => { return false; }), - new MenuItem ("", "", () => {}, () => { return false; }), - new MenuItem (" _Default", "", () => SetCursor(CursorVisibility.Default)), - new MenuItem (" _Vertical", "", () => SetCursor(CursorVisibility.Vertical)), - new MenuItem (" V_ertical Fix", "", () => SetCursor(CursorVisibility.VerticalFix)), - new MenuItem (" B_ox Fix", "", () => SetCursor(CursorVisibility.BoxFix)), - new MenuItem (" U_nderline Fix","", () => SetCursor(CursorVisibility.UnderlineFix)) - }), + new MenuBarItem ("_Cursor", CreateCursorRadio ()), new MenuBarItem ("Forma_t", new MenuItem [] { CreateWrapChecked (), CreateAutocomplete(), @@ -330,11 +318,6 @@ private void ReplaceAll () } } - private void SetCursor (CursorVisibility visibility) - { - _textView.DesiredCursorVisibility = visibility; - } - private bool CanCloseFile () { if (_textView.Text == _originalText) { @@ -438,7 +421,6 @@ private void CloseFile () private void Quit () { if (!CanCloseFile ()) { - _textView.SetFocus (); return; } @@ -581,6 +563,84 @@ private MenuItem CreateVisibleChecked () return item; } + MenuItem [] CreateCursorRadio () + { + List menuItems = new List (); + menuItems.Add (new MenuItem ("_Invisible", "", () => SetCursor (CursorVisibility.Invisible)) { + CheckType = MenuItemCheckStyle.Radio, + Checked = _textView.DesiredCursorVisibility == CursorVisibility.Invisible + }); + menuItems.Add (new MenuItem ("_Box", "", () => SetCursor (CursorVisibility.Box)) { + CheckType = MenuItemCheckStyle.Radio, + Checked = _textView.DesiredCursorVisibility == CursorVisibility.Box + }); + menuItems.Add (new MenuItem ("_Underline", "", () => SetCursor (CursorVisibility.Underline)) { + CheckType = MenuItemCheckStyle.Radio, + Checked = _textView.DesiredCursorVisibility == CursorVisibility.Underline + }); + menuItems.Add (new MenuItem ("", "", () => { }, () => false)); + menuItems.Add (new MenuItem ("xTerm :", "", () => { }, () => false)); + menuItems.Add (new MenuItem ("", "", () => { }, () => false)); + menuItems.Add (new MenuItem (" _Default", "", () => SetCursor (CursorVisibility.Default)) { + CheckType = MenuItemCheckStyle.Radio, + Checked = _textView.DesiredCursorVisibility == CursorVisibility.Default + }); + menuItems.Add (new MenuItem (" _Vertical", "", () => SetCursor (CursorVisibility.Vertical)) { + CheckType = MenuItemCheckStyle.Radio, + Checked = _textView.DesiredCursorVisibility == CursorVisibility.Vertical + }); + menuItems.Add (new MenuItem (" V_ertical Fix", "", () => SetCursor (CursorVisibility.VerticalFix)) { + CheckType = MenuItemCheckStyle.Radio, + Checked = _textView.DesiredCursorVisibility == CursorVisibility.VerticalFix + }); + menuItems.Add (new MenuItem (" B_ox Fix", "", () => SetCursor (CursorVisibility.BoxFix)) { + CheckType = MenuItemCheckStyle.Radio, + Checked = _textView.DesiredCursorVisibility == CursorVisibility.BoxFix + }); + menuItems.Add (new MenuItem (" U_nderline Fix", "", () => SetCursor (CursorVisibility.UnderlineFix)) { + CheckType = MenuItemCheckStyle.Radio, + Checked = _textView.DesiredCursorVisibility == CursorVisibility.UnderlineFix + }); + + void SetCursor (CursorVisibility visibility) + { + _textView.DesiredCursorVisibility = visibility; + var title = ""; + switch (visibility) { + case CursorVisibility.Default: + title = " _Default"; + break; + case CursorVisibility.Invisible: + title = "_Invisible"; + break; + case CursorVisibility.Underline: + title = "_Underline"; + break; + case CursorVisibility.UnderlineFix: + title = " U_nderline Fix"; + break; + case CursorVisibility.Vertical: + title = " _Vertical"; + break; + case CursorVisibility.VerticalFix: + title = " V_ertical Fix"; + break; + case CursorVisibility.Box: + title = "_Box"; + break; + case CursorVisibility.BoxFix: + title = " B_ox Fix"; + break; + } + + foreach (var menuItem in menuItems) { + menuItem.Checked = menuItem.Title.Equals (title) && visibility == _textView.DesiredCursorVisibility; + } + } + + return menuItems.ToArray (); + } + private void CreateFindReplace (bool isFind = true) { if (winDialog != null) { From 1dc72d32f043f45fbda38e4b6f75ce7c1133763a Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 15 Oct 2021 01:37:23 +0100 Subject: [PATCH 10/16] Prevents TextField being focused by mouse if CanFocus is false. --- Terminal.Gui/Views/TextField.cs | 7 +++--- UnitTests/TextFieldTests.cs | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 57012d72d7..0c4c71a965 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -728,10 +728,11 @@ public override bool MouseEvent (MouseEvent ev) return false; } + if (!CanFocus) { + return true; + } + if (ev.Flags == MouseFlags.Button1Pressed) { - if (!CanFocus) { - return true; - } if (!HasFocus) { SetFocus (); } diff --git a/UnitTests/TextFieldTests.cs b/UnitTests/TextFieldTests.cs index 6ff23ced36..e3e6209681 100644 --- a/UnitTests/TextFieldTests.cs +++ b/UnitTests/TextFieldTests.cs @@ -819,5 +819,43 @@ public void TextField_SpaceHandling () tf.MouseEvent (ev); Assert.Equal (1, tf.SelectedLength); } + + [Fact] + [InitShutdown] + public void CanFocus_False_Wont_Focus_With_Mouse () + { + var tf = new TextField () { + Width = Dim.Fill (), + CanFocus = false, + ReadOnly = true, + Text = "some text" + }; + var fv = new FrameView ("I shouldn't get focus") { + Width = Dim.Fill (), + Height = Dim.Fill (), + CanFocus = false, + }; + fv.Add (tf); + Application.Top.Add (fv); + + Assert.False (tf.CanFocus); + + tf.MouseEvent (new MouseEvent () { + X = 1, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked + }); + + Assert.Null (tf.SelectedText); + + tf.CanFocus = true; + tf.MouseEvent (new MouseEvent () { + X = 1, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked + }); + + Assert.Equal ("some ", tf.SelectedText); + } } } From d6c00742335c5f41a63b593c1e20c832b8460ed4 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 15 Oct 2021 13:07:25 +0100 Subject: [PATCH 11/16] Fixes the CanFocus on content views. --- Terminal.Gui/Core/Border.cs | 9 +++++ Terminal.Gui/Core/Window.cs | 9 +++++ Terminal.Gui/Views/FrameView.cs | 9 +++++ Terminal.Gui/Views/TextField.cs | 13 +++++-- UnitTests/TextFieldTests.cs | 31 +++++++++++++++- UnitTests/TextViewTests.cs | 66 +++++++++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/Core/Border.cs b/Terminal.Gui/Core/Border.cs index cf8f6e32a5..d7ec4e8840 100644 --- a/Terminal.Gui/Core/Border.cs +++ b/Terminal.Gui/Core/Border.cs @@ -257,6 +257,15 @@ public override void Redraw (Rect bounds) SuperView.SetNeedsDisplay (); } } + + /// + public override void OnCanFocusChanged () + { + if (Border.Child != null) { + Border.Child.CanFocus = CanFocus; + } + base.OnCanFocusChanged (); + } } private class ChildContentView : View { diff --git a/Terminal.Gui/Core/Window.cs b/Terminal.Gui/Core/Window.cs index 6f84081da9..f030d8f1e4 100644 --- a/Terminal.Gui/Core/Window.cs +++ b/Terminal.Gui/Core/Window.cs @@ -293,6 +293,15 @@ public override void Redraw (Rect bounds) } } + /// + public override void OnCanFocusChanged () + { + if (contentView != null) { + contentView.CanFocus = CanFocus; + } + base.OnCanFocusChanged (); + } + /// /// The text displayed by the . /// diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs index 8a3937142b..58741cd850 100644 --- a/Terminal.Gui/Views/FrameView.cs +++ b/Terminal.Gui/Views/FrameView.cs @@ -268,5 +268,14 @@ public override bool OnEnter (View view) return base.OnEnter (view); } + + /// + public override void OnCanFocusChanged () + { + if (contentView != null) { + contentView.CanFocus = CanFocus; + } + base.OnCanFocusChanged (); + } } } diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 0c4c71a965..8b3d5f5585 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -733,9 +733,7 @@ public override bool MouseEvent (MouseEvent ev) } if (ev.Flags == MouseFlags.Button1Pressed) { - if (!HasFocus) { - SetFocus (); - } + EnsureHasFocus (); PositionCursor (ev); if (isButtonReleased) { ClearAllSelection (); @@ -754,6 +752,7 @@ public override bool MouseEvent (MouseEvent ev) isButtonPressed = false; Application.UngrabMouse (); } else if (ev.Flags == MouseFlags.Button1DoubleClicked) { + EnsureHasFocus (); int x = PositionCursor (ev); int sbw = x; if (x == text.Count || (x > 0 && (char)Text [x - 1] != ' ' @@ -772,6 +771,7 @@ public override bool MouseEvent (MouseEvent ev) } PrepareSelection (sbw, sfw - sbw); } else if (ev.Flags == MouseFlags.Button1TripleClicked) { + EnsureHasFocus (); PositionCursor (0); ClearAllSelection (); PrepareSelection (0, text.Count); @@ -779,6 +779,13 @@ public override bool MouseEvent (MouseEvent ev) SetNeedsDisplay (); return true; + + void EnsureHasFocus () + { + if (!HasFocus) { + SetFocus (); + } + } } int PositionCursor (MouseEvent ev) diff --git a/UnitTests/TextFieldTests.cs b/UnitTests/TextFieldTests.cs index e3e6209681..26c153f9a5 100644 --- a/UnitTests/TextFieldTests.cs +++ b/UnitTests/TextFieldTests.cs @@ -824,6 +824,7 @@ public void TextField_SpaceHandling () [InitShutdown] public void CanFocus_False_Wont_Focus_With_Mouse () { + var top = Application.Top; var tf = new TextField () { Width = Dim.Fill (), CanFocus = false, @@ -836,9 +837,14 @@ public void CanFocus_False_Wont_Focus_With_Mouse () CanFocus = false, }; fv.Add (tf); - Application.Top.Add (fv); + top.Add (fv); + + Application.Begin (top); Assert.False (tf.CanFocus); + Assert.False (tf.HasFocus); + Assert.False (fv.CanFocus); + Assert.False (fv.HasFocus); tf.MouseEvent (new MouseEvent () { X = 1, @@ -847,7 +853,13 @@ public void CanFocus_False_Wont_Focus_With_Mouse () }); Assert.Null (tf.SelectedText); + Assert.False (tf.CanFocus); + Assert.False (tf.HasFocus); + Assert.False (fv.CanFocus); + Assert.False (fv.HasFocus); + Assert.Throws (() => tf.CanFocus = true); + fv.CanFocus = true; tf.CanFocus = true; tf.MouseEvent (new MouseEvent () { X = 1, @@ -856,6 +868,23 @@ public void CanFocus_False_Wont_Focus_With_Mouse () }); Assert.Equal ("some ", tf.SelectedText); + Assert.True (tf.CanFocus); + Assert.True (tf.HasFocus); + Assert.True (fv.CanFocus); + Assert.True (fv.HasFocus); + + fv.CanFocus = false; + tf.MouseEvent (new MouseEvent () { + X = 1, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked + }); + + Assert.Equal ("some ", tf.SelectedText); // Setting CanFocus to false don't change the SelectedText + Assert.False (tf.CanFocus); + Assert.False (tf.HasFocus); + Assert.False (fv.CanFocus); + Assert.False (fv.HasFocus); } } } diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index ca8a12e585..fc857e567f 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -2115,5 +2115,71 @@ public void TextView_SpaceHandling () Assert.Equal (1, tv.SelectedLength); } + [Fact] + [InitShutdown] + public void CanFocus_False_Wont_Focus_With_Mouse () + { + var top = Application.Top; + var tv = new TextView () { + Width = Dim.Fill (), + CanFocus = false, + ReadOnly = true, + Text = "some text" + }; + var fv = new FrameView ("I shouldn't get focus") { + Width = Dim.Fill (), + Height = Dim.Fill (), + CanFocus = false, + }; + fv.Add (tv); + top.Add (fv); + + Application.Begin (top); + + Assert.False (tv.CanFocus); + Assert.False (tv.HasFocus); + Assert.False (fv.CanFocus); + Assert.False (fv.HasFocus); + + tv.MouseEvent (new MouseEvent () { + X = 1, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked + }); + + Assert.Empty (tv.SelectedText); + Assert.False (tv.CanFocus); + Assert.False (tv.HasFocus); + Assert.False (fv.CanFocus); + Assert.False (fv.HasFocus); + + Assert.Throws (() => tv.CanFocus = true); + fv.CanFocus = true; + tv.CanFocus = true; + tv.MouseEvent (new MouseEvent () { + X = 1, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked + }); + + Assert.Equal ("some ", tv.SelectedText); + Assert.True (tv.CanFocus); + Assert.True (tv.HasFocus); + Assert.True (fv.CanFocus); + Assert.True (fv.HasFocus); + + fv.CanFocus = false; + tv.MouseEvent (new MouseEvent () { + X = 1, + Y = 0, + Flags = MouseFlags.Button1DoubleClicked + }); + + Assert.Equal ("some ", tv.SelectedText); // Setting CanFocus to false don't change the SelectedText + Assert.False (tv.CanFocus); + Assert.False (tv.HasFocus); + Assert.False (fv.CanFocus); + Assert.False (fv.HasFocus); + } } } From 4feff2e714dc003feef327d58d2361d008ae9e2a Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 15 Oct 2021 16:18:15 +0100 Subject: [PATCH 12/16] Fixes #1470. Not all WindowsConsole.InputRecord are caught in WindowsDriver. --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 114 +++++++++---------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 2a1c5a1d55..077a1ef4a0 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -937,66 +937,67 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) Y = mouseEvent.MousePosition.Y }; - if (!isButtonPressed && buttonPressedCount < 2 - && mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved - && (mouseEvent.ButtonState == WindowsConsole.ButtonState.Button1Pressed - || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button2Pressed - || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button3Pressed)) { - - lastMouseButtonPressed = mouseEvent.ButtonState; - buttonPressedCount++; - } else if (!isButtonPressed && buttonPressedCount > 0 && mouseEvent.ButtonState == 0 - && mouseEvent.EventFlags == 0) { - - buttonPressedCount++; - } + //if (!isButtonPressed && buttonPressedCount < 2 + // && mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved + // && (mouseEvent.ButtonState == WindowsConsole.ButtonState.Button1Pressed + // || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button2Pressed + // || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button3Pressed)) { + + // lastMouseButtonPressed = mouseEvent.ButtonState; + // buttonPressedCount++; + //} else if (!isButtonPressed && buttonPressedCount > 0 && mouseEvent.ButtonState == 0 + // && mouseEvent.EventFlags == 0) { + + // buttonPressedCount++; + //} //System.Diagnostics.Debug.WriteLine ($"isButtonPressed: {isButtonPressed};buttonPressedCount: {buttonPressedCount};lastMouseButtonPressed: {lastMouseButtonPressed}"); //System.Diagnostics.Debug.WriteLine ($"isOneFingerDoubleClicked: {isOneFingerDoubleClicked}"); - if (buttonPressedCount == 1 && lastMouseButtonPressed != null && p == point - && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed - || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed - || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) { - - switch (lastMouseButtonPressed) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1DoubleClicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2DoubleClicked; - break; - - case WindowsConsole.ButtonState.Button3Pressed: - mouseFlag = MouseFlags.Button3DoubleClicked; - break; - } - isOneFingerDoubleClicked = true; + //if (buttonPressedCount == 1 && lastMouseButtonPressed != null && p == point + // && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed + // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed + // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) { + + // switch (lastMouseButtonPressed) { + // case WindowsConsole.ButtonState.Button1Pressed: + // mouseFlag = MouseFlags.Button1DoubleClicked; + // break; + + // case WindowsConsole.ButtonState.Button2Pressed: + // mouseFlag = MouseFlags.Button2DoubleClicked; + // break; + + // case WindowsConsole.ButtonState.Button3Pressed: + // mouseFlag = MouseFlags.Button3DoubleClicked; + // break; + // } + // isOneFingerDoubleClicked = true; + + //} else if (buttonPressedCount == 3 && lastMouseButtonPressed != null && isOneFingerDoubleClicked && p == point + // && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed + // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed + // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) { + + // switch (lastMouseButtonPressed) { + // case WindowsConsole.ButtonState.Button1Pressed: + // mouseFlag = MouseFlags.Button1TripleClicked; + // break; + + // case WindowsConsole.ButtonState.Button2Pressed: + // mouseFlag = MouseFlags.Button2TripleClicked; + // break; + + // case WindowsConsole.ButtonState.Button3Pressed: + // mouseFlag = MouseFlags.Button3TripleClicked; + // break; + // } + // buttonPressedCount = 0; + // lastMouseButtonPressed = null; + // isOneFingerDoubleClicked = false; + // isButtonReleased = false; - } else if (buttonPressedCount == 3 && lastMouseButtonPressed != null && isOneFingerDoubleClicked && p == point - && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed - || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed - || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) { - - switch (lastMouseButtonPressed) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1TripleClicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2TripleClicked; - break; - - case WindowsConsole.ButtonState.Button3Pressed: - mouseFlag = MouseFlags.Button3TripleClicked; - break; - } - buttonPressedCount = 0; - lastMouseButtonPressed = null; - isOneFingerDoubleClicked = false; - isButtonReleased = false; - - } else if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && lastMouseButtonPressed == null && !isButtonDoubleClicked) || + //} + if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && lastMouseButtonPressed == null && !isButtonDoubleClicked) || (lastMouseButtonPressed == null && mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved && mouseEvent.ButtonState != 0 && !isButtonReleased && !isButtonDoubleClicked)) { switch (mouseEvent.ButtonState) { @@ -1807,7 +1808,6 @@ bool IMainLoopDriver.EventsPending (bool wait) return true; } - result = null; waitForProbe.Set (); winChange.Set (); From 02245b86529945515b40f4a5eda42553c6fc28e5 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 15 Oct 2021 16:40:53 +0100 Subject: [PATCH 13/16] Changing the input for a Queue object. --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 077a1ef4a0..9aec89de7f 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1733,7 +1733,7 @@ internal class WindowsMainLoop : IMainLoopDriver { CancellationTokenSource tokenSource = new CancellationTokenSource (); // The records that we keep fetching - WindowsConsole.InputRecord [] result = new WindowsConsole.InputRecord [1]; + Queue resultQueue = new Queue (); /// /// Invoked when a Key is pressed or released. @@ -1764,7 +1764,9 @@ void WindowsInputHandler () waitForProbe.Wait (); waitForProbe.Reset (); - result = winConsole.ReadConsoleInput (); + if (resultQueue?.Count == 0) { + resultQueue.Enqueue (winConsole.ReadConsoleInput ()); + } eventReady.Set (); } @@ -1822,7 +1824,7 @@ bool IMainLoopDriver.EventsPending (bool wait) } if (!tokenSource.IsCancellationRequested) { - return result != null || CheckTimers (wait, out _) || winChanged; + return resultQueue.Count > 0 || CheckTimers (wait, out _) || winChanged; } tokenSource.Dispose (); @@ -1855,9 +1857,8 @@ bool CheckTimers (bool wait, out int waitTimeout) void IMainLoopDriver.MainIteration () { - if (result != null) { - var inputEvent = result [0]; - result = null; + while (resultQueue.Count > 0) { + var inputEvent = resultQueue.Dequeue()[0]; ProcessInput?.Invoke (inputEvent); } if (winChanged) { From 71918b779c47a7b3b05bbd5a19133d8db99498d3 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 16 Oct 2021 16:06:01 +0100 Subject: [PATCH 14/16] Suppress warnings. --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 9aec89de7f..8652457304 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -904,7 +904,7 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) bool isButtonReleased = false; bool isButtonDoubleClicked = false; Point point; - int buttonPressedCount; + //int buttonPressedCount; bool isOneFingerDoubleClicked = false; bool processButtonClick; @@ -1174,7 +1174,7 @@ async Task ProcessButtonDoubleClickedAsync () await Task.Delay (300); isButtonDoubleClicked = false; isOneFingerDoubleClicked = false; - buttonPressedCount = 0; + //buttonPressedCount = 0; } async Task ProcessContinuousButtonPressedAsync (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag) From c1107cdb9b4d5a21b5f8346787ebeb772a3c7638 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 16 Oct 2021 16:10:49 +0100 Subject: [PATCH 15/16] Fixed yet the visibility cursor and adding more unit tests. --- Terminal.Gui/Views/TextView.cs | 6 ++- UnitTests/TextViewTests.cs | 78 ++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index dcfe4a0a26..a4d14e032a 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -1357,8 +1357,10 @@ public override void PositionCursor () } } } - if (col >= leftColumn && currentColumn - leftColumn + RightOffset < Frame.Width - && topRow <= currentRow && currentRow - topRow + BottomOffset < Frame.Height) { + var posX = currentColumn - leftColumn; + var posY = currentRow - topRow; + if ( posX > -1 && col >= posX && posX < Frame.Width - RightOffset + && topRow <= currentRow && posY < Frame.Height - BottomOffset) { ResetCursorVisibility (); Move (col, currentRow - topRow); } else { diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index fc857e567f..53866d7e2b 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -2181,5 +2181,83 @@ public void CanFocus_False_Wont_Focus_With_Mouse () Assert.False (fv.CanFocus); Assert.False (fv.HasFocus); } + + [Fact] + [InitShutdown] + public void DesiredCursorVisibility_Vertical_Navigation () + { + string text = ""; + + for (int i = 0; i < 12; i++) { + text += $"This is the line {i}\n"; + } + var tv = new TextView () { Width = 10, Height = 10 }; + tv.Text = text; + + Assert.Equal (0, tv.TopRow); + tv.PositionCursor (); + Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility); + + for (int i = 0; i < 12; i++) { + tv.MouseEvent (new MouseEvent () { + Flags = MouseFlags.WheeledDown + }); + tv.PositionCursor (); + Assert.Equal (i + 1, tv.TopRow); + Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility); + } + + for (int i = 12; i > 0; i--) { + tv.MouseEvent (new MouseEvent () { + Flags = MouseFlags.WheeledUp + }); + tv.PositionCursor (); + Assert.Equal (i - 1, tv.TopRow); + if (i - 1 == 0) { + Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility); + } else { + Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility); + } + } + } + + [Fact] + [InitShutdown] + public void DesiredCursorVisibility_Horizontal_Navigation () + { + string text = ""; + + for (int i = 0; i < 12; i++) { + text += $"{i.ToString () [^1]}"; + } + var tv = new TextView () { Width = 10, Height = 10 }; + tv.Text = text; + + Assert.Equal (0, tv.LeftColumn); + tv.PositionCursor (); + Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility); + + for (int i = 0; i < 12; i++) { + tv.MouseEvent (new MouseEvent () { + Flags = MouseFlags.WheeledRight + }); + tv.PositionCursor (); + Assert.Equal (Math.Min (i + 1, 11), tv.LeftColumn); + Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility); + } + + for (int i = 11; i > 0; i--) { + tv.MouseEvent (new MouseEvent () { + Flags = MouseFlags.WheeledLeft + }); + tv.PositionCursor (); + Assert.Equal (i - 1, tv.LeftColumn); + if (i - 1 == 0) { + Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility); + } else { + Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility); + } + } + } } } From 5d36fa2da11693d2d37edca290c834be29d37d0c Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 16 Oct 2021 16:12:36 +0100 Subject: [PATCH 16/16] Suppressing more warnings. --- Terminal.Gui/Core/View.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index a7b5a912c8..e53a85c510 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1602,7 +1602,7 @@ public override bool ProcessColdKey (KeyEvent keyEvent) /// public event Action KeyDown; - /// Contains the details about the key that produced the event. + /// public override bool OnKeyDown (KeyEvent keyEvent) { if (!Enabled) { @@ -1626,7 +1626,7 @@ public override bool OnKeyDown (KeyEvent keyEvent) /// public event Action KeyUp; - /// Contains the details about the key that produced the event. + /// public override bool OnKeyUp (KeyEvent keyEvent) { if (!Enabled) {