Merge branch 'develop' into fixes_2336_ignore_border

This commit is contained in:
Tig
2023-02-09 22:08:52 +09:00
committed by GitHub
5 changed files with 149 additions and 31 deletions

View File

@@ -324,6 +324,7 @@ namespace Terminal.Gui {
if (IsWordChar ((char)kb.Key)) {
Visible = true;
closed = false;
return false;
}
if (kb.Key == Reopen) {
@@ -332,6 +333,9 @@ namespace Terminal.Gui {
if (closed || Suggestions.Count == 0) {
Visible = false;
if (!closed) {
Close ();
}
return false;
}
@@ -345,6 +349,17 @@ namespace Terminal.Gui {
return true;
}
if (kb.Key == Key.CursorLeft || kb.Key == Key.CursorRight) {
GenerateSuggestions (kb.Key == Key.CursorLeft ? -1 : 1);
if (Suggestions.Count == 0) {
Visible = false;
if (!closed) {
Close ();
}
}
return false;
}
if (kb.Key == SelectionKey) {
return Select ();
}
@@ -368,6 +383,9 @@ namespace Terminal.Gui {
public virtual bool MouseEvent (MouseEvent me, bool fromHost = false)
{
if (fromHost) {
if (!Visible) {
return false;
}
GenerateSuggestions ();
if (Visible && Suggestions.Count == 0) {
Visible = false;
@@ -444,7 +462,8 @@ namespace Terminal.Gui {
/// Populates <see cref="Suggestions"/> with all strings in <see cref="AllSuggestions"/> that
/// match with the current cursor position/text in the <see cref="HostControl"/>
/// </summary>
public virtual void GenerateSuggestions ()
/// <param name="columnOffset">The column offset.</param>
public virtual void GenerateSuggestions (int columnOffset = 0)
{
// if there is nothing to pick from
if (AllSuggestions.Count == 0) {
@@ -452,7 +471,7 @@ namespace Terminal.Gui {
return;
}
var currentWord = GetCurrentWord ();
var currentWord = GetCurrentWord (columnOffset);
if (string.IsNullOrWhiteSpace (currentWord)) {
ClearSuggestions ();
@@ -524,11 +543,12 @@ namespace Terminal.Gui {
/// <summary>
/// Returns the currently selected word from the <see cref="HostControl"/>.
/// <para>
/// When overriding this method views can make use of <see cref="IdxToWord(List{Rune}, int)"/>
/// When overriding this method views can make use of <see cref="IdxToWord(List{Rune}, int, int)"/>
/// </para>
/// </summary>
/// <param name="columnOffset">The column offset.</param>
/// <returns></returns>
protected abstract string GetCurrentWord ();
protected abstract string GetCurrentWord (int columnOffset = 0);
/// <summary>
/// <para>
@@ -536,37 +556,40 @@ namespace Terminal.Gui {
/// or null. Also returns null if the <paramref name="idx"/> is positioned in the middle of a word.
/// </para>
///
/// <para>Use this method to determine whether autocomplete should be shown when the cursor is at
/// a given point in a line and to get the word from which suggestions should be generated.</para>
/// <para>
/// Use this method to determine whether autocomplete should be shown when the cursor is at
/// a given point in a line and to get the word from which suggestions should be generated.
/// Use the <paramref name="columnOffset"/> to indicate if search the word at left (negative),
/// at right (positive) or at the current column (zero) which is the default.
/// </para>
/// </summary>
/// <param name="line"></param>
/// <param name="idx"></param>
/// <param name="columnOffset"></param>
/// <returns></returns>
protected virtual string IdxToWord (List<Rune> line, int idx)
protected virtual string IdxToWord (List<Rune> line, int idx, int columnOffset = 0)
{
StringBuilder sb = new StringBuilder ();
var endIdx = idx;
// do not generate suggestions if the cursor is positioned in the middle of a word
bool areMidWord;
if (idx == line.Count) {
// the cursor positioned at the very end of the line
areMidWord = false;
} else {
// we are in the middle of a word if the cursor is over a letter/number
areMidWord = IsWordChar (line [idx]);
// get the ending word index
while (endIdx < line.Count) {
if (IsWordChar (line [endIdx])) {
endIdx++;
} else {
break;
}
}
// if we are in the middle of a word then there is no way to autocomplete that word
if (areMidWord) {
// It isn't a word char then there is no way to autocomplete that word
if (endIdx == idx && columnOffset != 0) {
return null;
}
// we are at the end of a word. Work out what has been typed so far
while (idx-- > 0) {
if (IsWordChar (line [idx])) {
sb.Insert (0, (char)line [idx]);
while (endIdx-- > 0) {
if (IsWordChar (line [endIdx])) {
sb.Insert (0, (char)line [endIdx]);
} else {
break;
}

View File

@@ -109,6 +109,7 @@ namespace Terminal.Gui {
/// Populates <see cref="Suggestions"/> with all strings in <see cref="AllSuggestions"/> that
/// match with the current cursor position/text in the <see cref="HostControl"/>.
/// </summary>
void GenerateSuggestions ();
/// <param name="columnOffset">The column offset. Current (zero - default), left (negative), right (positive).</param>
void GenerateSuggestions (int columnOffset = 0);
}
}

View File

@@ -1346,12 +1346,12 @@ namespace Terminal.Gui {
}
/// <inheritdoc/>
protected override string GetCurrentWord ()
protected override string GetCurrentWord (int columnOffset = 0)
{
var host = (TextField)HostControl;
var currentLine = host.Text.ToRuneList ();
var cursorPosition = Math.Min (host.CursorPosition, currentLine.Count);
return IdxToWord (currentLine, cursorPosition);
var cursorPosition = Math.Min (host.CursorPosition + columnOffset, currentLine.Count);
return IdxToWord (currentLine, cursorPosition, columnOffset);
}
/// <inheritdoc/>

View File

@@ -2447,6 +2447,10 @@ namespace Terminal.Gui {
PositionCursor ();
if (clickWithSelecting) {
clickWithSelecting = false;
return;
}
if (SelectedLength > 0)
return;
@@ -2677,8 +2681,10 @@ namespace Terminal.Gui {
need = true;
} else if ((wordWrap && leftColumn > 0) || (dSize.size + RightOffset < Frame.Width + offB.width
&& tSize.size + RightOffset < Frame.Width + offB.width)) {
leftColumn = 0;
need = true;
if (leftColumn > 0) {
leftColumn = 0;
need = true;
}
}
if (currentRow < topRow) {
@@ -4279,6 +4285,7 @@ namespace Terminal.Gui {
}
bool isButtonShift;
bool clickWithSelecting;
///<inheritdoc/>
public override bool MouseEvent (MouseEvent ev)
@@ -4372,6 +4379,7 @@ namespace Terminal.Gui {
columnTrack = currentColumn;
} else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed)) {
if (shiftSelecting) {
clickWithSelecting = true;
StopSelecting ();
}
ProcessMouseClick (ev, out _);
@@ -4475,12 +4483,12 @@ namespace Terminal.Gui {
public class TextViewAutocomplete : Autocomplete {
///<inheritdoc/>
protected override string GetCurrentWord ()
protected override string GetCurrentWord (int columnOffset = 0)
{
var host = (TextView)HostControl;
var currentLine = host.GetCurrentLine ();
var cursorPosition = Math.Min (host.CurrentColumn, currentLine.Count);
return IdxToWord (currentLine, cursorPosition);
var cursorPosition = Math.Min (host.CurrentColumn + columnOffset, currentLine.Count);
return IdxToWord (currentLine, cursorPosition, columnOffset);
}
/// <inheritdoc/>

View File

@@ -6,9 +6,16 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Terminal.Gui;
using Xunit;
using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests {
public class AutocompleteTests {
readonly ITestOutputHelper output;
public AutocompleteTests (ITestOutputHelper output)
{
this.output = output;
}
[Fact]
public void Test_GenerateSuggestions_Simple ()
@@ -151,5 +158,84 @@ namespace Terminal.Gui.ViewTests {
Assert.Empty (tv.Autocomplete.Suggestions);
Assert.Equal (3, tv.Autocomplete.AllSuggestions.Count);
}
[Fact, AutoInitShutdown]
public void CursorLeft_CursorRight_Mouse_Button_Pressed_Does_Not_Show_Popup ()
{
var tv = new TextView () {
Width = 50,
Height = 5,
Text = "This a long line and against TextView."
};
tv.Autocomplete.AllSuggestions = Regex.Matches (tv.Text.ToString (), "\\w+")
.Select (s => s.Value)
.Distinct ().ToList ();
var top = Application.Top;
top.Add (tv);
Application.Begin (top);
for (int i = 0; i < 7; i++) {
Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This a long line and against TextView.", output);
}
Assert.True (tv.MouseEvent (new MouseEvent () {
X = 6,
Y = 0,
Flags = MouseFlags.Button1Pressed
}));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This a long line and against TextView.", output);
Assert.True (tv.ProcessKey (new KeyEvent (Key.g, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This ag long line and against TextView.
against ", output);
Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This ag long line and against TextView.
against ", output);
Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This ag long line and against TextView.
against ", output);
Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This ag long line and against TextView.", output);
for (int i = 0; i < 3; i++) {
Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This ag long line and against TextView.", output);
}
Assert.True (tv.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This a long line and against TextView.", output);
Assert.True (tv.ProcessKey (new KeyEvent (Key.n, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This an long line and against TextView.
and ", output);
Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
This an long line and against TextView.", output);
}
}
}