From a1249198bd884cd532d56e34eadb0a85d7ce9bf9 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 7 May 2024 10:07:14 -0600 Subject: [PATCH] PositionCursor unit tests and flashing cursor fixes --- Terminal.Gui/Application.cs | 39 ++--- .../CursesDriver/CursesDriver.cs | 4 + Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 17 +- Terminal.Gui/View/View.cs | 7 +- Terminal.Gui/View/ViewSubViews.cs | 34 ++-- Terminal.Gui/Views/Button.cs | 11 +- Terminal.Gui/Views/CheckBox.cs | 11 -- Terminal.Gui/Views/ColorPicker.cs | 7 - Terminal.Gui/Views/FrameView.cs | 11 -- Terminal.Gui/Views/GraphView/GraphView.cs | 9 -- Terminal.Gui/Views/HexView.cs | 25 +-- Terminal.Gui/Views/Label.cs | 7 - Terminal.Gui/Views/ListView.cs | 7 +- Terminal.Gui/Views/Menu/Menu.cs | 11 +- Terminal.Gui/Views/Menu/MenuBar.cs | 11 +- Terminal.Gui/Views/ProgressBar.cs | 8 - Terminal.Gui/Views/RadioGroup.cs | 10 +- Terminal.Gui/Views/ScrollBarView.cs | 7 - Terminal.Gui/Views/ScrollView.cs | 13 +- Terminal.Gui/Views/Slider.cs | 23 +-- Terminal.Gui/Views/StatusBar.cs | 8 - Terminal.Gui/Views/TabView.cs | 7 - Terminal.Gui/Views/TableView/TableView.cs | 3 - Terminal.Gui/Views/TextField.cs | 14 +- Terminal.Gui/Views/TextValidateField.cs | 24 --- Terminal.Gui/Views/TextView.cs | 109 ++++++------- Terminal.Gui/Views/TileView.cs | 10 +- Terminal.Gui/Views/Toplevel.cs | 12 +- Terminal.Gui/Views/TreeView/TreeView.cs | 77 +++------ UICatalog/Scenarios/CharacterMap.cs | 4 +- UICatalog/Scenarios/Editor.cs | 21 ++- UICatalog/Scenarios/TreeViewFileSystem.cs | 2 +- UnitTests/Application/CursorTests.cs | 148 ++++++++++++++++++ UnitTests/View/NavigationTests.cs | 36 ++++- UnitTests/Views/StatusBarTests.cs | 34 ---- UnitTests/Views/TextViewTests.cs | 4 +- UnitTests/Views/TreeViewTests.cs | 10 +- 37 files changed, 336 insertions(+), 459 deletions(-) create mode 100644 UnitTests/Application/CursorTests.cs diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index dfdae2b0b..46c4ea820 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -557,7 +557,7 @@ public static partial class Application return rs; } - private static CursorVisibility _cachedCursorVisibility; + //private static CursorVisibility _cachedCursorVisibility; /// /// Calls on the most focused view in the view starting with . @@ -579,16 +579,14 @@ public static partial class Application return false; } - CursorVisibility cachedCursorVisibility; // If the view is not visible or enabled, don't position the cursor if (!mostFocused.Visible || !mostFocused.Enabled) { - Driver.GetCursorVisibility (out cachedCursorVisibility); - - if (cachedCursorVisibility != CursorVisibility.Invisible) + Driver.GetCursorVisibility (out CursorVisibility current); + if (current != CursorVisibility.Invisible) { - _cachedCursorVisibility = cachedCursorVisibility; + //_cachedCursorVisibility = current; Driver.SetCursorVisibility (CursorVisibility.Invisible); } @@ -606,40 +604,37 @@ public static partial class Application Point? prevCursor = new (Driver.Row, Driver.Col); Point? cursor = mostFocused.PositionCursor (); - // If the cursor is not in a visible location in the SuperView, hide it + Driver.GetCursorVisibility (out CursorVisibility currentCursorVisibility); + if (cursor is { }) { // Convert cursor to screen coords cursor = mostFocused.ViewportToScreen (mostFocused.Viewport with { Location = cursor.Value }).Location; + + // If the cursor is not in a visible location in the SuperView, hide it if (!superViewViewport.Contains (cursor.Value)) { - Driver.GetCursorVisibility (out cachedCursorVisibility); - - if (cachedCursorVisibility != CursorVisibility.Invisible) + if (currentCursorVisibility != CursorVisibility.Invisible) { - _cachedCursorVisibility = cachedCursorVisibility; + //_cachedCursorVisibility = currentCursorVisibility; + Driver.SetCursorVisibility (CursorVisibility.Invisible); } - Driver.SetCursorVisibility (CursorVisibility.Invisible); - return false; } - Driver.GetCursorVisibility (out cachedCursorVisibility); - - if (cachedCursorVisibility == CursorVisibility.Invisible) + // Show it + if (currentCursorVisibility == CursorVisibility.Invisible/* && currentCursorVisibility != _cachedCursorVisibility*/) { - Driver.SetCursorVisibility (_cachedCursorVisibility); + Driver.SetCursorVisibility (mostFocused.CursorVisibility); } - return prevCursor != cursor; + return true; } - Driver.GetCursorVisibility (out cachedCursorVisibility); - - if (cachedCursorVisibility != CursorVisibility.Invisible) + if (currentCursorVisibility != CursorVisibility.Invisible) { - _cachedCursorVisibility = cachedCursorVisibility; + //_cachedCursorVisibility = currentCursorVisibility; Driver.SetCursorVisibility (CursorVisibility.Invisible); } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index d372c52d4..f79e6005d 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -214,9 +214,13 @@ internal class CursesDriver : ConsoleDriver if (!RunningUnitTests && Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) { Curses.move (Row, Col); + Curses.raw (); + Curses.noecho (); + Curses.refresh (); } } + public override void UpdateScreen () { for (var row = 0; row < Rows; row++) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 4dc4eb706..896487ac3 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -20,6 +20,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping; +using static Terminal.Gui.SpinnerStyle; namespace Terminal.Gui; @@ -156,10 +157,7 @@ internal class WindowsConsole } } - if (!_initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) - { - _initialCursorVisibility = visibility; - } + SetInitialCursorVisibility(); if (!SetConsoleActiveScreenBuffer (_screenBuffer)) { @@ -216,11 +214,11 @@ internal class WindowsConsole } else if (info.dwSize > 50) { - visibility = CursorVisibility.Box; + visibility = CursorVisibility.Default; } else { - visibility = CursorVisibility.Underline; + visibility = CursorVisibility.Default; } return true; @@ -815,6 +813,11 @@ internal class WindowsConsole [StructLayout (LayoutKind.Sequential)] public struct ConsoleCursorInfo { + /// + /// The percentage of the character cell that is filled by the cursor.This value is between 1 and 100. + /// The cursor appearance varies, ranging from completely filling the cell to showing up as a horizontal + /// line at the bottom of the cell. + /// public uint dwSize; public bool bVisible; } @@ -1436,6 +1439,8 @@ internal class WindowsDriver : ConsoleDriver #if HACK_CHECK_WINCHANGED _mainLoopDriver.WinChanged = ChangeWin; #endif + + WinConsole?.SetInitialCursorVisibility (); return new MainLoop (_mainLoopDriver); } diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs index 05ab9f2d9..1632eca2f 100644 --- a/Terminal.Gui/View/View.cs +++ b/Terminal.Gui/View/View.cs @@ -32,10 +32,11 @@ namespace Terminal.Gui; /// , , and will receive focus. /// /// -/// Views that are focusable should implement the to make sure that the cursor is -/// placed in a location that makes sense. Unix terminals do not have a way of hiding the cursor, so it can be +/// Views that are focusable should override to make sure that the cursor is +/// placed in a location that makes sense. Some terminals do not have a way of hiding the cursor, so it can be /// distracting to have the cursor left at the last focused view. So views should make sure that they place the -/// cursor in a visually sensible place. +/// cursor in a visually sensible place. The default implementation of will place the +/// cursor at either the hotkey (if defined) or 0,0. /// /// /// The View defines the base functionality for user interface elements in Terminal.Gui. Views can contain one or diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs index 6eea039f3..ee161e3f7 100644 --- a/Terminal.Gui/View/ViewSubViews.cs +++ b/Terminal.Gui/View/ViewSubViews.cs @@ -484,7 +484,9 @@ public partial class View } } - /// Method invoked when a view gets focus. + /// + /// Called when a view gets focus. + /// /// The view that is losing focus. /// true, if the event was handled, false otherwise. public virtual bool OnEnter (View view) @@ -513,18 +515,16 @@ public partial class View return true; } - // BUGBUG: This is a hack to ensure that the cursor is hidden when the view loses focus. - // BUGBUG: This is not needed as the minloop will take care of this. - //Driver?.SetCursorVisibility (CursorVisibility.Invisible); - return false; } - /// Returns the currently focused view inside this view, or null if nothing is focused. + // BUGBUG: This API is poorly defined and implemented. It does not specify what it means if THIS view is focused and has no subviews. + /// Returns the currently focused Subview inside this view, or null if nothing is focused. /// The focused. public View Focused { get; private set; } - /// Returns the most focused view in the chain of subviews (the leaf view that has the focus). + // BUGBUG: This API is poorly defined and implemented. It does not specify what it means if THIS view is focused and has no subviews. + /// Returns the most focused Subview in the chain of subviews (the leaf view that has the focus). /// The most focused View. public View MostFocused { @@ -852,19 +852,21 @@ public partial class View return view.Focused is { } ? GetMostFocused (view.Focused) : view; } + /// + /// Gets or sets the cursor style to be used when the view is focused. The default is . + /// + public CursorVisibility CursorVisibility { get; set; } = CursorVisibility.Invisible; + /// /// Positions the cursor in the right position based on the currently focused view in the chain. /// /// /// - /// Views that are focusable and want the cursor visible should override , - /// use to place the cursor in a location that makes sense, use - /// - /// to make the cursor visible, and return the position where the cursor was placed. - /// - /// - /// Unix terminals do not have a way of hiding the cursor, so it can be distracting to have the cursor left at - /// the last focused view. Views should make sure that they place the cursor in a visually sensible place. + /// Views that are focusable should override to make sure that the cursor is + /// placed in a location that makes sense. Some terminals do not have a way of hiding the cursor, so it can be + /// distracting to have the cursor left at the last focused view. So views should make sure that they place the + /// cursor in a visually sensible place. The default implementation of will place the + /// cursor at either the hotkey (if defined) or 0,0. /// /// /// Viewport-relative cursor position. Return to ensure the cursor is not visible. @@ -872,7 +874,7 @@ public partial class View { if (IsInitialized && CanFocus && HasFocus && ContentSize.HasValue) { - // Base class will position the cursor at the end of the text. + // By default, position the cursor at the hotkey (if any) or 0, 0. Point location = Viewport.Location; location.X = TextFormatter.HotKeyPos == -1 ? 0 : TextFormatter.CursorPosition; location.Y = 0; diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 3569036a4..3639866ff 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -137,14 +137,6 @@ public class Button : View /// public bool NoPadding { get; set; } - ///// - //public override bool OnEnter (View view) - //{ - // Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - // return base.OnEnter (view); - //} - /// public override Point? PositionCursor () { @@ -155,8 +147,7 @@ public class Button : View if (TextFormatter.Text [i] == Text [0]) { Move (i, 0); - - return null;//new (i,0); + return null; // Don't show the cursor } } } diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs index 693821ca2..0535f5f43 100644 --- a/Terminal.Gui/Views/CheckBox.cs +++ b/Terminal.Gui/Views/CheckBox.cs @@ -94,14 +94,6 @@ public class CheckBox : View } } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } - /// Called when the property changes. Invokes the event. /// /// @@ -150,9 +142,6 @@ public class CheckBox : View return true; } - /// - public override Point? PositionCursor () { Move (0, 0); return Point.Empty; } - /// Toggled event, raised when the is toggled. /// /// diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs index f9f492874..0510e3be5 100644 --- a/Terminal.Gui/Views/ColorPicker.cs +++ b/Terminal.Gui/Views/ColorPicker.cs @@ -188,13 +188,6 @@ public class ColorPicker : View } } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } /// Add the commands. private void AddCommands () diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs index 37344abe5..a9208578d 100644 --- a/Terminal.Gui/Views/FrameView.cs +++ b/Terminal.Gui/Views/FrameView.cs @@ -39,15 +39,4 @@ public class FrameView : View [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] [JsonConverter (typeof (JsonStringEnumConverter))] public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single; - - /// - public override bool OnEnter (View view) - { - if (Subviews.Count == 0 || !Subviews.Any (subview => subview.CanFocus)) - { - Application.Driver?.SetCursorVisibility (CursorVisibility.Invisible); - } - - return base.OnEnter (view); - } } diff --git a/Terminal.Gui/Views/GraphView/GraphView.cs b/Terminal.Gui/Views/GraphView/GraphView.cs index 6f4f05bf1..d810957dd 100644 --- a/Terminal.Gui/Views/GraphView/GraphView.cs +++ b/Terminal.Gui/Views/GraphView/GraphView.cs @@ -277,15 +277,6 @@ public class GraphView : View } } - /// - /// Also ensures that cursor is invisible after entering the . - public override bool OnEnter (View view) - { - Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } - /// Scrolls the graph down 1 page. public void PageDown () { Scroll (0, -1 * CellSize.Y * Viewport.Height); } diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs index 4c7068927..bdb3fb59b 100644 --- a/Terminal.Gui/Views/HexView.cs +++ b/Terminal.Gui/Views/HexView.cs @@ -31,7 +31,6 @@ public class HexView : View private const int displayWidth = 9; private int bpl; - private CursorVisibility desiredCursorVisibility = CursorVisibility.Default; private long displayStart, pos; private SortedDictionary edits = []; private bool firstNibble, leftSide; @@ -50,6 +49,7 @@ public class HexView : View // BUG: This will always call the most-derived definition of CanFocus. // Either seal it or don't set it here. CanFocus = true; + CursorVisibility = CursorVisibility.Default; leftSide = true; firstNibble = true; @@ -129,21 +129,6 @@ public class HexView : View } } - /// Get / Set the wished cursor when the field is focused - public CursorVisibility DesiredCursorVisibility - { - get => desiredCursorVisibility; - set - { - if (desiredCursorVisibility != value && HasFocus) - { - Application.Driver.SetCursorVisibility (value); - } - - desiredCursorVisibility = value; - } - } - /// /// Sets or gets the offset into the that will displayed at the top of the /// @@ -462,14 +447,6 @@ public class HexView : View /// The key value pair. public virtual void OnEdited (HexViewEditEventArgs e) { Edited?.Invoke (this, e); } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (DesiredCursorVisibility); - - return base.OnEnter (view); - } - /// /// Method used to invoke the event passing the /// arguments. diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs index 8844e9a1b..40861a926 100644 --- a/Terminal.Gui/Views/Label.cs +++ b/Terminal.Gui/Views/Label.cs @@ -64,11 +64,4 @@ public class Label : View return true; } - - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - return base.OnEnter (view); - } } diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index dbad1f8e3..b91ede6cb 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -700,11 +700,6 @@ public class ListView : View /// public override bool OnEnter (View view) { - //if (IsInitialized) - //{ - // Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - //} - if (_lastSelectedItem != _selected) { EnsureSelectedItemVisible (); @@ -792,7 +787,7 @@ public class ListView : View Move (x, y); - return null;//new Point (x, y); + return null; // Don't show the cursor } /// This event is invoked when this is being drawn before rendering. diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs index 9a4b11435..6d0353f9b 100644 --- a/Terminal.Gui/Views/Menu/Menu.cs +++ b/Terminal.Gui/Views/Menu/Menu.cs @@ -958,7 +958,8 @@ internal sealed class Menu : View { Move (2, 1 + _currentChild); - return null;//new (2, 1 + _currentChild); + return null; // Don't show the cursor + } } @@ -1334,14 +1335,6 @@ internal sealed class Menu : View return pos; } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } - protected override void Dispose (bool disposing) { if (Application.Current is { }) diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs index 0617c7990..8ca8e3ba6 100644 --- a/Terminal.Gui/Views/Menu/MenuBar.cs +++ b/Terminal.Gui/Views/Menu/MenuBar.cs @@ -621,7 +621,7 @@ public class MenuBar : View pos++; Move (pos + 1, 0); - return new (pos +1, 0); + return null; // Don't show the cursor } pos += _leftPadding @@ -631,7 +631,7 @@ public class MenuBar : View : 0) + _rightPadding; } - return null; + return null; // Don't show the cursor } // Activates the menu, handles either first focus, or activating an entry when it was already active @@ -1651,13 +1651,6 @@ public class MenuBar : View #region Mouse Handling - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } /// public override bool OnLeave (View view) diff --git a/Terminal.Gui/Views/ProgressBar.cs b/Terminal.Gui/Views/ProgressBar.cs index cfddca1e0..fed490d69 100644 --- a/Terminal.Gui/Views/ProgressBar.cs +++ b/Terminal.Gui/Views/ProgressBar.cs @@ -198,14 +198,6 @@ public class ProgressBar : View } } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } - /// Notifies the that some progress has taken place. /// /// If the is percentage mode, it switches to activity mode. If is in activity mode, the diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index d102dd573..83cc14c3c 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -276,14 +276,6 @@ public class RadioGroup : View } } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } - /// public override bool? OnInvokingKeyBindings (Key keyEvent) { @@ -371,7 +363,7 @@ public class RadioGroup : View } Move (x, y); - return new Point (x, y); + return null; // Don't show the cursor } /// Allow to invoke the after their creation. diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs index e22e68864..a695a3d5b 100644 --- a/Terminal.Gui/Views/ScrollBarView.cs +++ b/Terminal.Gui/Views/ScrollBarView.cs @@ -667,13 +667,6 @@ public class ScrollBarView : View } } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } /// Only used for a hosted view that will update and redraw the scrollbars. public virtual void Refresh () { ShowHideScrollBars (); } diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index 5340208fa..6e2361d53 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -384,17 +384,6 @@ public class ScrollView : View DrawScrollBars (); } - /// - public override bool OnEnter (View view) - { - if (Subviews.Count == 0 || !Subviews.Any (subview => subview.CanFocus)) - { - Application.Driver?.SetCursorVisibility (CursorVisibility.Invisible); - } - - return base.OnEnter (view); - } - /// public override bool OnKeyDown (Key a) { @@ -461,7 +450,7 @@ public class ScrollView : View { Move (0, 0); - return null;//Point.Empty; + return null; // Don't show the cursor } return base.PositionCursor (); } diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs index 9d111f525..4a1c0ac57 100644 --- a/Terminal.Gui/Views/Slider.cs +++ b/Terminal.Gui/Views/Slider.cs @@ -245,6 +245,7 @@ public class Slider : View Width = Dim.Auto (Dim.DimAutoStyle.Content); Height = Dim.Auto (Dim.DimAutoStyle.Content); CanFocus = true; + CursorVisibility = CursorVisibility.Default; _options = options ?? new List> (); @@ -255,15 +256,6 @@ public class Slider : View SetDefaultStyle (); SetCommands (); - // When we lose focus of the View(Slider), if we are range selecting we stop it. - Leave += (s, e) => - { - //if (_settingRange == true) { - // _settingRange = false; - //} - Driver.SetCursorVisibility (CursorVisibility.Invisible); - }; - Enter += (s, e) => { }; // BUGBUG: This should not be needed - Need to ensure SetRelativeLayout gets called during EndInit @@ -888,17 +880,6 @@ public class Slider : View /// public override Point? PositionCursor () { - //base.PositionCursor (); - - if (HasFocus) - { - Driver?.SetCursorVisibility (CursorVisibility.Default); - } - else - { - Driver?.SetCursorVisibility (CursorVisibility.Invisible); - } - if (TryGetPositionByOption (FocusedOption, out (int x, int y) position)) { if (IsInitialized && Viewport.Contains (position.x, position.y)) @@ -908,7 +889,7 @@ public class Slider : View return new (position.x, position.x); } } - return null; + return base.PositionCursor (); } /// diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs index 6dc96f663..a021ca544 100644 --- a/Terminal.Gui/Views/StatusBar.cs +++ b/Terminal.Gui/Views/StatusBar.cs @@ -215,14 +215,6 @@ public class StatusBar : View } } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } - /// public override bool? OnInvokingKeyBindings (Key keyEvent) { diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs index 44375baf9..a3bde6eb2 100644 --- a/Terminal.Gui/Views/TabView.cs +++ b/Terminal.Gui/Views/TabView.cs @@ -1204,13 +1204,6 @@ public class TabView : View } } - public override bool OnEnter (View view) - { - Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnEnter (view); - } - private int GetUnderlineYPosition () { if (_host.Style.TabsOnBottom) diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index ba310f205..2ae46c7fd 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -978,8 +978,6 @@ public class TableView : View { if (TableIsNullOrInvisible ()) { - //PositionCursor (); - return false; } @@ -1530,7 +1528,6 @@ public class TableView : View SelectedRow = match; EnsureValidSelection (); EnsureSelectedCellIsVisible (); - //PositionCursor (); SetNeedsDisplay (); return true; diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 423fa2be5..9bb71b523 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -36,6 +36,7 @@ public class TextField : View Height = 1; CanFocus = true; + CursorVisibility = CursorVisibility.Default; Used = true; WantMousePositionReports = true; @@ -931,7 +932,7 @@ public class TextField : View ShowContextMenu (); } - SetNeedsDisplay (); + //SetNeedsDisplay (); return true; @@ -1035,17 +1036,6 @@ public class TextField : View _isDrawing = false; } - ///// - //public override bool OnEnter (View view) - //{ - // if (IsInitialized) - // { - // Application.Driver.SetCursorVisibility (CursorVisibility.Default); - // } - - // return base.OnEnter (view); - //} - /// public override bool? OnInvokingKeyBindings (Key a) { diff --git a/Terminal.Gui/Views/TextValidateField.cs b/Terminal.Gui/Views/TextValidateField.cs index f421fb931..a51e55e54 100644 --- a/Terminal.Gui/Views/TextValidateField.cs +++ b/Terminal.Gui/Views/TextValidateField.cs @@ -598,22 +598,6 @@ namespace Terminal.Gui } } - /// - public override bool OnEnter (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Default); - - return base.OnEnter (view); - } - - /// - public override bool OnLeave (View view) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - - return base.OnLeave (view); - } - /// public override bool OnProcessKeyDown (Key a) { @@ -659,14 +643,6 @@ namespace Terminal.Gui } Move (curPos, 0); - if (curPos < 0 || curPos >= Viewport.Width) - { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - } - else - { - Application.Driver.SetCursorVisibility (CursorVisibility.Default); - } return new (curPos, 0); } diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index a83be01a3..22e487d72 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -243,7 +243,7 @@ internal class TextModel foreach (Rune rune in str.EnumerateRunes ()) { - cells.Add (new() { Rune = rune, ColorScheme = colorScheme }); + cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); } return cells; @@ -906,7 +906,7 @@ internal class TextModel foreach (Rune rune in str.ToRunes ()) { - cells.Add (new() { Rune = rune, ColorScheme = colorScheme }); + cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); } return cells; @@ -918,7 +918,7 @@ internal class TextModel foreach (Rune rune in runes) { - cells.Add (new() { Rune = rune, ColorScheme = colorScheme }); + cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); } return cells; @@ -1979,9 +1979,6 @@ public class TextView : View private WordWrapManager? _wrapManager; private bool _wrapNeeded; - /// Get or sets the cursor to be used when the text view has focus. - - public CursorVisibility DesiredCursorVisibility { get; set; } = CursorVisibility.Default; /// /// Initializes a on the specified area, with dimensions controlled with the X, Y, Width @@ -1990,6 +1987,7 @@ public class TextView : View public TextView () { CanFocus = true; + CursorVisibility = CursorVisibility.Default; Used = true; _model.LinesLoaded += Model_LinesLoaded!; @@ -2512,7 +2510,7 @@ public class TextView : View _currentCulture = Thread.CurrentThread.CurrentUICulture; - ContextMenu = new() { MenuItems = BuildContextMenuBarItem () }; + ContextMenu = new () { MenuItems = BuildContextMenuBarItem () }; ContextMenu.KeyChanged += ContextMenu_KeyChanged!; KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.ShowContextMenu); @@ -2782,7 +2780,7 @@ public class TextView : View } } - /// Get or sets the selecting. + /// Get or sets whether the user is currently selecting text. public bool Selecting { get; set; } /// Start column position of the selected text. @@ -2970,7 +2968,7 @@ public class TextView : View ClearRegion (); _historyText.Add ( - new() { new (GetCurrentLine ()) }, + new () { new (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -3009,14 +3007,14 @@ public class TextView : View if (Selecting) { - _historyText.Add (new() { new (GetCurrentLine ()) }, CursorPosition); + _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition); ClearSelectedRegion (); List currentLine = GetCurrentLine (); _historyText.Add ( - new() { new (currentLine) }, + new () { new (currentLine) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -3053,14 +3051,14 @@ public class TextView : View if (Selecting) { - _historyText.Add (new() { new (GetCurrentLine ()) }, CursorPosition); + _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition); ClearSelectedRegion (); List currentLine = GetCurrentLine (); _historyText.Add ( - new() { new (currentLine) }, + new () { new (currentLine) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -3314,7 +3312,7 @@ public class TextView : View && !ev.Flags.HasFlag (MouseFlags.Button1TripleClicked) && !ev.Flags.HasFlag (ContextMenu!.MouseFlags)) { - return false; + return base.OnMouseEvent (ev); } if (!CanFocus) @@ -3671,20 +3669,11 @@ public class TextView : View ClearRegion (viewport.Left, row, right, bottom); } - PositionCursor (); + //PositionCursor (); _isDrawing = false; } - /// - public override bool OnEnter (View view) - { - //TODO: Improve it by handling read only mode of the text field - Application.Driver.SetCursorVisibility (DesiredCursorVisibility); - - return base.OnEnter (view); - } - /// public override bool? OnInvokingKeyBindings (Key a) { @@ -3777,7 +3766,7 @@ public class TextView : View List runeList = contents is null ? new () : TextModel.ToRuneCellList (contents); List currentLine = GetCurrentLine (); - _historyText.Add (new() { new (currentLine) }, CursorPosition); + _historyText.Add (new () { new (currentLine) }, CursorPosition); List> addedLine = new () { new (currentLine), runeList }; @@ -3791,7 +3780,7 @@ public class TextView : View CurrentRow++; _historyText.Add ( - new() { new (GetCurrentLine ()) }, + new () { new (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -3812,7 +3801,7 @@ public class TextView : View if (Selecting) { _historyText.ReplaceLast ( - new() { new (GetCurrentLine ()) }, + new () { new (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Original ); @@ -3836,7 +3825,7 @@ public class TextView : View return null; } - if (Selecting) + if (Application.MouseGrabView == this && Selecting) { // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method. //var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Frame.Height); @@ -3882,7 +3871,7 @@ public class TextView : View return new (col, CurrentRow - _topRow); } - return null; + return null; // Hide cursor } /// Redoes the latest changes. @@ -4276,7 +4265,7 @@ public class TextView : View var endCol = (int)(end & 0xffffffff); List line = _model.GetLine (startRow); - _historyText.Add (new() { new (line) }, new (startCol, startRow)); + _historyText.Add (new () { new (line) }, new (startCol, startRow)); List> removedLines = new (); @@ -4366,7 +4355,7 @@ public class TextView : View // Delete backwards List currentLine = GetCurrentLine (); - _historyText.Add (new() { new (currentLine) }, CursorPosition); + _historyText.Add (new () { new (currentLine) }, CursorPosition); currentLine.RemoveAt (CurrentColumn - 1); @@ -4378,7 +4367,7 @@ public class TextView : View CurrentColumn--; _historyText.Add ( - new() { new (currentLine) }, + new () { new (currentLine) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4406,7 +4395,7 @@ public class TextView : View int prowIdx = CurrentRow - 1; List prevRow = _model.GetLine (prowIdx); - _historyText.Add (new() { new (prevRow) }, CursorPosition); + _historyText.Add (new () { new (prevRow) }, CursorPosition); List> removedLines = new () { new (prevRow) }; @@ -4430,7 +4419,7 @@ public class TextView : View CurrentRow--; _historyText.Add ( - new() { GetCurrentLine () }, + new () { GetCurrentLine () }, new (CurrentColumn, prowIdx), HistoryText.LineStatus.Replaced ); @@ -4459,7 +4448,7 @@ public class TextView : View return true; } - _historyText.Add (new() { new (currentLine) }, CursorPosition); + _historyText.Add (new () { new (currentLine) }, CursorPosition); List> removedLines = new () { new (currentLine) }; @@ -4473,7 +4462,7 @@ public class TextView : View _model.RemoveLine (CurrentRow + 1); _historyText.Add ( - new() { new (currentLine) }, + new () { new (currentLine) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4487,12 +4476,12 @@ public class TextView : View } else { - _historyText.Add ([[..currentLine]], CursorPosition); + _historyText.Add ([ [.. currentLine]], CursorPosition); currentLine.RemoveAt (CurrentColumn); _historyText.Add ( - [[..currentLine]], + [ [.. currentLine]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4824,7 +4813,7 @@ public class TextView : View List line = GetCurrentLine (); - _historyText.Add (new() { new (line) }, CursorPosition); + _historyText.Add (new () { new (line) }, CursorPosition); // Optimize single line if (lines.Count == 1) @@ -4833,7 +4822,7 @@ public class TextView : View CurrentColumn += lines [0].Count; _historyText.Add ( - new() { new (line) }, + new () { new (line) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4903,7 +4892,7 @@ public class TextView : View Adjust (); _historyText.Add ( - new() { new (line) }, + new () { new (line) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4922,7 +4911,7 @@ public class TextView : View SetWrapModel (); - _historyText.Add (new() { new (GetCurrentLine ()) }, CursorPosition); + _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition); if (Selecting) { @@ -4943,7 +4932,7 @@ public class TextView : View { if (Used) { - Insert (new() { Rune = a.AsRune, ColorScheme = colorScheme }); + Insert (new () { Rune = a.AsRune, ColorScheme = colorScheme }); CurrentColumn++; if (CurrentColumn >= _leftColumn + Frame.Width) @@ -4954,13 +4943,13 @@ public class TextView : View } else { - Insert (new() { Rune = a.AsRune, ColorScheme = colorScheme }); + Insert (new () { Rune = a.AsRune, ColorScheme = colorScheme }); CurrentColumn++; } } _historyText.Add ( - new() { new (GetCurrentLine ()) }, + new () { new (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4998,7 +4987,7 @@ public class TextView : View return; } - _historyText.Add (new() { new (currentLine) }, CursorPosition); + _historyText.Add (new () { new (currentLine) }, CursorPosition); if (currentLine.Count == 0) { @@ -5057,7 +5046,7 @@ public class TextView : View } _historyText.Add ( - [[..GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5097,7 +5086,7 @@ public class TextView : View return; } - _historyText.Add ([[..currentLine]], CursorPosition); + _historyText.Add ([ [.. currentLine]], CursorPosition); if (currentLine.Count == 0) { @@ -5135,7 +5124,7 @@ public class TextView : View ]; _historyText.Add ( - [..removedLine], + [.. removedLine], CursorPosition, HistoryText.LineStatus.Removed ); @@ -5164,7 +5153,7 @@ public class TextView : View } _historyText.Add ( - [[..GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5188,14 +5177,14 @@ public class TextView : View List currentLine = GetCurrentLine (); - _historyText.Add ([[..GetCurrentLine ()]], CursorPosition); + _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition); if (CurrentColumn == 0) { DeleteTextBackwards (); _historyText.ReplaceLast ( - [[..GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5234,7 +5223,7 @@ public class TextView : View } _historyText.Add ( - [[..GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5256,14 +5245,14 @@ public class TextView : View List currentLine = GetCurrentLine (); - _historyText.Add ([[..GetCurrentLine ()]], CursorPosition); + _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition); if (currentLine.Count == 0 || CurrentColumn == currentLine.Count) { DeleteTextForwards (); _historyText.ReplaceLast ( - [[..GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5293,7 +5282,7 @@ public class TextView : View } _historyText.Add ( - [[..GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5665,13 +5654,13 @@ public class TextView : View if (currentLine.Count > 0 && currentLine [CurrentColumn - 1].Rune.Value == '\t') { - _historyText.Add (new() { new (currentLine) }, CursorPosition); + _historyText.Add (new () { new (currentLine) }, CursorPosition); currentLine.RemoveAt (CurrentColumn - 1); CurrentColumn--; _historyText.Add ( - new() { new (GetCurrentLine ()) }, + new () { new (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced ); @@ -6112,7 +6101,7 @@ public class TextView : View List currentLine = GetCurrentLine (); - _historyText.Add (new() { new (currentLine) }, CursorPosition); + _historyText.Add (new () { new (currentLine) }, CursorPosition); if (Selecting) { @@ -6145,7 +6134,7 @@ public class TextView : View CurrentColumn = 0; _historyText.Add ( - new() { new (GetCurrentLine ()) }, + new () { new (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced ); diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index fe964f06d..c184969e9 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -979,14 +979,6 @@ public class TileView : View DrawSplitterSymbol (); } - //public override bool OnEnter (View view) - //{ - // Driver.SetCursorVisibility (CursorVisibility.Default); - // PositionCursor (); - - // return base.OnEnter (view); - //} - public override Point? PositionCursor () { base.PositionCursor (); @@ -994,7 +986,7 @@ public class TileView : View Point location = moveRuneRenderLocation ?? new Point (Viewport.Width / 2, Viewport.Height / 2); Move (location.X, location.Y); - return null; //location; + return null; // Hide cursor } /// diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs index 57f5d0a23..235034820 100644 --- a/Terminal.Gui/Views/Toplevel.cs +++ b/Terminal.Gui/Views/Toplevel.cs @@ -340,11 +340,6 @@ public partial class Toplevel : View if (Focused is null) { EnsureFocus (); - - if (Focused is null) - { - Driver.SetCursorVisibility (CursorVisibility.Invisible); - } } return null; @@ -368,12 +363,7 @@ public partial class Toplevel : View var cursor2 = base.PositionCursor (); - if (Focused is null) - { - Driver.SetCursorVisibility (CursorVisibility.Invisible); - } - - return null;//cursor2; + return null; } /// diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index 31e3b59b3..db93da9a8 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -64,8 +64,6 @@ public class TreeView : View, ITreeView where T : class /// Cached result of private IReadOnlyCollection> cachedLineMap; - private CursorVisibility desiredCursorVisibility = CursorVisibility.Invisible; - private KeyCode objectActivationKey = KeyCode.Enter; private int scrollOffsetHorizontal; private int scrollOffsetVertical; @@ -325,27 +323,6 @@ public class TreeView : View, ITreeView where T : class /// The current number of rows in the tree (ignoring the controls bounds). public int ContentHeight => BuildLineMap ().Count (); - /// - /// Get / Set the wished cursor when the tree is focused. Only applies when is true. - /// Defaults to . - /// - public CursorVisibility DesiredCursorVisibility - { - get => MultiSelect ? desiredCursorVisibility : CursorVisibility.Invisible; - set - { - if (desiredCursorVisibility != value) - { - desiredCursorVisibility = value; - - if (HasFocus) - { - Application.Driver.SetCursorVisibility (DesiredCursorVisibility); - } - } - } - } - /// /// Gets the that searches the collection as the user /// types. @@ -468,7 +445,6 @@ public class TreeView : View, ITreeView where T : class // TODO: Should this be cancelable? ObjectActivatedEventArgs e = new (this, o); OnObjectActivated (e); - //PositionCursor (); return true; } return false; @@ -675,8 +651,6 @@ public class TreeView : View, ITreeView where T : class // search for next branch that begins with that letter var characterAsStr = character.ToString (); AdjustSelectionToNext (b => AspectGetter (b.Model).StartsWith (characterAsStr, caseSensitivity)); - - //PositionCursor (); } /// @@ -1183,8 +1157,6 @@ public class TreeView : View, ITreeView where T : class /// public override bool OnEnter (View view) { - Application.Driver.SetCursorVisibility (DesiredCursorVisibility); - if (SelectedObject is null && Objects.Any ()) { SelectedObject = Objects.First (); @@ -1201,37 +1173,27 @@ public class TreeView : View, ITreeView where T : class return false; } - try + // BUGBUG: this should move to OnInvokingKeyBindings + // If not a keybinding, is the key a searchable key press? + if (CollectionNavigatorBase.IsCompatibleKey (keyEvent) && AllowLetterBasedNavigation) { - // BUGBUG: this should move to OnInvokingKeyBindings - // If not a keybinding, is the key a searchable key press? - if (CollectionNavigatorBase.IsCompatibleKey (keyEvent) && AllowLetterBasedNavigation) + IReadOnlyCollection> map; + + // If there has been a call to InvalidateMap since the last time + // we need a new one to reflect the new exposed tree state + map = BuildLineMap (); + + // Find the current selected object within the tree + int current = map.IndexOf (b => b.Model == SelectedObject); + int? newIndex = KeystrokeNavigator?.GetNextMatchingItem (current, (char)keyEvent); + + if (newIndex is int && newIndex != -1) { - IReadOnlyCollection> map; + SelectedObject = map.ElementAt ((int)newIndex).Model; + EnsureVisible (selectedObject); + SetNeedsDisplay (); - // If there has been a call to InvalidateMap since the last time - // we need a new one to reflect the new exposed tree state - map = BuildLineMap (); - - // Find the current selected object within the tree - int current = map.IndexOf (b => b.Model == SelectedObject); - int? newIndex = KeystrokeNavigator?.GetNextMatchingItem (current, (char)keyEvent); - - if (newIndex is int && newIndex != -1) - { - SelectedObject = map.ElementAt ((int)newIndex).Model; - EnsureVisible (selectedObject); - SetNeedsDisplay (); - - return true; - } - } - } - finally - { - if (IsInitialized) - { - //PositionCursor (); + return true; } } @@ -1250,9 +1212,8 @@ public class TreeView : View, ITreeView where T : class if (idx - ScrollOffsetVertical >= 0 && idx - ScrollOffsetVertical < Viewport.Height) { Move (0, idx - ScrollOffsetVertical); - Application.Driver.SetCursorVisibility (DesiredCursorVisibility); - return null;//new Point (0, idx - ScrollOffsetVertical); + return MultiSelect ? new (0, idx - ScrollOffsetVertical) : null ; } } return base.PositionCursor (); diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 3d0ddb798..a3aa100a8 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -326,6 +326,7 @@ internal class CharMap : View { ColorScheme = Colors.ColorSchemes ["Dialog"]; CanFocus = true; + CursorVisibility = CursorVisibility.Default; ContentSize = new (RowWidth, (MaxCodePoint / 16 + 2) * _rowHeight); @@ -810,13 +811,10 @@ internal class CharMap : View && Cursor.Y > 0 && Cursor.Y < Viewport.Height) { - Driver.SetCursorVisibility (CursorVisibility.Default); Move (Cursor.X, Cursor.Y); } else { - Driver.SetCursorVisibility (CursorVisibility.Invisible); - return null; } diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 21e4b249a..301b1b168 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -267,7 +267,6 @@ public class Editor : Scenario _textView.UnwrappedCursorPosition += (s, e) => { siCursorPosition.Title = $"Ln {e.Point.Y + 1}, Col {e.Point.X + 1}"; - statusBar.SetNeedsDisplay (); }; Top.Add (statusBar); @@ -587,7 +586,7 @@ public class Editor : Scenario new ("_Invisible", "", () => SetCursor (CursorVisibility.Invisible)) { CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.DesiredCursorVisibility + Checked = _textView.CursorVisibility == CursorVisibility.Invisible } ); @@ -596,7 +595,7 @@ public class Editor : Scenario new ("_Box", "", () => SetCursor (CursorVisibility.Box)) { CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.DesiredCursorVisibility == CursorVisibility.Box + Checked = _textView.CursorVisibility == CursorVisibility.Box } ); @@ -604,7 +603,7 @@ public class Editor : Scenario new ("_Underline", "", () => SetCursor (CursorVisibility.Underline)) { CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.DesiredCursorVisibility + Checked = _textView.CursorVisibility == CursorVisibility.Underline } ); @@ -616,7 +615,7 @@ public class Editor : Scenario new (" _Default", "", () => SetCursor (CursorVisibility.Default)) { CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.DesiredCursorVisibility + Checked = _textView.CursorVisibility == CursorVisibility.Default } ); @@ -625,7 +624,7 @@ public class Editor : Scenario new (" _Vertical", "", () => SetCursor (CursorVisibility.Vertical)) { CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.DesiredCursorVisibility + Checked = _textView.CursorVisibility == CursorVisibility.Vertical } ); @@ -634,7 +633,7 @@ public class Editor : Scenario new (" V_ertical Fix", "", () => SetCursor (CursorVisibility.VerticalFix)) { CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.DesiredCursorVisibility == CursorVisibility.VerticalFix + Checked = _textView.CursorVisibility == CursorVisibility.VerticalFix } ); @@ -642,7 +641,7 @@ public class Editor : Scenario new (" B_ox Fix", "", () => SetCursor (CursorVisibility.BoxFix)) { CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.DesiredCursorVisibility + Checked = _textView.CursorVisibility == CursorVisibility.BoxFix } ); @@ -651,13 +650,13 @@ public class Editor : Scenario new (" U_nderline Fix", "", () => SetCursor (CursorVisibility.UnderlineFix)) { CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.DesiredCursorVisibility == CursorVisibility.UnderlineFix + Checked = _textView.CursorVisibility == CursorVisibility.UnderlineFix } ); void SetCursor (CursorVisibility visibility) { - _textView.DesiredCursorVisibility = visibility; + _textView.CursorVisibility = visibility; var title = ""; switch (visibility) @@ -698,7 +697,7 @@ public class Editor : Scenario foreach (MenuItem menuItem in menuItems) { - menuItem.Checked = menuItem.Title.Equals (title) && visibility == _textView.DesiredCursorVisibility; + menuItem.Checked = menuItem.Title.Equals (title) && visibility == _textView.CursorVisibility; } } diff --git a/UICatalog/Scenarios/TreeViewFileSystem.cs b/UICatalog/Scenarios/TreeViewFileSystem.cs index c16758a09..1277ae4c8 100644 --- a/UICatalog/Scenarios/TreeViewFileSystem.cs +++ b/UICatalog/Scenarios/TreeViewFileSystem.cs @@ -223,7 +223,7 @@ public class TreeViewFileSystem : Scenario { _miCursor.Checked = !_miCursor.Checked; - _treeViewFiles.DesiredCursorVisibility = + _treeViewFiles.CursorVisibility = _miCursor.Checked == true ? CursorVisibility.Default : CursorVisibility.Invisible; } diff --git a/UnitTests/Application/CursorTests.cs b/UnitTests/Application/CursorTests.cs new file mode 100644 index 000000000..151b2926d --- /dev/null +++ b/UnitTests/Application/CursorTests.cs @@ -0,0 +1,148 @@ +using Xunit.Abstractions; + +namespace Terminal.Gui.ApplicationTests; + +public class CursorTests +{ + private readonly ITestOutputHelper _output; + + public CursorTests (ITestOutputHelper output) + { + _output = output; + ConsoleDriver.RunningUnitTests = true; + } + + private class TestView : View + { + public Point? TestLocation { get; set; } + + /// + public override Point? PositionCursor () + { + if (TestLocation.HasValue && HasFocus) + { + Driver.SetCursorVisibility (CursorVisibility.Default); + } + return TestLocation; + } + } + + [Fact] + [SetupFakeDriver] + public void PositionCursor_No_Focus_Returns_False () + { + Assert.False (Application.PositionCursor (null)); + + TestView view = new () + { + CanFocus = false, + Width = 1, + Height = 1, + }; + view.TestLocation = new Point (0, 0); + Assert.False (Application.PositionCursor (view)); + } + + [Fact] + [SetupFakeDriver] + public void PositionCursor_No_Position_Returns_False () + { + TestView view = new () + { + CanFocus = false, + Width = 1, + Height = 1, + }; + + view.CanFocus = true; + view.SetFocus(); + Assert.False (Application.PositionCursor (view)); + } + + [Fact] + [SetupFakeDriver] + public void PositionCursor_No_IntersectSuperView_Returns_False () + { + View superView = new () + { + Width = 1, + Height = 1, + }; + + TestView view = new () + { + CanFocus = false, + X = 1, + Y =1, + Width = 1, + Height = 1, + }; + superView.Add (view); + + view.CanFocus = true; + view.SetFocus (); + view.TestLocation = new Point (0, 0); + Assert.False (Application.PositionCursor (view)); + } + + [Fact] + [SetupFakeDriver] + public void PositionCursor_Position_OutSide_SuperView_Returns_False () + { + View superView = new () + { + Width = 1, + Height = 1, + }; + + TestView view = new () + { + CanFocus = false, + X = 0, + Y = 0, + Width = 2, + Height = 2, + }; + superView.Add (view); + + view.CanFocus = true; + view.SetFocus (); + view.TestLocation = new Point (1, 1); + Assert.False (Application.PositionCursor (view)); + } + + [Fact, Trait("BUGBUG", "Views without subviews don't support Focused or MostFocused")] + [SetupFakeDriver] + public void PositionCursor_Focused_With_Position_Returns_True () + { + TestView view = new () + { + CanFocus = false, + Width = 1, + Height = 1, + }; + view.CanFocus = true; + view.SetFocus (); + view.TestLocation = new Point (0, 0); + Assert.False (Application.PositionCursor (view)); // BUGBUG: This should be true + } + + [Fact] + [SetupFakeDriver] + public void PositionCursor_Defaults_Invisible () + { + View view = new () + { + CanFocus = true, + Width = 1, + Height = 1, + }; + view.SetFocus(); + + Assert.True (view.HasFocus); + Assert.False (Application.PositionCursor (view)); + Application.Driver.GetCursorVisibility (out CursorVisibility cursor); + Assert.Equal (CursorVisibility.Invisible, cursor); + } + +} diff --git a/UnitTests/View/NavigationTests.cs b/UnitTests/View/NavigationTests.cs index bb57fac73..d4611eab2 100644 --- a/UnitTests/View/NavigationTests.cs +++ b/UnitTests/View/NavigationTests.cs @@ -869,7 +869,7 @@ public class NavigationTests Assert.Equal (0, screen.Y); var found = View.FindDeepestView (top, 0, 0); Assert.Equal (top.Border, found); - + Assert.Equal (0, found.Frame.X); Assert.Equal (0, found.Frame.Y); Assert.Equal (new Point (3, 2), top.ScreenToFrame (3, 2)); @@ -934,14 +934,14 @@ public class NavigationTests Assert.Equal (3, screen.Y); found = View.FindDeepestView (top, 4, 3); Assert.Equal (view, found); - + Assert.Equal (new Point (9, -1), view.ScreenToFrame (13, 2)); screen = view.ViewportToScreen (new (10, 0, 0, 0)); Assert.Equal (14, screen.X); Assert.Equal (3, screen.Y); found = View.FindDeepestView (top, 14, 3); Assert.Equal (top, found); - + Assert.Equal (new Point (10, 0), view.ScreenToFrame (14, 3)); screen = view.ViewportToScreen (new (11, 1, 0, 0)); Assert.Equal (15, screen.X); @@ -1042,7 +1042,7 @@ public class NavigationTests Assert.Equal (15, screen.X); Assert.Equal (4, screen.Y); Assert.Equal (top, View.FindDeepestView (top, 14, 3)); - + // view Assert.Equal (new Point (-7, -5), view.ScreenToFrame (0, 0)); screen = view.Margin.ViewportToScreen (new (-6, -4, 0, 0)); @@ -1550,4 +1550,32 @@ public class NavigationTests // Assert does Not throw NullReferenceException top.SetFocus (); } + + // View.Focused & View.MostFocused tests + + // View.Focused - No subviews + [Fact, Trait("BUGBUG", "Fix in Issue #3444")] + public void Focused_NoSubviews () + { + var view = new View (); + Assert.Null (view.Focused); + + view.CanFocus = true; + view.SetFocus (); + Assert.True (view.HasFocus); + Assert.Null (view.Focused); // BUGBUG: Should be view + } + + // View.MostFocused - No subviews + [Fact, Trait ("BUGBUG", "Fix in Issue #3444")] + public void Most_Focused_NoSubviews () + { + var view = new View (); + Assert.Null (view.Focused); + + view.CanFocus = true; + view.SetFocus (); + Assert.True (view.HasFocus); + Assert.Null (view.MostFocused); // BUGBUG: Should be view + } } diff --git a/UnitTests/Views/StatusBarTests.cs b/UnitTests/Views/StatusBarTests.cs index 563c4fc0d..feaae7292 100644 --- a/UnitTests/Views/StatusBarTests.cs +++ b/UnitTests/Views/StatusBarTests.cs @@ -190,40 +190,6 @@ CTRL-O Open { Assert.Equal ("AnchorEnd(1)", sb.Y.ToString ()); Assert.Equal (Dim.Fill (), sb.Width); Assert.Equal (1, sb.Height); - - var driver = new FakeDriver (); - Application.Init (driver); - - sb = new StatusBar (); - - driver.SetCursorVisibility (CursorVisibility.Default); - driver.GetCursorVisibility (out CursorVisibility cv); - Assert.Equal (CursorVisibility.Default, cv); - Assert.True (FakeConsole.CursorVisible); - - Application.Iteration += (s, a) => - { - Assert.Equal (24, sb.Frame.Y); - - driver.SetWindowSize (driver.Cols, 15); - - Assert.Equal (14, sb.Frame.Y); - - sb.OnEnter (null); - driver.GetCursorVisibility (out cv); - Assert.Equal (CursorVisibility.Invisible, cv); - Assert.False (FakeConsole.CursorVisible); - - Application.RequestStop (); - }; - - var top = new Toplevel (); - top.Add (sb); - - Application.Run (top); - - top.Dispose (); - Application.Shutdown (); } [Fact] diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 946398315..67336031c 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -1072,7 +1072,7 @@ This is the second line. Assert.Equal (0, tv.LeftColumn); Assert.Equal (Point.Empty, tv.CursorPosition); Application.PositionCursor (top); - Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility); + Assert.Equal (CursorVisibility.Default, tv.CursorVisibility); for (var i = 0; i < 12; i++) { @@ -1124,7 +1124,7 @@ This is the second line. Assert.Equal (0, tv.TopRow); Application.PositionCursor (top); - Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility); + Assert.Equal (CursorVisibility.Default, tv.CursorVisibility); for (var i = 0; i < 12; i++) { diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index 6b0ad1c7d..a587f7b49 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -101,7 +101,7 @@ public class TreeViewTests [Fact] [AutoInitShutdown] - public void DesiredCursorVisibility_MultiSelect () + public void CursorVisibility_MultiSelect () { var tv = new TreeView { Width = 20, Height = 10 }; @@ -116,13 +116,13 @@ public class TreeViewTests Assert.True (tv.MultiSelect); Assert.True (tv.HasFocus); - Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility); + Assert.Equal (CursorVisibility.Invisible, tv.CursorVisibility); tv.SelectAll (); - tv.DesiredCursorVisibility = CursorVisibility.Default; - Application.Refresh (); + tv.CursorVisibility = CursorVisibility.Default; + Application.PositionCursor (top); Application.Driver.GetCursorVisibility (out CursorVisibility visibility); - Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility); + Assert.Equal (CursorVisibility.Default, tv.CursorVisibility); Assert.Equal (CursorVisibility.Default, visibility); }