Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #1948. Get unwrapped cursor position when word wrap is enabled on TextView. #1949

Merged
merged 1 commit into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Terminal.Gui/Views/TextView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,11 @@ public class TextView : View {
/// </summary>
public event Action TextChanged;

/// <summary>
/// Invoked with the unwrapped <see cref="CursorPosition"/>.
/// </summary>
public event Action<Point> UnwrappedCursorPosition;

/// <summary>
/// Provides autocomplete context menu based on suggestions at the current cursor
/// position. Populate <see cref="Autocomplete.AllSuggestions"/> to enable this feature
Expand Down Expand Up @@ -2320,6 +2325,20 @@ void UpdateWrapModel ([CallerMemberName] string caller = null)
throw new InvalidOperationException ($"WordWrap settings was changed after the {currentCaller} call.");
}

/// <summary>
/// Invoke the <see cref="UnwrappedCursorPosition"/> event with the unwrapped <see cref="CursorPosition"/>.
/// </summary>
public virtual void OnUnwrappedCursorPosition ()
{
var row = currentRow;
var col = currentColumn;
if (wordWrap) {
row = wrapManager.GetModelLineFromWrappedLines (currentRow);
col = wrapManager.GetModelColFromWrappedLines (currentRow, currentColumn);
}
UnwrappedCursorPosition?.Invoke (new Point (col, row));
}

///<inheritdoc/>
public override void Redraw (Rect bounds)
{
Expand Down Expand Up @@ -2617,6 +2636,8 @@ void Adjust ()
} else {
PositionCursor ();
}

OnUnwrappedCursorPosition ();
}

(int width, int height) OffSetBackground ()
Expand Down
61 changes: 34 additions & 27 deletions UICatalog/Scenarios/Editor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public class Editor : Scenario {
private string _textToReplace;
private bool _matchCase;
private bool _matchWholeWord;
private Window winDialog;
private Window _winDialog;
private TabView _tabView;
private MenuItem miForceMinimumPosToZero;
private bool forceMinimumPosToZero = true;
private readonly List<CultureInfo> cultureInfos = Application.SupportedCultures;
private MenuItem _miForceMinimumPosToZero;
private bool _forceMinimumPosToZero = true;
private readonly List<CultureInfo> _cultureInfos = Application.SupportedCultures;

public override void Init (Toplevel top, ColorScheme colorScheme)
{
Expand Down Expand Up @@ -60,6 +60,12 @@ public override void Init (Toplevel top, ColorScheme colorScheme)

CreateDemoFile (_fileName);

var siCursorPosition = new StatusItem (Key.Null, "", null);

_textView.UnwrappedCursorPosition += (e) => {
siCursorPosition.Title = $"Ln {e.Y + 1}, Col {e.X + 1}";
};

LoadFile ();

Win.Add (_textView);
Expand Down Expand Up @@ -103,16 +109,17 @@ public override void Init (Toplevel top, ColorScheme colorScheme)
CreateVisibleChecked ()
}),
new MenuBarItem ("Conte_xtMenu", new MenuItem [] {
miForceMinimumPosToZero = new MenuItem ("ForceMinimumPosTo_Zero", "", () => {
miForceMinimumPosToZero.Checked = forceMinimumPosToZero = !forceMinimumPosToZero;
_textView.ContextMenu.ForceMinimumPosToZero = forceMinimumPosToZero;
}) { CheckType = MenuItemCheckStyle.Checked, Checked = forceMinimumPosToZero },
_miForceMinimumPosToZero = new MenuItem ("ForceMinimumPosTo_Zero", "", () => {
_miForceMinimumPosToZero.Checked = _forceMinimumPosToZero = !_forceMinimumPosToZero;
_textView.ContextMenu.ForceMinimumPosToZero = _forceMinimumPosToZero;
}) { CheckType = MenuItemCheckStyle.Checked, Checked = _forceMinimumPosToZero },
new MenuBarItem ("_Languages", GetSupportedCultures ())
})
});
Top.Add (menu);

var statusBar = new StatusBar (new StatusItem [] {
siCursorPosition,
new StatusItem(Key.F2, "~F2~ Open", () => Open()),
new StatusItem(Key.F3, "~F3~ Save", () => Save()),
new StatusItem(Key.F4, "~F4~ Save As", () => SaveAs()),
Expand Down Expand Up @@ -168,20 +175,20 @@ public override void Init (Toplevel top, ColorScheme colorScheme)

Win.KeyPress += (e) => {
var keys = ShortcutHelper.GetModifiersKey (e.KeyEvent);
if (winDialog != null && (e.KeyEvent.Key == Key.Esc
if (_winDialog != null && (e.KeyEvent.Key == Key.Esc
|| e.KeyEvent.Key == (Key.Q | Key.CtrlMask))) {
DisposeWinDialog ();
} else if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) {
Quit ();
e.Handled = true;
} else if (winDialog != null && keys == (Key.Tab | Key.CtrlMask)) {
} else if (_winDialog != null && keys == (Key.Tab | Key.CtrlMask)) {
if (_tabView.SelectedTab == _tabView.Tabs.ElementAt (_tabView.Tabs.Count - 1)) {
_tabView.SelectedTab = _tabView.Tabs.ElementAt (0);
} else {
_tabView.SwitchTabBy (1);
}
e.Handled = true;
} else if (winDialog != null && keys == (Key.Tab | Key.CtrlMask | Key.ShiftMask)) {
} else if (_winDialog != null && keys == (Key.Tab | Key.CtrlMask | Key.ShiftMask)) {
if (_tabView.SelectedTab == _tabView.Tabs.ElementAt (0)) {
_tabView.SelectedTab = _tabView.Tabs.ElementAt (_tabView.Tabs.Count - 1);
} else {
Expand All @@ -196,9 +203,9 @@ public override void Init (Toplevel top, ColorScheme colorScheme)

private void DisposeWinDialog ()
{
winDialog.Dispose ();
Win.Remove (winDialog);
winDialog = null;
_winDialog.Dispose ();
Win.Remove (_winDialog);
_winDialog = null;
}

public override void Setup ()
Expand Down Expand Up @@ -276,7 +283,7 @@ private void ContinueFind (bool next = true, bool replace = false)
Find ();
return;
} else if (replace && (string.IsNullOrEmpty (_textToFind)
|| (winDialog == null && string.IsNullOrEmpty (_textToReplace)))) {
|| (_winDialog == null && string.IsNullOrEmpty (_textToReplace)))) {
Replace ();
return;
}
Expand Down Expand Up @@ -323,7 +330,7 @@ private void ReplacePrevious ()

private void ReplaceAll ()
{
if (string.IsNullOrEmpty (_textToFind) || (string.IsNullOrEmpty (_textToReplace) && winDialog == null)) {
if (string.IsNullOrEmpty (_textToFind) || (string.IsNullOrEmpty (_textToReplace) && _winDialog == null)) {
Replace ();
return;
}
Expand Down Expand Up @@ -468,7 +475,7 @@ private MenuItem [] GetSupportedCultures ()
List<MenuItem> supportedCultures = new List<MenuItem> ();
var index = -1;

foreach (var c in cultureInfos) {
foreach (var c in _cultureInfos) {
var culture = new MenuItem {
CheckType = MenuItemCheckStyle.Checked
};
Expand Down Expand Up @@ -714,17 +721,17 @@ void SetCursor (CursorVisibility visibility)

private void CreateFindReplace (bool isFind = true)
{
if (winDialog != null) {
winDialog.SetFocus ();
if (_winDialog != null) {
_winDialog.SetFocus ();
return;
}

winDialog = new Window (isFind ? "Find" : "Replace") {
_winDialog = new Window (isFind ? "Find" : "Replace") {
X = Win.Bounds.Width / 2 - 30,
Y = Win.Bounds.Height / 2 - 10,
ColorScheme = Colors.TopLevel
};
winDialog.Border.Effect3D = true;
_winDialog.Border.Effect3D = true;

_tabView = new TabView () {
X = 0,
Expand All @@ -737,15 +744,15 @@ private void CreateFindReplace (bool isFind = true)
var replace = ReplaceTab ();
_tabView.AddTab (new TabView.Tab ("Replace", replace), !isFind);
_tabView.SelectedTabChanged += (s, e) => _tabView.SelectedTab.View.FocusFirst ();
winDialog.Add (_tabView);
_winDialog.Add (_tabView);

Win.Add (winDialog);
Win.Add (_winDialog);

winDialog.Width = replace.Width + 4;
winDialog.Height = replace.Height + 4;
_winDialog.Width = replace.Width + 4;
_winDialog.Height = replace.Height + 4;

winDialog.SuperView.BringSubviewToFront (winDialog);
winDialog.SetFocus ();
_winDialog.SuperView.BringSubviewToFront (_winDialog);
_winDialog.SetFocus ();
}

private void SetFindText ()
Expand Down
91 changes: 91 additions & 0 deletions UnitTests/TextViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5934,5 +5934,96 @@ public void Mouse_Button_Shift_Preserves_Selection ()
Assert.True (_textView.Selecting);
Assert.Equal ("", _textView.SelectedText);
}

[Fact, AutoInitShutdown]
public void UnwrappedCursorPosition_Event ()
{
var cp = Point.Empty;
var tv = new TextView () {
Width = Dim.Fill (),
Height = Dim.Fill (),
Text = "This is the first line.\nThis is the second line.\n"
};
tv.UnwrappedCursorPosition += (e) => {
cp = e;
};
Application.Top.Add (tv);
Application.Begin (Application.Top);

Assert.False (tv.WordWrap);
Assert.Equal (Point.Empty, tv.CursorPosition);
Assert.Equal (Point.Empty, cp);
GraphViewTests.AssertDriverContentsWithFrameAre (@"
This is the first line.
This is the second line.
", output);

tv.WordWrap = true;
tv.CursorPosition = new Point (12, 0);
tv.Redraw (tv.Bounds);
Assert.Equal (new Point (12, 0), tv.CursorPosition);
Assert.Equal (new Point (12, 0), cp);
GraphViewTests.AssertDriverContentsWithFrameAre (@"
This is the first line.
This is the second line.
", output);

((FakeDriver)Application.Driver).SetBufferSize (6, 25);
tv.Redraw (tv.Bounds);
Assert.Equal (new Point (4, 2), tv.CursorPosition);
Assert.Equal (new Point (12, 0), cp);
GraphViewTests.AssertDriverContentsWithFrameAre (@"
This
is
the
first

line.
This
is
the
secon
d
line.
", output);

Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
tv.Redraw (tv.Bounds);
Assert.Equal (new Point (0, 3), tv.CursorPosition);
Assert.Equal (new Point (12, 0), cp);
GraphViewTests.AssertDriverContentsWithFrameAre (@"
This
is
the
first

line.
This
is
the
secon
d
line.
", output);

Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
tv.Redraw (tv.Bounds);
Assert.Equal (new Point (1, 3), tv.CursorPosition);
Assert.Equal (new Point (13, 0), cp);
GraphViewTests.AssertDriverContentsWithFrameAre (@"
This
is
the
first

line.
This
is
the
secon
d
line.
", output);
}
}
}