See end for list of issues this design addresses.
More GUI than Command Line. The concept of a cursor on the command line of a terminal is intrinsically tied to enabling the user to know where keyboard import is going to impact text editing. TUI apps have many more modalities than text editing where the keyboard is used (e.g. scrolling through a ColorPicker). Terminal.Gui's cursor system is biased towards the broader TUI experiences.
Be Consistent With the User's Platform - Users get to choose the platform they run Terminal.Gui apps on and the cursor should behave in a way consistent with the terminal.
view.HasFocus == true), and there is only one View in a focused hierarchy that is the most-focused; the one receiving keyboard input. See Navigation for a deep-dive.ListView the Cursor and Selection (SelectedItem) are the same, but the Cursor is not visible. In a TextView with text selected, the Cursor is at either the start or end of the Selection. A `TableView' supports mutliple things being selected at once.Application.CursorStyle.this.CursorPosition.this.CursorPostion to null.Enabled == trueVisible == trueCanFocus == truethis == SuperView.MostFocusedConsoleDriver supports Cursor Styles other than Default, they should be supported per-application (NOT View).Application, not View.Driver. API. Only Application and the View base class should call ConsoleDriver APIs; before we ship v2, all ConsoleDriver APIs will be made internal.View Focus ChangesIt doesn't make sense the every View instance has it's own notion of MostFocused. The current implemention is overly complicated and fragile because the concept of "MostFocused" is handled by View. There can be only ONE "most focused" view in an application. MostFocused should be a property on Application.
View.MostFocusedApplication.MostFocusedView (see Application below)view._hasFocus = and change them to use SetHasFocus (today, anyplace that sets _hasFocus is a BUG!!).SetFocus/SetHasFocus etc... such that if the focus is changed to a different view heirarchy, Application.MostFocusedView gets set appropriately.MORE THOUGHT REQUIRED HERE - There be dragons given how Toplevel has OnEnter/OnLeave overrrides. The above needs more study, but is directioally correct.
View Cursor Changespublic Point? CursorPosition
private Point? _cursorPosition!HasValue the cursor is not visibleHasValue the cursor is visible at the Point.value != _cursorPosition, call OnCursorPositionChanged()public event EventHandler<LocaitonChangedEventArgs>? CursorPositionChangedinternal void OnCursorPositionChanged(LocationChangedEventArgs a)
CursorPositionChangedConsoleDriversRemove Refresh and have UpdateScreen and UpdateCursor be called separately. The fact that Refresh in all drivers currently calls both is a source of flicker.
Remove the xxxCursorVisibility APIs and replace with:
internal int CursorStyle {get; internal set; }private int _cursorStyleOnCursorStyleChanged()internal abstract void OnCursorStyleChanged()Called by base whenever the cursor style changes, but ONLY if value != _cursorStyle.
Add internal virtual (int Id, string StyleName) [] GetCursorStyles()
Returns an array of styles supported by the driver, NOT including Invisible.
The first item in array is always "Default".
Base implementation returns { 0, "Default" }
CursesDriver and WindowsDriver will need to implement overrides.
Add internal Point? CursorPosition {get; internal set; }
Backed with private Point? _cursorPosition
If !HasValue the cursor is not visible
If HasValue the cursor is visible at the Point.
On set, calls OnCursorPositionChanged ONLY if value != _cursorPosition.
Add internal abstract void OnCursorPositionChanged()
Called by base whenever the cursor position changes.
Depending on the value of CursorPosition:
!HasValue the cursor is not visible - does whatever is needed to make the cursor invisible.HasValue the cursor is visible at the CursorPosition - does whatever is needed to make the cursor visible (using CursorStyle).Make sure the drivers only make the cursor visible (or leave it visible) when CursorPosition changes!
ApplicationAdd internal static View FocusedView {get; private set;}
private static _focusedViewvalue != _focusedView
_focusedView.CursorPositionChangedvalue.CursorPositionChanged += CursorPositionChanged_focusedView = valueUpdateCursorAdd internal bool CursorPositionChanged (object sender, LocationChangedEventArgs a)
Called when:
FocusedView
FocusedView.Visible/Enable changes)CursorPositionCursorStyle has changedDoes:
FocusedView is {} and FocusedView.CursorPosition is visible (e.g. w/in FocusedView.SuperView.Viewport)
Driver.CursorPosition = ToScreen(FocusedView.CursorPosition)Driver.CursorPosition = nullAdd public static int CursorStyle {get; internal set; }
value != _cursorStyleConsoleDriver.CursorStyle = _cursorStyleUpdateCursorAdd public (int Id, string StyleName) [] GetCursorStyles()
ConsoleDriver.GetCursorStyles()Driver.Row/Pos, which are changed via Move serves two purposes that confuse each other:a) Where the next AddRune will put the next rune
b) The current "Cursor Location"
If most TUI apps acted like a command line where the visible cursor was always visible, this might make sense. But the fact that only a very few View subclasses we've seen actually care to show the cursor illustrates a problem:
Any drawing causes the "Cursor Position" to be changed/lost. This means we have a ton of code that is constantly repositioning the cursor every MainLoop iteration.
Mainloop.Iteration).Derived from above, the current design means we need to callView.PositionCursor` every iteration. For some views this is a low-cost operation. For others it involves a lot of math.
This is just stupid.
Related to the above, we need constantly Show/Hide the cursor every iteration. This causes ridiculous cursor flicker.
View.PositionCursor is poorly spec'd and confusing to implement correctlyShould a view call base.PositionCursor? If so, before or after doing stuff?
OnEnter actually makes no senseFirst, leaving it up to views to do this is fragile.
Second, when a View gets focus is but one of many places where cursor visibilty should be updated.