diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs
index 132cb23f3..c7c634212 100644
--- a/Terminal.Gui/Application.cs
+++ b/Terminal.Gui/Application.cs
@@ -1,7 +1,10 @@
+using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
+using System.Reflection.Metadata.Ecma335;
using System.Text.Json.Serialization;
+using static Unix.Terminal.Curses;
namespace Terminal.Gui;
@@ -542,8 +545,11 @@ public static partial class Application
toplevel.OnLoaded ();
toplevel.SetNeedsDisplay ();
toplevel.Draw ();
- toplevel.PositionCursor ();
- Driver.Refresh ();
+ Driver.UpdateScreen ();
+ if (PositionCursor (toplevel))
+ {
+ Driver.UpdateCursor ();
+ }
}
NotifyNewRunState?.Invoke (toplevel, new (rs));
@@ -551,6 +557,100 @@ public static partial class Application
return rs;
}
+ private static CursorVisibility _cachedCursorVisibility;
+
+ ///
+ /// Calls on the most focused view in the view starting with .
+ ///
+ ///
+ /// Does nothing if is or if the most focused view is not visible or enabled.
+ ///
+ /// If the most focused view is not visible within it's superview, the cursor will be hidden.
+ ///
+ ///
+ /// if a view positioned the cursor and the position is visible.
+ internal static bool PositionCursor (View view)
+ {
+ if (view is null)
+ {
+ return false;
+ }
+
+ // Find the most focused view and position the cursor there.
+ View mostFocused = view.MostFocused;
+
+ if (mostFocused is null)
+ {
+ 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)
+ {
+ _cachedCursorVisibility = cachedCursorVisibility;
+ Driver.SetCursorVisibility (CursorVisibility.Invisible);
+ }
+
+ return false;
+ }
+
+ // If the view is not visible within it's superview, don't position the cursor
+ Rectangle mostFocusedViewport = mostFocused.ViewportToScreen (mostFocused.Viewport with { Location = Point.Empty });
+ Rectangle superViewViewport = mostFocused.SuperView?.ViewportToScreen (mostFocused.SuperView.Viewport with { Location = Point.Empty }) ?? Driver.Screen;
+ if (!superViewViewport.IntersectsWith (mostFocusedViewport))
+ {
+ return false;
+ }
+
+ 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
+ if (cursor is { })
+ {
+ // Convert cursor to screen coords
+ cursor = mostFocused.ViewportToScreen (mostFocused.Viewport with { Location = cursor.Value }).Location;
+ if (!superViewViewport.Contains (cursor.Value))
+ {
+ Driver.GetCursorVisibility (out cachedCursorVisibility);
+
+ if (cachedCursorVisibility != CursorVisibility.Invisible)
+ {
+ _cachedCursorVisibility = cachedCursorVisibility;
+ }
+
+ Driver.SetCursorVisibility (CursorVisibility.Invisible);
+
+ return false;
+ }
+
+ Driver.GetCursorVisibility (out cachedCursorVisibility);
+
+ if (cachedCursorVisibility == CursorVisibility.Invisible)
+ {
+ Driver.SetCursorVisibility (_cachedCursorVisibility);
+ }
+
+ return prevCursor != cursor;
+ }
+
+ Driver.GetCursorVisibility (out cachedCursorVisibility);
+
+ if (cachedCursorVisibility != CursorVisibility.Invisible)
+ {
+ _cachedCursorVisibility = cachedCursorVisibility;
+ Driver.SetCursorVisibility (CursorVisibility.Invisible);
+ }
+
+ return false;
+ }
+
///
/// Runs the application by creating a object and calling
/// .
@@ -764,7 +864,6 @@ public static partial class Application
last = v;
}
- last?.PositionCursor ();
Driver.Refresh ();
}
@@ -877,11 +976,21 @@ public static partial class Application
if (state.Toplevel.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded || OverlappedChildNeedsDisplay ())
{
state.Toplevel.Draw ();
- state.Toplevel.PositionCursor ();
- Driver.Refresh ();
+ Driver.UpdateScreen ();
+ //Driver.UpdateCursor ();
}
- else
+
+ if (PositionCursor (state.Toplevel))
{
+ Driver.UpdateCursor();
+ }
+
+ // else
+ {
+ //if (PositionCursor (state.Toplevel))
+ //{
+ // Driver.Refresh ();
+ //}
//Driver.UpdateCursor ();
}
@@ -1315,6 +1424,11 @@ public static partial class Application
t.LayoutSubviews ();
t.PositionToplevels ();
t.OnSizeChanging (new (args.Size));
+
+ if (PositionCursor (t))
+ {
+ Driver.UpdateCursor ();
+ }
}
Refresh ();
diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs
index 8e1cd3b66..844529334 100644
--- a/Terminal.Gui/View/ViewSubViews.cs
+++ b/Terminal.Gui/View/ViewSubViews.cs
@@ -494,7 +494,7 @@ public partial class View
{
return true;
}
-
+
return false;
}
@@ -512,7 +512,9 @@ public partial class View
return true;
}
- Driver?.SetCursorVisibility (CursorVisibility.Invisible);
+ // 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;
}
@@ -857,35 +859,30 @@ public partial class View
/// 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.
- public virtual void PositionCursor ()
+ /// Viewport-relative cursor position.
+ public virtual Point? PositionCursor ()
{
- if (!CanBeVisible (this) || !Enabled)
+ if (!IsInitialized)
{
- return;
+ return null;
}
- // BUGBUG: v2 - This needs to support Subviews of Adornments too
+ // TODO: v2 - This needs to support Subviews of Adornments too
- if (Focused is null && SuperView is { })
+ // By default we will position the cursor at the top left corner of the Viewport.
+ // Overrides should return the position where the cursor has been placed.
+ Point location = Viewport.Location;
+
+ if (CanFocus && HasFocus && ContentSize != Size.Empty)
{
- SuperView.EnsureFocus ();
- }
- else if (Focused is { Visible: true, Enabled: true, Frame: { Width: > 0, Height: > 0 } })
- {
- Focused.PositionCursor ();
- }
- else if (Focused?.Visible == true && Focused?.Enabled == false)
- {
- Focused = null;
- }
- else if (CanFocus && HasFocus && Visible && Frame.Width > 0 && Frame.Height > 0)
- {
- Move (TextFormatter.HotKeyPos == -1 ? 0 : TextFormatter.CursorPosition, 0);
- }
- else
- {
- Move (_frame.X, _frame.Y);
+ location.X = TextFormatter.HotKeyPos == -1 ? 0 : TextFormatter.CursorPosition;
+ location.Y = 0;
+ Move (location.X, location.Y);
+ return location;
}
+
+ return null;
+
}
#endregion Focus
diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs
index 15a30def0..35b8f8397 100644
--- a/Terminal.Gui/Views/Button.cs
+++ b/Terminal.Gui/Views/Button.cs
@@ -146,7 +146,7 @@ public class Button : View
}
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
if (HotKey.IsValid && Text != "")
{
@@ -156,12 +156,12 @@ public class Button : View
{
Move (i, 0);
- return;
+ return new (i,0);
}
}
}
- base.PositionCursor ();
+ return base.PositionCursor ();
}
///
diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs
index d4a030d06..693821ca2 100644
--- a/Terminal.Gui/Views/CheckBox.cs
+++ b/Terminal.Gui/Views/CheckBox.cs
@@ -151,7 +151,7 @@ public class CheckBox : View
}
///
- public override void PositionCursor () { Move (0, 0); }
+ public override Point? PositionCursor () { Move (0, 0); return Point.Empty; }
/// Toggled event, raised when the is toggled.
///
diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs
index 2d166db3c..4c7068927 100644
--- a/Terminal.Gui/Views/HexView.cs
+++ b/Terminal.Gui/Views/HexView.cs
@@ -547,7 +547,7 @@ public class HexView : View
public event EventHandler PositionChanged;
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
var delta = (int)(position - displayStart);
int line = delta / bytesPerLine;
@@ -555,14 +555,15 @@ public class HexView : View
int block = item / bsize;
int column = item % bsize * 3;
- if (leftSide)
+ int x = displayWidth + block * 14 + column + (firstNibble ? 0 : 1);
+ int y = line;
+ if (!leftSide)
{
- Move (displayWidth + block * 14 + column + (firstNibble ? 0 : 1), line);
- }
- else
- {
- Move (displayWidth + bytesPerLine / bsize * 14 + item - 1, line);
+ x = displayWidth + bytesPerLine / bsize * 14 + item - 1;
}
+
+ Move (x, y);
+ return new (x, y);
}
internal void SetDisplayStart (long value)
diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs
index 0fb229afe..aef82e959 100644
--- a/Terminal.Gui/Views/ListView.cs
+++ b/Terminal.Gui/Views/ListView.cs
@@ -781,16 +781,17 @@ public class ListView : View
public event EventHandler OpenSelectedItem;
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
- if (_allowsMarking)
+ int x = 0;
+ int y = _selected - Viewport.Y;
+ if (!_allowsMarking)
{
- Move (0, _selected - Viewport.Y);
- }
- else
- {
- Move (Viewport.Width - 1, _selected - Viewport.Y);
+ x = Viewport.Width - 1;
}
+
+ Move (x, y);
+ return new Point (x, y);
}
/// 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 c36e68740..e2bc4630c 100644
--- a/Terminal.Gui/Views/Menu/Menu.cs
+++ b/Terminal.Gui/Views/Menu/Menu.cs
@@ -946,23 +946,22 @@ internal sealed class Menu : View
}
}
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
if (_host?.IsMenuOpen != false)
{
if (_barItems.IsTopLevel)
{
- _host?.PositionCursor ();
+ return _host?.PositionCursor ();
}
else
{
Move (2, 1 + _currentChild);
+ return new (2, 1 + _currentChild);
}
}
- else
- {
- _host?.PositionCursor ();
- }
+
+ return _host?.PositionCursor ();
}
public void Run (Action action)
diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs
index 5306d1673..943c55579 100644
--- a/Terminal.Gui/Views/Menu/MenuBar.cs
+++ b/Terminal.Gui/Views/Menu/MenuBar.cs
@@ -605,7 +605,7 @@ public class MenuBar : View
}
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
if (_selected == -1 && HasFocus && Menus.Length > 0)
{
@@ -621,7 +621,7 @@ public class MenuBar : View
pos++;
Move (pos + 1, 0);
- return;
+ return new (pos +1, 0);
}
pos += _leftPadding
@@ -631,6 +631,7 @@ public class MenuBar : View
: 0)
+ _rightPadding;
}
+ return null;
}
// Activates the menu, handles either first focus, or activating an entry when it was already active
diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs
index 5c9d6b477..d102dd573 100644
--- a/Terminal.Gui/Views/RadioGroup.cs
+++ b/Terminal.Gui/Views/RadioGroup.cs
@@ -351,19 +351,27 @@ public class RadioGroup : View
public event EventHandler OrientationChanged;
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
+ int x = 0;
+ int y = 0;
switch (Orientation)
{
case Orientation.Vertical:
- Move (0, _cursor);
+ y = _cursor;
break;
case Orientation.Horizontal:
- Move (_horizontal [_cursor].pos, 0);
+ x = _horizontal [_cursor].pos;
break;
+
+ default:
+ return null;
}
+
+ Move (x, y);
+ return new Point (x, y);
}
/// Allow to invoke the after their creation.
diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs
index 60affffb2..b26167634 100644
--- a/Terminal.Gui/Views/ScrollView.cs
+++ b/Terminal.Gui/Views/ScrollView.cs
@@ -455,16 +455,14 @@ public class ScrollView : View
}
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
if (InternalSubviews.Count == 0)
{
Move (0, 0);
+ return Point.Empty;
}
- else
- {
- base.PositionCursor ();
- }
+ return base.PositionCursor ();
}
/// Removes the view from the scrollview.
diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs
index 9ccc59abf..c6d9b4e34 100644
--- a/Terminal.Gui/Views/Slider.cs
+++ b/Terminal.Gui/Views/Slider.cs
@@ -983,7 +983,7 @@ public class Slider : View
#region Cursor and Drawing
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
//base.PositionCursor ();
@@ -1001,8 +1001,11 @@ public class Slider : View
if (IsInitialized && Viewport.Contains (position.x, position.y))
{
Move (position.x, position.y);
+
+ return new (position.x, position.x);
}
}
+ return null;
}
///
diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs
index e82a00666..0465d6e0d 100644
--- a/Terminal.Gui/Views/TableView/TableView.cs
+++ b/Terminal.Gui/Views/TableView/TableView.cs
@@ -1017,13 +1017,11 @@ public class TableView : View
/// Positions the cursor in the area of the screen in which the start of the active cell is rendered. Calls base
/// implementation if active cell is not visible due to scrolling or table is loaded etc
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
if (TableIsNullOrInvisible ())
{
- base.PositionCursor ();
-
- return;
+ return base.PositionCursor ();
}
Point? screenPoint = CellToScreen (SelectedColumn, SelectedRow);
@@ -1031,7 +1029,10 @@ public class TableView : View
if (screenPoint is { })
{
Move (screenPoint.Value.X, screenPoint.Value.Y);
+ return screenPoint;
}
+
+ return null;
}
///
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index 5ee281596..dc755289f 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -9,10 +9,8 @@ namespace Terminal.Gui;
public class TextField : View
{
private readonly HistoryText _historyText;
- private readonly CursorVisibility _savedCursorVisibility;
private CultureInfo _currentCulture;
private int _cursorPosition;
- private CursorVisibility _desiredCursorVisibility;
private bool _isButtonPressed;
private bool _isButtonReleased;
private bool _isDrawing;
@@ -21,7 +19,6 @@ public class TextField : View
private string _selectedText;
private int _start;
private List _text;
- private CursorVisibility _visibility;
///
/// Initializes a new instance of the class using
@@ -30,7 +27,6 @@ public class TextField : View
public TextField ()
{
_historyText = new HistoryText ();
- _desiredCursorVisibility = CursorVisibility.Default;
_isButtonReleased = true;
_selectedStart = -1;
_text = new List ();
@@ -42,7 +38,6 @@ public class TextField : View
CanFocus = true;
Used = true;
WantMousePositionReports = true;
- _savedCursorVisibility = _desiredCursorVisibility;
_historyText.ChangeText += HistoryText_ChangeText;
@@ -469,21 +464,6 @@ public class TextField : View
}
}
- /// Get / Set the wished cursor when the field is focused
- public CursorVisibility DesiredCursorVisibility
- {
- get => _desiredCursorVisibility;
- set
- {
- if ((_desiredCursorVisibility != value || _visibility != value) && HasFocus)
- {
- Application.Driver.SetCursorVisibility (value);
- }
-
- _desiredCursorVisibility = _visibility = value;
- }
- }
-
///
/// Indicates whatever the text has history changes or not. if the text has history changes
/// otherwise.
@@ -1060,7 +1040,7 @@ public class TextField : View
{
if (IsInitialized)
{
- Application.Driver.SetCursorVisibility (DesiredCursorVisibility);
+ Application.Driver.SetCursorVisibility (CursorVisibility.Default);
}
return base.OnEnter (view);
@@ -1172,13 +1152,8 @@ public class TextField : View
}
/// Sets the cursor position.
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
- if (!IsInitialized)
- {
- return;
- }
-
ProcessAutocomplete ();
var col = 0;
@@ -1195,31 +1170,8 @@ public class TextField : View
}
int pos = _cursorPosition - ScrollOffset + Math.Min (Frame.X, 0);
- int offB = OffSetBackground ();
- Rectangle containerFrame = SuperView?.ViewportToScreen (SuperView.Viewport) ?? default (Rectangle);
- Rectangle thisFrame = ViewportToScreen (Viewport);
-
- if (pos > -1
- && col >= pos
- && pos < Frame.Width + offB
- && containerFrame.IntersectsWith (thisFrame))
- {
- RestoreCursorVisibility ();
- Move (col, 0);
- }
- else
- {
- HideCursorVisibility ();
-
- if (pos < 0)
- {
- Move (pos, 0);
- }
- else
- {
- Move (pos - offB, 0);
- }
- }
+ Move (pos, 0);
+ return new Point (pos, 0);
}
/// Redoes the latest changes.
@@ -1231,21 +1183,6 @@ public class TextField : View
}
_historyText.Redo ();
-
- //if (string.IsNullOrEmpty (Clipboard.Contents))
- // return true;
- //var clip = TextModel.ToRunes (Clipboard.Contents);
- //if (clip is null)
- // return true;
-
- //if (point == text.Count) {
- // point = text.Count;
- // SetText(text.Concat(clip).ToList());
- //} else {
- // point += clip.Count;
- // SetText(text.GetRange(0, oldCursorPos).Concat(clip).Concat(text.GetRange(oldCursorPos, text.Count - oldCursorPos)));
- //}
- //Adjust ();
}
/// Selects all text.
@@ -1465,14 +1402,6 @@ public class TextField : View
return new Attribute (cs.Disabled.Foreground, cs.Focus.Background);
}
- private void HideCursorVisibility ()
- {
- if (_desiredCursorVisibility != CursorVisibility.Invisible)
- {
- DesiredCursorVisibility = CursorVisibility.Invisible;
- }
- }
-
private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItem obj)
{
if (obj is null)
@@ -1886,16 +1815,6 @@ public class TextField : View
Driver.AddStr (render);
}
- private void RestoreCursorVisibility ()
- {
- Application.Driver.GetCursorVisibility (out _visibility);
-
- if (_desiredCursorVisibility != _savedCursorVisibility || _visibility != _savedCursorVisibility)
- {
- DesiredCursorVisibility = _savedCursorVisibility;
- }
- }
-
private void SetClipboard (IEnumerable text)
{
if (!Secret)
diff --git a/Terminal.Gui/Views/TextValidateField.cs b/Terminal.Gui/Views/TextValidateField.cs
index a62fa4a54..f421fb931 100644
--- a/Terminal.Gui/Views/TextValidateField.cs
+++ b/Terminal.Gui/Views/TextValidateField.cs
@@ -640,7 +640,7 @@ namespace Terminal.Gui
}
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
(int left, _) = GetMargins (Viewport.Width);
@@ -652,13 +652,12 @@ namespace Terminal.Gui
if (_provider?.Fixed == false && TextAlignment == TextAlignment.Right)
{
curPos = _cursorPosition + left - 1;
- Move (curPos, 0);
}
else
{
curPos = _cursorPosition + left;
- Move (curPos, 0);
}
+ Move (curPos, 0);
if (curPos < 0 || curPos >= Viewport.Width)
{
@@ -668,6 +667,7 @@ namespace Terminal.Gui
{
Application.Driver.SetCursorVisibility (CursorVisibility.Default);
}
+ return new (curPos, 0);
}
/// Delete char at cursor position - 1, moving the cursor.
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index a20b4cbbc..a83be01a3 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -67,7 +67,7 @@ internal class TextModel
}
FilePath = null;
- _lines = new List> ();
+ _lines = new ();
return true;
}
@@ -89,7 +89,7 @@ internal class TextModel
return _lines [Count - 1];
}
- _lines.Add (new List ());
+ _lines.Add (new ());
return _lines [0];
}
@@ -153,7 +153,7 @@ internal class TextModel
throw new ArgumentNullException (nameof (input));
}
- _lines = new List> ();
+ _lines = new ();
var buff = new BufferedStream (input);
int v;
List line = new ();
@@ -215,7 +215,7 @@ internal class TextModel
{
if (_lines.Count > 0 && pos < _lines.Count)
{
- _lines [pos] = new List (runes);
+ _lines [pos] = new (runes);
}
else if (_lines.Count == 0 || (_lines.Count > 0 && pos >= _lines.Count))
{
@@ -243,7 +243,7 @@ internal class TextModel
foreach (Rune rune in str.EnumerateRunes ())
{
- cells.Add (new RuneCell { Rune = rune, ColorScheme = colorScheme });
+ cells.Add (new() { Rune = rune, ColorScheme = colorScheme });
}
return cells;
@@ -759,7 +759,7 @@ internal class TextModel
_lines.Count - 1,
matchCase,
matchWholeWord,
- new Point (_lines [_lines.Count - 1].Count, _lines.Count)
+ new (_lines [_lines.Count - 1].Count, _lines.Count)
);
}
@@ -850,7 +850,7 @@ internal class TextModel
_lines [i] = ToRuneCellList (ReplaceText (x, textToReplace!, matchText, col));
x = _lines [i];
txt = GetText (x);
- pos = new Point (col, i);
+ pos = new (col, i);
col += textToReplace!.Length - matchText.Length;
}
@@ -906,7 +906,7 @@ internal class TextModel
foreach (Rune rune in str.ToRunes ())
{
- cells.Add (new RuneCell { 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 RuneCell { Rune = rune, ColorScheme = colorScheme });
+ cells.Add (new() { Rune = rune, ColorScheme = colorScheme });
}
return cells;
@@ -981,7 +981,7 @@ internal class TextModel
if (col > -1 && ((i == start.Y && col >= start.X) || i > start.Y) && txt.Contains (matchText))
{
- return (new Point (col, i), true);
+ return (new (col, i), true);
}
if (col == -1 && start.X > 0)
@@ -1026,7 +1026,7 @@ internal class TextModel
if (col > -1 && ((i <= linesCount && col <= start.X) || i < start.Y) && txt.Contains (matchText))
{
- return (new Point (col, i), true);
+ return (new (col, i), true);
}
}
@@ -1292,7 +1292,7 @@ internal partial class HistoryText
);
}
- _historyTextItems.Add (new HistoryTextItem (lines, curPos, lineStatus));
+ _historyTextItems.Add (new (lines, curPos, lineStatus));
_idxHistoryText++;
}
@@ -1370,7 +1370,7 @@ internal partial class HistoryText
_idxHistoryText--;
}
- historyTextItem = new HistoryTextItem (_historyTextItems [_idxHistoryText]);
+ historyTextItem = new (_historyTextItems [_idxHistoryText]);
historyTextItem.IsUndoing = true;
historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
}
@@ -1378,7 +1378,7 @@ internal partial class HistoryText
if (historyTextItem.LineStatus == LineStatus.Removed && _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Added)
{
historyTextItem.RemovedOnAdded =
- new HistoryTextItem (_historyTextItems [_idxHistoryText + 1]);
+ new (_historyTextItems [_idxHistoryText + 1]);
}
if ((historyTextItem.LineStatus == LineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original)
@@ -1390,7 +1390,7 @@ internal partial class HistoryText
&& historyTextItem.CursorPosition == _historyTextItems [_idxHistoryText - 1].CursorPosition)
{
historyTextItem.Lines [0] =
- new List (_historyTextItems [_idxHistoryText - 1].Lines [0]);
+ new (_historyTextItems [_idxHistoryText - 1].Lines [0]);
}
if (historyTextItem.LineStatus == LineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed)
@@ -1425,7 +1425,7 @@ internal partial class HistoryText
|| _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Removed))
{
_idxHistoryText++;
- historyTextItem = new HistoryTextItem (_historyTextItems [_idxHistoryText]);
+ historyTextItem = new (_historyTextItems [_idxHistoryText]);
historyTextItem.IsUndoing = false;
historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
}
@@ -1433,7 +1433,7 @@ internal partial class HistoryText
if (historyTextItem.LineStatus == LineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed)
{
historyTextItem.RemovedOnAdded =
- new HistoryTextItem (_historyTextItems [_idxHistoryText - 1]);
+ new (_historyTextItems [_idxHistoryText - 1]);
}
if ((historyTextItem.LineStatus == LineStatus.Removed && _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Replaced)
@@ -1445,7 +1445,7 @@ internal partial class HistoryText
.SequenceEqual (_historyTextItems [_idxHistoryText + 1].Lines [0]))
{
historyTextItem.Lines [0] =
- new List (_historyTextItems [_idxHistoryText + 1].Lines [0]);
+ new (_historyTextItems [_idxHistoryText + 1].Lines [0]);
}
historyTextItem.FinalCursorPosition =
@@ -1962,15 +1962,14 @@ public class TextView : View
private bool _copyWithoutSelection;
private string? _currentCaller;
private CultureInfo? _currentCulture;
- private CursorVisibility _desiredCursorVisibility = CursorVisibility.Default;
private bool _isButtonShift;
+ private bool _isButtonReleased;
private bool _isDrawing;
private bool _isReadOnly;
private bool _lastWasKill;
private int _leftColumn;
private TextModel _model = new ();
private bool _multiline = true;
- private CursorVisibility _savedCursorVisibility;
private Dim? _savedHeight;
private int _selectionStartColumn, _selectionStartRow;
private bool _shiftSelecting;
@@ -1980,6 +1979,10 @@ 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
/// and Height properties.
@@ -2398,10 +2401,10 @@ public class TextView : View
Command.ShowContextMenu,
() =>
{
- ContextMenu!.Position = new Point (
- CursorPosition.X - _leftColumn + 2,
- CursorPosition.Y - _topRow + 2
- );
+ ContextMenu!.Position = new (
+ CursorPosition.X - _leftColumn + 2,
+ CursorPosition.Y - _topRow + 2
+ );
ShowContextMenu ();
return true;
@@ -2509,7 +2512,7 @@ public class TextView : View
_currentCulture = Thread.CurrentThread.CurrentUICulture;
- ContextMenu = new ContextMenu { MenuItems = BuildContextMenuBarItem () };
+ ContextMenu = new() { MenuItems = BuildContextMenuBarItem () };
ContextMenu.KeyChanged += ContextMenu_KeyChanged!;
KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.ShowContextMenu);
@@ -2626,21 +2629,6 @@ public class TextView : View
}
}
- /// Get / Set the wished cursor when the field is focused
- public CursorVisibility DesiredCursorVisibility
- {
- get => _desiredCursorVisibility;
- set
- {
- if (HasFocus)
- {
- Application.Driver.SetCursorVisibility (value);
- }
-
- _desiredCursorVisibility = value;
- SetNeedsDisplay ();
- }
- }
///
/// Indicates whatever the text has history changes or not. if the text has history changes
@@ -2862,17 +2850,17 @@ public class TextView : View
}
set
{
- var old = Text;
+ string old = Text;
ResetPosition ();
_model.LoadString (value);
if (_wordWrap)
{
- _wrapManager = new WordWrapManager (_model);
+ _wrapManager = new (_model);
_model = _wrapManager.WrapModel (_frameWidth, out _, out _, out _, out _);
}
- OnTextChanged (old,Text);
+ OnTextChanged (old, Text);
SetNeedsDisplay ();
_historyText.Clear (Text);
@@ -2913,7 +2901,7 @@ public class TextView : View
if (_wordWrap)
{
- _wrapManager = new WordWrapManager (_model);
+ _wrapManager = new (_model);
_model = _wrapManager.WrapModel (_frameWidth, out _, out _, out _, out _);
}
else if (!_wordWrap && _wrapManager is { })
@@ -2982,7 +2970,7 @@ public class TextView : View
ClearRegion ();
_historyText.Add (
- new List> { new (GetCurrentLine ()) },
+ new() { new (GetCurrentLine ()) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -3021,14 +3009,14 @@ public class TextView : View
if (Selecting)
{
- _historyText.Add (new List> { new (GetCurrentLine ()) }, CursorPosition);
+ _historyText.Add (new() { new (GetCurrentLine ()) }, CursorPosition);
ClearSelectedRegion ();
List currentLine = GetCurrentLine ();
_historyText.Add (
- new List> { new (currentLine) },
+ new() { new (currentLine) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -3065,14 +3053,14 @@ public class TextView : View
if (Selecting)
{
- _historyText.Add (new List> { new (GetCurrentLine ()) }, CursorPosition);
+ _historyText.Add (new() { new (GetCurrentLine ()) }, CursorPosition);
ClearSelectedRegion ();
List currentLine = GetCurrentLine ();
_historyText.Add (
- new List> { new (currentLine) },
+ new() { new (currentLine) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -3204,7 +3192,7 @@ public class TextView : View
if (ColorScheme is null)
{
- cs = new ColorScheme ();
+ cs = new ();
}
return Enabled ? cs.Focus : cs.Disabled;
@@ -3223,7 +3211,7 @@ public class TextView : View
try
{
- key = new Key (ch);
+ key = new (ch);
}
catch (Exception)
{
@@ -3312,7 +3300,7 @@ public class TextView : View
}
///
- protected internal override bool OnMouseEvent (MouseEvent ev)
+ protected internal override bool OnMouseEvent (MouseEvent ev)
{
if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked)
&& !ev.Flags.HasFlag (MouseFlags.Button1Pressed)
@@ -3349,6 +3337,13 @@ public class TextView : View
if (ev.Flags == MouseFlags.Button1Clicked)
{
+ if (_isButtonReleased)
+ {
+ _isButtonReleased = false;
+
+ return true;
+ }
+
if (_shiftSelecting && !_isButtonShift)
{
StopSelecting ();
@@ -3476,6 +3471,7 @@ public class TextView : View
}
else if (ev.Flags.HasFlag (MouseFlags.Button1Released))
{
+ _isButtonReleased = true;
Application.UngrabMouse ();
}
else if (ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
@@ -3544,7 +3540,7 @@ public class TextView : View
}
else if (ev.Flags == ContextMenu!.MouseFlags)
{
- ContextMenu.Position = new Point (ev.X + 2, ev.Y + 2);
+ ContextMenu.Position = new (ev.X + 2, ev.Y + 2);
ShowContextMenu ();
}
@@ -3579,7 +3575,7 @@ public class TextView : View
///
public virtual void OnContentsChanged ()
{
- ContentsChanged?.Invoke (this, new ContentsChangedEventArgs (CurrentRow, CurrentColumn));
+ ContentsChanged?.Invoke (this, new (CurrentRow, CurrentColumn));
ProcessInheritsPreviousColorScheme (CurrentRow, CurrentColumn);
ProcessAutocomplete ();
@@ -3762,7 +3758,7 @@ public class TextView : View
col = _wrapManager.GetModelColFromWrappedLines (CurrentRow, CurrentColumn);
}
- UnwrappedCursorPosition?.Invoke (this, new PointEventArgs (new Point ((int)col, (int)row)));
+ UnwrappedCursorPosition?.Invoke (this, new (new ((int)col, (int)row)));
}
/// Paste the clipboard contents into the current selected position.
@@ -3778,15 +3774,15 @@ public class TextView : View
if (_copyWithoutSelection && contents.FirstOrDefault (x => x == '\n' || x == '\r') == 0)
{
- List runeList = contents is null ? new List () : TextModel.ToRuneCellList (contents);
+ List runeList = contents is null ? new () : TextModel.ToRuneCellList (contents);
List currentLine = GetCurrentLine ();
- _historyText.Add (new List> { new (currentLine) }, CursorPosition);
+ _historyText.Add (new() { new (currentLine) }, CursorPosition);
- List> addedLine = new () { new List (currentLine), runeList };
+ List> addedLine = new () { new (currentLine), runeList };
_historyText.Add (
- new List> (addedLine),
+ new (addedLine),
CursorPosition,
HistoryText.LineStatus.Added
);
@@ -3795,7 +3791,7 @@ public class TextView : View
CurrentRow++;
_historyText.Add (
- new List> { new (GetCurrentLine ()) },
+ new() { new (GetCurrentLine ()) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -3816,7 +3812,7 @@ public class TextView : View
if (Selecting)
{
_historyText.ReplaceLast (
- new List> { new (GetCurrentLine ()) },
+ new() { new (GetCurrentLine ()) },
CursorPosition,
HistoryText.LineStatus.Original
);
@@ -3831,13 +3827,13 @@ public class TextView : View
}
/// Positions the cursor on the current row and column
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
ProcessAutocomplete ();
if (!CanFocus || !Enabled || Application.Driver is null)
{
- return;
+ return null;
}
if (Selecting)
@@ -3882,13 +3878,11 @@ public class TextView : View
if (posX > -1 && col >= posX && posX < Frame.Width - RightOffset && _topRow <= CurrentRow && posY < Frame.Height - BottomOffset)
{
- ResetCursorVisibility ();
Move (col, CurrentRow - _topRow);
+ return new (col, CurrentRow - _topRow);
}
- else
- {
- SaveCursorVisibility ();
- }
+
+ return null;
}
/// Redoes the latest changes.
@@ -4042,11 +4036,11 @@ public class TextView : View
if (colorScheme!.Disabled.Foreground == colorScheme.Focus.Background)
{
- attribute = new Attribute (colorScheme.Focus.Foreground, colorScheme.Focus.Background);
+ attribute = new (colorScheme.Focus.Foreground, colorScheme.Focus.Background);
}
else
{
- attribute = new Attribute (colorScheme.Disabled.Foreground, colorScheme.Focus.Background);
+ attribute = new (colorScheme.Disabled.Foreground, colorScheme.Focus.Background);
}
Driver.SetAttribute (attribute);
@@ -4072,16 +4066,16 @@ public class TextView : View
ColorScheme? colorScheme = line [idxCol].ColorScheme;
Driver.SetAttribute (
- new Attribute (colorScheme!.Focus.Background, colorScheme.Focus.Foreground)
+ new (colorScheme!.Focus.Background, colorScheme.Focus.Foreground)
);
}
else
{
Driver.SetAttribute (
- new Attribute (
- ColorScheme.Focus.Background,
- ColorScheme.Focus.Foreground
- )
+ new (
+ ColorScheme.Focus.Background,
+ ColorScheme.Focus.Foreground
+ )
);
}
}
@@ -4190,67 +4184,67 @@ public class TextView : View
private MenuBarItem BuildContextMenuBarItem ()
{
- return new MenuBarItem (
- new MenuItem []
- {
- new (
- Strings.ctxSelectAll,
- "",
- SelectAll,
- null,
- null,
- (KeyCode)KeyBindings.GetKeyFromCommands (Command.SelectAll)
- ),
- new (
- Strings.ctxDeleteAll,
- "",
- DeleteAll,
- null,
- null,
- (KeyCode)KeyBindings.GetKeyFromCommands (Command.DeleteAll)
- ),
- new (
- Strings.ctxCopy,
- "",
- Copy,
- null,
- null,
- (KeyCode)KeyBindings.GetKeyFromCommands (Command.Copy)
- ),
- new (
- Strings.ctxCut,
- "",
- Cut,
- null,
- null,
- (KeyCode)KeyBindings.GetKeyFromCommands (Command.Cut)
- ),
- new (
- Strings.ctxPaste,
- "",
- Paste,
- null,
- null,
- (KeyCode)KeyBindings.GetKeyFromCommands (Command.Paste)
- ),
- new (
- Strings.ctxUndo,
- "",
- Undo,
- null,
- null,
- (KeyCode)KeyBindings.GetKeyFromCommands (Command.Undo)
- ),
- new (
- Strings.ctxRedo,
- "",
- Redo,
- null,
- null,
- (KeyCode)KeyBindings.GetKeyFromCommands (Command.Redo)
- )
- }
- );
+ return new (
+ new MenuItem []
+ {
+ new (
+ Strings.ctxSelectAll,
+ "",
+ SelectAll,
+ null,
+ null,
+ (KeyCode)KeyBindings.GetKeyFromCommands (Command.SelectAll)
+ ),
+ new (
+ Strings.ctxDeleteAll,
+ "",
+ DeleteAll,
+ null,
+ null,
+ (KeyCode)KeyBindings.GetKeyFromCommands (Command.DeleteAll)
+ ),
+ new (
+ Strings.ctxCopy,
+ "",
+ Copy,
+ null,
+ null,
+ (KeyCode)KeyBindings.GetKeyFromCommands (Command.Copy)
+ ),
+ new (
+ Strings.ctxCut,
+ "",
+ Cut,
+ null,
+ null,
+ (KeyCode)KeyBindings.GetKeyFromCommands (Command.Cut)
+ ),
+ new (
+ Strings.ctxPaste,
+ "",
+ Paste,
+ null,
+ null,
+ (KeyCode)KeyBindings.GetKeyFromCommands (Command.Paste)
+ ),
+ new (
+ Strings.ctxUndo,
+ "",
+ Undo,
+ null,
+ null,
+ (KeyCode)KeyBindings.GetKeyFromCommands (Command.Undo)
+ ),
+ new (
+ Strings.ctxRedo,
+ "",
+ Redo,
+ null,
+ null,
+ (KeyCode)KeyBindings.GetKeyFromCommands (Command.Redo)
+ )
+ }
+ );
}
private void ClearRegion (int left, int top, int right, int bottom)
@@ -4282,13 +4276,13 @@ public class TextView : View
var endCol = (int)(end & 0xffffffff);
List line = _model.GetLine (startRow);
- _historyText.Add (new List> { new (line) }, new Point (startCol, startRow));
+ _historyText.Add (new() { new (line) }, new (startCol, startRow));
List> removedLines = new ();
if (startRow == maxrow)
{
- removedLines.Add (new List (line));
+ removedLines.Add (new (line));
line.RemoveRange (startCol, endCol - startCol);
CurrentColumn = startCol;
@@ -4306,7 +4300,7 @@ public class TextView : View
}
_historyText.Add (
- new List> (removedLines),
+ new (removedLines),
CursorPosition,
HistoryText.LineStatus.Removed
);
@@ -4316,7 +4310,7 @@ public class TextView : View
return;
}
- removedLines.Add (new List (line));
+ removedLines.Add (new (line));
line.RemoveRange (startCol, line.Count - startCol);
List line2 = _model.GetLine (maxrow);
@@ -4324,7 +4318,7 @@ public class TextView : View
for (int row = startRow + 1; row <= maxrow; row++)
{
- removedLines.Add (new List (_model.GetLine (startRow + 1)));
+ removedLines.Add (new (_model.GetLine (startRow + 1)));
_model.RemoveLine (startRow + 1);
}
@@ -4337,7 +4331,7 @@ public class TextView : View
CurrentColumn = startCol;
_historyText.Add (
- new List> (removedLines),
+ new (removedLines),
CursorPosition,
HistoryText.LineStatus.Removed
);
@@ -4372,7 +4366,7 @@ public class TextView : View
// Delete backwards
List currentLine = GetCurrentLine ();
- _historyText.Add (new List> { new (currentLine) }, CursorPosition);
+ _historyText.Add (new() { new (currentLine) }, CursorPosition);
currentLine.RemoveAt (CurrentColumn - 1);
@@ -4384,7 +4378,7 @@ public class TextView : View
CurrentColumn--;
_historyText.Add (
- new List> { new (currentLine) },
+ new() { new (currentLine) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -4412,15 +4406,15 @@ public class TextView : View
int prowIdx = CurrentRow - 1;
List prevRow = _model.GetLine (prowIdx);
- _historyText.Add (new List> { new (prevRow) }, CursorPosition);
+ _historyText.Add (new() { new (prevRow) }, CursorPosition);
- List> removedLines = new () { new List (prevRow) };
+ List> removedLines = new () { new (prevRow) };
- removedLines.Add (new List (GetCurrentLine ()));
+ removedLines.Add (new (GetCurrentLine ()));
_historyText.Add (
removedLines,
- new Point (CurrentColumn, prowIdx),
+ new (CurrentColumn, prowIdx),
HistoryText.LineStatus.Removed
);
@@ -4436,8 +4430,8 @@ public class TextView : View
CurrentRow--;
_historyText.Add (
- new List> { GetCurrentLine () },
- new Point (CurrentColumn, prowIdx),
+ new() { GetCurrentLine () },
+ new (CurrentColumn, prowIdx),
HistoryText.LineStatus.Replaced
);
@@ -4465,13 +4459,13 @@ public class TextView : View
return true;
}
- _historyText.Add (new List> { new (currentLine) }, CursorPosition);
+ _historyText.Add (new() { new (currentLine) }, CursorPosition);
- List> removedLines = new () { new List (currentLine) };
+ List> removedLines = new () { new (currentLine) };
List nextLine = _model.GetLine (CurrentRow + 1);
- removedLines.Add (new List (nextLine));
+ removedLines.Add (new (nextLine));
_historyText.Add (removedLines, CursorPosition, HistoryText.LineStatus.Removed);
@@ -4479,7 +4473,7 @@ public class TextView : View
_model.RemoveLine (CurrentRow + 1);
_historyText.Add (
- new List> { new (currentLine) },
+ new() { new (currentLine) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -4586,13 +4580,13 @@ public class TextView : View
List currentLine = GetCurrentLine ();
int cursorPosition = Math.Min (CurrentColumn, currentLine.Count);
- Autocomplete.Context = new AutocompleteContext (
- currentLine,
- cursorPosition,
- Autocomplete.Context != null
- ? Autocomplete.Context.Canceled
- : false
- );
+ Autocomplete.Context = new (
+ currentLine,
+ cursorPosition,
+ Autocomplete.Context != null
+ ? Autocomplete.Context.Canceled
+ : false
+ );
Autocomplete.GenerateSuggestions (
Autocomplete.Context
@@ -4830,7 +4824,7 @@ public class TextView : View
List line = GetCurrentLine ();
- _historyText.Add (new List> { new (line) }, CursorPosition);
+ _historyText.Add (new() { new (line) }, CursorPosition);
// Optimize single line
if (lines.Count == 1)
@@ -4839,7 +4833,7 @@ public class TextView : View
CurrentColumn += lines [0].Count;
_historyText.Add (
- new List> { new (line) },
+ new() { new (line) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -4883,13 +4877,13 @@ public class TextView : View
//model.AddLine (currentRow, lines [0]);
- List> addedLines = new () { new List (line) };
+ List> addedLines = new () { new (line) };
for (var i = 1; i < lines.Count; i++)
{
_model.AddLine (CurrentRow + i, lines [i]);
- addedLines.Add (new List (lines [i]));
+ addedLines.Add (new (lines [i]));
}
if (rest is { })
@@ -4909,7 +4903,7 @@ public class TextView : View
Adjust ();
_historyText.Add (
- new List> { new (line) },
+ new() { new (line) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -4928,7 +4922,7 @@ public class TextView : View
SetWrapModel ();
- _historyText.Add (new List> { new (GetCurrentLine ()) }, CursorPosition);
+ _historyText.Add (new() { new (GetCurrentLine ()) }, CursorPosition);
if (Selecting)
{
@@ -4937,7 +4931,7 @@ public class TextView : View
if ((uint)a.KeyCode == '\n')
{
- _model.AddLine (CurrentRow + 1, new List ());
+ _model.AddLine (CurrentRow + 1, new ());
CurrentRow++;
CurrentColumn = 0;
}
@@ -4949,7 +4943,7 @@ public class TextView : View
{
if (Used)
{
- Insert (new RuneCell { Rune = a.AsRune, ColorScheme = colorScheme });
+ Insert (new() { Rune = a.AsRune, ColorScheme = colorScheme });
CurrentColumn++;
if (CurrentColumn >= _leftColumn + Frame.Width)
@@ -4960,13 +4954,13 @@ public class TextView : View
}
else
{
- Insert (new RuneCell { Rune = a.AsRune, ColorScheme = colorScheme });
+ Insert (new() { Rune = a.AsRune, ColorScheme = colorScheme });
CurrentColumn++;
}
}
_historyText.Add (
- new List> { new (GetCurrentLine ()) },
+ new() { new (GetCurrentLine ()) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -5004,20 +4998,20 @@ public class TextView : View
return;
}
- _historyText.Add (new List> { new (currentLine) }, CursorPosition);
+ _historyText.Add (new() { new (currentLine) }, CursorPosition);
if (currentLine.Count == 0)
{
if (CurrentRow < _model.Count - 1)
{
- List> removedLines = new () { new List (currentLine) };
+ List> removedLines = new () { new (currentLine) };
_model.RemoveLine (CurrentRow);
- removedLines.Add (new List (GetCurrentLine ()));
+ removedLines.Add (new (GetCurrentLine ()));
_historyText.Add (
- new List> (removedLines),
+ new (removedLines),
CursorPosition,
HistoryText.LineStatus.Removed
);
@@ -5671,13 +5665,13 @@ public class TextView : View
if (currentLine.Count > 0 && currentLine [CurrentColumn - 1].Rune.Value == '\t')
{
- _historyText.Add (new List> { new (currentLine) }, CursorPosition);
+ _historyText.Add (new() { new (currentLine) }, CursorPosition);
currentLine.RemoveAt (CurrentColumn - 1);
CurrentColumn--;
_historyText.Add (
- new List> { new (GetCurrentLine ()) },
+ new() { new (GetCurrentLine ()) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -6118,7 +6112,7 @@ public class TextView : View
List currentLine = GetCurrentLine ();
- _historyText.Add (new List> { new (currentLine) }, CursorPosition);
+ _historyText.Add (new() { new (currentLine) }, CursorPosition);
if (Selecting)
{
@@ -6130,11 +6124,11 @@ public class TextView : View
List rest = currentLine.GetRange (CurrentColumn, restCount);
currentLine.RemoveRange (CurrentColumn, restCount);
- List> addedLines = new () { new List (currentLine) };
+ List> addedLines = new () { new (currentLine) };
_model.AddLine (CurrentRow + 1, rest);
- addedLines.Add (new List (_model.GetLine (CurrentRow + 1)));
+ addedLines.Add (new (_model.GetLine (CurrentRow + 1)));
_historyText.Add (addedLines, CursorPosition, HistoryText.LineStatus.Added);
@@ -6151,7 +6145,7 @@ public class TextView : View
CurrentColumn = 0;
_historyText.Add (
- new List> { new (GetCurrentLine ()) },
+ new() { new (GetCurrentLine ()) },
CursorPosition,
HistoryText.LineStatus.Replaced
);
@@ -6231,7 +6225,7 @@ public class TextView : View
{
int col = Selecting ? _selectionStartColumn : CurrentColumn;
int row = Selecting ? _selectionStartRow : CurrentRow;
- _model.ResetContinuousFind (new Point (col, row));
+ _model.ResetContinuousFind (new (col, row));
}
}
@@ -6243,33 +6237,11 @@ public class TextView : View
_continuousFind = false;
}
- private void ResetCursorVisibility ()
- {
- if (_savedCursorVisibility != 0)
- {
- DesiredCursorVisibility = _savedCursorVisibility;
- _savedCursorVisibility = 0;
- }
- }
private void ResetPosition ()
{
_topRow = _leftColumn = CurrentRow = CurrentColumn = 0;
StopSelecting ();
- ResetCursorVisibility ();
- }
-
- private void SaveCursorVisibility ()
- {
- if (_desiredCursorVisibility != CursorVisibility.Invisible)
- {
- if (_savedCursorVisibility == 0)
- {
- _savedCursorVisibility = _desiredCursorVisibility;
- }
-
- DesiredCursorVisibility = CursorVisibility.Invisible;
- }
}
private void SetClipboard (string text)
@@ -6342,7 +6314,7 @@ public class TextView : View
{
// BUGBUG: (v2 truecolor) This code depends on 8-bit color names; disabling for now
//if ((colorScheme!.HotNormal.Foreground & colorScheme.Focus.Background) == colorScheme.Focus.Foreground) {
- Driver.SetAttribute (new Attribute (colorScheme.Focus.Background, colorScheme.Focus.Foreground));
+ Driver.SetAttribute (new (colorScheme.Focus.Background, colorScheme.Focus.Foreground));
}
/// Restore from original model.
@@ -6558,6 +6530,6 @@ public class TextViewAutocomplete : PopupAutocomplete
protected override void SetCursorPosition (int column)
{
((TextView)HostControl).CursorPosition =
- new Point (column, ((TextView)HostControl).CurrentRow);
+ new (column, ((TextView)HostControl).CurrentRow);
}
}
diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs
index cbc2b9f22..b3608b668 100644
--- a/Terminal.Gui/Views/TileView.cs
+++ b/Terminal.Gui/Views/TileView.cs
@@ -987,12 +987,13 @@ public class TileView : View
return base.OnEnter (view);
}
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
base.PositionCursor ();
Point location = moveRuneRenderLocation ?? new Point (Viewport.Width / 2, Viewport.Height / 2);
Move (location.X, location.Y);
+ return location;
}
///
diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs
index 5c9ca2414..bdafa7576 100644
--- a/Terminal.Gui/Views/Toplevel.cs
+++ b/Terminal.Gui/Views/Toplevel.cs
@@ -333,12 +333,10 @@ public partial class Toplevel : View
}
///
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
if (!IsOverlappedContainer)
{
- base.PositionCursor ();
-
if (Focused is null)
{
EnsureFocus ();
@@ -349,28 +347,32 @@ public partial class Toplevel : View
}
}
- return;
+ return null;
}
+ // This code path only happens when the Toplevel is an Overlapped container
+
if (Focused is null)
{
+ // TODO: this is an Overlapped hack
foreach (Toplevel top in Application.OverlappedChildren)
{
if (top != this && top.Visible)
{
top.SetFocus ();
- return;
+ return null;
}
}
}
- base.PositionCursor ();
+ var cursor2 = base.PositionCursor ();
if (Focused is null)
{
Driver.SetCursorVisibility (CursorVisibility.Invisible);
}
+ return cursor2;
}
///
diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs
index 2eb67267c..1b8ad7d48 100644
--- a/Terminal.Gui/Views/TreeView/TreeView.cs
+++ b/Terminal.Gui/Views/TreeView/TreeView.cs
@@ -1002,7 +1002,7 @@ public class TreeView : View, ITreeView where T : class
public bool IsSelected (T model) { return Equals (SelectedObject, model) || (MultiSelect && multiSelectedRegions.Any (s => s.Contains (model))); }
///
- protected internal override bool OnMouseEvent (MouseEvent me)
+ protected internal override bool OnMouseEvent (MouseEvent me)
{
// If it is not an event we care about
if (!me.Flags.HasFlag (MouseFlags.Button1Clicked)
@@ -1239,7 +1239,7 @@ public class TreeView : View, ITreeView where T : class
}
/// Positions the cursor at the start of the selected objects line (if visible).
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
if (CanFocus && HasFocus && Visible && SelectedObject is { })
{
@@ -1250,16 +1250,10 @@ public class TreeView : View, ITreeView where T : class
if (idx - ScrollOffsetVertical >= 0 && idx - ScrollOffsetVertical < Viewport.Height)
{
Move (0, idx - ScrollOffsetVertical);
- }
- else
- {
- base.PositionCursor ();
+ return new Point (0, idx - ScrollOffsetVertical);
}
}
- else
- {
- base.PositionCursor ();
- }
+ return base.PositionCursor ();
}
///
diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs
index b0d7a03ca..96287152e 100644
--- a/UICatalog/Scenarios/CharacterMap.cs
+++ b/UICatalog/Scenarios/CharacterMap.cs
@@ -824,7 +824,7 @@ internal class CharMap : View
return base.OnLeave (view);
}
- public override void PositionCursor ()
+ public override Point? PositionCursor ()
{
if (HasFocus
&& Cursor.X >= RowLabelWidth
@@ -839,6 +839,8 @@ internal class CharMap : View
{
Driver.SetCursorVisibility (CursorVisibility.Invisible);
}
+
+ return Cursor;
}
public event EventHandler SelectedCodePointChanged;
@@ -870,16 +872,11 @@ internal class CharMap : View
return;
}
- args.Handled = true;
-
if (me.Y == 0)
{
me.Y = Cursor.Y;
}
- if (me.Y > 0)
- { }
-
if (me.X < RowLabelWidth || me.X > RowLabelWidth + 16 * COLUMN_WIDTH - 1)
{
me.X = Cursor.X;
@@ -905,10 +902,16 @@ internal class CharMap : View
Hover?.Invoke (this, new (val, null));
}
+ if (!HasFocus && CanFocus)
+ {
+ SetFocus ();
+ }
+
+ args.Handled = true;
+
if (me.Flags == MouseFlags.Button1Clicked)
{
SelectedCodePoint = val;
-
return;
}
diff --git a/UnitTests/View/Layout/ViewportTests.cs b/UnitTests/View/Layout/ViewportTests.cs
index 8d9035359..cd576c210 100644
--- a/UnitTests/View/Layout/ViewportTests.cs
+++ b/UnitTests/View/Layout/ViewportTests.cs
@@ -359,4 +359,97 @@ public class ViewportTests (ITestOutputHelper output)
Assert.Equal (view.Viewport.Size, view.ContentSize);
}
+ //[Theory]
+ //[InlineData (0, 0, true)]
+ //[InlineData (-1, 0, true)]
+ //[InlineData (0, -1, true)]
+ //[InlineData (-1, -1, true)]
+ //[InlineData (-2, -2, true)]
+ //[InlineData (-3, -3, true)]
+ //[InlineData (-4, -4, true)]
+ //[InlineData (-5, -4, false)]
+ //[InlineData (-4, -5, false)]
+ //[InlineData (-5, -5, false)]
+
+ //[InlineData (1, 1, true)]
+ //[InlineData (2, 2, true)]
+ //[InlineData (3, 3, true)]
+ //[InlineData (4, 4, true)]
+ //[InlineData (5, 4, false)]
+ //[InlineData (4, 5, false)]
+ //[InlineData (5, 5, false)]
+ //public void IsVisibleInSuperView_No_Driver_No_SuperView (int x, int y, bool expected)
+ //{
+ // var view = new View { X = 1, Y = 1, Width = 5, Height = 5 };
+ // Assert.True (view.IsVisibleInSuperView (x, y) == expected);
+ //}
+
+ //[Theory]
+ //[InlineData (0, 0, true)]
+ //[InlineData (-1, 0, true)]
+ //[InlineData (0, -1, true)]
+ //[InlineData (-1, -1, true)]
+ //[InlineData (-2, -2, true)]
+ //[InlineData (-3, -3, true)]
+ //[InlineData (-4, -4, true)]
+ //[InlineData (-5, -4, true)]
+ //[InlineData (-4, -5, true)]
+ //[InlineData (-5, -5, true)]
+ //[InlineData (-6, -5, false)]
+ //[InlineData (-5, -6, false)]
+ //[InlineData (-6, -6, false)]
+
+ //[InlineData (1, 1, true)]
+ //[InlineData (2, 2, true)]
+ //[InlineData (3, 3, true)]
+ //[InlineData (4, 4, true)]
+ //[InlineData (5, 4, true)]
+ //[InlineData (4, 5, true)]
+ //[InlineData (5, 5, true)]
+ //[InlineData (6, 5, true)]
+ //[InlineData (6, 6, true)]
+ //[InlineData (7, 7, true)]
+ //[InlineData (8, 8, true)]
+ //[InlineData (9, 8, false)]
+ //[InlineData (8, 9, false)]
+ //[InlineData (9, 9, false)]
+ //public void IsVisibleInSuperView_No_Driver_With_SuperView (int x, int y, bool expected)
+ //{
+ // var view = new View { X = 1, Y = 1, Width = 5, Height = 5 };
+ // var top = new Toplevel { Width = 10, Height = 10 };
+ // top.Add (view);
+
+ // Assert.True (view.IsVisibleInSuperView (x, y) == expected);
+ //}
+
+ //[SetupFakeDriver]
+ //[Theory]
+ //[InlineData (0, 0, true)]
+ //[InlineData (-1, 0, false)]
+ //[InlineData (0, -1, false)]
+ //[InlineData (-1, -1, false)]
+
+ //[InlineData (1, 0, true)]
+ //[InlineData (0, 1, true)]
+ //[InlineData (1, 1, true)]
+ //[InlineData (2, 2, true)]
+ //[InlineData (3, 3, true)]
+ //[InlineData (4, 4, true)]
+ //[InlineData (5, 4, false)]
+ //[InlineData (4, 5, false)]
+ //[InlineData (5, 5, false)]
+ //public void IsVisibleInSuperView_With_Driver (int x, int y, bool expected)
+ //{
+ // ((FakeDriver)Application.Driver).SetBufferSize (10, 10);
+
+ // var view = new View { X = 1, Y = 1, Width = 5, Height = 5 };
+ // var top = new Toplevel ();
+ // top.Add (view);
+ // Application.Begin (top);
+
+ // Assert.True (view.IsVisibleInSuperView (x, y) == expected);
+
+ // top.Dispose ();
+ // Application.Shutdown ();
+ //}
}
diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs
index b0c13a98c..946398315 100644
--- a/UnitTests/Views/TextViewTests.cs
+++ b/UnitTests/Views/TextViewTests.cs
@@ -1065,34 +1065,44 @@ This is the second line.
var tv = new TextView { Width = 10, Height = 10 };
tv.Text = text;
+ var top = new Toplevel ();
+ top.Add (tv);
+ Application.Begin (top);
Assert.Equal (0, tv.LeftColumn);
- tv.PositionCursor ();
+ Assert.Equal (Point.Empty, tv.CursorPosition);
+ Application.PositionCursor (top);
Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility);
for (var i = 0; i < 12; i++)
{
tv.NewMouseEvent (new MouseEvent { Flags = MouseFlags.WheeledRight });
- tv.PositionCursor ();
Assert.Equal (Math.Min (i + 1, 11), tv.LeftColumn);
- Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility);
+ Application.PositionCursor (top);
+ Application.Driver.GetCursorVisibility (out CursorVisibility cursorVisibility);
+ Assert.Equal (CursorVisibility.Invisible, cursorVisibility);
}
for (var i = 11; i > 0; i--)
{
tv.NewMouseEvent (new MouseEvent { Flags = MouseFlags.WheeledLeft });
- tv.PositionCursor ();
Assert.Equal (i - 1, tv.LeftColumn);
+ Application.PositionCursor (top);
+ Application.Driver.GetCursorVisibility (out CursorVisibility cursorVisibility);
+
if (i - 1 == 0)
{
- Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility);
+ Assert.Equal (CursorVisibility.Default, cursorVisibility);
}
else
{
- Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility);
+ Assert.Equal (CursorVisibility.Invisible, cursorVisibility);
}
}
+
+ top.Dispose ();
+ Application.Shutdown ();
}
[Fact]
@@ -1108,34 +1118,44 @@ This is the second line.
var tv = new TextView { Width = 10, Height = 10 };
tv.Text = text;
+ var top = new Toplevel ();
+ top.Add (tv);
+ Application.Begin (top);
Assert.Equal (0, tv.TopRow);
- tv.PositionCursor ();
+ Application.PositionCursor (top);
Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility);
for (var i = 0; i < 12; i++)
{
tv.NewMouseEvent (new MouseEvent { Flags = MouseFlags.WheeledDown });
- tv.PositionCursor ();
+ Application.PositionCursor (top);
Assert.Equal (i + 1, tv.TopRow);
- Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility);
+ Application.Driver.GetCursorVisibility (out CursorVisibility cursorVisibility);
+ Assert.Equal (CursorVisibility.Invisible, cursorVisibility);
}
for (var i = 12; i > 0; i--)
{
tv.NewMouseEvent (new MouseEvent { Flags = MouseFlags.WheeledUp });
- tv.PositionCursor ();
+ Application.PositionCursor (top);
Assert.Equal (i - 1, tv.TopRow);
+ Application.PositionCursor (top);
+ Application.Driver.GetCursorVisibility (out CursorVisibility cursorVisibility);
+
if (i - 1 == 0)
{
- Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility);
+ Assert.Equal (CursorVisibility.Default, cursorVisibility);
}
else
{
- Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility);
+ Assert.Equal (CursorVisibility.Invisible, cursorVisibility);
}
}
+
+ top.Dispose ();
+ Application.Shutdown ();
}
[Fact]
diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs
index 7ba9a4611..84940b0a0 100644
--- a/UnitTests/Views/ToplevelTests.cs
+++ b/UnitTests/Views/ToplevelTests.cs
@@ -1261,12 +1261,13 @@ public class ToplevelTests
Application.Begin (top);
Assert.True (tf.HasFocus);
+ Application.PositionCursor (top);
Application.Driver.GetCursorVisibility (out CursorVisibility cursor);
Assert.Equal (CursorVisibility.Default, cursor);
view.Enabled = false;
Assert.False (tf.HasFocus);
- Application.Refresh ();
+ Application.PositionCursor (top);
Application.Driver.GetCursorVisibility (out cursor);
Assert.Equal (CursorVisibility.Invisible, cursor);
}
diff --git a/docfx/docs/cursor.md b/docfx/docs/cursor.md
new file mode 100644
index 000000000..917f8015d
--- /dev/null
+++ b/docfx/docs/cursor.md
@@ -0,0 +1,162 @@
+# Proposed Design for a modern Cursor system in v2
+
+See end for list of issues this design addresses.
+
+## Tenets for Cursor Support (Unless you know better ones...)
+
+1. **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 keybaord 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.
+
+2. **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.
+
+## Lexicon & Taxonomy
+
+- Cursor - A visual indicator to the user where keyboard input will have an impact. There is one Cursor per terminal sesssion.
+- Cursor Location - The top-left corner of the Cursor. In text entry scenarios, new text will be inserted to the left/top of the Cursor Location.
+- Cursor Size - The width and height of the cursor. Currently the size is limited to 1x1.
+- Cursor Style - How the cursor renders. Some terminals support various cursor styles such as Block and Underline.
+- Cursor Visibilty - Whether the cursor is visible to the user or not. NOTE: Some ConsoleDrivers overload Cursor Style and Cursor Visibility, making "invisible" a style. Terminal.Gui HIDES this from developers and changing the visibilty of the cursor does NOT change the style.
+- Caret - Visual indicator that where text entry will occur.
+- Selection - A visual indicator to the user that something is selected. It is common for the Selection and Cursor to be the same. It is also common for the Selection and Cursor to be distinct. In a `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.
+
+## Requirements
+
+- No flickering. The Cursor should blink/pulse at the rate dictated by the terminal. Typing, moving the mouse, view layout, etc... should not caue the cursor to flicker.
+- By default, the Cursor should not be visible. A View or View subclass should have to do anything (this is already the case) to keep the Cursor invisible.
+- Views that just want to show the cursor at a particular location in the Viewport should only have to:
+ - Optionally, declare a desired Cursor Style. Set `Application.CursorStyle`.
+ - Indicate the Cursor Locaiton when internal state dictates the location has changed (debatable if this should be in content or viewport-relative coords). Just set `this.CursorPosition`.
+ - To hide the cursor, simply set `this.CursorPostion` to `null`.
+- The Cursor should only be visible in Views where
+ - `Enabled == true`
+ - `Visible == true`
+ - `CanFocus == true`
+ - `this == SuperView.MostFocused`
+- If a `ConsoleDriver` supports Cursor Styles other than Default, they should be supported per-application (NOT View).
+- Ensuring the cursor is visible or not should be handled by `Application`, not `View`.
+- General V2 Requirement: View sub-class code should NEVER call a `Driver.` API. Only `Application` and the `View` base class should call `ConsoleDriver` APIs; before we ship v2, all `ConsoleDriver` APIs will be made `internal`.
+
+## Design
+
+### `View` Focus Changes
+
+It 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`.
+
+* Remove `View.MostFocused`
+* Change all references to access `Application.MostFocusedView` (see `Application` below)
+* Find all instances of `view._hasFocus = ` and change them to use `SetHasFocus` (today, anyplace that sets `_hasFocus` is a BUG!!).
+* Change `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 Changes
+* Add `public Point? CursorPosition`
+ - Backed with `private Point? _cursorPosition`
+ - If `!HasValue` the cursor is not visible
+ - If `HasValue` the cursor is visible at the Point.
+ - On set, if `value != _cursorPosition`, call `OnCursorPositionChanged()`
+* Add `public event EventHandler? CursorPositionChanged`
+* Add `internal void OnCursorPositionChanged(LocationChangedEventArgs a)`
+ * Not virtual
+ * Fires `CursorPositionChanged`
+
+### `ConsoleDriver`s
+
+* Remove `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; }`
+ - Backed with `private int _cursorStyle`
+ - On set, calls `OnCursorStyleChanged()`
+ * Add `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`:
+ - If `!HasValue` the cursor is not visible - does whatever is needed to make the cursor invisible.
+ - If `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!
+
+### `Application`
+
+*
+
+* Add `internal static View FocusedView {get; private set;}`
+ - Backed by `private static _focusedView`
+ - On set,
+ - if `value != _focusedView`
+ - Unsubscribe from `_focusedView.CursorPositionChanged`
+ - Subscribe to `value.CursorPositionChanged += CursorPositionChanged`
+ - `_focusedView = value`
+ - Call `UpdateCursor`
+
+* Add `internal bool CursorPositionChanged (object sender, LocationChangedEventArgs a)`
+
+ Called when:
+
+ - `FocusedView`
+ - Has changed to another View (should cover `FocusedView.Visible/Enable` changes)
+ - Has changed layout -
+ - Has changeed it's `CursorPosition`
+ - `CursorStyle` has changed
+
+ Does:
+
+ - If `FocusedView is {}` and `FocusedView.CursorPosition` is visible (e.g. w/in `FocusedView.SuperView.Viewport`)
+ - Does `Driver.CursorPosition = ToScreen(FocusedView.CursorPosition)`
+ - Else
+ - Makes driver cursor invisible with `Driver.CursorPosition = null`
+
+* Add `public static int CursorStyle {get; internal set; }`
+ - Backed with `private static int _cursorStyle
+ - If `value != _cursorStyle`
+ - Calls `ConsoleDriver.CursorStyle = _cursorStyle`
+ - Calls `UpdateCursor`
+
+* Add `public (int Id, string StyleName) [] GetCursorStyles()`
+ - Calls through to `ConsoleDriver.GetCursorStyles()`
+
+
+
+# Issues with Current Design
+
+## `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.
+
+## The actual cursor position RARELY changes (relative to `Mainloop.Iteration`).
+
+Derived from abo`ve, the current design means we need to call `View.PositionCursor` every iteration. For some views this is a low-cost operation. For others it involves a lot of math.
+
+This is just stupid.
+
+## Flicker
+
+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 correctly
+
+Should a view call `base.PositionCursor`? If so, before or after doing stuff?
+
+## Setting cursor visibility in `OnEnter` actually makes no sense
+
+First, 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.