From 6a6c210d9b3e66e4a4bcbe4e583f25be98d3945a Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 1 Oct 2024 21:09:17 +0100 Subject: [PATCH 01/24] Fixes #3771. TextView doesn't consider no-printable rune in draw and cursor position. --- Terminal.Gui/Views/TextView.cs | 7 +++++++ UnitTests/Views/TextViewTests.cs | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index b81877b531..78f3bea18c 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3591,6 +3591,8 @@ public override void OnDrawContent (Rectangle viewport) else { AddRune (col, row, rune); + // Ensures that cols less than 0 to be 1 because it will be converted to a printable rune + cols = Math.Max (cols, 1); } if (!TextModel.SetCol (ref col, viewport.Right, cols)) @@ -3802,6 +3804,11 @@ public void Paste () { cols += TabWidth + 1; } + else + { + // Ensures that cols less than 0 to be 1 because it will be converted to a printable rune + cols = Math.Max (cols, 1); + } if (!TextModel.SetCol (ref col, Viewport.Width, cols)) { diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 2ab55e7aea..cdbde04480 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -8660,4 +8660,18 @@ public void Autocomplete_Visible_False_By_Default () Assert.True (t.Visible); Assert.False (t.Autocomplete.Visible); } + + [Fact] + [AutoInitShutdown] + public void Draw_Esc_Rune () + { + var tv = new TextView { Width = 5, Height = 1, Text = "\u001b" }; + tv.BeginInit (); + tv.EndInit (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ("\u241b", _output); + + tv.Dispose (); + } } From 7bf6aaa1adf5b4094932e91c4a53a3ba45ebf52a Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 1 Oct 2024 21:17:24 +0100 Subject: [PATCH 02/24] Add unit test which already works in TextField. --- UnitTests/Views/TextFieldTests.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index a5c6fe4e1e..60a999bff9 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -2047,4 +2047,18 @@ public void Autocomplete_Visible_False_By_Default () Assert.True (t.Visible); Assert.False (t.Autocomplete.Visible); } + + [Fact] + [AutoInitShutdown] + public void Draw_Esc_Rune () + { + var tf = new TextField { Width = 5, Text = "\u001b" }; + tf.BeginInit (); + tf.EndInit (); + tf.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ("\u241b", output); + + tf.Dispose (); + } } From e4d30a1fe1725e9a2ed32a9fd072408e655182b3 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 1 Oct 2024 21:18:24 +0100 Subject: [PATCH 03/24] Replace Frame to Viewport in TextField. --- Terminal.Gui/Views/TextField.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 64e3d0482e..88d2d9348f 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -952,7 +952,7 @@ public override void OnDrawContent (Rectangle viewport) int p = ScrollOffset; var col = 0; - int width = Frame.Width + OffSetBackground (); + int width = Viewport.Width + OffSetBackground (); int tcount = _text.Count; Attribute roc = GetReadOnlyColor (); @@ -1140,10 +1140,10 @@ public virtual void Paste () } int cols = _text [idx].GetColumns (); - TextModel.SetCol (ref col, Frame.Width - 1, cols); + TextModel.SetCol (ref col, Viewport.Width - 1, cols); } - int pos = _cursorPosition - ScrollOffset + Math.Min (Frame.X, 0); + int pos = _cursorPosition - ScrollOffset + Math.Min (Viewport.X, 0); Move (pos, 0); return new Point (pos, 0); } @@ -1225,16 +1225,16 @@ private void Adjust () ScrollOffset = _cursorPosition; need = true; } - else if (Frame.Width > 0 - && (ScrollOffset + _cursorPosition - (Frame.Width + offB) == 0 - || TextModel.DisplaySize (_text, ScrollOffset, _cursorPosition).size >= Frame.Width + offB)) + else if (Viewport.Width > 0 + && (ScrollOffset + _cursorPosition - (Viewport.Width + offB) == 0 + || TextModel.DisplaySize (_text, ScrollOffset, _cursorPosition).size >= Viewport.Width + offB)) { ScrollOffset = Math.Max ( TextModel.CalculateLeftColumn ( _text, ScrollOffset, _cursorPosition, - Frame.Width + offB + Viewport.Width + offB ), 0 ); From 026a20ee7fcec4ffbdb8ae91fe5774eb34073f7c Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 2 Oct 2024 14:14:02 +0100 Subject: [PATCH 04/24] Fixes #3774. TextModel.ToRuneCellList is internal and is better move it to the public RuneCell class. --- .../Views/AutocompleteFilepathContext.cs | 2 +- Terminal.Gui/Views/TextField.cs | 4 +- Terminal.Gui/Views/TextView.cs | 38 +++++++++---------- UnitTests/Text/AutocompleteTests.cs | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Terminal.Gui/Views/AutocompleteFilepathContext.cs b/Terminal.Gui/Views/AutocompleteFilepathContext.cs index f577e554fe..96ce1dfaef 100644 --- a/Terminal.Gui/Views/AutocompleteFilepathContext.cs +++ b/Terminal.Gui/Views/AutocompleteFilepathContext.cs @@ -6,7 +6,7 @@ namespace Terminal.Gui; internal class AutocompleteFilepathContext : AutocompleteContext { public AutocompleteFilepathContext (string currentLine, int cursorPosition, FileDialogState state) - : base (TextModel.ToRuneCellList (currentLine), cursorPosition) + : base (RuneCell.ToRuneCellList (currentLine), cursorPosition) { State = state; } diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 88d2d9348f..aa4322e2e0 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -546,7 +546,7 @@ public string SelectedText if (!Secret && !_historyText.IsFromHistory) { _historyText.Add ( - new List> { TextModel.ToRuneCellList (oldText) }, + new List> { RuneCell.ToRuneCellList (oldText) }, new Point (_cursorPosition, 0) ); @@ -1342,7 +1342,7 @@ private List DeleteSelectedText () private void GenerateSuggestions () { - List currentLine = TextModel.ToRuneCellList (Text); + List currentLine = RuneCell.ToRuneCellList (Text); int cursorPosition = Math.Min (CursorPosition, currentLine.Count); Autocomplete.Context = new AutocompleteContext ( diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 78f3bea18c..5c4a82fd8d 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -42,6 +42,22 @@ public override string ToString () return $"U+{Rune.Value:X4} '{Rune.ToString ()}'; {colorSchemeStr}"; } + + /// Converts the string into a . + /// The string to convert. + /// The to use. + /// + public static List ToRuneCellList (string str, ColorScheme? colorScheme = null) + { + List cells = new (); + + foreach (Rune rune in str.EnumerateRunes ()) + { + cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); + } + + return cells; + } } internal class TextModel @@ -233,22 +249,6 @@ public static List> StringToLinesOfRuneCells (string content, Col return SplitNewLines (cells); } - /// Converts the string into a . - /// The string to convert. - /// The to use. - /// - public static List ToRuneCellList (string str, ColorScheme? colorScheme = null) - { - List cells = new (); - - foreach (Rune rune in str.EnumerateRunes ()) - { - cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); - } - - return cells; - } - public override string ToString () { var sb = new StringBuilder (); @@ -855,7 +855,7 @@ internal static int GetColFromX (List t, int start, int x, int tabWidth = found = true; } - _lines [i] = ToRuneCellList (ReplaceText (x, textToReplace!, matchText, col)); + _lines [i] = RuneCell.ToRuneCellList (ReplaceText (x, textToReplace!, matchText, col)); x = _lines [i]; txt = GetText (x); pos = new (col, i); @@ -1706,7 +1706,7 @@ public List> ToListRune (List textList) foreach (string text in textList) { - runesList.Add (TextModel.ToRuneCellList (text)); + runesList.Add (RuneCell.ToRuneCellList (text)); } return runesList; @@ -3715,7 +3715,7 @@ public void Paste () if (_copyWithoutSelection && contents.FirstOrDefault (x => x == '\n' || x == '\r') == 0) { - List runeList = contents is null ? new () : TextModel.ToRuneCellList (contents); + List runeList = contents is null ? new () : RuneCell.ToRuneCellList (contents); List currentLine = GetCurrentLine (); _historyText.Add (new () { new (currentLine) }, CursorPosition); diff --git a/UnitTests/Text/AutocompleteTests.cs b/UnitTests/Text/AutocompleteTests.cs index c7d907e1a9..1689c70c8d 100644 --- a/UnitTests/Text/AutocompleteTests.cs +++ b/UnitTests/Text/AutocompleteTests.cs @@ -254,7 +254,7 @@ public void Test_GenerateSuggestions_Simple () ac.GenerateSuggestions ( new ( - TextModel.ToRuneCellList (tv.Text), + RuneCell.ToRuneCellList (tv.Text), 2 ) ); From 153a9f2fc2808e42e5683c4182941629d87e380e Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 2 Oct 2024 20:31:34 +0100 Subject: [PATCH 05/24] Trying fix unit test error. --- UnitTests/View/Mouse/MouseTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UnitTests/View/Mouse/MouseTests.cs b/UnitTests/View/Mouse/MouseTests.cs index 28613dadfa..8eadb5bbe6 100644 --- a/UnitTests/View/Mouse/MouseTests.cs +++ b/UnitTests/View/Mouse/MouseTests.cs @@ -344,6 +344,7 @@ public void WantContinuousButtonPressed_True_And_WantMousePositionReports_True_M me.Handled = false; view.Dispose (); + Application.ResetState (ignoreDisposed: true); } [Theory] From 29799f493c5f30c782ecdfab1abdfb4624bd134b Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 3 Oct 2024 15:50:09 +0100 Subject: [PATCH 06/24] Move necessary methods to the RuneCell class per https://github.com/gui-cs/Terminal.Gui/issues/3774#issuecomment-2391190526. --- .../Views/AutocompleteFilepathContext.cs | 2 +- Terminal.Gui/Views/TextField.cs | 10 +- Terminal.Gui/Views/TextView.cs | 217 +++++++++--------- UnitTests/Views/RuneCellTests.cs | 6 +- UnitTests/Views/TextViewTests.cs | 18 +- 5 files changed, 132 insertions(+), 121 deletions(-) diff --git a/Terminal.Gui/Views/AutocompleteFilepathContext.cs b/Terminal.Gui/Views/AutocompleteFilepathContext.cs index 96ce1dfaef..a379f20010 100644 --- a/Terminal.Gui/Views/AutocompleteFilepathContext.cs +++ b/Terminal.Gui/Views/AutocompleteFilepathContext.cs @@ -30,7 +30,7 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context) return Enumerable.Empty (); } - var path = TextModel.ToString (context.CurrentLine); + var path = RuneCell.ToString (context.CurrentLine); int last = path.LastIndexOfAny (FileDialog.Separators); if (string.IsNullOrWhiteSpace (path) || !Path.IsPathRooted (path)) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index aa4322e2e0..acbefdf9a7 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -551,7 +551,7 @@ public string SelectedText ); _historyText.Add ( - new List> { TextModel.ToRuneCells (_text) }, + new List> { RuneCell.ToRuneCells (_text) }, new Point (_cursorPosition, 0), HistoryText.LineStatus.Replaced ); @@ -648,7 +648,7 @@ public virtual void DeleteCharLeft (bool usePreTextChangedCursorPos) } _historyText.Add ( - new List> { TextModel.ToRuneCells (_text) }, + new List> { RuneCell.ToRuneCells (_text) }, new Point (_cursorPosition, 0) ); @@ -702,7 +702,7 @@ public virtual void DeleteCharRight () } _historyText.Add ( - new List> { TextModel.ToRuneCells (_text) }, + new List> { RuneCell.ToRuneCells (_text) }, new Point (_cursorPosition, 0) ); @@ -1390,7 +1390,7 @@ private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItemE return; } - Text = TextModel.ToString (obj?.Lines [obj.CursorPosition.Y]); + Text = RuneCell.ToString (obj?.Lines [obj.CursorPosition.Y]); CursorPosition = obj.CursorPosition.X; Adjust (); } @@ -1398,7 +1398,7 @@ private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItemE private void InsertText (Key a, bool usePreTextChangedCursorPos) { _historyText.Add ( - new List> { TextModel.ToRuneCells (_text) }, + new List> { RuneCell.ToRuneCells (_text) }, new Point (_cursorPosition, 0) ); diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 5c4a82fd8d..5223c210e2 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -58,6 +58,109 @@ public static List ToRuneCellList (string str, ColorScheme? colorSchem return cells; } + + /// + /// Splits a string into a List that will contain a for each line. + /// + /// The string content. + /// The color scheme. + /// A for each line. + public static List> StringToLinesOfRuneCells (string content, ColorScheme? colorScheme = null) + { + List cells = content.EnumerateRunes () + .Select (x => new RuneCell { Rune = x, ColorScheme = colorScheme }) + .ToList (); + + return SplitNewLines (cells); + } + + /// Converts a generic collection into a string. + /// The enumerable cell to convert. + /// + public static string ToString (IEnumerable cells) + { + var str = string.Empty; + + foreach (RuneCell cell in cells) + { + str += cell.Rune.ToString (); + } + + return str; + } + + // Turns the string into cells, this does not split the contents on a newline if it is present. + internal static List StringToRuneCells (string str, ColorScheme? colorScheme = null) + { + List cells = new (); + + foreach (Rune rune in str.ToRunes ()) + { + cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); + } + + return cells; + } + + internal static List ToRuneCells (IEnumerable runes, ColorScheme? colorScheme = null) + { + List cells = new (); + + foreach (Rune rune in runes) + { + cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); + } + + return cells; + } + + private static List> SplitNewLines (List cells) + { + List> lines = new (); + int start = 0, i = 0; + var hasCR = false; + + // ASCII code 13 = Carriage Return. + // ASCII code 10 = Line Feed. + for (; i < cells.Count; i++) + { + if (cells [i].Rune.Value == 13) + { + hasCR = true; + + continue; + } + + if (cells [i].Rune.Value == 10) + { + if (i - start > 0) + { + lines.Add (cells.GetRange (start, hasCR ? i - 1 - start : i - start)); + } + else + { + lines.Add (StringToRuneCells (string.Empty)); + } + + start = i + 1; + hasCR = false; + } + } + + if (i - start >= 0) + { + lines.Add (cells.GetRange (start, i - start)); + } + + return lines; + } + + /// + /// Splits a rune cell list into a List that will contain a for each line. + /// + /// The cells list. + /// + public static List> ToRuneCells (List cells) { return SplitNewLines (cells); } } internal class TextModel @@ -157,7 +260,7 @@ public void LoadListRuneCells (List> cellsList, ColorScheme? colo public void LoadRuneCells (List cells, ColorScheme? colorScheme) { - _lines = ToRuneCells (cells); + _lines = RuneCell.ToRuneCells (cells); SetColorSchemes (colorScheme); OnLinesLoaded (); } @@ -207,7 +310,7 @@ public void LoadStream (Stream input) public void LoadString (string content) { - _lines = StringToLinesOfRuneCells (content); + _lines = RuneCell.StringToLinesOfRuneCells (content); OnLinesLoaded (); } @@ -239,15 +342,7 @@ public void ReplaceLine (int pos, List runes) } } - // Splits a string into a List that contains a List for each line - public static List> StringToLinesOfRuneCells (string content, ColorScheme? colorScheme = null) - { - List cells = content.EnumerateRunes () - .Select (x => new RuneCell { Rune = x, ColorScheme = colorScheme }) - .ToList (); - return SplitNewLines (cells); - } public override string ToString () { @@ -255,7 +350,7 @@ public override string ToString () for (var i = 0; i < _lines.Count; i++) { - sb.Append (ToString (_lines [i])); + sb.Append (RuneCell.ToString (_lines [i])); if (i + 1 < _lines.Count) { @@ -266,21 +361,6 @@ public override string ToString () return sb.ToString (); } - /// Converts a generic collection into a string. - /// The enumerable cell to convert. - /// - public static string ToString (IEnumerable cells) - { - var str = string.Empty; - - foreach (RuneCell cell in cells) - { - str += cell.Rune.ToString (); - } - - return str; - } - public (int col, int row)? WordBackward (int fromCol, int fromRow) { if (fromRow == 0 && fromCol == 0) @@ -873,7 +953,7 @@ internal static int GetColFromX (List t, int start, int x, int tabWidth = string GetText (List x) { - string txt = ToString (x); + string txt = RuneCell.ToString (x); if (!matchCase) { @@ -906,36 +986,10 @@ internal static bool SetCol (ref int col, int width, int cols) return false; } - // Turns the string into cells, this does not split the - // contents on a newline if it is present. - internal static List StringToRuneCells (string str, ColorScheme? colorScheme = null) - { - List cells = new (); - - foreach (Rune rune in str.ToRunes ()) - { - cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); - } - - return cells; - } - - internal static List ToRuneCells (IEnumerable runes, ColorScheme? colorScheme = null) - { - List cells = new (); - - foreach (Rune rune in runes) - { - cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); - } - - return cells; - } - private void Append (List line) { var str = StringExtensions.ToString (line.ToArray ()); - _lines.Add (StringToRuneCells (str)); + _lines.Add (RuneCell.StringToRuneCells (str)); } private bool ApplyToFind ((Point current, bool found) foundPos) @@ -972,7 +1026,7 @@ Point start for (int i = start.Y; i < linesCount; i++) { List x = _lines [i]; - string txt = ToString (x); + string txt = RuneCell.ToString (x); if (!matchCase) { @@ -1012,7 +1066,7 @@ Point start for (int i = linesCount; i >= 0; i--) { List x = _lines [i]; - string txt = ToString (x); + string txt = RuneCell.ToString (x); if (!matchCase) { @@ -1175,7 +1229,7 @@ private bool MovePrev (ref int col, ref int row, out Rune rune) private string ReplaceText (List source, string textToReplace, string matchText, int col) { - string origTxt = ToString (source); + string origTxt = RuneCell.ToString (source); (_, int len) = DisplaySize (source, 0, col, false); (_, int len2) = DisplaySize (source, col, col + matchText.Length, false); (_, int len3) = DisplaySize (source, col + matchText.Length, origTxt.GetRuneCount (), false); @@ -1206,49 +1260,6 @@ private void SetColorSchemes (ColorScheme? colorScheme) } } - private static List> SplitNewLines (List cells) - { - List> lines = new (); - int start = 0, i = 0; - var hasCR = false; - - // ASCII code 13 = Carriage Return. - // ASCII code 10 = Line Feed. - for (; i < cells.Count; i++) - { - if (cells [i].Rune.Value == 13) - { - hasCR = true; - - continue; - } - - if (cells [i].Rune.Value == 10) - { - if (i - start > 0) - { - lines.Add (cells.GetRange (start, hasCR ? i - 1 - start : i - start)); - } - else - { - lines.Add (StringToRuneCells (string.Empty)); - } - - start = i + 1; - hasCR = false; - } - } - - if (i - start >= 0) - { - lines.Add (cells.GetRange (start, i - start)); - } - - return lines; - } - - private static List> ToRuneCells (List cells) { return SplitNewLines (cells); } - private enum RuneType { IsSymbol, @@ -1782,7 +1793,7 @@ public TextModel WrapModel ( List> wrappedLines = ToListRune ( TextFormatter.Format ( - TextModel.ToString (line), + RuneCell.ToString (line), width, Alignment.Start, true, @@ -2905,7 +2916,7 @@ public void Copy () else { List currentLine = GetCurrentLine (); - SetClipboard (TextModel.ToString (currentLine)); + SetClipboard (RuneCell.ToString (currentLine)); _copyWithoutSelection = true; } @@ -4762,7 +4773,7 @@ private void InsertAllText (string text) return; } - List> lines = TextModel.StringToLinesOfRuneCells (text); + List> lines = RuneCell.StringToLinesOfRuneCells (text); if (lines.Count == 0) { diff --git a/UnitTests/Views/RuneCellTests.cs b/UnitTests/Views/RuneCellTests.cs index 9b09b249d5..ccbce08c98 100644 --- a/UnitTests/Views/RuneCellTests.cs +++ b/UnitTests/Views/RuneCellTests.cs @@ -218,10 +218,10 @@ public void RuneCellEventArgs_WordWrap_True () List> text = new () { - TextModel.ToRuneCells ( + RuneCell.ToRuneCells ( "This is the first line.".ToRunes () ), - TextModel.ToRuneCells ( + RuneCell.ToRuneCells ( "This is the second line.".ToRunes () ) }; @@ -237,7 +237,7 @@ void _textView_DrawColor (object sender, RuneCellEventArgs e) eventCount++; } - tv.Text = $"{TextModel.ToString (text [0])}\n{TextModel.ToString (text [1])}\n"; + tv.Text = $"{RuneCell.ToString (text [0])}\n{RuneCell.ToString (text [1])}\n"; Assert.False (tv.WordWrap); var top = new Toplevel (); top.Add (tv); diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index cdbde04480..84a0c931e1 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -4717,7 +4717,7 @@ public void HistoryText_Undo_Redo_Two_Line_Selected_Return () public void Internal_Tests () { var txt = "This is a text."; - List txtRunes = TextModel.StringToRuneCells (txt); + List txtRunes = RuneCell.StringToRuneCells (txt); Assert.Equal (txt.Length, txtRunes.Count); Assert.Equal ('T', txtRunes [0].Rune.Value); Assert.Equal ('h', txtRunes [1].Rune.Value); @@ -4757,8 +4757,8 @@ public void Internal_Tests () Assert.Equal (2, TextModel.CalculateLeftColumn (txtRunes, 0, 9, 8)); var tm = new TextModel (); - tm.AddLine (0, TextModel.StringToRuneCells ("This is first line.")); - tm.AddLine (1, TextModel.StringToRuneCells ("This is last line.")); + tm.AddLine (0, RuneCell.StringToRuneCells ("This is first line.")); + tm.AddLine (1, RuneCell.StringToRuneCells ("This is last line.")); Assert.Equal ((new Point (2, 0), true), tm.FindNextText ("is", out bool gaveFullTurn)); Assert.False (gaveFullTurn); Assert.Equal ((new Point (5, 0), true), tm.FindNextText ("is", out gaveFullTurn)); @@ -4782,14 +4782,14 @@ public void Internal_Tests () Assert.True (gaveFullTurn); Assert.Equal ((new Point (9, 1), true), tm.ReplaceAllText ("is", false, false, "really")); - Assert.Equal (TextModel.StringToRuneCells ("Threally really first line."), tm.GetLine (0)); - Assert.Equal (TextModel.StringToRuneCells ("Threally really last line."), tm.GetLine (1)); + Assert.Equal (RuneCell.StringToRuneCells ("Threally really first line."), tm.GetLine (0)); + Assert.Equal (RuneCell.StringToRuneCells ("Threally really last line."), tm.GetLine (1)); tm = new TextModel (); - tm.AddLine (0, TextModel.StringToRuneCells ("This is first line.")); - tm.AddLine (1, TextModel.StringToRuneCells ("This is last line.")); + tm.AddLine (0, RuneCell.StringToRuneCells ("This is first line.")); + tm.AddLine (1, RuneCell.StringToRuneCells ("This is last line.")); Assert.Equal ((new Point (5, 1), true), tm.ReplaceAllText ("is", false, true, "really")); - Assert.Equal (TextModel.StringToRuneCells ("This really first line."), tm.GetLine (0)); - Assert.Equal (TextModel.StringToRuneCells ("This really last line."), tm.GetLine (1)); + Assert.Equal (RuneCell.StringToRuneCells ("This really first line."), tm.GetLine (0)); + Assert.Equal (RuneCell.StringToRuneCells ("This really last line."), tm.GetLine (1)); } [Fact] From 5a1a0ceb3b0c03afa677ab281177966ef647f6ff Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 3 Oct 2024 20:58:20 +0100 Subject: [PATCH 07/24] Fix LineDrawing PromptForColor to support 16 colors and true color. --- UICatalog/Scenarios/LineDrawing.cs | 32 +++++++++++++++++------- UICatalog/Scenarios/ProgressBarStyles.cs | 2 +- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/UICatalog/Scenarios/LineDrawing.cs b/UICatalog/Scenarios/LineDrawing.cs index 6302f386d0..aabd9917a4 100644 --- a/UICatalog/Scenarios/LineDrawing.cs +++ b/UICatalog/Scenarios/LineDrawing.cs @@ -135,13 +135,14 @@ public static bool PromptForColor (string title, Color current, out Color newCol var d = new Dialog { Title = title, - Height = 7 + Width = Application.Force16Colors ? 35 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)), + Height = 10 }; var btnOk = new Button { X = Pos.Center () - 5, - Y = 4, + Y = Application.Force16Colors ? 6 : 4, Text = "Ok", Width = Dim.Auto (), IsDefault = true @@ -171,21 +172,34 @@ public static bool PromptForColor (string title, Color current, out Color newCol d.Add (btnOk); d.Add (btnCancel); - /* Does not work d.AddButton (btnOk); d.AddButton (btnCancel); - */ - var cp = new ColorPicker + + View cp; + if (Application.Force16Colors) { - SelectedColor = current, - Width = Dim.Fill () - }; + cp = new ColorPicker16 + { + SelectedColor = current.GetClosestNamedColor16 (), + Width = Dim.Fill () + }; + } + else + { + cp = new ColorPicker + { + SelectedColor = current, + Width = Dim.Fill (), + Style = new () { ShowColorName = true, ShowTextFields = true } + }; + ((ColorPicker)cp).ApplyStyleChanges (); + } d.Add (cp); Application.Run (d); d.Dispose (); - newColor = cp.SelectedColor; + newColor = Application.Force16Colors ? ((ColorPicker16)cp).SelectedColor : ((ColorPicker)cp).SelectedColor; return accept; } diff --git a/UICatalog/Scenarios/ProgressBarStyles.cs b/UICatalog/Scenarios/ProgressBarStyles.cs index 54855ce529..d863c33c32 100644 --- a/UICatalog/Scenarios/ProgressBarStyles.cs +++ b/UICatalog/Scenarios/ProgressBarStyles.cs @@ -35,7 +35,7 @@ public override void Main () var editor = new AdornmentsEditor () { - AutoSelectViewToEdit = true + AutoSelectViewToEdit = false }; app.Add (editor); From bb5af016f8bc805f246857124525137b35e35202 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 4 Oct 2024 00:15:21 +0100 Subject: [PATCH 08/24] Add support for colored runes in the Editor scenario. --- UICatalog/Scenarios/Editor.cs | 178 +++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 1 deletion(-) diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 65d2506709..150e1cfaa2 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -202,7 +202,31 @@ public override void Main () CreateWrapChecked (), CreateAutocomplete (), CreateAllowsTabChecked (), - CreateReadOnlyChecked () + CreateReadOnlyChecked (), + new MenuItem ( + "Colors", + "", + () => + { + if (!PromptForColor ( + "Colors", + GetSelectedRuneCellAttribute (), + out Attribute newAttribute + )) + { + return; + } + + var cs = new ColorScheme (_textView.ColorScheme) + { + Focus = new ( + newAttribute.Foreground, + newAttribute.Background + ) + }; + + ApplyRuneCellAttribute (cs); + }) } ), new ( @@ -317,6 +341,158 @@ public override void Main () } + private void ApplyRuneCellAttribute (ColorScheme cs) + { + if (_textView.SelectedLength > 0) + { + int selectedLength = _textView.SelectedLength + _textView.CurrentRow - _textView.SelectionStartRow; + + for (int r = _textView.SelectionStartRow; r <= _textView.CurrentRow; r++) + { + List line = _textView.GetLine (r); + + for (int c = r == _textView.SelectionStartRow ? _textView.SelectionStartColumn : 0; + c < Math.Min ((r == _textView.SelectionStartRow ? _textView.SelectionStartColumn : 0) + (selectedLength > line.Count ? line.Count : selectedLength), line.Count); + c++) + { + line [c].ColorScheme = cs; + } + + selectedLength = selectedLength - line.Count + 1 > -1 ? selectedLength - line.Count + 1 : selectedLength; + } + } + } + + private Attribute? GetSelectedRuneCellAttribute () + { + List line; + + if (_textView.SelectedLength > 0) + { + line = _textView.GetLine (Math.Min (_textView.SelectionStartRow, _textView.CurrentRow)); + + if (line [Math.Min (Math.Min (_textView.SelectionStartColumn, _textView.CurrentColumn), line.Count - 1)].ColorScheme is { } csSel) + { + return new (csSel.Focus); + } + + return new (_textView.ColorScheme!.Focus); + } + + line = _textView.GetCurrentLine (); + + if (line [Math.Min (_textView.CurrentColumn, line.Count - 1)].ColorScheme is { } cs) + { + return new (cs!.Focus); + } + + return new (_textView.ColorScheme!.Focus); + } + + public static bool PromptForColor (string title, Attribute? current, out Attribute newAttribute) + { + var accept = false; + + var d = new Dialog + { + Title = title, + Width = Application.Force16Colors ? 35 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)), + Height = 20 + }; + + var btnOk = new Button + { + X = Pos.Center () - 5, + Y = Application.Force16Colors ? 6 : 4, + Text = "Ok", + Width = Dim.Auto (), + IsDefault = true + }; + + btnOk.Accept += (s, e) => + { + accept = true; + e.Handled = true; + Application.RequestStop (); + }; + + var btnCancel = new Button + { + X = Pos.Center () + 5, + Y = 4, + Text = "Cancel", + Width = Dim.Auto () + }; + + btnCancel.Accept += (s, e) => + { + e.Handled = true; + Application.RequestStop (); + }; + + d.Add (btnOk); + d.Add (btnCancel); + + d.AddButton (btnOk); + d.AddButton (btnCancel); + + View cpForeground; + if (Application.Force16Colors) + { + cpForeground = new ColorPicker16 + { + SelectedColor = current!.Value.Foreground.GetClosestNamedColor16 (), + Width = Dim.Fill () + }; + } + else + { + cpForeground = new ColorPicker + { + SelectedColor = current!.Value.Foreground, + Width = Dim.Fill (), + Style = new () { ShowColorName = true, ShowTextFields = true }, + BorderStyle = LineStyle.Single, + Title = "Foreground" + }; + ((ColorPicker)cpForeground).ApplyStyleChanges (); + } + + View cpBackground; + if (Application.Force16Colors) + { + cpBackground = new ColorPicker16 + { + SelectedColor = current!.Value.Background.GetClosestNamedColor16 (), + Y = Pos.Bottom (cpForeground) + 1, + Width = Dim.Fill () + }; + } + else + { + cpBackground = new ColorPicker + { + SelectedColor = current!.Value.Background, + Width = Dim.Fill (), + Y = Pos.Bottom (cpForeground) + 1, + Style = new () { ShowColorName = true, ShowTextFields = true }, + BorderStyle = LineStyle.Single, + Title = "Background" + }; + ((ColorPicker)cpBackground).ApplyStyleChanges (); + } + + d.Add (cpForeground, cpBackground); + + Application.Run (d); + d.Dispose (); + var newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor; + var newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor; + newAttribute = new (newForeColor, newBackColor); + + return accept; + } + private bool CanCloseFile () { if (_textView.Text == Encoding.Unicode.GetString (_originalText)) From bd42a48b47f996d8e7cd96c02d04236248aeff72 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 4 Oct 2024 00:34:22 +0100 Subject: [PATCH 09/24] Add borders to the 16 colors. --- UICatalog/Scenarios/Editor.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 150e1cfaa2..5b66b0e5f7 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -442,7 +442,9 @@ public static bool PromptForColor (string title, Attribute? current, out Attribu cpForeground = new ColorPicker16 { SelectedColor = current!.Value.Foreground.GetClosestNamedColor16 (), - Width = Dim.Fill () + Width = Dim.Fill (), + BorderStyle = LineStyle.Single, + Title = "Foreground" }; } else @@ -465,7 +467,9 @@ public static bool PromptForColor (string title, Attribute? current, out Attribu { SelectedColor = current!.Value.Background.GetClosestNamedColor16 (), Y = Pos.Bottom (cpForeground) + 1, - Width = Dim.Fill () + Width = Dim.Fill (), + BorderStyle = LineStyle.Single, + Title = "Background" }; } else From d3be2ac4053b6a6ba7c01ebd3ff1bc59b6dca678 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 4 Oct 2024 00:36:08 +0100 Subject: [PATCH 10/24] Only consider SelectionStartRow and SelectionStartColumn when SelectedLength is grater than 0. --- UICatalog/Scenarios/Editor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 5b66b0e5f7..16d6de7b04 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -369,9 +369,9 @@ private void ApplyRuneCellAttribute (ColorScheme cs) if (_textView.SelectedLength > 0) { - line = _textView.GetLine (Math.Min (_textView.SelectionStartRow, _textView.CurrentRow)); + line = _textView.GetLine (_textView.SelectionStartRow); - if (line [Math.Min (Math.Min (_textView.SelectionStartColumn, _textView.CurrentColumn), line.Count - 1)].ColorScheme is { } csSel) + if (line [Math.Min (_textView.SelectionStartColumn, line.Count - 1)].ColorScheme is { } csSel) { return new (csSel.Focus); } From a4f23520b36851bb7f402fe24e08725ba5e6c2cb Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 4 Oct 2024 12:29:23 +0100 Subject: [PATCH 11/24] Fix ApplyRuneCellAttribute method. --- UICatalog/Scenarios/Editor.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 16d6de7b04..b9e44e55e6 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -343,22 +343,18 @@ out Attribute newAttribute private void ApplyRuneCellAttribute (ColorScheme cs) { - if (_textView.SelectedLength > 0) + if (!_textView.ReadOnly && _textView.SelectedLength > 0) { - int selectedLength = _textView.SelectedLength + _textView.CurrentRow - _textView.SelectionStartRow; - for (int r = _textView.SelectionStartRow; r <= _textView.CurrentRow; r++) { List line = _textView.GetLine (r); for (int c = r == _textView.SelectionStartRow ? _textView.SelectionStartColumn : 0; - c < Math.Min ((r == _textView.SelectionStartRow ? _textView.SelectionStartColumn : 0) + (selectedLength > line.Count ? line.Count : selectedLength), line.Count); + c < (r == _textView.CurrentRow ? _textView.CurrentColumn : line.Count); c++) { line [c].ColorScheme = cs; } - - selectedLength = selectedLength - line.Count + 1 > -1 ? selectedLength - line.Count + 1 : selectedLength; } } } From 439bddf5c55639b100944ade082812ea31f5a7c3 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 4 Oct 2024 12:30:07 +0100 Subject: [PATCH 12/24] Fix width for 16 colors. --- UICatalog/Scenarios/Editor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index b9e44e55e6..48cd600b4b 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -392,7 +392,7 @@ public static bool PromptForColor (string title, Attribute? current, out Attribu var d = new Dialog { Title = title, - Width = Application.Force16Colors ? 35 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)), + Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)), Height = 20 }; From 903827a147af7bc7885ac4ad24a6699a0bcbd3d7 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 4 Oct 2024 22:04:05 +0100 Subject: [PATCH 13/24] Fix start/end row and cols. --- UICatalog/Scenarios/Editor.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 48cd600b4b..1fcc6dafda 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -345,12 +345,17 @@ private void ApplyRuneCellAttribute (ColorScheme cs) { if (!_textView.ReadOnly && _textView.SelectedLength > 0) { - for (int r = _textView.SelectionStartRow; r <= _textView.CurrentRow; r++) + var startRow = Math.Min (_textView.SelectionStartRow, _textView.CurrentRow); + var endRow = Math.Max (_textView.CurrentRow, _textView.SelectionStartRow); + var startCol = _textView.SelectionStartRow <= _textView.CurrentRow ? _textView.SelectionStartColumn : _textView.CurrentColumn; + var endCol = _textView.CurrentRow >= _textView.SelectionStartRow ? _textView.CurrentColumn : _textView.SelectionStartColumn; + + for (int r = startRow; r <= endRow; r++) { List line = _textView.GetLine (r); - for (int c = r == _textView.SelectionStartRow ? _textView.SelectionStartColumn : 0; - c < (r == _textView.CurrentRow ? _textView.CurrentColumn : line.Count); + for (int c = r == startRow ? startCol : 0; + c < (r == endRow ? endCol : line.Count); c++) { line [c].ColorScheme = cs; From ccf2daa0241eb796380843736227d1806750ba9f Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 5 Oct 2024 18:01:29 +0100 Subject: [PATCH 14/24] Change all RuneCell to Cell and move methods to the Cell record struct. --- Terminal.Gui/Drawing/Cell.cs | 137 ++++- .../CellEventArgs.cs} | 16 +- Terminal.Gui/Drawing/LineCanvas.cs | 2 +- Terminal.Gui/Drawing/StraightLine.cs | 2 +- .../Text/Autocomplete/AutocompleteContext.cs | 4 +- .../Views/AutocompleteFilepathContext.cs | 4 +- .../Views/HistoryTextItemEventArgs.cs | 6 +- Terminal.Gui/Views/TextField.cs | 14 +- Terminal.Gui/Views/TextView.cs | 536 +++++++----------- Terminal.Gui/Views/TreeView/Branch.cs | 18 +- .../TreeView/DrawTreeViewLineEventArgs.cs | 8 +- UICatalog/Scenarios/Editor.cs | 32 +- UICatalog/Scenarios/SyntaxHighlighting.cs | 59 +- UICatalog/Scenarios/TreeViewFileSystem.cs | 16 +- UnitTests/Configuration/ThemeTests.cs | 1 + .../RuneCellTests.cs => Drawing/CellTests.cs} | 168 +++--- UnitTests/Text/AutocompleteTests.cs | 2 +- UnitTests/Views/TextViewTests.cs | 22 +- UnitTests/Views/TreeViewTests.cs | 14 +- 19 files changed, 523 insertions(+), 538 deletions(-) rename Terminal.Gui/{Views/RuneCellEventArgs.cs => Drawing/CellEventArgs.cs} (57%) rename UnitTests/{Views/RuneCellTests.cs => Drawing/CellTests.cs} (61%) diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index 55da051845..581cc6bd61 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -4,19 +4,18 @@ /// Represents a single row/column in a Terminal.Gui rendering surface (e.g. and /// ). /// -public record struct Cell () +public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Rune Rune = default) { - /// The attributes to use when drawing the Glyph. - public Attribute? Attribute { get; set; } = null; + public Attribute? Attribute { get; set; } = Attribute; /// /// Gets or sets a value indicating whether this has been modified since the /// last time it was drawn. /// - public bool IsDirty { get; set; } = false; + public bool IsDirty { get; set; } = IsDirty; - private Rune _rune = default; + private Rune _rune = Rune; /// The character to display. If is , then is ignored. public Rune Rune @@ -29,6 +28,8 @@ public Rune Rune } } + private List _combiningMarks; + /// /// The combining marks for that when combined makes this Cell a combining sequence. If /// empty, then is ignored. @@ -37,8 +38,132 @@ public Rune Rune /// Only valid in the rare case where is a combining sequence that could not be normalized to a /// single Rune. /// - internal List CombiningMarks { get; } = new (); + internal List CombiningMarks + { + get => _combiningMarks ?? []; + private set => _combiningMarks = value ?? []; + } /// public override string ToString () { return $"[{Rune}, {Attribute}]"; } + + /// Converts the string into a . + /// The string to convert. + /// The to use. + /// + public static List ToCellList (string str, Attribute? attribute = null) + { + List cells = new (); + + foreach (Rune rune in str.EnumerateRunes ()) + { + cells.Add (new () { Rune = rune, Attribute = attribute }); + } + + return cells; + } + + /// + /// Splits a string into a List that will contain a for each line. + /// + /// The string content. + /// The color scheme. + /// A for each line. + public static List> StringToLinesOfCells (string content, Attribute? attribute = null) + { + List cells = content.EnumerateRunes () + .Select (x => new Cell { Rune = x, Attribute = attribute }) + .ToList (); + + return SplitNewLines (cells); + } + + /// Converts a generic collection into a string. + /// The enumerable cell to convert. + /// + public static string ToString (IEnumerable cells) + { + var str = string.Empty; + + foreach (Cell cell in cells) + { + str += cell.Rune.ToString (); + } + + return str; + } + + // Turns the string into cells, this does not split the contents on a newline if it is present. + + internal static List StringToCells (string str, Attribute? attribute = null) + { + List cells = []; + + foreach (Rune rune in str.ToRunes ()) + { + cells.Add (new () { Rune = rune, Attribute = attribute }); + } + + return cells; + } + + internal static List ToCells (IEnumerable runes, Attribute? attribute = null) + { + List cells = new (); + + foreach (Rune rune in runes) + { + cells.Add (new () { Rune = rune, Attribute = attribute }); + } + + return cells; + } + + private static List> SplitNewLines (List cells) + { + List> lines = []; + int start = 0, i = 0; + var hasCR = false; + + // ASCII code 13 = Carriage Return. + // ASCII code 10 = Line Feed. + for (; i < cells.Count; i++) + { + if (cells [i].Rune.Value == 13) + { + hasCR = true; + + continue; + } + + if (cells [i].Rune.Value == 10) + { + if (i - start > 0) + { + lines.Add (cells.GetRange (start, hasCR ? i - 1 - start : i - start)); + } + else + { + lines.Add (StringToCells (string.Empty)); + } + + start = i + 1; + hasCR = false; + } + } + + if (i - start >= 0) + { + lines.Add (cells.GetRange (start, i - start)); + } + + return lines; + } + + /// + /// Splits a rune cell list into a List that will contain a for each line. + /// + /// The cells list. + /// + public static List> ToCells (List cells) { return SplitNewLines (cells); } } diff --git a/Terminal.Gui/Views/RuneCellEventArgs.cs b/Terminal.Gui/Drawing/CellEventArgs.cs similarity index 57% rename from Terminal.Gui/Views/RuneCellEventArgs.cs rename to Terminal.Gui/Drawing/CellEventArgs.cs index 1283cfe570..14b98f36b3 100644 --- a/Terminal.Gui/Views/RuneCellEventArgs.cs +++ b/Terminal.Gui/Drawing/CellEventArgs.cs @@ -1,27 +1,27 @@ namespace Terminal.Gui; -/// Args for events that relate to a specific . -public class RuneCellEventArgs +/// Args for events that relate to a specific . +public class CellEventArgs { - /// Creates a new instance of the class. + /// Creates a new instance of the class. /// The line. /// The col index. /// The unwrapped row and col index. - public RuneCellEventArgs (List line, int col, (int Row, int Col) unwrappedPosition) + public CellEventArgs (List line, int col, (int Row, int Col) unwrappedPosition) { Line = line; Col = col; UnwrappedPosition = unwrappedPosition; } - /// The index of the RuneCell in the line. + /// The index of the Cell in the line. public int Col { get; } - /// The list of runes the RuneCell is part of. - public List Line { get; } + /// The list of runes the Cell is part of. + public List Line { get; } /// - /// The unwrapped row and column index into the text containing the RuneCell. Unwrapped means the text without + /// The unwrapped row and column index into the text containing the Cell. Unwrapped means the text without /// word wrapping or other visual formatting having been applied. /// public (int Row, int Col) UnwrappedPosition { get; } diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs index 9a7365f264..235d657d98 100644 --- a/Terminal.Gui/Drawing/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas.cs @@ -138,7 +138,7 @@ public void AddLine ( int length, Orientation orientation, LineStyle style, - Attribute? attribute = default + Attribute? attribute = null ) { _cachedViewport = Rectangle.Empty; diff --git a/Terminal.Gui/Drawing/StraightLine.cs b/Terminal.Gui/Drawing/StraightLine.cs index 2f36995df6..fe2ccdc1d0 100644 --- a/Terminal.Gui/Drawing/StraightLine.cs +++ b/Terminal.Gui/Drawing/StraightLine.cs @@ -16,7 +16,7 @@ public StraightLine ( int length, Orientation orientation, LineStyle style, - Attribute? attribute = default + Attribute? attribute = null ) { Start = start; diff --git a/Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs b/Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs index 2686c10242..ed55b0d3f4 100644 --- a/Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs +++ b/Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs @@ -7,7 +7,7 @@ namespace Terminal.Gui; public class AutocompleteContext { /// Creates a new instance of the class - public AutocompleteContext (List currentLine, int cursorPosition, bool canceled = false) + public AutocompleteContext (List currentLine, int cursorPosition, bool canceled = false) { CurrentLine = currentLine; CursorPosition = cursorPosition; @@ -18,7 +18,7 @@ public AutocompleteContext (List currentLine, int cursorPosition, bool public bool Canceled { get; set; } /// The text on the current line. - public List CurrentLine { get; set; } + public List CurrentLine { get; set; } /// The position of the input cursor within the . public int CursorPosition { get; set; } diff --git a/Terminal.Gui/Views/AutocompleteFilepathContext.cs b/Terminal.Gui/Views/AutocompleteFilepathContext.cs index a379f20010..b21724816e 100644 --- a/Terminal.Gui/Views/AutocompleteFilepathContext.cs +++ b/Terminal.Gui/Views/AutocompleteFilepathContext.cs @@ -6,7 +6,7 @@ namespace Terminal.Gui; internal class AutocompleteFilepathContext : AutocompleteContext { public AutocompleteFilepathContext (string currentLine, int cursorPosition, FileDialogState state) - : base (RuneCell.ToRuneCellList (currentLine), cursorPosition) + : base (Cell.ToCellList (currentLine), cursorPosition) { State = state; } @@ -30,7 +30,7 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context) return Enumerable.Empty (); } - var path = RuneCell.ToString (context.CurrentLine); + var path = Cell.ToString (context.CurrentLine); int last = path.LastIndexOfAny (FileDialog.Separators); if (string.IsNullOrWhiteSpace (path) || !Path.IsPathRooted (path)) diff --git a/Terminal.Gui/Views/HistoryTextItemEventArgs.cs b/Terminal.Gui/Views/HistoryTextItemEventArgs.cs index ca2ad3ad7e..c75f4a5e8b 100644 --- a/Terminal.Gui/Views/HistoryTextItemEventArgs.cs +++ b/Terminal.Gui/Views/HistoryTextItemEventArgs.cs @@ -9,11 +9,11 @@ public class HistoryTextItemEventArgs : EventArgs public Point CursorPosition; public Point FinalCursorPosition; public bool IsUndoing; - public List> Lines; + public List> Lines; public LineStatus LineStatus; public HistoryTextItemEventArgs RemovedOnAdded; - public HistoryTextItemEventArgs (List> lines, Point curPos, LineStatus linesStatus) + public HistoryTextItemEventArgs (List> lines, Point curPos, LineStatus linesStatus) { Lines = lines; CursorPosition = curPos; @@ -22,7 +22,7 @@ public HistoryTextItemEventArgs (List> lines, Point curPos, LineS public HistoryTextItemEventArgs (HistoryTextItemEventArgs historyTextItem) { - Lines = new List> (historyTextItem.Lines); + Lines = new List> (historyTextItem.Lines); CursorPosition = new Point (historyTextItem.CursorPosition.X, historyTextItem.CursorPosition.Y); LineStatus = historyTextItem.LineStatus; } diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index acbefdf9a7..a352e586c8 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -546,12 +546,12 @@ public string SelectedText if (!Secret && !_historyText.IsFromHistory) { _historyText.Add ( - new List> { RuneCell.ToRuneCellList (oldText) }, + new List> { Cell.ToCellList (oldText) }, new Point (_cursorPosition, 0) ); _historyText.Add ( - new List> { RuneCell.ToRuneCells (_text) }, + new List> { Cell.ToCells (_text) }, new Point (_cursorPosition, 0), HistoryText.LineStatus.Replaced ); @@ -648,7 +648,7 @@ public virtual void DeleteCharLeft (bool usePreTextChangedCursorPos) } _historyText.Add ( - new List> { RuneCell.ToRuneCells (_text) }, + new List> { Cell.ToCells (_text) }, new Point (_cursorPosition, 0) ); @@ -702,7 +702,7 @@ public virtual void DeleteCharRight () } _historyText.Add ( - new List> { RuneCell.ToRuneCells (_text) }, + new List> { Cell.ToCells (_text) }, new Point (_cursorPosition, 0) ); @@ -1342,7 +1342,7 @@ private List DeleteSelectedText () private void GenerateSuggestions () { - List currentLine = RuneCell.ToRuneCellList (Text); + List currentLine = Cell.ToCellList (Text); int cursorPosition = Math.Min (CursorPosition, currentLine.Count); Autocomplete.Context = new AutocompleteContext ( @@ -1390,7 +1390,7 @@ private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItemE return; } - Text = RuneCell.ToString (obj?.Lines [obj.CursorPosition.Y]); + Text = Cell.ToString (obj?.Lines [obj.CursorPosition.Y]); CursorPosition = obj.CursorPosition.X; Adjust (); } @@ -1398,7 +1398,7 @@ private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItemE private void InsertText (Key a, bool usePreTextChangedCursorPos) { _historyText.Add ( - new List> { RuneCell.ToRuneCells (_text) }, + new List> { Cell.ToCells (_text) }, new Point (_cursorPosition, 0) ); diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 5223c210e2..67b2afe2eb 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -9,163 +9,9 @@ namespace Terminal.Gui; -/// -/// Represents a single row/column within the . Includes the glyph and the -/// foreground/background colors. -/// -[DebuggerDisplay ("{ColorSchemeDebuggerDisplay}")] -public class RuneCell : IEquatable -{ - /// The color sets to draw the glyph with. - [JsonConverter (typeof (ColorSchemeJsonConverter))] - public ColorScheme? ColorScheme { get; set; } - - /// The glyph to draw. - [JsonConverter (typeof (RuneJsonConverter))] - public Rune Rune { get; set; } - - private string ColorSchemeDebuggerDisplay => ToString (); - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// - /// if the current object is equal to the parameter; otherwise, - /// . - /// - public bool Equals (RuneCell? other) { return other is { } && Rune.Equals (other.Rune) && ColorScheme == other.ColorScheme; } - - /// Returns a string that represents the current object. - /// A string that represents the current object. - public override string ToString () - { - string colorSchemeStr = ColorScheme?.ToString () ?? "null"; - - return $"U+{Rune.Value:X4} '{Rune.ToString ()}'; {colorSchemeStr}"; - } - - /// Converts the string into a . - /// The string to convert. - /// The to use. - /// - public static List ToRuneCellList (string str, ColorScheme? colorScheme = null) - { - List cells = new (); - - foreach (Rune rune in str.EnumerateRunes ()) - { - cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); - } - - return cells; - } - - /// - /// Splits a string into a List that will contain a for each line. - /// - /// The string content. - /// The color scheme. - /// A for each line. - public static List> StringToLinesOfRuneCells (string content, ColorScheme? colorScheme = null) - { - List cells = content.EnumerateRunes () - .Select (x => new RuneCell { Rune = x, ColorScheme = colorScheme }) - .ToList (); - - return SplitNewLines (cells); - } - - /// Converts a generic collection into a string. - /// The enumerable cell to convert. - /// - public static string ToString (IEnumerable cells) - { - var str = string.Empty; - - foreach (RuneCell cell in cells) - { - str += cell.Rune.ToString (); - } - - return str; - } - - // Turns the string into cells, this does not split the contents on a newline if it is present. - internal static List StringToRuneCells (string str, ColorScheme? colorScheme = null) - { - List cells = new (); - - foreach (Rune rune in str.ToRunes ()) - { - cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); - } - - return cells; - } - - internal static List ToRuneCells (IEnumerable runes, ColorScheme? colorScheme = null) - { - List cells = new (); - - foreach (Rune rune in runes) - { - cells.Add (new () { Rune = rune, ColorScheme = colorScheme }); - } - - return cells; - } - - private static List> SplitNewLines (List cells) - { - List> lines = new (); - int start = 0, i = 0; - var hasCR = false; - - // ASCII code 13 = Carriage Return. - // ASCII code 10 = Line Feed. - for (; i < cells.Count; i++) - { - if (cells [i].Rune.Value == 13) - { - hasCR = true; - - continue; - } - - if (cells [i].Rune.Value == 10) - { - if (i - start > 0) - { - lines.Add (cells.GetRange (start, hasCR ? i - 1 - start : i - start)); - } - else - { - lines.Add (StringToRuneCells (string.Empty)); - } - - start = i + 1; - hasCR = false; - } - } - - if (i - start >= 0) - { - lines.Add (cells.GetRange (start, i - start)); - } - - return lines; - } - - /// - /// Splits a rune cell list into a List that will contain a for each line. - /// - /// The cells list. - /// - public static List> ToRuneCells (List cells) { return SplitNewLines (cells); } -} - internal class TextModel { - private List> _lines = new (); + private List> _lines = new (); private (Point startPointToFind, Point currentPointToFind, bool found) _toFind; /// The number of text lines in the model @@ -175,8 +21,8 @@ internal class TextModel /// Adds a line to the model at the specified position. /// Line number where the line will be inserted. - /// The line of text and color, as a List of RuneCell. - public void AddLine (int pos, List cells) { _lines.Insert (pos, cells); } + /// The line of text and color, as a List of Cell. + public void AddLine (int pos, List cells) { _lines.Insert (pos, cells); } public bool CloseFile () { @@ -191,12 +37,12 @@ public bool CloseFile () return true; } - public List> GetAllLines () { return _lines; } + public List> GetAllLines () { return _lines; } /// Returns the specified line as a List of Rune /// The line. /// Line number to retrieve. - public List GetLine (int line) + public List GetLine (int line) { if (_lines.Count > 0) { @@ -224,7 +70,7 @@ public int GetMaxVisibleLine (int first, int last, int tabWidth) for (int i = first; i < last; i++) { - List line = GetLine (i); + List line = GetLine (i); int tabSum = line.Sum (c => c.Rune.Value == '\t' ? Math.Max (tabWidth - 1, 0) : 0); int l = line.Count + tabSum; @@ -251,17 +97,17 @@ public bool LoadFile (string file) } } - public void LoadListRuneCells (List> cellsList, ColorScheme? colorScheme) + public void LoadListCells (List> cellsList, Attribute? attribute) { _lines = cellsList; - SetColorSchemes (colorScheme); + SetAttributes (attribute); OnLinesLoaded (); } - public void LoadRuneCells (List cells, ColorScheme? colorScheme) + public void LoadCells (List cells, Attribute? attribute) { - _lines = RuneCell.ToRuneCells (cells); - SetColorSchemes (colorScheme); + _lines = Cell.ToCells ((List)cells); + SetAttributes (attribute); OnLinesLoaded (); } @@ -310,7 +156,7 @@ public void LoadStream (Stream input) public void LoadString (string content) { - _lines = RuneCell.StringToLinesOfRuneCells (content); + _lines = Cell.StringToLinesOfCells (content); OnLinesLoaded (); } @@ -330,7 +176,7 @@ public void RemoveLine (int pos) } } - public void ReplaceLine (int pos, List runes) + public void ReplaceLine (int pos, List runes) { if (_lines.Count > 0 && pos < _lines.Count) { @@ -350,7 +196,7 @@ public override string ToString () for (var i = 0; i < _lines.Count; i++) { - sb.Append (RuneCell.ToString (_lines [i])); + sb.Append (Cell.ToString (_lines [i])); if (i + 1 < _lines.Count) { @@ -373,12 +219,12 @@ public override string ToString () try { - RuneCell? cell = RuneAt (col, row); + Cell? cell = RuneAt (col, row); Rune rune; if (cell is { }) { - rune = cell.Rune; + rune = cell.Value.Rune; } else { @@ -390,7 +236,7 @@ public override string ToString () if (col == 0 && row > 0) { row--; - List line = GetLine (row); + List line = GetLine (row); return (line.Count, row); } @@ -464,7 +310,7 @@ void ProcMovePrev (ref int nCol, ref int nRow, Rune nRune) return; } - List line = GetLine (nRow); + List line = GetLine (nRow); if (nCol == 0 && nRow == fromRow @@ -523,7 +369,7 @@ void ProcMovePrev (ref int nCol, ref int nRow, Rune nRune) try { - Rune rune = RuneAt (col, row).Rune; + Rune rune = RuneAt (col, row)!.Value.Rune; RuneType runeType = GetRuneType (rune); int lastValidCol = IsSameRuneType (rune, runeType) && (Rune.IsLetterOrDigit (rune) || Rune.IsPunctuation (rune) || Rune.IsSymbol (rune)) @@ -590,7 +436,7 @@ void ProcMoveNext (ref int nCol, ref int nRow, Rune nRune) return; } - List line = GetLine (nRow); + List line = GetLine (nRow); if (nCol == line.Count && nRow == fromRow @@ -630,11 +476,11 @@ void ProcMoveNext (ref int nCol, ref int nRow, Rune nRune) } } - internal static int CalculateLeftColumn (List t, int start, int end, int width, int tabWidth = 0) + internal static int CalculateLeftColumn (List t, int start, int end, int width, int tabWidth = 0) { List runes = new (); - foreach (RuneCell cell in t) + foreach (Cell cell in t) { runes.Add (cell.Rune); } @@ -686,7 +532,7 @@ internal static int CalculateLeftColumn (List t, int start, int end, int w } internal static (int size, int length) DisplaySize ( - List t, + List t, int start = -1, int end = -1, bool checkNextRune = true, @@ -695,7 +541,7 @@ internal static (int size, int length) DisplaySize ( { List runes = new (); - foreach (RuneCell cell in t) + foreach (Cell cell in t) { runes.Add (cell.Rune); } @@ -856,11 +702,11 @@ internal Size GetDisplaySize () return foundPos; } - internal static int GetColFromX (List t, int start, int x, int tabWidth = 0) + internal static int GetColFromX (List t, int start, int x, int tabWidth = 0) { List runes = new (); - foreach (RuneCell cell in t) + foreach (Cell cell in t) { runes.Add (cell.Rune); } @@ -909,7 +755,7 @@ internal static int GetColFromX (List t, int start, int x, int tabWidth = for (var i = 0; i < _lines.Count; i++) { - List x = _lines [i]; + List x = _lines [i]; string txt = GetText (x); string matchText = !matchCase ? text.ToUpper () : text; int col = txt.IndexOf (matchText); @@ -935,7 +781,7 @@ internal static int GetColFromX (List t, int start, int x, int tabWidth = found = true; } - _lines [i] = RuneCell.ToRuneCellList (ReplaceText (x, textToReplace!, matchText, col)); + _lines [i] = Cell.ToCellList (ReplaceText (x, textToReplace!, matchText, col)); x = _lines [i]; txt = GetText (x); pos = new (col, i); @@ -951,9 +797,9 @@ internal static int GetColFromX (List t, int start, int x, int tabWidth = } } - string GetText (List x) + string GetText (List x) { - string txt = RuneCell.ToString (x); + string txt = Cell.ToString (x); if (!matchCase) { @@ -989,7 +835,7 @@ internal static bool SetCol (ref int col, int width, int cols) private void Append (List line) { var str = StringExtensions.ToString (line.ToArray ()); - _lines.Add (RuneCell.StringToRuneCells (str)); + _lines.Add (Cell.StringToCells (str)); } private bool ApplyToFind ((Point current, bool found) foundPos) @@ -1025,8 +871,8 @@ Point start { for (int i = start.Y; i < linesCount; i++) { - List x = _lines [i]; - string txt = RuneCell.ToString (x); + List x = _lines [i]; + string txt = Cell.ToString (x); if (!matchCase) { @@ -1065,8 +911,8 @@ Point start { for (int i = linesCount; i >= 0; i--) { - List x = _lines [i]; - string txt = RuneCell.ToString (x); + List x = _lines [i]; + string txt = Cell.ToString (x); if (!matchCase) { @@ -1148,7 +994,7 @@ private bool MatchWholeWord (string source, string matchText, int index = 0) private bool MoveNext (ref int col, ref int row, out Rune rune) { - List line = GetLine (row); + List line = GetLine (row); if (col + 1 < line.Count) { @@ -1189,7 +1035,7 @@ private bool MoveNext (ref int col, ref int row, out Rune rune) private bool MovePrev (ref int col, ref int row, out Rune rune) { - List line = GetLine (row); + List line = GetLine (row); if (col > 0) { @@ -1227,9 +1073,9 @@ private bool MovePrev (ref int col, ref int row, out Rune rune) private void OnLinesLoaded () { LinesLoaded?.Invoke (this, EventArgs.Empty); } - private string ReplaceText (List source, string textToReplace, string matchText, int col) + private string ReplaceText (List source, string textToReplace, string matchText, int col) { - string origTxt = RuneCell.ToString (source); + string origTxt = Cell.ToString (source); (_, int len) = DisplaySize (source, 0, col, false); (_, int len2) = DisplaySize (source, col, col + matchText.Length, false); (_, int len3) = DisplaySize (source, col + matchText.Length, origTxt.GetRuneCount (), false); @@ -1237,25 +1083,27 @@ private string ReplaceText (List source, string textToReplace, string return origTxt [..len] + textToReplace + origTxt.Substring (len + len2, len3); } - private RuneCell RuneAt (int col, int row) + private Cell? RuneAt (int col, int row) { - List line = GetLine (row); + List line = GetLine (row); if (line.Count > 0) { return line [col > line.Count - 1 ? line.Count - 1 : col]; } - return default (RuneCell)!; + return null; } - private void SetColorSchemes (ColorScheme? colorScheme) + private void SetAttributes (Attribute? attribute) { - foreach (List line in _lines) + foreach (List line in _lines) { - foreach (RuneCell cell in line) + for (var i = 0; i < line.Count; i++) { - cell.ColorScheme ??= colorScheme; + Cell cell = line [i]; + cell.Attribute ??= attribute; + line [i] = cell; } } } @@ -1286,7 +1134,7 @@ public enum LineStatus public bool HasHistoryChanges => _idxHistoryText > -1; public bool IsFromHistory { get; private set; } - public void Add (List> lines, Point curPos, LineStatus lineStatus = LineStatus.Original) + public void Add (List> lines, Point curPos, LineStatus lineStatus = LineStatus.Original) { if (lineStatus == LineStatus.Original && _historyTextItems.Count > 0 && _historyTextItems.Last ().LineStatus == LineStatus.Original) { @@ -1343,7 +1191,7 @@ public void Redo () } } - public void ReplaceLast (List> lines, Point curPos, LineStatus lineStatus) + public void ReplaceLast (List> lines, Point curPos, LineStatus lineStatus) { HistoryTextItemEventArgs? found = _historyTextItems.FindLast (x => x.LineStatus == lineStatus); @@ -1498,9 +1346,9 @@ public void AddLine (int row, int col) { int modelRow = GetModelLineFromWrappedLines (row); int modelCol = GetModelColFromWrappedLines (row, col); - List line = GetCurrentLine (modelRow); + List line = GetCurrentLine (modelRow); int restCount = line.Count - modelCol; - List rest = line.GetRange (modelCol, restCount); + List rest = line.GetRange (modelCol, restCount); line.RemoveRange (modelCol, restCount); Model.AddLine (modelRow + 1, rest); _isWrapModelRefreshing = true; @@ -1584,9 +1432,9 @@ public int GetWrappedLineColWidth (int line, int col, WordWrapManager wrapManage return modelCol - colWidthOffset; } - public bool Insert (int row, int col, RuneCell cell) + public bool Insert (int row, int col, Cell cell) { - List line = GetCurrentLine (GetModelLineFromWrappedLines (row)); + List line = GetCurrentLine (GetModelLineFromWrappedLines (row)); line.Insert (GetModelColFromWrappedLines (row, col), cell); if (line.Count > _frameWidth) @@ -1600,7 +1448,7 @@ public bool Insert (int row, int col, RuneCell cell) public bool RemoveAt (int row, int col) { int modelRow = GetModelLineFromWrappedLines (row); - List line = GetCurrentLine (modelRow); + List line = GetCurrentLine (modelRow); int modelCol = GetModelColFromWrappedLines (row, col); if (modelCol > line.Count) @@ -1628,7 +1476,7 @@ public bool RemoveLine (int row, int col, out bool lineRemoved, bool forward = t { lineRemoved = false; int modelRow = GetModelLineFromWrappedLines (row); - List line = GetCurrentLine (modelRow); + List line = GetCurrentLine (modelRow); int modelCol = GetModelColFromWrappedLines (row, col); if (modelCol == 0 && line.Count == 0) @@ -1664,7 +1512,7 @@ public bool RemoveLine (int row, int col, out bool lineRemoved, bool forward = t return false; } - List nextLine = Model.GetLine (modelRow + 1); + List nextLine = Model.GetLine (modelRow + 1); line.AddRange (nextLine); Model.RemoveLine (modelRow + 1); @@ -1680,7 +1528,7 @@ public bool RemoveLine (int row, int col, out bool lineRemoved, bool forward = t return false; } - List prevLine = Model.GetLine (modelRow - 1); + List prevLine = Model.GetLine (modelRow - 1); prevLine.AddRange (line); Model.RemoveLine (modelRow); @@ -1696,7 +1544,7 @@ public bool RemoveLine (int row, int col, out bool lineRemoved, bool forward = t public bool RemoveRange (int row, int index, int count) { int modelRow = GetModelLineFromWrappedLines (row); - List line = GetCurrentLine (modelRow); + List line = GetCurrentLine (modelRow); int modelCol = GetModelColFromWrappedLines (row, index); try @@ -1711,13 +1559,13 @@ public bool RemoveRange (int row, int index, int count) return true; } - public List> ToListRune (List textList) + public List> ToListRune (List textList) { - List> runesList = new (); + List> runesList = new (); foreach (string text in textList) { - runesList.Add (RuneCell.ToRuneCellList (text)); + runesList.Add (Cell.ToCellList (text)); } return runesList; @@ -1789,11 +1637,11 @@ public TextModel WrapModel ( for (var i = 0; i < Model.Count; i++) { - List line = Model.GetLine (i); + List line = Model.GetLine (i); - List> wrappedLines = ToListRune ( + List> wrappedLines = ToListRune ( TextFormatter.Format ( - RuneCell.ToString (line), + Cell.ToString (line), width, Alignment.Start, true, @@ -1805,7 +1653,7 @@ public TextModel WrapModel ( for (var j = 0; j < wrappedLines.Count; j++) { - List wrapLine = wrappedLines [j]; + List wrapLine = wrappedLines [j]; if (!isRowAndColSet && modelRow == i) { @@ -1863,7 +1711,9 @@ public TextModel WrapModel ( for (int k = j; k < wrapLine.Count; k++) { - wrapLine [k].ColorScheme = line [k].ColorScheme; + Cell cell = wrapLine [k]; + cell.Attribute = line [k].Attribute; + wrapLine [k] = cell; } wrappedModel.AddLine (lines, wrapLine); @@ -1883,7 +1733,7 @@ public TextModel WrapModel ( return wrappedModel; } - private List GetCurrentLine (int row) { return Model.GetLine (row); } + private List GetCurrentLine (int row) { return Model.GetLine (row); } private class WrappedLine { @@ -2613,7 +2463,7 @@ public Point CursorPosition get => new (CurrentColumn, CurrentRow); set { - List line = _model.GetLine (Math.Max (Math.Min (value.Y, _model.Count - 1), 0)); + List line = _model.GetLine (Math.Max (Math.Min (value.Y, _model.Count - 1), 0)); CurrentColumn = value.X < 0 ? 0 : value.X > line.Count ? line.Count : value.X; @@ -2633,11 +2483,11 @@ public Point CursorPosition public bool HasHistoryChanges => _historyText.HasHistoryChanges; /// - /// If and the current is null will inherit from the + /// If and the current is null will inherit from the /// previous, otherwise if (default) do nothing. If the text is load with - /// this property is automatically sets to . + /// this property is automatically sets to . /// - public bool InheritsPreviousColorScheme { get; set; } + public bool InheritsPreviousAttribute { get; set; } /// /// Indicates whatever the text was changed or not. if the text was changed @@ -2758,7 +2608,7 @@ public int SelectionStartColumn get => _selectionStartColumn; set { - List line = _model.GetLine (_selectionStartRow); + List line = _model.GetLine (_selectionStartRow); _selectionStartColumn = value < 0 ? 0 : value > line.Count ? line.Count : value; @@ -2915,8 +2765,8 @@ public void Copy () } else { - List currentLine = GetCurrentLine (); - SetClipboard (RuneCell.ToString (currentLine)); + List currentLine = GetCurrentLine (); + SetClipboard (Cell.ToString (currentLine)); _copyWithoutSelection = true; } @@ -2978,7 +2828,7 @@ public void DeleteCharLeft () ClearSelectedRegion (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); _historyText.Add ( new () { new (currentLine) }, @@ -3022,7 +2872,7 @@ public void DeleteCharRight () ClearSelectedRegion (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); _historyText.Add ( new () { new (currentLine) }, @@ -3051,19 +2901,19 @@ public void DeleteCharRight () } /// Invoked when the normal color is drawn. - public event EventHandler? DrawNormalColor; + public event EventHandler? DrawNormalColor; /// Invoked when the ready only color is drawn. - public event EventHandler? DrawReadOnlyColor; + public event EventHandler? DrawReadOnlyColor; /// Invoked when the selection color is drawn. - public event EventHandler? DrawSelectionColor; + public event EventHandler? DrawSelectionColor; /// /// Invoked when the used color is drawn. The Used Color is used to indicate if the /// was pressed and enabled. /// - public event EventHandler? DrawUsedColor; + public event EventHandler? DrawUsedColor; /// Find the next text based on the match case with the option to replace it. /// The text to find. @@ -3136,19 +2986,19 @@ public bool FindPreviousText ( /// Gets all lines of characters. /// - public List> GetAllLines () { return _model.GetAllLines (); } + public List> GetAllLines () { return _model.GetAllLines (); } /// /// Returns the characters on the current line (where the cursor is positioned). Use /// to determine the position of the cursor within that line /// /// - public List GetCurrentLine () { return _model.GetLine (CurrentRow); } + public List GetCurrentLine () { return _model.GetLine (CurrentRow); } /// Returns the characters on the . /// The intended line. /// - public List GetLine (int line) { return _model.GetLine (line); } + public List GetLine (int line) { return _model.GetLine (line); } /// public override Attribute GetNormalColor () @@ -3231,26 +3081,26 @@ public void Load (Stream stream) UpdateWrapModel (); } - /// Loads the contents of the list into the . + /// Loads the contents of the list into the . /// Rune cells list to load the contents from. - public void Load (List cells) + public void Load (List cells) { SetWrapModel (); - _model.LoadRuneCells (cells, ColorScheme); + _model.LoadCells (cells, ColorScheme?.Focus); _historyText.Clear (Text); ResetPosition (); SetNeedsDisplay (); UpdateWrapModel (); - InheritsPreviousColorScheme = true; + InheritsPreviousAttribute = true; } - /// Loads the contents of the list of list into the . + /// Loads the contents of the list of list into the . /// List of rune cells list to load the contents from. - public void Load (List> cellsList) + public void Load (List> cellsList) { SetWrapModel (); - InheritsPreviousColorScheme = true; - _model.LoadListRuneCells (cellsList, ColorScheme); + InheritsPreviousAttribute = true; + _model.LoadListCells (cellsList, ColorScheme?.Focus); _historyText.Clear (Text); ResetPosition (); SetNeedsDisplay (); @@ -3347,7 +3197,7 @@ protected internal override bool OnMouseEvent (MouseEvent ev) } else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) { - ProcessMouseClick (ev, out List line); + ProcessMouseClick (ev, out List line); PositionCursor (); if (_model.Count > 0 && _shiftSelecting && Selecting) @@ -3446,7 +3296,7 @@ protected internal override bool OnMouseEvent (MouseEvent ev) StopSelecting (); } - ProcessMouseClick (ev, out List line); + ProcessMouseClick (ev, out List line); (int col, int row)? newPos; if (CurrentColumn == line.Count @@ -3483,7 +3333,7 @@ protected internal override bool OnMouseEvent (MouseEvent ev) StopSelecting (); } - ProcessMouseClick (ev, out List line); + ProcessMouseClick (ev, out List line); CurrentColumn = 0; if (!Selecting) @@ -3509,7 +3359,7 @@ protected internal override bool OnMouseEvent (MouseEvent ev) public void MoveEnd () { CurrentRow = _model.Count - 1; - List line = GetCurrentLine (); + List line = GetCurrentLine (); CurrentColumn = line.Count; TrackColumn (); PositionCursor (); @@ -3554,7 +3404,7 @@ public override void OnDrawContent (Rectangle viewport) for (int idxRow = _topRow; idxRow < _model.Count; idxRow++) { - List line = _model.GetLine (idxRow); + List line = _model.GetLine (idxRow); int lineRuneCount = line.Count; var col = 0; @@ -3726,12 +3576,12 @@ public void Paste () if (_copyWithoutSelection && contents.FirstOrDefault (x => x == '\n' || x == '\r') == 0) { - List runeList = contents is null ? new () : RuneCell.ToRuneCellList (contents); - List currentLine = GetCurrentLine (); + List runeList = contents is null ? new () : Cell.ToCellList (contents); + List currentLine = GetCurrentLine (); _historyText.Add (new () { new (currentLine) }, CursorPosition); - List> addedLine = new () { new (currentLine), runeList }; + List> addedLine = new () { new (currentLine), runeList }; _historyText.Add ( new (addedLine), @@ -3797,7 +3647,7 @@ public void Paste () SetNeedsDisplay (); } - List line = _model.GetLine (CurrentRow); + List line = _model.GetLine (CurrentRow); var col = 0; if (line.Count > 0) @@ -3956,16 +3806,16 @@ public void Undo () /// The line. /// The col index. /// The row index. - protected virtual void OnDrawNormalColor (List line, int idxCol, int idxRow) + protected virtual void OnDrawNormalColor (List line, int idxCol, int idxRow) { (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol); - var ev = new RuneCellEventArgs (line, idxCol, unwrappedPos); + var ev = new CellEventArgs (line, idxCol, unwrappedPos); DrawNormalColor?.Invoke (this, ev); - if (line [idxCol].ColorScheme is { }) + if (line [idxCol].Attribute is { }) { - ColorScheme? colorScheme = line [idxCol].ColorScheme; - Driver.SetAttribute (Enabled ? colorScheme!.Focus : colorScheme!.Disabled); + Attribute? attribute = line [idxCol].Attribute; + Driver.SetAttribute ((Attribute)attribute!); } else { @@ -3982,22 +3832,22 @@ protected virtual void OnDrawNormalColor (List line, int idxCol, int i /// The col index. /// /// /// The row index. - protected virtual void OnDrawReadOnlyColor (List line, int idxCol, int idxRow) + protected virtual void OnDrawReadOnlyColor (List line, int idxCol, int idxRow) { (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol); - var ev = new RuneCellEventArgs (line, idxCol, unwrappedPos); + var ev = new CellEventArgs (line, idxCol, unwrappedPos); DrawReadOnlyColor?.Invoke (this, ev); - ColorScheme? colorScheme = line [idxCol].ColorScheme is { } ? line [idxCol].ColorScheme : ColorScheme; + Attribute? cellAttribute = line [idxCol].Attribute is { } ? line [idxCol].Attribute : ColorScheme?.Disabled; Attribute attribute; - if (colorScheme!.Disabled.Foreground == colorScheme.Focus.Background) + if (cellAttribute!.Value.Foreground == cellAttribute.Value.Background) { - attribute = new (colorScheme.Focus.Foreground, colorScheme.Focus.Background); + attribute = new (cellAttribute.Value.Foreground, cellAttribute.Value.Background); } else { - attribute = new (colorScheme.Disabled.Foreground, colorScheme.Focus.Background); + attribute = new (cellAttribute.Value.Foreground, ColorScheme!.Focus.Background); } Driver.SetAttribute (attribute); @@ -4012,18 +3862,18 @@ protected virtual void OnDrawReadOnlyColor (List line, int idxCol, int /// The col index. /// /// /// The row index. - protected virtual void OnDrawSelectionColor (List line, int idxCol, int idxRow) + protected virtual void OnDrawSelectionColor (List line, int idxCol, int idxRow) { (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol); - var ev = new RuneCellEventArgs (line, idxCol, unwrappedPos); + var ev = new CellEventArgs (line, idxCol, unwrappedPos); DrawSelectionColor?.Invoke (this, ev); - if (line [idxCol].ColorScheme is { }) + if (line [idxCol].Attribute is { }) { - ColorScheme? colorScheme = line [idxCol].ColorScheme; + Attribute? attribute = line [idxCol].Attribute; Driver.SetAttribute ( - new (colorScheme!.Focus.Background, colorScheme.Focus.Foreground) + new (attribute!.Value.Background, attribute.Value.Foreground) ); } else @@ -4046,20 +3896,20 @@ protected virtual void OnDrawSelectionColor (List line, int idxCol, in /// The col index. /// /// /// The row index. - protected virtual void OnDrawUsedColor (List line, int idxCol, int idxRow) + protected virtual void OnDrawUsedColor (List line, int idxCol, int idxRow) { (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol); - var ev = new RuneCellEventArgs (line, idxCol, unwrappedPos); + var ev = new CellEventArgs (line, idxCol, unwrappedPos); DrawUsedColor?.Invoke (this, ev); - if (line [idxCol].ColorScheme is { }) + if (line [idxCol].Attribute is { }) { - ColorScheme? colorScheme = line [idxCol].ColorScheme; - SetValidUsedColor (colorScheme!); + Attribute? attribute = line [idxCol].Attribute; + SetValidUsedColor (attribute!); } else { - SetValidUsedColor (ColorScheme); + SetValidUsedColor (ColorScheme?.Focus); } } @@ -4072,7 +3922,7 @@ protected virtual void OnDrawUsedColor (List line, int idxCol, int idx private void Adjust () { (int width, int height) offB = OffSetBackground (); - List line = GetCurrentLine (); + List line = GetCurrentLine (); bool need = NeedsDisplay || _wrapNeeded || !Used; (int size, int length) tSize = TextModel.DisplaySize (line, -1, -1, false, TabWidth); (int size, int length) dSize = TextModel.DisplaySize (line, _leftColumn, CurrentColumn, true, TabWidth); @@ -4234,11 +4084,11 @@ private void ClearRegion () var maxrow = (int)(end >> 32); var startCol = (int)(start & 0xffffffff); var endCol = (int)(end & 0xffffffff); - List line = _model.GetLine (startRow); + List line = _model.GetLine (startRow); _historyText.Add (new () { new (line) }, new (startCol, startRow)); - List> removedLines = new (); + List> removedLines = new (); if (startRow == maxrow) { @@ -4273,7 +4123,7 @@ private void ClearRegion () removedLines.Add (new (line)); line.RemoveRange (startCol, line.Count - startCol); - List line2 = _model.GetLine (maxrow); + List line2 = _model.GetLine (maxrow); line.AddRange (line2.Skip (endCol)); for (int row = startRow + 1; row <= maxrow; row++) @@ -4324,7 +4174,7 @@ private bool DeleteTextBackwards () if (CurrentColumn > 0) { // Delete backwards - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); _historyText.Add (new () { new (currentLine) }, CursorPosition); @@ -4364,11 +4214,11 @@ private bool DeleteTextBackwards () } int prowIdx = CurrentRow - 1; - List prevRow = _model.GetLine (prowIdx); + List prevRow = _model.GetLine (prowIdx); _historyText.Add (new () { new (prevRow) }, CursorPosition); - List> removedLines = new () { new (prevRow) }; + List> removedLines = new () { new (prevRow) }; removedLines.Add (new (GetCurrentLine ())); @@ -4408,7 +4258,7 @@ private bool DeleteTextForwards () { SetWrapModel (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); if (CurrentColumn == currentLine.Count) { @@ -4421,9 +4271,9 @@ private bool DeleteTextForwards () _historyText.Add (new () { new (currentLine) }, CursorPosition); - List> removedLines = new () { new (currentLine) }; + List> removedLines = new () { new (currentLine) }; - List nextLine = _model.GetLine (CurrentRow + 1); + List nextLine = _model.GetLine (CurrentRow + 1); removedLines.Add (new (nextLine)); @@ -4503,7 +4353,7 @@ private void DoSetNeedsDisplay (Rectangle rect) } } - private IEnumerable<(int col, int row, RuneCell rune)> ForwardIterator (int col, int row) + private IEnumerable<(int col, int row, Cell rune)> ForwardIterator (int col, int row) { if (col < 0 || row < 0) { @@ -4515,7 +4365,7 @@ private void DoSetNeedsDisplay (Rectangle rect) yield break; } - List line = GetCurrentLine (); + List line = GetCurrentLine (); if (col >= line.Count) { @@ -4537,7 +4387,7 @@ private void DoSetNeedsDisplay (Rectangle rect) private void GenerateSuggestions () { - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); int cursorPosition = Math.Min (CurrentColumn, currentLine.Count); Autocomplete.Context = new ( @@ -4613,7 +4463,7 @@ private string GetRegion ( var maxrow = (int)(end >> 32); var startCol = (int)(start & 0xffffffff); var endCol = (int)(end & 0xffffffff); - List line = model is null ? _model.GetLine (startRow) : model.GetLine (startRow); + List line = model is null ? _model.GetLine (startRow) : model.GetLine (startRow); if (startRow == maxrow) { @@ -4738,9 +4588,9 @@ private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItemE OnContentsChanged (); } - private void Insert (RuneCell cell) + private void Insert (Cell cell) { - List line = GetCurrentLine (); + List line = GetCurrentLine (); if (Used) { @@ -4773,7 +4623,7 @@ private void InsertAllText (string text) return; } - List> lines = RuneCell.StringToLinesOfRuneCells (text); + List> lines = Cell.StringToLinesOfCells (text); if (lines.Count == 0) { @@ -4782,7 +4632,7 @@ private void InsertAllText (string text) SetWrapModel (); - List line = GetCurrentLine (); + List line = GetCurrentLine (); _historyText.Add (new () { new (line) }, CursorPosition); @@ -4821,7 +4671,7 @@ private void InsertAllText (string text) return; } - List? rest = null; + List? rest = null; var lastp = 0; if (_model.Count > 0 && line.Count > 0 && !_copyWithoutSelection) @@ -4837,7 +4687,7 @@ private void InsertAllText (string text) //model.AddLine (currentRow, lines [0]); - List> addedLines = new () { new (line) }; + List> addedLines = new () { new (line) }; for (var i = 1; i < lines.Count; i++) { @@ -4848,7 +4698,7 @@ private void InsertAllText (string text) if (rest is { }) { - List last = _model.GetLine (CurrentRow + lines.Count - 1); + List last = _model.GetLine (CurrentRow + lines.Count - 1); lastp = last.Count; last.InsertRange (last.Count, rest); @@ -4872,7 +4722,7 @@ private void InsertAllText (string text) OnContentsChanged (); } - private bool InsertText (Key a, ColorScheme? colorScheme = null) + private bool InsertText (Key a, Attribute? attribute = null) { //So that special keys like tab can be processed if (_isReadOnly) @@ -4891,7 +4741,7 @@ private bool InsertText (Key a, ColorScheme? colorScheme = null) if ((uint)a.KeyCode == '\n') { - _model.AddLine (CurrentRow + 1, new ()); + _model.AddLine (CurrentRow + 1, []); CurrentRow++; CurrentColumn = 0; } @@ -4903,7 +4753,7 @@ private bool InsertText (Key a, ColorScheme? colorScheme = null) { if (Used) { - Insert (new () { Rune = a.AsRune, ColorScheme = colorScheme }); + Insert (new () { Rune = a.AsRune, Attribute = attribute }); CurrentColumn++; if (CurrentColumn >= _leftColumn + Viewport.Width) @@ -4914,7 +4764,7 @@ private bool InsertText (Key a, ColorScheme? colorScheme = null) } else { - Insert (new () { Rune = a.AsRune, ColorScheme = colorScheme }); + Insert (new () { Rune = a.AsRune, Attribute = attribute }); CurrentColumn++; } } @@ -4946,7 +4796,7 @@ private void KillToEndOfLine () SetWrapModel (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); var setLastWasKill = true; if (currentLine.Count > 0 && CurrentColumn == currentLine.Count) @@ -4964,7 +4814,7 @@ private void KillToEndOfLine () { if (CurrentRow < _model.Count - 1) { - List> removedLines = new () { new (currentLine) }; + List> removedLines = new () { new (currentLine) }; _model.RemoveLine (CurrentRow); @@ -5000,7 +4850,7 @@ private void KillToEndOfLine () else { int restCount = currentLine.Count - CurrentColumn; - List rest = currentLine.GetRange (CurrentColumn, restCount); + List rest = currentLine.GetRange (CurrentColumn, restCount); var val = string.Empty; val += StringFromRunes (rest); @@ -5045,7 +4895,7 @@ private void KillToLeftStart () SetWrapModel (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); var setLastWasKill = true; if (currentLine.Count > 0 && CurrentColumn == 0) @@ -5088,7 +4938,7 @@ private void KillToLeftStart () CurrentRow--; currentLine = _model.GetLine (CurrentRow); - List> removedLine = + List> removedLine = [ [..currentLine], [] @@ -5106,7 +4956,7 @@ private void KillToLeftStart () else { int restCount = CurrentColumn; - List rest = currentLine.GetRange (0, restCount); + List rest = currentLine.GetRange (0, restCount); var val = string.Empty; val += StringFromRunes (rest); @@ -5146,7 +4996,7 @@ private void KillWordBackward () SetWrapModel (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition); @@ -5214,7 +5064,7 @@ private void KillWordForward () SetWrapModel (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition); @@ -5335,7 +5185,7 @@ private bool MoveDown () private void MoveEndOfLine () { - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); CurrentColumn = Math.Max (currentLine.Count - (ReadOnly ? 1 : 0), 0); Adjust (); DoNeededAction (); @@ -5359,7 +5209,7 @@ private bool MoveLeft () SetNeedsDisplay (); } - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); CurrentColumn = Math.Max (currentLine.Count - (ReadOnly ? 1 : 0), 0); } else @@ -5432,7 +5282,7 @@ private void MovePageUp () private bool MoveRight () { - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); if ((ReadOnly ? CurrentColumn + 1 : CurrentColumn) < currentLine.Count) { @@ -5628,7 +5478,7 @@ private bool ProcessBackTab () { SetWrapModel (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); if (currentLine.Count > 0 && currentLine [CurrentColumn - 1].Rune.Value == '\t') { @@ -5683,13 +5533,13 @@ private void ProcessDeleteCharRight () // the very most previous valid color scheme. private void ProcessInheritsPreviousColorScheme (int row, int col) { - if (!InheritsPreviousColorScheme || (Lines == 1 && GetLine (Lines).Count == 0)) + if (!InheritsPreviousAttribute || (Lines == 1 && GetLine (Lines).Count == 0)) { return; } - List line = GetLine (row); - List lineToSet = line; + List line = GetLine (row); + List lineToSet = line; while (line.Count == 0) { @@ -5704,20 +5554,25 @@ private void ProcessInheritsPreviousColorScheme (int row, int col) } int colWithColor = Math.Max (Math.Min (col - 2, line.Count - 1), 0); - RuneCell cell = line [colWithColor]; + Cell cell = line [colWithColor]; int colWithoutColor = Math.Max (col - 1, 0); - if (cell.ColorScheme is { } && colWithColor == 0 && lineToSet [colWithoutColor].ColorScheme is { }) + Cell lineTo = lineToSet [colWithoutColor]; + + if (cell.Attribute is { } && colWithColor == 0 && lineTo.Attribute is { }) { for (int r = row - 1; r > -1; r--) { - List l = GetLine (r); + List l = GetLine (r); for (int c = l.Count - 1; c > -1; c--) { - if (l [c].ColorScheme is null) + Cell cell1 = l [c]; + + if (cell1.Attribute is null) { - l [c].ColorScheme = cell.ColorScheme; + cell1.Attribute = cell.Attribute; + l [c] = cell1; } else { @@ -5729,18 +5584,18 @@ private void ProcessInheritsPreviousColorScheme (int row, int col) return; } - if (cell.ColorScheme is null) + if (cell.Attribute is null) { for (int r = row; r > -1; r--) { - List l = GetLine (r); + List l = GetLine (r); colWithColor = l.FindLastIndex ( colWithColor > -1 ? colWithColor : l.Count - 1, - rc => rc.ColorScheme != null + c => c.Attribute != null ); - if (colWithColor > -1 && l [colWithColor].ColorScheme is { }) + if (colWithColor > -1 && l [colWithColor].Attribute is { }) { cell = l [colWithColor]; @@ -5752,9 +5607,9 @@ private void ProcessInheritsPreviousColorScheme (int row, int col) { int cRow = row; - while (cell.ColorScheme is null) + while (cell.Attribute is null) { - if ((colWithColor == 0 || cell.ColorScheme is null) && cRow > 0) + if ((colWithColor == 0 || cell.Attribute is null) && cRow > 0) { line = GetLine (--cRow); colWithColor = line.Count - 1; @@ -5767,11 +5622,12 @@ private void ProcessInheritsPreviousColorScheme (int row, int col) } } - if (cell.ColorScheme is { } && colWithColor > -1 && colWithoutColor < lineToSet.Count && lineToSet [colWithoutColor].ColorScheme is null) + if (cell.Attribute is { } && colWithColor > -1 && colWithoutColor < lineToSet.Count && lineTo.Attribute is null) { - while (lineToSet [colWithoutColor].ColorScheme is null) + while (lineTo.Attribute is null) { - lineToSet [colWithoutColor].ColorScheme = cell.ColorScheme; + lineTo.Attribute = cell.Attribute; + lineToSet [colWithoutColor] = lineTo; colWithoutColor--; if (colWithoutColor == -1 && row > 0) @@ -5795,9 +5651,9 @@ private void ProcessKillWordForward () KillWordForward (); } - private void ProcessMouseClick (MouseEvent ev, out List line) + private void ProcessMouseClick (MouseEvent ev, out List line) { - List? r = null; + List? r = null; if (_model.Count > 0) { @@ -6069,7 +5925,7 @@ private bool ProcessReturn () SetWrapModel (); - List currentLine = GetCurrentLine (); + List currentLine = GetCurrentLine (); _historyText.Add (new () { new (currentLine) }, CursorPosition); @@ -6080,10 +5936,10 @@ private bool ProcessReturn () } int restCount = currentLine.Count - CurrentColumn; - List rest = currentLine.GetRange (CurrentColumn, restCount); + List rest = currentLine.GetRange (CurrentColumn, restCount); currentLine.RemoveRange (CurrentColumn, restCount); - List> addedLines = new () { new (currentLine) }; + List> addedLines = new () { new (currentLine) }; _model.AddLine (CurrentRow + 1, rest); @@ -6269,11 +6125,11 @@ private void SetOverwrite (bool overwrite) DoNeededAction (); } - private static void SetValidUsedColor (ColorScheme? colorScheme) + private static void SetValidUsedColor (Attribute? attribute) { // 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 (colorScheme!.Focus.Background, colorScheme!.Focus.Foreground)); + Driver.SetAttribute (new (attribute!.Value.Background, attribute!.Value.Foreground)); } /// Restore from original model. @@ -6328,7 +6184,7 @@ private void StopSelecting () _isButtonShift = false; } - private string StringFromRunes (List cells) + private string StringFromRunes (List cells) { if (cells is null) { @@ -6337,7 +6193,7 @@ private string StringFromRunes (List cells) var size = 0; - foreach (RuneCell cell in cells) + foreach (Cell cell in cells) { size += cell.Rune.GetEncodingLength (); } @@ -6345,7 +6201,7 @@ private string StringFromRunes (List cells) var encoded = new byte [size]; var offset = 0; - foreach (RuneCell cell in cells) + foreach (Cell cell in cells) { offset += cell.Rune.Encode (encoded, offset); } @@ -6389,7 +6245,7 @@ private void ToggleSelecting () private void TrackColumn () { // Now track the column - List line = GetCurrentLine (); + List line = GetCurrentLine (); if (line.Count < _columnTrack) { diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index 0b37314cdd..2b9f637d8d 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -75,7 +75,7 @@ public bool CanExpand () /// public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, int availableWidth) { - List cells = new (); + List cells = new (); int? indexOfExpandCollapseSymbol = null; int indexOfModelText; @@ -106,7 +106,7 @@ public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, } else { - cells.Add (NewRuneCell (attr, r)); + cells.Add (NewCell (attr, r)); availableWidth -= r.GetColumns (); } } @@ -148,7 +148,7 @@ public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, else { indexOfExpandCollapseSymbol = cells.Count; - cells.Add (NewRuneCell (attr, expansion)); + cells.Add (NewCell (attr, expansion)); availableWidth -= expansion.GetColumns (); } @@ -211,7 +211,7 @@ public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, } attr = modelColor; - cells.AddRange (lineBody.Select (r => NewRuneCell (attr, new Rune (r)))); + cells.AddRange (lineBody.Select (r => NewCell (attr, new Rune (r)))); if (availableWidth > 0) { @@ -219,7 +219,7 @@ public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, cells.AddRange ( Enumerable.Repeat ( - NewRuneCell (attr, new Rune (' ')), + NewCell (attr, new Rune (' ')), availableWidth ) ); @@ -229,7 +229,7 @@ public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, { Model = Model, Y = y, - RuneCells = cells, + Cells = cells, Tree = tree, IndexOfExpandCollapseSymbol = indexOfExpandCollapseSymbol, @@ -239,9 +239,9 @@ public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, if (!e.Handled) { - foreach (RuneCell cell in cells) + foreach (Cell cell in cells) { - driver.SetAttribute (cell.ColorScheme.Normal); + driver.SetAttribute ((Attribute)cell.Attribute!); driver.AddRune (cell.Rune); } } @@ -529,5 +529,5 @@ private bool IsLast () return Parent.ChildBranches.Values.LastOrDefault () == this; } - private static RuneCell NewRuneCell (Attribute attr, Rune r) { return new RuneCell { Rune = r, ColorScheme = new ColorScheme (attr) }; } + private static Cell NewCell (Attribute attr, Rune r) { return new Cell { Rune = r, Attribute = new (attr) }; } } diff --git a/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs b/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs index 1454abff7e..6cd03747d1 100644 --- a/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs +++ b/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs @@ -12,16 +12,16 @@ public class DrawTreeViewLineEventArgs where T : class public bool Handled { get; set; } /// - /// If line contains a branch that can be expanded/collapsed then this is the index in at + /// If line contains a branch that can be expanded/collapsed then this is the index in at /// which the symbol is (or null for leaf elements). /// public int? IndexOfExpandCollapseSymbol { get; init; } /// - /// The notional index in which contains the first character of the + /// The notional index in which contains the first character of the /// text (i.e. after all branch lines and expansion/collapse symbols). /// - /// May be negative or outside of bounds of if the view has been scrolled horizontally. + /// May be negative or outside of bounds of if the view has been scrolled horizontally. public int IndexOfModelText { get; init; } /// The object at this line in the tree @@ -32,7 +32,7 @@ public class DrawTreeViewLineEventArgs where T : class /// respected. You can modify these to change what is rendered. /// /// Changing the length of this collection may result in corrupt rendering - public List RuneCells { get; init; } + public List Cells { get; init; } /// The that is performing the rendering. public TreeView Tree { get; init; } diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 1fcc6dafda..ec17e6a391 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -210,22 +210,18 @@ public override void Main () { if (!PromptForColor ( "Colors", - GetSelectedRuneCellAttribute (), + GetSelectedCellAttribute (), out Attribute newAttribute )) { return; } - var cs = new ColorScheme (_textView.ColorScheme) - { - Focus = new ( - newAttribute.Foreground, + var attribute = new Attribute (newAttribute.Foreground, newAttribute.Background - ) - }; + ); - ApplyRuneCellAttribute (cs); + ApplyCellAttribute (attribute); }) } ), @@ -341,7 +337,7 @@ out Attribute newAttribute } - private void ApplyRuneCellAttribute (ColorScheme cs) + private void ApplyCellAttribute (Attribute attribute) { if (!_textView.ReadOnly && _textView.SelectedLength > 0) { @@ -352,29 +348,31 @@ private void ApplyRuneCellAttribute (ColorScheme cs) for (int r = startRow; r <= endRow; r++) { - List line = _textView.GetLine (r); + List line = _textView.GetLine (r); for (int c = r == startRow ? startCol : 0; c < (r == endRow ? endCol : line.Count); c++) { - line [c].ColorScheme = cs; + Cell cell = line [c]; // Copy value to a new variable + cell.Attribute = attribute; // Modify the copy + line [c] = cell; // Assign the modified copy back } } } } - private Attribute? GetSelectedRuneCellAttribute () + private Attribute? GetSelectedCellAttribute () { - List line; + List line; if (_textView.SelectedLength > 0) { line = _textView.GetLine (_textView.SelectionStartRow); - if (line [Math.Min (_textView.SelectionStartColumn, line.Count - 1)].ColorScheme is { } csSel) + if (line [Math.Min (_textView.SelectionStartColumn, line.Count - 1)].Attribute is { } attributeSel) { - return new (csSel.Focus); + return new (attributeSel); } return new (_textView.ColorScheme!.Focus); @@ -382,9 +380,9 @@ private void ApplyRuneCellAttribute (ColorScheme cs) line = _textView.GetCurrentLine (); - if (line [Math.Min (_textView.CurrentColumn, line.Count - 1)].ColorScheme is { } cs) + if (line [Math.Min (_textView.CurrentColumn, line.Count - 1)].Attribute is { } attribute) { - return new (cs!.Focus); + return new (attribute); } return new (_textView.ColorScheme!.Focus); diff --git a/UICatalog/Scenarios/SyntaxHighlighting.cs b/UICatalog/Scenarios/SyntaxHighlighting.cs index ce4e970a2c..398355e34d 100644 --- a/UICatalog/Scenarios/SyntaxHighlighting.cs +++ b/UICatalog/Scenarios/SyntaxHighlighting.cs @@ -85,13 +85,13 @@ public class SyntaxHighlighting : Scenario "exists" }; - private readonly string _path = "RuneCells.rce"; - private ColorScheme _blue; - private ColorScheme _green; - private ColorScheme _magenta; + private readonly string _path = "Cells.rce"; + private Attribute _blue; + private Attribute _green; + private Attribute _magenta; private MenuItem _miWrap; private TextView _textView; - private ColorScheme _white; + private Attribute _white; /// /// Reads an object instance from an Json file. @@ -155,12 +155,12 @@ public override void Main () new ( "_Load Rune Cells", "", - () => ApplyLoadRuneCells () + () => ApplyLoadCells () ), new ( "_Save Rune Cells", "", - () => SaveRuneCells () + () => SaveCells () ), null, new ("_Quit", "", () => Quit ()) @@ -231,11 +231,11 @@ public override void Main () } } - private void ApplyLoadRuneCells () + private void ApplyLoadCells () { ClearAllEvents (); - List runeCells = new (); + List cells = new (); foreach (KeyValuePair color in Colors.ColorSchemes) { @@ -243,21 +243,21 @@ private void ApplyLoadRuneCells () foreach (Rune rune in csName.EnumerateRunes ()) { - runeCells.Add (new() { Rune = rune, ColorScheme = color.Value }); + cells.Add (new() { Rune = rune, Attribute = color.Value.Normal }); } - runeCells.Add (new() { Rune = (Rune)'\n', ColorScheme = color.Value }); + cells.Add (new() { Rune = (Rune)'\n', Attribute = color.Value.Focus }); } if (File.Exists (_path)) { - //Reading the file - List> cells = ReadFromJsonFile>> (_path); - _textView.Load (cells); + //Reading the file + List> fileCells = ReadFromJsonFile>> (_path); + _textView.Load (fileCells); } else { - _textView.Load (runeCells); + _textView.Load (cells); } _textView.Autocomplete.SuggestionGenerator = new SingleWordSuggestionGenerator (); @@ -267,11 +267,11 @@ private void ApplySyntaxHighlighting () { ClearAllEvents (); - _green = new (new Attribute (Color.Green, Color.Black)); - _blue = new (new Attribute (Color.Blue, Color.Black)); - _magenta = new (new Attribute (Color.Magenta, Color.Black)); - _white = new (new Attribute (Color.White, Color.Black)); - _textView.ColorScheme = _white; + _green = new Attribute (Color.Green, Color.Black); + _blue = new Attribute (Color.Blue, Color.Black); + _magenta = new Attribute (Color.Magenta, Color.Black); + _white = new Attribute (Color.White, Color.Black); + _textView.ColorScheme = new () { Focus = _white }; _textView.Text = "/*Query to select:\nLots of data*/\nSELECT TOP 100 * \nfrom\n MyDb.dbo.Biochemistry where TestCode = 'blah';"; @@ -292,7 +292,7 @@ private void ClearAllEvents () _textView.ClearEventHandlers ("DrawContent"); _textView.ClearEventHandlers ("DrawContentComplete"); - _textView.InheritsPreviousColorScheme = false; + _textView.InheritsPreviousAttribute = false; } private bool ContainsPosition (Match m, int pos) { return pos >= m.Index && pos < m.Index + m.Length; } @@ -317,27 +317,30 @@ private void HighlightTextBasedOnKeywords () for (var y = 0; y < _textView.Lines; y++) { - List line = _textView.GetLine (y); + List line = _textView.GetLine (y); for (var x = 0; x < line.Count; x++) { + Cell cell = line [x]; + if (commentMatches.Any (m => ContainsPosition (m, pos))) { - line [x].ColorScheme = _green; + cell.Attribute = _green; } else if (singleQuoteMatches.Any (m => ContainsPosition (m, pos))) { - line [x].ColorScheme = _magenta; + cell.Attribute = _magenta; } else if (keywordMatches.Any (m => ContainsPosition (m, pos))) { - line [x].ColorScheme = _blue; + cell.Attribute = _blue; } else { - line [x].ColorScheme = _white; + cell.Attribute = _white; } + line [x] = cell; pos++; } @@ -384,10 +387,10 @@ private bool IsKeyword (List line, int idx) private void Quit () { Application.RequestStop (); } - private void SaveRuneCells () + private void SaveCells () { //Writing to file - List> cells = _textView.GetAllLines (); + List> cells = _textView.GetAllLines (); WriteToJsonFile (_path, cells); } diff --git a/UICatalog/Scenarios/TreeViewFileSystem.cs b/UICatalog/Scenarios/TreeViewFileSystem.cs index d2073bd18c..590af81b1c 100644 --- a/UICatalog/Scenarios/TreeViewFileSystem.cs +++ b/UICatalog/Scenarios/TreeViewFileSystem.cs @@ -441,16 +441,14 @@ private void TreeViewFiles_DrawLine (object sender, DrawTreeViewLineEventArgs 0 && e.IndexOfModelText < e.RuneCells.Count) + if (e.IndexOfModelText > 0 && e.IndexOfModelText < e.Cells.Count) { - RuneCell cell = e.RuneCells [e.IndexOfModelText]; - - cell.ColorScheme = new ColorScheme ( - new Attribute ( - Color.BrightYellow, - cell.ColorScheme.Normal.Background - ) - ); + Cell cell = e.Cells [e.IndexOfModelText]; + + cell.Attribute = new Attribute ( + Color.BrightYellow, + cell.Attribute!.Value.Background + ); } } } diff --git a/UnitTests/Configuration/ThemeTests.cs b/UnitTests/Configuration/ThemeTests.cs index ffce969d82..e7d023d100 100644 --- a/UnitTests/Configuration/ThemeTests.cs +++ b/UnitTests/Configuration/ThemeTests.cs @@ -10,6 +10,7 @@ public class ThemeTests Converters = { new AttributeJsonConverter (), new ColorJsonConverter () } }; + [Fact] [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)] public void TestApply () { diff --git a/UnitTests/Views/RuneCellTests.cs b/UnitTests/Drawing/CellTests.cs similarity index 61% rename from UnitTests/Views/RuneCellTests.cs rename to UnitTests/Drawing/CellTests.cs index ccbce08c98..459c6d136d 100644 --- a/UnitTests/Views/RuneCellTests.cs +++ b/UnitTests/Drawing/CellTests.cs @@ -1,59 +1,59 @@ using System.Text; using Xunit.Abstractions; -namespace Terminal.Gui.ViewsTests; +namespace Terminal.Gui.DrawingTests; -public class RuneCellTests (ITestOutputHelper output) +public class CellTests (ITestOutputHelper output) { [Fact] public void Constructor_Defaults () { - var rc = new RuneCell (); - Assert.NotNull (rc); - Assert.Equal (0, rc.Rune.Value); - Assert.Null (rc.ColorScheme); + var c = new Cell (); + Assert.True (c is { }); + Assert.Equal (0, c.Rune.Value); + Assert.Null (c.Attribute); } [Fact] public void Equals_False () { - var rc1 = new RuneCell (); + var c1 = new Cell (); - var rc2 = new RuneCell + var c2 = new Cell { - Rune = new ('a'), ColorScheme = new() { Normal = new (Color.Red) } + Rune = new ('a'), Attribute = new (Color.Red) }; - Assert.False (rc1.Equals (rc2)); - Assert.False (rc2.Equals (rc1)); - - rc1.Rune = new ('a'); - rc1.ColorScheme = new (); - Assert.Equal (rc1.Rune, rc2.Rune); - Assert.False (rc1.Equals (rc2)); - Assert.False (rc2.Equals (rc1)); + Assert.False (c1.Equals (c2)); + Assert.False (c2.Equals (c1)); + + c1.Rune = new ('a'); + c1.Attribute = new (); + Assert.Equal (c1.Rune, c2.Rune); + Assert.False (c1.Equals (c2)); + Assert.False (c2.Equals (c1)); } [Fact] public void Equals_True () { - var rc1 = new RuneCell (); - var rc2 = new RuneCell (); - Assert.True (rc1.Equals (rc2)); - Assert.True (rc2.Equals (rc1)); - - rc1.Rune = new ('a'); - rc1.ColorScheme = new (); - rc2.Rune = new ('a'); - rc2.ColorScheme = new (); - Assert.True (rc1.Equals (rc2)); - Assert.True (rc2.Equals (rc1)); + var c1 = new Cell (); + var c2 = new Cell (); + Assert.True (c1.Equals (c2)); + Assert.True (c2.Equals (c1)); + + c1.Rune = new ('a'); + c1.Attribute = new (); + c2.Rune = new ('a'); + c2.Attribute = new (); + Assert.True (c1.Equals (c2)); + Assert.True (c2.Equals (c1)); } [Fact] [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)] - public void RuneCell_LoadRuneCells_InheritsPreviousColorScheme () + public void Cell_LoadCells_InheritsPreviousAttribute () { - List runeCells = new (); + List cells = []; foreach (KeyValuePair color in Colors.ColorSchemes) { @@ -61,18 +61,18 @@ public void RuneCell_LoadRuneCells_InheritsPreviousColorScheme () foreach (Rune rune in csName.EnumerateRunes ()) { - runeCells.Add (new() { Rune = rune, ColorScheme = color.Value }); + cells.Add (new() { Rune = rune, Attribute = color.Value.Normal }); } - runeCells.Add (new() { Rune = (Rune)'\n', ColorScheme = color.Value }); + cells.Add (new() { Rune = (Rune)'\n', Attribute = color.Value.Focus }); } TextView tv = CreateTextView (); - tv.Load (runeCells); + tv.Load (cells); var top = new Toplevel (); top.Add (tv); RunState rs = Application.Begin (top); - Assert.True (tv.InheritsPreviousColorScheme); + Assert.True (tv.InheritsPreviousAttribute); var expectedText = @" TopLevel @@ -85,27 +85,30 @@ public void RuneCell_LoadRuneCells_InheritsPreviousColorScheme () Attribute [] attributes = { // 0 - Colors.ColorSchemes ["TopLevel"].Focus, + Colors.ColorSchemes ["TopLevel"].Normal, // 1 - Colors.ColorSchemes ["Base"].Focus, + Colors.ColorSchemes ["Base"].Normal, // 2 - Colors.ColorSchemes ["Dialog"].Focus, + Colors.ColorSchemes ["Dialog"].Normal, // 3 - Colors.ColorSchemes ["Menu"].Focus, + Colors.ColorSchemes ["Menu"].Normal, // 4 - Colors.ColorSchemes ["Error"].Focus + Colors.ColorSchemes ["Error"].Normal, + + // 5 + tv.ColorScheme!.Focus }; var expectedColor = @" -0000000000 -1111000000 -2222220000 -3333000000 -4444400000"; +0000000055 +1111555555 +2222225555 +3333555555 +4444455555"; TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); tv.WordWrap = true; @@ -134,13 +137,13 @@ Colors.ColorSchemes ["Error"].Focus TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output); expectedColor = @" -0000000000 -1111000000 -2222220000 -3333000000 -4444444444 -4444000000 -4444444440"; +0000000055 +1111555555 +2222225555 +3333555555 +4455555555 +5555555555 +5555544445"; TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); tv.Undo (); @@ -170,14 +173,14 @@ Colors.ColorSchemes ["Error"].Focus TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output); expectedColor = @" -0000000000 -1111000000 -2222220000 -3333000000 +0000000055 +1111555555 +2222225555 +3333555555 4444444444 -4444000000 -4444440000 -4440000000"; +4444555555 +4444445555 +4445555555"; TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); Application.End (rs); @@ -185,9 +188,9 @@ Colors.ColorSchemes ["Error"].Focus } [Fact] - public void RuneCell_LoadRuneCells_Without_ColorScheme_Is_Never_Null () + public void Cell_LoadCells_Without_ColorScheme_Is_Never_Null () { - List cells = new () + List cells = new () { new() { Rune = new ('T') }, new() { Rune = new ('e') }, @@ -201,43 +204,44 @@ public void RuneCell_LoadRuneCells_Without_ColorScheme_Is_Never_Null () for (var i = 0; i < tv.Lines; i++) { - List line = tv.GetLine (i); + List line = tv.GetLine (i); - foreach (RuneCell rc in line) + foreach (Cell c in line) { - Assert.NotNull (rc.ColorScheme); + Assert.NotNull (c.Attribute); } } } [Fact] [AutoInitShutdown] - public void RuneCellEventArgs_WordWrap_True () + public void CellEventArgs_WordWrap_True () { var eventCount = 0; - List> text = new () - { - RuneCell.ToRuneCells ( - "This is the first line.".ToRunes () - ), - RuneCell.ToRuneCells ( - "This is the second line.".ToRunes () - ) - }; + List> text = + [ + Cell.ToCells ( + "This is the first line.".ToRunes () + ), + + Cell.ToCells ( + "This is the second line.".ToRunes () + ) + ]; TextView tv = CreateTextView (); tv.DrawNormalColor += _textView_DrawColor; tv.DrawReadOnlyColor += _textView_DrawColor; tv.DrawSelectionColor += _textView_DrawColor; tv.DrawUsedColor += _textView_DrawColor; - void _textView_DrawColor (object sender, RuneCellEventArgs e) + void _textView_DrawColor (object sender, CellEventArgs e) { Assert.Equal (e.Line [e.Col], text [e.UnwrappedPosition.Row] [e.UnwrappedPosition.Col]); eventCount++; } - tv.Text = $"{RuneCell.ToString (text [0])}\n{RuneCell.ToString (text [1])}\n"; + tv.Text = $"{Cell.ToString (text [0])}\n{Cell.ToString (text [1])}\n"; Assert.False (tv.WordWrap); var top = new Toplevel (); top.Add (tv); @@ -275,20 +279,20 @@ This is [Fact] public void ToString_Override () { - var rc1 = new RuneCell (); + var c1 = new Cell (); - var rc2 = new RuneCell + var c2 = new Cell { - Rune = new ('a'), ColorScheme = new() { Normal = new (Color.Red) } + Rune = new ('a'), Attribute = new (Color.Red) }; - Assert.Equal ("U+0000 '\0'; null", rc1.ToString ()); + Assert.Equal ("[\0, ]", c1.ToString ()); Assert.Equal ( - "U+0061 'a'; Normal: [Red,Red]; Focus: [White,Black]; HotNormal: [White,Black]; HotFocus: [White,Black]; Disabled: [White,Black]", - rc2.ToString () + "[a, [Red,Red]]", + c2.ToString () ); } - // TODO: Move the tests below to View or Color - they test ColorScheme, not RuneCell primitives. + // TODO: Move the tests below to View or Color - they test ColorScheme, not Cell primitives. private TextView CreateTextView () { return new() { Width = 30, Height = 10 }; } } diff --git a/UnitTests/Text/AutocompleteTests.cs b/UnitTests/Text/AutocompleteTests.cs index 1689c70c8d..e5b1cf4b93 100644 --- a/UnitTests/Text/AutocompleteTests.cs +++ b/UnitTests/Text/AutocompleteTests.cs @@ -254,7 +254,7 @@ public void Test_GenerateSuggestions_Simple () ac.GenerateSuggestions ( new ( - RuneCell.ToRuneCellList (tv.Text), + Cell.ToCellList (tv.Text), 2 ) ); diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 84a0c931e1..213abf6149 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -1160,7 +1160,7 @@ public void HistoryText_Exceptions () { Assert.Throws ( () => ht.Add ( - new List> { new () }, + new List> { new () }, Point.Empty, (HistoryText.LineStatus)ls ) @@ -1168,7 +1168,7 @@ public void HistoryText_Exceptions () } } - Assert.Null (Record.Exception (() => ht.Add (new List> { new () }, Point.Empty))); + Assert.Null (Record.Exception (() => ht.Add (new List> { new () }, Point.Empty))); } [Fact] @@ -4717,7 +4717,7 @@ public void HistoryText_Undo_Redo_Two_Line_Selected_Return () public void Internal_Tests () { var txt = "This is a text."; - List txtRunes = RuneCell.StringToRuneCells (txt); + List txtRunes = Cell.StringToCells (txt); Assert.Equal (txt.Length, txtRunes.Count); Assert.Equal ('T', txtRunes [0].Rune.Value); Assert.Equal ('h', txtRunes [1].Rune.Value); @@ -4757,8 +4757,8 @@ public void Internal_Tests () Assert.Equal (2, TextModel.CalculateLeftColumn (txtRunes, 0, 9, 8)); var tm = new TextModel (); - tm.AddLine (0, RuneCell.StringToRuneCells ("This is first line.")); - tm.AddLine (1, RuneCell.StringToRuneCells ("This is last line.")); + tm.AddLine (0, Cell.StringToCells ("This is first line.")); + tm.AddLine (1, Cell.StringToCells ("This is last line.")); Assert.Equal ((new Point (2, 0), true), tm.FindNextText ("is", out bool gaveFullTurn)); Assert.False (gaveFullTurn); Assert.Equal ((new Point (5, 0), true), tm.FindNextText ("is", out gaveFullTurn)); @@ -4782,14 +4782,14 @@ public void Internal_Tests () Assert.True (gaveFullTurn); Assert.Equal ((new Point (9, 1), true), tm.ReplaceAllText ("is", false, false, "really")); - Assert.Equal (RuneCell.StringToRuneCells ("Threally really first line."), tm.GetLine (0)); - Assert.Equal (RuneCell.StringToRuneCells ("Threally really last line."), tm.GetLine (1)); + Assert.Equal (Cell.StringToCells ("Threally really first line."), tm.GetLine (0)); + Assert.Equal (Cell.StringToCells ("Threally really last line."), tm.GetLine (1)); tm = new TextModel (); - tm.AddLine (0, RuneCell.StringToRuneCells ("This is first line.")); - tm.AddLine (1, RuneCell.StringToRuneCells ("This is last line.")); + tm.AddLine (0, Cell.StringToCells ("This is first line.")); + tm.AddLine (1, Cell.StringToCells ("This is last line.")); Assert.Equal ((new Point (5, 1), true), tm.ReplaceAllText ("is", false, true, "really")); - Assert.Equal (RuneCell.StringToRuneCells ("This really first line."), tm.GetLine (0)); - Assert.Equal (RuneCell.StringToRuneCells ("This really last line."), tm.GetLine (1)); + Assert.Equal (Cell.StringToCells ("This really first line."), tm.GetLine (0)); + Assert.Equal (Cell.StringToCells ("This really last line."), tm.GetLine (1)); } [Fact] diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index 9156302675..47124f8f88 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -970,10 +970,10 @@ public void TestTreeView_DrawLineEvent () Assert.All (eventArgs, ea => Assert.Equal (ea.Tree, tv)); Assert.All (eventArgs, ea => Assert.False (ea.Handled)); - Assert.Equal ("├-root one", eventArgs [0].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("│ ├─leaf 1", eventArgs [1].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("│ └─leaf 2", eventArgs [2].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("└─root two", eventArgs [3].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("├-root one", eventArgs [0].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("│ ├─leaf 1", eventArgs [1].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("│ └─leaf 2", eventArgs [2].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("└─root two", eventArgs [3].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); Assert.Equal (1, eventArgs [0].IndexOfExpandCollapseSymbol); Assert.Equal (3, eventArgs [1].IndexOfExpandCollapseSymbol); @@ -1083,9 +1083,9 @@ oot two Assert.All (eventArgs, ea => Assert.Equal (ea.Tree, tv)); Assert.All (eventArgs, ea => Assert.False (ea.Handled)); - Assert.Equal ("─leaf 1", eventArgs [0].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("─leaf 2", eventArgs [1].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("oot two", eventArgs [2].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("─leaf 1", eventArgs [0].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("─leaf 2", eventArgs [1].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("oot two", eventArgs [2].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); Assert.Equal (0, eventArgs [0].IndexOfExpandCollapseSymbol); Assert.Equal (0, eventArgs [1].IndexOfExpandCollapseSymbol); From f93b47512738b368da2c274f56e805c8f12bb791 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 5 Oct 2024 22:40:17 +0100 Subject: [PATCH 15/24] Using the attribute at the cursor position on paste. --- Terminal.Gui/Views/TextView.cs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 67b2afe2eb..d88f18c194 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -4623,7 +4623,10 @@ private void InsertAllText (string text) return; } - List> lines = Cell.StringToLinesOfCells (text); + // Get selected attribute + Attribute? attribute = GetSelectedAttribute (CurrentRow, CurrentColumn); + + List> lines = Cell.StringToLinesOfCells (text, attribute); if (lines.Count == 0) { @@ -5528,6 +5531,34 @@ private void ProcessDeleteCharRight () DeleteCharRight (); } + private Attribute? GetSelectedAttribute (int row, int col) + { + if (!InheritsPreviousAttribute || (Lines == 1 && GetLine (Lines).Count == 0)) + { + return null; + } + + List line = GetLine (row); + int foundRow = row; + + while (line.Count == 0) + { + if (foundRow == 0 && line.Count == 0) + { + return null; + } + + foundRow--; + line = GetLine (foundRow); + } + + int foundCol = foundRow < row ? line.Count - 1 : Math.Min (col, line.Count - 1); + + Cell cell = line [foundCol]; + + return cell.Attribute; + } + // If InheritsPreviousColorScheme is enabled this method will check if the rune cell on // the row and col location and around has a not null color scheme. If it's null will set it with // the very most previous valid color scheme. From c54041e73e3402bb0de58bba4379fcb30bf59611 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 5 Oct 2024 22:56:07 +0100 Subject: [PATCH 16/24] Fix unit test. --- UnitTests/Drawing/CellTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UnitTests/Drawing/CellTests.cs b/UnitTests/Drawing/CellTests.cs index 459c6d136d..253850173a 100644 --- a/UnitTests/Drawing/CellTests.cs +++ b/UnitTests/Drawing/CellTests.cs @@ -141,9 +141,9 @@ public void Cell_LoadCells_InheritsPreviousAttribute () 1111555555 2222225555 3333555555 -4455555555 -5555555555 -5555544445"; +4444444444 +4444555555 +4444444445"; TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); tv.Undo (); From 6a0aa9a3b2796da038300f56f6c3604e25361b38 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 6 Oct 2024 01:18:58 +0100 Subject: [PATCH 17/24] Add feature to copy, cut and paste with the cells attributes. --- Terminal.Gui/Views/TextView.cs | 96 +++++++++++++++++++++----------- UnitTests/Drawing/CellTests.cs | 12 ++-- UnitTests/Views/TextViewTests.cs | 2 + 3 files changed, 73 insertions(+), 37 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index d88f18c194..3eda736a54 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2585,6 +2585,16 @@ public bool ReadOnly /// Length of the selected text. public int SelectedLength => GetSelectedLength (); + private List> _selectedCellsList = []; + + /// + /// Gets the selected text as + /// + /// List{List{Cell}} + /// + /// + public List> SelectedCellsList => _selectedCellsList; + /// The selected text. public string SelectedText { @@ -2753,6 +2763,9 @@ public bool CloseFile () /// public event EventHandler? ContentsChanged; + private string _copiedText; + private List> _copiedCellsList = []; + /// Copy the selected text to the clipboard contents. public void Copy () { @@ -2760,13 +2773,16 @@ public void Copy () if (Selecting) { - SetClipboard (GetRegion ()); + _copiedText = GetRegion (out _copiedCellsList); + SetClipboard (_copiedText); _copyWithoutSelection = false; } else { List currentLine = GetCurrentLine (); - SetClipboard (Cell.ToString (currentLine)); + _copiedCellsList.Add (currentLine); + _copiedText = Cell.ToString (currentLine); + SetClipboard (_copiedText); _copyWithoutSelection = true; } @@ -2778,14 +2794,15 @@ public void Copy () public void Cut () { SetWrapModel (); - SetClipboard (GetRegion ()); + _copiedText = GetRegion (out _copiedCellsList); + SetClipboard (_copiedText); if (!_isReadOnly) { ClearRegion (); _historyText.Add ( - new () { new (GetCurrentLine ()) }, + [new (GetCurrentLine ())], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -3574,17 +3591,17 @@ public void Paste () SetWrapModel (); string? contents = Clipboard.Contents; - if (_copyWithoutSelection && contents.FirstOrDefault (x => x == '\n' || x == '\r') == 0) + if (_copyWithoutSelection && contents.FirstOrDefault (x => x is '\n' or '\r') == 0) { - List runeList = contents is null ? new () : Cell.ToCellList (contents); + List runeList = contents is null ? [] : Cell.ToCellList (contents); List currentLine = GetCurrentLine (); - _historyText.Add (new () { new (currentLine) }, CursorPosition); + _historyText.Add ([new (currentLine)], CursorPosition); - List> addedLine = new () { new (currentLine), runeList }; + List> addedLine = [new (currentLine), runeList]; _historyText.Add ( - new (addedLine), + [..addedLine], CursorPosition, HistoryText.LineStatus.Added ); @@ -3593,7 +3610,7 @@ public void Paste () CurrentRow++; _historyText.Add ( - new () { new (GetCurrentLine ()) }, + [new (GetCurrentLine ())], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -3609,12 +3626,12 @@ public void Paste () } _copyWithoutSelection = false; - InsertAllText (contents); + InsertAllText (contents, true); if (Selecting) { _historyText.ReplaceLast ( - new () { new (GetCurrentLine ()) }, + [new (GetCurrentLine ())], CursorPosition, HistoryText.LineStatus.Original ); @@ -4444,6 +4461,7 @@ private void GetEncodedRegionBounds ( // region. // private string GetRegion ( + out List> cellsList, int? sRow = null, int? sCol = null, int? cRow = null, @@ -4451,8 +4469,9 @@ private string GetRegion ( TextModel? model = null ) { - long start, end; - GetEncodedRegionBounds (out start, out end, sRow, sCol, cRow, cCol); + GetEncodedRegionBounds (out long start, out long end, sRow, sCol, cRow, cCol); + + cellsList = []; if (start == end) { @@ -4460,31 +4479,38 @@ private string GetRegion ( } var startRow = (int)(start >> 32); - var maxrow = (int)(end >> 32); + var maxRow = (int)(end >> 32); var startCol = (int)(start & 0xffffffff); var endCol = (int)(end & 0xffffffff); List line = model is null ? _model.GetLine (startRow) : model.GetLine (startRow); + List cells; - if (startRow == maxrow) + if (startRow == maxRow) { - return StringFromRunes (line.GetRange (startCol, endCol - startCol)); + cells = line.GetRange (startCol, endCol - startCol); + cellsList.Add (cells); + return StringFromRunes (cells); } - string res = StringFromRunes (line.GetRange (startCol, line.Count - startCol)); + cells = line.GetRange (startCol, line.Count - startCol); + cellsList.Add (cells); + string res = StringFromRunes (cells); - for (int row = startRow + 1; row < maxrow; row++) + for (int row = startRow + 1; row < maxRow; row++) { + cellsList.AddRange ([]); + cells = model == null ? _model.GetLine (row) : model.GetLine (row); + cellsList.Add (cells); res = res + Environment.NewLine - + StringFromRunes ( - model == null - ? _model.GetLine (row) - : model.GetLine (row) - ); + + StringFromRunes (cells); } - line = model is null ? _model.GetLine (maxrow) : model.GetLine (maxrow); - res = res + Environment.NewLine + StringFromRunes (line.GetRange (0, endCol)); + line = model is null ? _model.GetLine (maxRow) : model.GetLine (maxRow); + cellsList.AddRange ([]); + cells = line.GetRange (0, endCol); + cellsList.Add (cells); + res = res + Environment.NewLine + StringFromRunes (cells); return res; } @@ -4510,7 +4536,7 @@ private string GetSelectedRegion () OnUnwrappedCursorPosition (cRow, cCol); - return GetRegion (startRow, startCol, cRow, cCol, model); + return GetRegion (out _selectedCellsList, sRow: startRow, sCol: startCol, cRow: cRow, cCol: cCol, model: model); } private (int Row, int Col) GetUnwrappedPosition (int line, int col) @@ -4616,17 +4642,25 @@ private void Insert (Cell cell) } } - private void InsertAllText (string text) + private void InsertAllText (string text, bool fromClipboard = false) { if (string.IsNullOrEmpty (text)) { return; } - // Get selected attribute - Attribute? attribute = GetSelectedAttribute (CurrentRow, CurrentColumn); + List> lines; - List> lines = Cell.StringToLinesOfCells (text, attribute); + if (fromClipboard && text == _copiedText) + { + lines = _copiedCellsList; + } + else + { + // Get selected attribute + Attribute? attribute = GetSelectedAttribute (CurrentRow, CurrentColumn); + lines = Cell.StringToLinesOfCells (text, attribute); + } if (lines.Count == 0) { diff --git a/UnitTests/Drawing/CellTests.cs b/UnitTests/Drawing/CellTests.cs index 253850173a..32ba32d697 100644 --- a/UnitTests/Drawing/CellTests.cs +++ b/UnitTests/Drawing/CellTests.cs @@ -141,9 +141,9 @@ public void Cell_LoadCells_InheritsPreviousAttribute () 1111555555 2222225555 3333555555 -4444444444 -4444555555 -4444444445"; +4400000000 +1111555555 +2222224445"; TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); tv.Undo (); @@ -177,9 +177,9 @@ public void Cell_LoadCells_InheritsPreviousAttribute () 1111555555 2222225555 3333555555 -4444444444 -4444555555 -4444445555 +4400000000 +1111555555 +2222225555 4445555555"; TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 213abf6149..fcc327eb48 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -6271,6 +6271,7 @@ public void Selected_Text_Shows () // TAB to jump between text fields. TestHelpers.AssertDriverAttributesAre ("0000000", Application.Driver, attributes); + Assert.Empty (_textView.SelectedCellsList); _textView.NewKeyDownEvent (Key.CursorRight.WithCtrl.WithShift); @@ -6280,6 +6281,7 @@ public void Selected_Text_Shows () // TAB to jump between text fields. TestHelpers.AssertDriverAttributesAre ("1111000", Application.Driver, attributes); + Assert.Equal ("TAB ", Cell.ToString (_textView.SelectedCellsList [^1])); top.Dispose (); } From e58c15f2e68b51333c323412d9ff6f073f4ca16b Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 7 Oct 2024 00:19:51 +0100 Subject: [PATCH 18/24] Add undo and redo support for cells attribute. --- Terminal.Gui/Drawing/Cell.cs | 25 +- Terminal.Gui/Drawing/ColorScheme.Colors.cs | 119 +++++ Terminal.Gui/Resources/Strings.Designer.cs | 9 + Terminal.Gui/Resources/Strings.fr-FR.resx | 27 +- Terminal.Gui/Resources/Strings.ja-JP.resx | 113 +++-- Terminal.Gui/Resources/Strings.pt-PT.resx | 29 +- Terminal.Gui/Resources/Strings.resx | 525 ++++++++++---------- Terminal.Gui/Resources/Strings.zh-Hans.resx | 15 +- Terminal.Gui/Views/TextField.cs | 4 +- Terminal.Gui/Views/TextView.cs | 228 +++++++-- UICatalog/Scenarios/Editor.cs | 181 +------ UnitTests/Views/TextViewTests.cs | 80 +++ 12 files changed, 792 insertions(+), 563 deletions(-) diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index 581cc6bd61..16af046a7f 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -72,8 +72,8 @@ public static List ToCellList (string str, Attribute? attribute = null) public static List> StringToLinesOfCells (string content, Attribute? attribute = null) { List cells = content.EnumerateRunes () - .Select (x => new Cell { Rune = x, Attribute = attribute }) - .ToList (); + .Select (x => new Cell { Rune = x, Attribute = attribute }) + .ToList (); return SplitNewLines (cells); } @@ -93,6 +93,27 @@ public static string ToString (IEnumerable cells) return str; } + /// Converts a generic collection into a string. + /// The enumerable cell to convert. + /// + public static string ToString (List> cellsList) + { + var str = string.Empty; + + for (var i = 0; i < cellsList.Count; i++) + { + IEnumerable cellList = cellsList [i]; + str += ToString (cellList); + + if (i + 1 < cellsList.Count) + { + str += Environment.NewLine; + } + } + + return str; + } + // Turns the string into cells, this does not split the contents on a newline if it is present. internal static List StringToCells (string str, Attribute? attribute = null) diff --git a/Terminal.Gui/Drawing/ColorScheme.Colors.cs b/Terminal.Gui/Drawing/ColorScheme.Colors.cs index ca14a49868..a7c827be5a 100644 --- a/Terminal.Gui/Drawing/ColorScheme.Colors.cs +++ b/Terminal.Gui/Drawing/ColorScheme.Colors.cs @@ -173,4 +173,123 @@ public ColorScheme? this [string key] return ColorSchemes; } + + /// + /// Open a with two or , based on the + /// is false or true, respectively, for + /// and colors. + /// + /// The title to show in the dialog. + /// The current attribute used. + /// The new attribute. + /// if a new color was accepted, otherwise . + public static bool PromptForColors (string title, Attribute? currentAttribute, out Attribute newAttribute) + { + var accept = false; + + var d = new Dialog + { + Title = title, + Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)), + Height = 20 + }; + + var btnOk = new Button + { + X = Pos.Center () - 5, + Y = Application.Force16Colors ? 6 : 4, + Text = "Ok", + Width = Dim.Auto (), + IsDefault = true + }; + + btnOk.Accept += (s, e) => + { + accept = true; + e.Handled = true; + Application.RequestStop (); + }; + + var btnCancel = new Button + { + X = Pos.Center () + 5, + Y = 4, + Text = "Cancel", + Width = Dim.Auto () + }; + + btnCancel.Accept += (s, e) => + { + e.Handled = true; + Application.RequestStop (); + }; + + d.Add (btnOk); + d.Add (btnCancel); + + d.AddButton (btnOk); + d.AddButton (btnCancel); + + View cpForeground; + + if (Application.Force16Colors) + { + cpForeground = new ColorPicker16 + { + SelectedColor = currentAttribute!.Value.Foreground.GetClosestNamedColor16 (), + Width = Dim.Fill (), + BorderStyle = LineStyle.Single, + Title = "Foreground" + }; + } + else + { + cpForeground = new ColorPicker + { + SelectedColor = currentAttribute!.Value.Foreground, + Width = Dim.Fill (), + Style = new () { ShowColorName = true, ShowTextFields = true }, + BorderStyle = LineStyle.Single, + Title = "Foreground" + }; + ((ColorPicker)cpForeground).ApplyStyleChanges (); + } + + View cpBackground; + + if (Application.Force16Colors) + { + cpBackground = new ColorPicker16 + { + SelectedColor = currentAttribute!.Value.Background.GetClosestNamedColor16 (), + Y = Pos.Bottom (cpForeground) + 1, + Width = Dim.Fill (), + BorderStyle = LineStyle.Single, + Title = "Background" + }; + } + else + { + cpBackground = new ColorPicker + { + SelectedColor = currentAttribute!.Value.Background, + Width = Dim.Fill (), + Y = Pos.Bottom (cpForeground) + 1, + Style = new () { ShowColorName = true, ShowTextFields = true }, + BorderStyle = LineStyle.Single, + Title = "Background" + }; + ((ColorPicker)cpBackground).ApplyStyleChanges (); + } + + d.Add (cpForeground, cpBackground); + + Application.Run (d); + d.Dispose (); + Color newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor; + Color newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor; + newAttribute = new (newForeColor, newBackColor); + + return accept; + } } diff --git a/Terminal.Gui/Resources/Strings.Designer.cs b/Terminal.Gui/Resources/Strings.Designer.cs index 491473014c..2befd2737b 100644 --- a/Terminal.Gui/Resources/Strings.Designer.cs +++ b/Terminal.Gui/Resources/Strings.Designer.cs @@ -1437,6 +1437,15 @@ internal static string btnYes { } } + /// + /// Looks up a localized string similar to Co_lors. + /// + internal static string ctxColors { + get { + return ResourceManager.GetString("ctxColors", resourceCulture); + } + } + /// /// Looks up a localized string similar to _Copy. /// diff --git a/Terminal.Gui/Resources/Strings.fr-FR.resx b/Terminal.Gui/Resources/Strings.fr-FR.resx index 746c454996..c20959da40 100644 --- a/Terminal.Gui/Resources/Strings.fr-FR.resx +++ b/Terminal.Gui/Resources/Strings.fr-FR.resx @@ -117,27 +117,27 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Tout _sélectionner + + + _Tout supprimer + _Copier Co_uper - - _Tout supprimer - C_oller - - _Rétablir - - - Tout _sélectionner - _Annuler + + _Rétablir + _Dossier @@ -168,16 +168,19 @@ Prochai_n... + + Ouvrir + Enregistrer E_nregistrer sous - - Ouvrir - Sélecteur de Date + + Cou_leurs + \ No newline at end of file diff --git a/Terminal.Gui/Resources/Strings.ja-JP.resx b/Terminal.Gui/Resources/Strings.ja-JP.resx index 4a825c51c6..fa4bda4210 100644 --- a/Terminal.Gui/Resources/Strings.ja-JP.resx +++ b/Terminal.Gui/Resources/Strings.ja-JP.resx @@ -117,27 +117,27 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 全て選択 (_S) + + + 全て削除 (_D) + コピー (_C) 切り取り (_T) - - 全て削除 (_D) - 貼り付け (_P) - - やり直し (_R) - - - 全て選択 (_S) - 元に戻す (_U) + + やり直し (_R) + ディレクトリ @@ -171,44 +171,47 @@ 同じ名前のディレクトリはすでに存在しました - - “{0}”を削除もよろしいですか?この操作は元に戻りません - - - タイプ + + すでに存在したディレクトリを選択してください - - サイズ + + 同じ名前のファイルはすでに存在しました - - パスを入力 + + すでに存在したファイルを選択してください ファイル名 - - 新規ディレクトリ - - - いいえ (_N) - - - はい (_Y) + + すでに存在したファイルまたはディレクトリを選択してください 変更日時 - - すでに存在したファイルまたはディレクトリを選択してください + + パスを入力 - - すでに存在したディレクトリを選択してください + + 検索を入力 - - すでに存在したファイルを選択してください + + サイズ - - 名前: + + タイプ + + + ファイルタイプが間違っでいます + + + 任意ファイル + + + “{0}”を削除もよろしいですか?この操作は元に戻りません + + + 削除失敗 {0} を削除 @@ -216,35 +219,26 @@ 新規失敗 - - 既存 + + 新規ディレクトリ - - 名前を変更 + + いいえ (_N) 変更失敗 - - 削除失敗 - - - 同じ名前のファイルはすでに存在しました - - - 検索を入力 - - - ファイルタイプが間違っでいます + + 名前: - - 任意ファイル + + 名前を変更 - - キャンセル (_C) + + はい (_Y) - - OK (_O) + + 既存 開く (_O) @@ -255,6 +249,12 @@ 名前を付けて保存 (_S) + + OK (_O) + + + キャンセル (_C) + 削除 (_D) @@ -276,4 +276,7 @@ 日付ピッカー + + 絵の具 (_L) + \ No newline at end of file diff --git a/Terminal.Gui/Resources/Strings.pt-PT.resx b/Terminal.Gui/Resources/Strings.pt-PT.resx index 2cffa6cd7e..28aabf522c 100644 --- a/Terminal.Gui/Resources/Strings.pt-PT.resx +++ b/Terminal.Gui/Resources/Strings.pt-PT.resx @@ -117,27 +117,27 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + _Selecionar Tudo + + + _Apagar Tudo + _Copiar Cor_tar - - _Apagar Tudo - Co_lar - - _Refazer - - - _Selecionar Tudo - _Desfazer + + _Refazer + Diretório @@ -168,16 +168,19 @@ S_eguir... - - Guardar como + + Abrir Guardar - - Abrir + + Guardar como Seletor de Data + + Co_res + \ No newline at end of file diff --git a/Terminal.Gui/Resources/Strings.resx b/Terminal.Gui/Resources/Strings.resx index 8380fb4089..9333d71578 100644 --- a/Terminal.Gui/Resources/Strings.resx +++ b/Terminal.Gui/Resources/Strings.resx @@ -1,6 +1,6 @@  - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + _Select All - + _Delete All - + _Copy - + Cu_t - + _Paste - + _Undo - + _Redo - + Directory - + File - + Save - + Save as - + Open - + Select folder - + Select Mixed - + _Back - + Fi_nish - + _Next... - + Directory already exists with that name When trying to save a file with a name already taken by a directory - + Must select an existing directory - + File already exists with that name - + Must select an existing file When trying to save a directory with a name already used by a file - + Filename - + Must select an existing file or directory - + Modified - + Enter Path - + Enter Search - + Size - + Type - + Wrong file type When trying to open/save a file that does not match the provided filter (e.g. csv) - + Any Files Describes an AllowedType that matches anything - + Are you sure you want to delete '{0}'? This operation is permanent - + Delete Failed - + Delete {0} - + New Failed - + New Folder - + _No - + Rename Failed - + Name: - + Rename - + _Yes - + Existing - + O_pen - + _Save - + Save _as - + _OK - + _Cancel - + _Delete - + _Hide {0} - + _New - + _Rename - + _Sort {0} ASC - + _Sort {0} DESC - + Date Picker - + AliceBlue - + AntiqueWhite - + Aquamarine - + Azure - + Beige - + Bisque - + Black - + BlanchedAlmond - + Blue - + BlueViolet - + Brown - + BurlyWood - + CadetBlue - + Chartreuse - + Chocolate - + Coral - + CornflowerBlue - + Cornsilk - + Crimson - + Cyan - + DarkBlue - + DarkCyan - + DarkGoldenRod - + DarkGrey - + DarkGreen - + DarkKhaki - + DarkMagenta - + DarkOliveGreen - + DarkOrange - + DarkOrchid - + DarkRed - + DarkSalmon - + DarkSeaGreen - + DarkSlateBlue - + DarkSlateGrey - + DarkTurquoise - + DarkViolet - + DeepPink - + DeepSkyBlue - + DimGray - + DodgerBlue - + FireBrick - + FloralWhite - + ForestGreen - + Gainsboro - + GhostWhite - + Gold - + GoldenRod - + Gray - + Green - + GreenYellow - + HoneyDew - + HotPink - + IndianRed - + Indigo - + Ivory - + Khaki - + Lavender - + LavenderBlush - + LawnGreen - + LemonChiffon - + LightBlue - + LightCoral - + LightCyan - + LightGoldenRodYellow - + LightGray - + LightGreen - + LightPink - + LightSalmon - + LightSeaGreen - + LightSkyBlue - + LightSlateGrey - + LightSteelBlue - + LightYellow - + Lime - + LimeGreen - + Linen - + Magenta - + Maroon - + MediumAquaMarine - + MediumBlue - + MediumOrchid - + MediumPurple - + MediumSeaGreen - + MediumSlateBlue - + MediumSpringGreen - + MediumTurquoise - + MediumVioletRed - + MidnightBlue - + MintCream - + MistyRose - + Moccasin - + NavajoWhite - + Navy - + OldLace - + Olive - + OliveDrab - + Orange - + OrangeRed - + Orchid - + PaleGoldenRod - + PaleGreen - + PaleTurquoise - + PaleVioletRed - + PapayaWhip - + PeachPuff - + Peru - + Pink - + Plum - + PowderBlue - + Purple - + RebeccaPurple - + Red - + RosyBrown - + RoyalBlue - + SaddleBrown - + Salmon - + SandyBrown - + SeaGreen - + SeaShell - + Sienna - + Silver - + SkyBlue - + SlateBlue - + SlateGray - + Snow - + SpringGreen - + SteelBlue - + Tan - + Teal - + Thistle - + Tomato - + Turquoise - + Violet - + Wheat - + White - + WhiteSmoke - + Yellow - + YellowGreen - + BrightBlue - + BrightCyan - + BrightRed - - + + BrightGreen - - + + BrightMagenta - - + + BrightYellow - - + + DarkGray - + + + Co_lors + \ No newline at end of file diff --git a/Terminal.Gui/Resources/Strings.zh-Hans.resx b/Terminal.Gui/Resources/Strings.zh-Hans.resx index 009fdd479f..8ea63e91d6 100644 --- a/Terminal.Gui/Resources/Strings.zh-Hans.resx +++ b/Terminal.Gui/Resources/Strings.zh-Hans.resx @@ -153,9 +153,6 @@ 打开 - - 下一步 (_N)... - 选择文件夹 (_S) @@ -168,6 +165,9 @@ 结束 (_N) + + 下一步 (_N)... + 已存在相同名称的目录 @@ -240,9 +240,6 @@ 已有 - - 确定 (_O) - 打开 (_O) @@ -252,6 +249,9 @@ 另存为 (_S) + + 确定 (_O) + 取消 (_C) @@ -276,4 +276,7 @@ 日期选择器 + + 旗帜 (_L) + \ No newline at end of file diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index a352e586c8..3862a4acbb 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -464,7 +464,7 @@ public virtual int CursorPosition /// Indicates whatever the text was changed or not. if the text was changed /// otherwise. /// - public bool IsDirty => _historyText.IsDirty (Text); + public bool IsDirty => _historyText.IsDirty ([Cell.StringToCells (Text)]); /// If set to true its not allow any changes in the text. public bool ReadOnly { get; set; } @@ -594,7 +594,7 @@ public void ClearAllSelection () } /// Allows clearing the items updating the original text. - public void ClearHistoryChanges () { _historyText.Clear (Text); } + public void ClearHistoryChanges () { _historyText.Clear ([Cell.StringToCells (Text)]); } /// Copy the selected text to the clipboard. public virtual void Copy () diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 3eda736a54..f283f47d3d 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -180,7 +180,7 @@ public void ReplaceLine (int pos, List runes) { if (_lines.Count > 0 && pos < _lines.Count) { - _lines [pos] = new (runes); + _lines [pos] = [..runes]; } else if (_lines.Count == 0 || (_lines.Count > 0 && pos >= _lines.Count)) { @@ -1125,12 +1125,13 @@ public enum LineStatus Original, Replaced, Removed, - Added + Added, + Attribute } - private readonly List _historyTextItems = new (); + private readonly List _historyTextItems = []; private int _idxHistoryText = -1; - private string? _originalText; + private List> _originalCellsList = []; public bool HasHistoryChanges => _idxHistoryText > -1; public bool IsFromHistory { get; private set; } @@ -1165,15 +1166,51 @@ public void Add (List> lines, Point curPos, LineStatus lineStatus = L public event EventHandler? ChangeText; - public void Clear (string text) + public void Clear (List> cellsList) { _historyTextItems.Clear (); _idxHistoryText = -1; - _originalText = text; + _originalCellsList.Clear (); + + foreach (List cells in cellsList) + { + _originalCellsList.Add ([..cells]); + } + OnChangeText (null); } - public bool IsDirty (string text) { return _originalText != text; } + public bool IsDirty (List> cellsList) + { + if (cellsList.Count != _originalCellsList.Count) + { + return true; + } + + for (var r = 0; r < cellsList.Count; r++) + { + List cells = cellsList [r]; + List originalCells = _originalCellsList [r]; + + if (cells.Count != originalCells.Count) + { + return true; + } + + for (var c = 0; c < cells.Count; c++) + { + Cell cell = cells [c]; + Cell originalCell = originalCells [c]; + + if (!cell.Equals (originalCell)) + { + return true; + } + } + } + + return false; + } public void Redo () { @@ -1227,7 +1264,8 @@ private void ProcessChanges (ref HistoryTextItemEventArgs historyTextItem) if (_idxHistoryText - 1 > -1 && (_historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Added || _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed - || (historyTextItem.LineStatus == LineStatus.Replaced && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original))) + || (historyTextItem.LineStatus == LineStatus.Replaced && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original) + || (historyTextItem.LineStatus == LineStatus.Attribute && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original))) { _idxHistoryText--; @@ -1858,7 +1896,7 @@ public TextView () CursorVisibility = CursorVisibility.Default; Used = true; - // By default, disable hotkeys (in case someome sets Title) + // By default, disable hotkeys (in case someone sets Title) HotKeySpecifier = new ('\xffff'); _model.LinesLoaded += Model_LinesLoaded!; @@ -2264,6 +2302,15 @@ public TextView () } ); + AddCommand ( + Command.Open, + () => + { + PromptForColors (); + + return true; + }); + // Default keybindings for this view KeyBindings.Add (Key.PageDown, Command.PageDown); KeyBindings.Add (Key.V.WithCtrl, Command.PageDown); @@ -2357,6 +2404,8 @@ public TextView () KeyBindings.Add (Key.G.WithCtrl, Command.DeleteAll); KeyBindings.Add (Key.D.WithCtrl.WithShift, Command.DeleteAll); + KeyBindings.Add (Key.L.WithCtrl, Command.Open); + _currentCulture = Thread.CurrentThread.CurrentUICulture; ContextMenu = new (); @@ -2495,8 +2544,8 @@ public Point CursorPosition /// public bool IsDirty { - get => _historyText.IsDirty (Text); - set => _historyText.Clear (Text); + get => _historyText.IsDirty (_model.GetAllLines ()); + set => _historyText.Clear (_model.GetAllLines ()); } /// Gets or sets the left column. @@ -2585,15 +2634,21 @@ public bool ReadOnly /// Length of the selected text. public int SelectedLength => GetSelectedLength (); - private List> _selectedCellsList = []; - /// /// Gets the selected text as /// /// List{List{Cell}} /// /// - public List> SelectedCellsList => _selectedCellsList; + public List> SelectedCellsList + { + get + { + GetRegion (out List> selectedCellsList); + + return selectedCellsList; + } + } /// The selected text. public string SelectedText @@ -2689,7 +2744,7 @@ public override string Text OnTextChanged (); SetNeedsDisplay (); - _historyText.Clear (Text); + _historyText.Clear (_model.GetAllLines ()); } } @@ -2741,7 +2796,7 @@ public bool WordWrap /// Allows clearing the items updating the original text. - public void ClearHistoryChanges () { _historyText?.Clear (Text); } + public void ClearHistoryChanges () { _historyText?.Clear (_model.GetAllLines ()); } /// Closes the contents of the stream into the . /// true, if stream was closed, false otherwise. @@ -2763,7 +2818,100 @@ public bool CloseFile () /// public event EventHandler? ContentsChanged; - private string _copiedText; + internal void ApplyCellsAttribute (Attribute attribute) + { + if (!ReadOnly && SelectedLength > 0) + { + int startRow = Math.Min (SelectionStartRow, CurrentRow); + int endRow = Math.Max (CurrentRow, SelectionStartRow); + int startCol = SelectionStartRow <= CurrentRow ? SelectionStartColumn : CurrentColumn; + int endCol = CurrentRow >= SelectionStartRow ? CurrentColumn : SelectionStartColumn; + List> selectedCellsOriginal = []; + List> selectedCellsChanged = []; + + for (int r = startRow; r <= endRow; r++) + { + List line = GetLine (r); + + selectedCellsOriginal.Add ([.. line]); + + for (int c = r == startRow ? startCol : 0; + c < (r == endRow ? endCol : line.Count); + c++) + { + Cell cell = line [c]; // Copy value to a new variable + cell.Attribute = attribute; // Modify the copy + line [c] = cell; // Assign the modified copy back + } + + selectedCellsChanged.Add ([..GetLine (r)]); + } + + GetSelectedRegion (); + Selecting = false; + + _historyText.Add ( + [.. selectedCellsOriginal], + new (startCol, startRow) + ); + + _historyText.Add ( + [.. selectedCellsChanged], + new (startCol, startRow), + HistoryText.LineStatus.Attribute + ); + } + } + + private Attribute? GetSelectedCellAttribute () + { + List line; + + if (SelectedLength > 0) + { + line = GetLine (SelectionStartRow); + + if (line [Math.Min (SelectionStartColumn, line.Count - 1)].Attribute is { } attributeSel) + { + return new (attributeSel); + } + + return new (ColorScheme!.Focus); + } + + line = GetCurrentLine (); + + if (line [Math.Min (CurrentColumn, line.Count - 1)].Attribute is { } attribute) + { + return new (attribute); + } + + return new (ColorScheme!.Focus); + } + + /// + /// Open a dialog to set the foreground and background colors. + /// + public void PromptForColors () + { + if (!Colors.PromptForColors ( + "Colors", + GetSelectedCellAttribute (), + out Attribute newAttribute + )) + { + return; + } + + var attribute = new Attribute ( + newAttribute.Foreground, + newAttribute.Background + ); + + ApplyCellsAttribute (attribute); + } + + private string? _copiedText; private List> _copiedCellsList = []; /// Copy the selected text to the clipboard contents. @@ -3070,7 +3218,7 @@ public bool Load (string path) { SetWrapModel (); res = _model.LoadFile (path); - _historyText.Clear (Text); + _historyText.Clear (_model.GetAllLines ()); ResetPosition (); } finally @@ -3092,7 +3240,7 @@ public void Load (Stream stream) { SetWrapModel (); _model.LoadStream (stream); - _historyText.Clear (Text); + _historyText.Clear (_model.GetAllLines ()); ResetPosition (); SetNeedsDisplay (); UpdateWrapModel (); @@ -3104,7 +3252,7 @@ public void Load (List cells) { SetWrapModel (); _model.LoadCells (cells, ColorScheme?.Focus); - _historyText.Clear (Text); + _historyText.Clear (_model.GetAllLines ()); ResetPosition (); SetNeedsDisplay (); UpdateWrapModel (); @@ -3118,7 +3266,7 @@ public void Load (List> cellsList) SetWrapModel (); InheritsPreviousAttribute = true; _model.LoadListCells (cellsList, ColorScheme?.Focus); - _historyText.Clear (Text); + _historyText.Clear (_model.GetAllLines ()); ResetPosition (); SetNeedsDisplay (); UpdateWrapModel (); @@ -4069,6 +4217,14 @@ private void Adjust () null, null, (KeyCode)KeyBindings.GetKeyFromCommands (Command.Redo) + ), + new ( + Strings.ctxColors, + "", + () => PromptForColors (), + null, + null, + (KeyCode)KeyBindings.GetKeyFromCommands (Command.Open) ) } ); @@ -4460,7 +4616,7 @@ private void GetEncodedRegionBounds ( // Returns a string with the text in the selected // region. // - private string GetRegion ( + internal string GetRegion ( out List> cellsList, int? sRow = null, int? sCol = null, @@ -4536,7 +4692,7 @@ private string GetSelectedRegion () OnUnwrappedCursorPosition (cRow, cCol); - return GetRegion (out _selectedCellsList, sRow: startRow, sCol: startCol, cRow: cRow, cCol: cCol, model: model); + return GetRegion (out _, sRow: startRow, sCol: startCol, cRow: cRow, cCol: cCol, model: model); } private (int Row, int Col) GetUnwrappedPosition (int line, int col) @@ -4588,12 +4744,12 @@ private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItemE for (var i = 0; i < obj.Lines.Count; i++) { - if (i == 0) + if (i == 0 || obj.LineStatus == HistoryText.LineStatus.Original || obj.LineStatus == HistoryText.LineStatus.Attribute) { _model.ReplaceLine (startLine, obj.Lines [i]); } - else if ((obj.IsUndoing && obj.LineStatus == HistoryText.LineStatus.Removed) - || (!obj.IsUndoing && obj.LineStatus == HistoryText.LineStatus.Added)) + else if (obj is { IsUndoing: true, LineStatus: HistoryText.LineStatus.Removed } + or { IsUndoing: false, LineStatus: HistoryText.LineStatus.Added }) { _model.AddLine (startLine, obj.Lines [i]); } @@ -4671,7 +4827,7 @@ private void InsertAllText (string text, bool fromClipboard = false) List line = GetCurrentLine (); - _historyText.Add (new () { new (line) }, CursorPosition); + _historyText.Add ([new (line)], CursorPosition); // Optimize single line if (lines.Count == 1) @@ -4680,7 +4836,7 @@ private void InsertAllText (string text, bool fromClipboard = false) CurrentColumn += lines [0].Count; _historyText.Add ( - new () { new (line) }, + [new (line)], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4709,7 +4865,7 @@ private void InsertAllText (string text, bool fromClipboard = false) } List? rest = null; - var lastp = 0; + var lastPosition = 0; if (_model.Count > 0 && line.Count > 0 && !_copyWithoutSelection) { @@ -4724,19 +4880,19 @@ private void InsertAllText (string text, bool fromClipboard = false) //model.AddLine (currentRow, lines [0]); - List> addedLines = new () { new (line) }; + List> addedLines = [new (line)]; for (var i = 1; i < lines.Count; i++) { _model.AddLine (CurrentRow + i, lines [i]); - addedLines.Add (new (lines [i])); + addedLines.Add ([..lines [i]]); } if (rest is { }) { List last = _model.GetLine (CurrentRow + lines.Count - 1); - lastp = last.Count; + lastPosition = last.Count; last.InsertRange (last.Count, rest); addedLines.Last ().InsertRange (addedLines.Last ().Count, rest); @@ -4746,11 +4902,11 @@ private void InsertAllText (string text, bool fromClipboard = false) // Now adjust column and row positions CurrentRow += lines.Count - 1; - CurrentColumn = rest is { } ? lastp : lines [lines.Count - 1].Count; + CurrentColumn = rest is { } ? lastPosition : lines [^1].Count; Adjust (); _historyText.Add ( - new () { new (line) }, + [new (line)], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4769,7 +4925,7 @@ private bool InsertText (Key a, Attribute? attribute = null) SetWrapModel (); - _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition); + _historyText.Add ([new (GetCurrentLine ())], CursorPosition); if (Selecting) { @@ -4807,7 +4963,7 @@ private bool InsertText (Key a, Attribute? attribute = null) } _historyText.Add ( - new () { new (GetCurrentLine ()) }, + [new (GetCurrentLine ())], CursorPosition, HistoryText.LineStatus.Replaced ); diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index ec17e6a391..97329d0fb4 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -206,23 +206,11 @@ public override void Main () new MenuItem ( "Colors", "", - () => - { - if (!PromptForColor ( - "Colors", - GetSelectedCellAttribute (), - out Attribute newAttribute - )) - { - return; - } - - var attribute = new Attribute (newAttribute.Foreground, - newAttribute.Background - ); - - ApplyCellAttribute (attribute); - }) + () => _textView.PromptForColors (), + null, + null, + KeyCode.CtrlMask | KeyCode.L + ) } ), new ( @@ -337,165 +325,6 @@ out Attribute newAttribute } - private void ApplyCellAttribute (Attribute attribute) - { - if (!_textView.ReadOnly && _textView.SelectedLength > 0) - { - var startRow = Math.Min (_textView.SelectionStartRow, _textView.CurrentRow); - var endRow = Math.Max (_textView.CurrentRow, _textView.SelectionStartRow); - var startCol = _textView.SelectionStartRow <= _textView.CurrentRow ? _textView.SelectionStartColumn : _textView.CurrentColumn; - var endCol = _textView.CurrentRow >= _textView.SelectionStartRow ? _textView.CurrentColumn : _textView.SelectionStartColumn; - - for (int r = startRow; r <= endRow; r++) - { - List line = _textView.GetLine (r); - - for (int c = r == startRow ? startCol : 0; - c < (r == endRow ? endCol : line.Count); - c++) - { - Cell cell = line [c]; // Copy value to a new variable - cell.Attribute = attribute; // Modify the copy - line [c] = cell; // Assign the modified copy back - } - } - } - } - - private Attribute? GetSelectedCellAttribute () - { - List line; - - if (_textView.SelectedLength > 0) - { - line = _textView.GetLine (_textView.SelectionStartRow); - - if (line [Math.Min (_textView.SelectionStartColumn, line.Count - 1)].Attribute is { } attributeSel) - { - return new (attributeSel); - } - - return new (_textView.ColorScheme!.Focus); - } - - line = _textView.GetCurrentLine (); - - if (line [Math.Min (_textView.CurrentColumn, line.Count - 1)].Attribute is { } attribute) - { - return new (attribute); - } - - return new (_textView.ColorScheme!.Focus); - } - - public static bool PromptForColor (string title, Attribute? current, out Attribute newAttribute) - { - var accept = false; - - var d = new Dialog - { - Title = title, - Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)), - Height = 20 - }; - - var btnOk = new Button - { - X = Pos.Center () - 5, - Y = Application.Force16Colors ? 6 : 4, - Text = "Ok", - Width = Dim.Auto (), - IsDefault = true - }; - - btnOk.Accept += (s, e) => - { - accept = true; - e.Handled = true; - Application.RequestStop (); - }; - - var btnCancel = new Button - { - X = Pos.Center () + 5, - Y = 4, - Text = "Cancel", - Width = Dim.Auto () - }; - - btnCancel.Accept += (s, e) => - { - e.Handled = true; - Application.RequestStop (); - }; - - d.Add (btnOk); - d.Add (btnCancel); - - d.AddButton (btnOk); - d.AddButton (btnCancel); - - View cpForeground; - if (Application.Force16Colors) - { - cpForeground = new ColorPicker16 - { - SelectedColor = current!.Value.Foreground.GetClosestNamedColor16 (), - Width = Dim.Fill (), - BorderStyle = LineStyle.Single, - Title = "Foreground" - }; - } - else - { - cpForeground = new ColorPicker - { - SelectedColor = current!.Value.Foreground, - Width = Dim.Fill (), - Style = new () { ShowColorName = true, ShowTextFields = true }, - BorderStyle = LineStyle.Single, - Title = "Foreground" - }; - ((ColorPicker)cpForeground).ApplyStyleChanges (); - } - - View cpBackground; - if (Application.Force16Colors) - { - cpBackground = new ColorPicker16 - { - SelectedColor = current!.Value.Background.GetClosestNamedColor16 (), - Y = Pos.Bottom (cpForeground) + 1, - Width = Dim.Fill (), - BorderStyle = LineStyle.Single, - Title = "Background" - }; - } - else - { - cpBackground = new ColorPicker - { - SelectedColor = current!.Value.Background, - Width = Dim.Fill (), - Y = Pos.Bottom (cpForeground) + 1, - Style = new () { ShowColorName = true, ShowTextFields = true }, - BorderStyle = LineStyle.Single, - Title = "Background" - }; - ((ColorPicker)cpBackground).ApplyStyleChanges (); - } - - d.Add (cpForeground, cpBackground); - - Application.Run (d); - d.Dispose (); - var newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor; - var newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor; - newAttribute = new (newForeColor, newBackColor); - - return accept; - } - private bool CanCloseFile () { if (_textView.Text == Encoding.Unicode.GetString (_originalText)) diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index fcc327eb48..b610553146 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -4713,6 +4713,86 @@ public void HistoryText_Undo_Redo_Two_Line_Selected_Return () Assert.Equal (new Point (0, 1), tv.CursorPosition); } + [Fact] + public void HistoryText_Undo_Redo_ApplyCellsAttribute () + { + var text = "This is the first line.\nThis is the second line.\nThis is the third line."; + var tv = new TextView { Text = text }; + + tv.SelectionStartColumn = 12; + tv.CursorPosition = new Point (18, 1); + + Assert.Equal (31, tv.SelectedLength); + Assert.Equal ($"first line.{Environment.NewLine}This is the second", tv.SelectedText); + Assert.Equal ($"first line.{Environment.NewLine}This is the second", Cell.ToString (tv.SelectedCellsList)); + Assert.Equal (new Point (18, 1), tv.CursorPosition); + Assert.False (tv.IsDirty); + + AssertNullAttribute (); + + tv.ApplyCellsAttribute (new (Color.Red, Color.Green)); + + AssertRedGreenAttribute (); + + Assert.Equal (0, tv.SelectedLength); + Assert.Equal ("", tv.SelectedText); + Assert.Equal ($"first line.{Environment.NewLine}This is the second", Cell.ToString (tv.SelectedCellsList)); + Assert.Equal (new Point (18, 1), tv.CursorPosition); + Assert.True (tv.IsDirty); + + // Undo + Assert.True (tv.NewKeyDownEvent (Key.Z.WithCtrl)); + + AssertNullAttribute (); + + Assert.Equal (12, tv.SelectionStartColumn); + Assert.Equal (0, tv.SelectionStartRow); + Assert.Equal (0, tv.SelectedLength); + Assert.Equal ("", tv.SelectedText); + Assert.Empty (tv.SelectedCellsList); + Assert.Equal (new Point (12, 0), tv.CursorPosition); + Assert.False (tv.IsDirty); + + // Redo + Assert.True (tv.NewKeyDownEvent (Key.R.WithCtrl)); + + AssertRedGreenAttribute (); + + Assert.Equal (12, tv.SelectionStartColumn); + Assert.Equal (0, tv.SelectionStartRow); + Assert.Equal (0, tv.SelectedLength); + Assert.Equal ("", tv.SelectedText); + Assert.Empty (tv.SelectedCellsList); + Assert.Equal (new Point (12, 0), tv.CursorPosition); + Assert.True (tv.IsDirty); + + void AssertNullAttribute () + { + tv.GetRegion (out List> region, 0, 12, 1, 18); + + foreach (List cells in region) + { + foreach (Cell cell in cells) + { + Assert.Null (cell.Attribute); + } + } + } + + void AssertRedGreenAttribute () + { + tv.GetRegion (out List> region, 0, 12, 1, 18); + + foreach (List cells in region) + { + foreach (Cell cell in cells) + { + Assert.Equal ("[Red,Green]", cell.Attribute.ToString ()); + } + } + } + } + [Fact] public void Internal_Tests () { From 35a191650252fca4bdf7c066f197cf961da99f45 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 7 Oct 2024 00:41:42 +0100 Subject: [PATCH 19/24] Replace with record struct per @tig. --- Terminal.Gui/Drawing/CellEventArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Drawing/CellEventArgs.cs b/Terminal.Gui/Drawing/CellEventArgs.cs index 14b98f36b3..f2a8115dc9 100644 --- a/Terminal.Gui/Drawing/CellEventArgs.cs +++ b/Terminal.Gui/Drawing/CellEventArgs.cs @@ -1,7 +1,7 @@ namespace Terminal.Gui; /// Args for events that relate to a specific . -public class CellEventArgs +public record struct CellEventArgs { /// Creates a new instance of the class. /// The line. From 853f95f7f7421359be747e5c13090c1fa331e9d5 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 7 Oct 2024 00:42:54 +0100 Subject: [PATCH 20/24] Move unit tests per @tig. --- UnitTests/Drawing/CellTests.cs | 246 ------------------------------- UnitTests/Views/TextViewTests.cs | 246 +++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 246 deletions(-) diff --git a/UnitTests/Drawing/CellTests.cs b/UnitTests/Drawing/CellTests.cs index 32ba32d697..a7fc88073b 100644 --- a/UnitTests/Drawing/CellTests.cs +++ b/UnitTests/Drawing/CellTests.cs @@ -33,249 +33,6 @@ public void Equals_False () Assert.False (c2.Equals (c1)); } - [Fact] - public void Equals_True () - { - var c1 = new Cell (); - var c2 = new Cell (); - Assert.True (c1.Equals (c2)); - Assert.True (c2.Equals (c1)); - - c1.Rune = new ('a'); - c1.Attribute = new (); - c2.Rune = new ('a'); - c2.Attribute = new (); - Assert.True (c1.Equals (c2)); - Assert.True (c2.Equals (c1)); - } - - [Fact] - [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)] - public void Cell_LoadCells_InheritsPreviousAttribute () - { - List cells = []; - - foreach (KeyValuePair color in Colors.ColorSchemes) - { - string csName = color.Key; - - foreach (Rune rune in csName.EnumerateRunes ()) - { - cells.Add (new() { Rune = rune, Attribute = color.Value.Normal }); - } - - cells.Add (new() { Rune = (Rune)'\n', Attribute = color.Value.Focus }); - } - - TextView tv = CreateTextView (); - tv.Load (cells); - var top = new Toplevel (); - top.Add (tv); - RunState rs = Application.Begin (top); - Assert.True (tv.InheritsPreviousAttribute); - - var expectedText = @" -TopLevel -Base -Dialog -Menu -Error "; - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output); - - Attribute [] attributes = - { - // 0 - Colors.ColorSchemes ["TopLevel"].Normal, - - // 1 - Colors.ColorSchemes ["Base"].Normal, - - // 2 - Colors.ColorSchemes ["Dialog"].Normal, - - // 3 - Colors.ColorSchemes ["Menu"].Normal, - - // 4 - Colors.ColorSchemes ["Error"].Normal, - - // 5 - tv.ColorScheme!.Focus - }; - - var expectedColor = @" -0000000055 -1111555555 -2222225555 -3333555555 -4444455555"; - TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); - - tv.WordWrap = true; - Application.Refresh (); - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output); - TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); - - tv.CursorPosition = new (6, 2); - tv.SelectionStartColumn = 0; - tv.SelectionStartRow = 0; - Assert.Equal ($"TopLevel{Environment.NewLine}Base{Environment.NewLine}Dialog", tv.SelectedText); - tv.Copy (); - tv.Selecting = false; - tv.CursorPosition = new (2, 4); - tv.Paste (); - Application.Refresh (); - - expectedText = @" -TopLevel -Base -Dialog -Menu -ErTopLevel -Base -Dialogror "; - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output); - - expectedColor = @" -0000000055 -1111555555 -2222225555 -3333555555 -4400000000 -1111555555 -2222224445"; - TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); - - tv.Undo (); - tv.CursorPosition = new (0, 3); - tv.SelectionStartColumn = 0; - tv.SelectionStartRow = 0; - - Assert.Equal ( - $"TopLevel{Environment.NewLine}Base{Environment.NewLine}Dialog{Environment.NewLine}", - tv.SelectedText - ); - tv.Copy (); - tv.Selecting = false; - tv.CursorPosition = new (2, 4); - tv.Paste (); - Application.Refresh (); - - expectedText = @" -TopLevel -Base -Dialog -Menu -ErTopLevel -Base -Dialog -ror "; - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output); - - expectedColor = @" -0000000055 -1111555555 -2222225555 -3333555555 -4400000000 -1111555555 -2222225555 -4445555555"; - TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); - - Application.End (rs); - top.Dispose (); - } - - [Fact] - public void Cell_LoadCells_Without_ColorScheme_Is_Never_Null () - { - List cells = new () - { - new() { Rune = new ('T') }, - new() { Rune = new ('e') }, - new() { Rune = new ('s') }, - new() { Rune = new ('t') } - }; - TextView tv = CreateTextView (); - var top = new Toplevel (); - top.Add (tv); - tv.Load (cells); - - for (var i = 0; i < tv.Lines; i++) - { - List line = tv.GetLine (i); - - foreach (Cell c in line) - { - Assert.NotNull (c.Attribute); - } - } - } - - [Fact] - [AutoInitShutdown] - public void CellEventArgs_WordWrap_True () - { - var eventCount = 0; - - List> text = - [ - Cell.ToCells ( - "This is the first line.".ToRunes () - ), - - Cell.ToCells ( - "This is the second line.".ToRunes () - ) - ]; - TextView tv = CreateTextView (); - tv.DrawNormalColor += _textView_DrawColor; - tv.DrawReadOnlyColor += _textView_DrawColor; - tv.DrawSelectionColor += _textView_DrawColor; - tv.DrawUsedColor += _textView_DrawColor; - - void _textView_DrawColor (object sender, CellEventArgs e) - { - Assert.Equal (e.Line [e.Col], text [e.UnwrappedPosition.Row] [e.UnwrappedPosition.Col]); - eventCount++; - } - - tv.Text = $"{Cell.ToString (text [0])}\n{Cell.ToString (text [1])}\n"; - Assert.False (tv.WordWrap); - var top = new Toplevel (); - top.Add (tv); - Application.Begin (top); - - TestHelpers.AssertDriverContentsWithFrameAre ( - @" -This is the first line. -This is the second line.", - output - ); - - tv.Width = 10; - tv.Height = 25; - tv.WordWrap = true; - Application.Refresh (); - - TestHelpers.AssertDriverContentsWithFrameAre ( - @" -This is -the -first -line. -This is -the -second -line. ", - output - ); - - Assert.Equal (eventCount, (text [0].Count + text [1].Count) * 2); - top.Dispose (); - } - [Fact] public void ToString_Override () { @@ -292,7 +49,4 @@ public void ToString_Override () c2.ToString () ); } - - // TODO: Move the tests below to View or Color - they test ColorScheme, not Cell primitives. - private TextView CreateTextView () { return new() { Width = 30, Height = 10 }; } } diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index b610553146..96e20b401d 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -8756,4 +8756,250 @@ public void Draw_Esc_Rune () tv.Dispose (); } + + [Fact] + public void Equals_True () + { + var c1 = new Cell (); + var c2 = new Cell (); + Assert.True (c1.Equals (c2)); + Assert.True (c2.Equals (c1)); + + c1.Rune = new ('a'); + c1.Attribute = new (); + c2.Rune = new ('a'); + c2.Attribute = new (); + Assert.True (c1.Equals (c2)); + Assert.True (c2.Equals (c1)); + } + + [Fact] + [AutoInitShutdown] + public void CellEventArgs_WordWrap_True () + { + var eventCount = 0; + + List> text = + [ + Cell.ToCells ( + "This is the first line.".ToRunes () + ), + + Cell.ToCells ( + "This is the second line.".ToRunes () + ) + ]; + TextView tv = CreateTextView (); + tv.DrawNormalColor += _textView_DrawColor; + tv.DrawReadOnlyColor += _textView_DrawColor; + tv.DrawSelectionColor += _textView_DrawColor; + tv.DrawUsedColor += _textView_DrawColor; + + void _textView_DrawColor (object sender, CellEventArgs e) + { + Assert.Equal (e.Line [e.Col], text [e.UnwrappedPosition.Row] [e.UnwrappedPosition.Col]); + eventCount++; + } + + tv.Text = $"{Cell.ToString (text [0])}\n{Cell.ToString (text [1])}\n"; + Assert.False (tv.WordWrap); + var top = new Toplevel (); + top.Add (tv); + Application.Begin (top); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +This is the first line. +This is the second line.", + _output + ); + + tv.Width = 10; + tv.Height = 25; + tv.WordWrap = true; + Application.Refresh (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +This is +the +first +line. +This is +the +second +line. ", + _output + ); + + Assert.Equal (eventCount, (text [0].Count + text [1].Count) * 2); + top.Dispose (); + } + + [Fact] + [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)] + public void Cell_LoadCells_InheritsPreviousAttribute () + { + List cells = []; + + foreach (KeyValuePair color in Colors.ColorSchemes) + { + string csName = color.Key; + + foreach (Rune rune in csName.EnumerateRunes ()) + { + cells.Add (new () { Rune = rune, Attribute = color.Value.Normal }); + } + + cells.Add (new () { Rune = (Rune)'\n', Attribute = color.Value.Focus }); + } + + TextView tv = CreateTextView (); + tv.Load (cells); + var top = new Toplevel (); + top.Add (tv); + RunState rs = Application.Begin (top); + Assert.True (tv.InheritsPreviousAttribute); + + var expectedText = @" +TopLevel +Base +Dialog +Menu +Error "; + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + + Attribute [] attributes = + { + // 0 + Colors.ColorSchemes ["TopLevel"].Normal, + + // 1 + Colors.ColorSchemes ["Base"].Normal, + + // 2 + Colors.ColorSchemes ["Dialog"].Normal, + + // 3 + Colors.ColorSchemes ["Menu"].Normal, + + // 4 + Colors.ColorSchemes ["Error"].Normal, + + // 5 + tv.ColorScheme!.Focus + }; + + var expectedColor = @" +0000000055 +1111555555 +2222225555 +3333555555 +4444455555"; + TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); + + tv.WordWrap = true; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); + + tv.CursorPosition = new (6, 2); + tv.SelectionStartColumn = 0; + tv.SelectionStartRow = 0; + Assert.Equal ($"TopLevel{Environment.NewLine}Base{Environment.NewLine}Dialog", tv.SelectedText); + tv.Copy (); + tv.Selecting = false; + tv.CursorPosition = new (2, 4); + tv.Paste (); + Application.Refresh (); + + expectedText = @" +TopLevel +Base +Dialog +Menu +ErTopLevel +Base +Dialogror "; + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + + expectedColor = @" +0000000055 +1111555555 +2222225555 +3333555555 +4400000000 +1111555555 +2222224445"; + TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); + + tv.Undo (); + tv.CursorPosition = new (0, 3); + tv.SelectionStartColumn = 0; + tv.SelectionStartRow = 0; + + Assert.Equal ( + $"TopLevel{Environment.NewLine}Base{Environment.NewLine}Dialog{Environment.NewLine}", + tv.SelectedText + ); + tv.Copy (); + tv.Selecting = false; + tv.CursorPosition = new (2, 4); + tv.Paste (); + Application.Refresh (); + + expectedText = @" +TopLevel +Base +Dialog +Menu +ErTopLevel +Base +Dialog +ror "; + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + + expectedColor = @" +0000000055 +1111555555 +2222225555 +3333555555 +4400000000 +1111555555 +2222225555 +4445555555"; + TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes); + + Application.End (rs); + top.Dispose (); + } + + [Fact] + public void Cell_LoadCells_Without_ColorScheme_Is_Never_Null () + { + List cells = new () + { + new() { Rune = new ('T') }, + new() { Rune = new ('e') }, + new() { Rune = new ('s') }, + new() { Rune = new ('t') } + }; + TextView tv = CreateTextView (); + var top = new Toplevel (); + top.Add (tv); + tv.Load (cells); + + for (var i = 0; i < tv.Lines; i++) + { + List line = tv.GetLine (i); + + foreach (Cell c in line) + { + Assert.NotNull (c.Attribute); + } + } + } + + private TextView CreateTextView () { return new () { Width = 30, Height = 10 }; } + } From 22cf92da851adb09c3685637c0f301d3dbb04949 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 7 Oct 2024 00:56:18 +0100 Subject: [PATCH 21/24] Fix newline unit test error on Linux. --- UnitTests/Views/TextViewTests.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 96e20b401d..7e1cce05c4 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -4722,7 +4722,14 @@ public void HistoryText_Undo_Redo_ApplyCellsAttribute () tv.SelectionStartColumn = 12; tv.CursorPosition = new Point (18, 1); - Assert.Equal (31, tv.SelectedLength); + if (Environment.NewLine.Length == 2) + { + Assert.Equal (31, tv.SelectedLength); + } + else + { + Assert.Equal (30, tv.SelectedLength); + } Assert.Equal ($"first line.{Environment.NewLine}This is the second", tv.SelectedText); Assert.Equal ($"first line.{Environment.NewLine}This is the second", Cell.ToString (tv.SelectedCellsList)); Assert.Equal (new Point (18, 1), tv.CursorPosition); From 17ad70f92dc59e2a3641aea32e3be3aed02987ef Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 7 Oct 2024 11:53:33 +0100 Subject: [PATCH 22/24] Make _originalCellsList field as readonly. --- Terminal.Gui/Views/TextView.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index f283f47d3d..0ba83778b5 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -1131,7 +1131,7 @@ public enum LineStatus private readonly List _historyTextItems = []; private int _idxHistoryText = -1; - private List> _originalCellsList = []; + private readonly List> _originalCellsList = []; public bool HasHistoryChanges => _idxHistoryText > -1; public bool IsFromHistory { get; private set; } @@ -1171,7 +1171,7 @@ public void Clear (List> cellsList) _historyTextItems.Clear (); _idxHistoryText = -1; _originalCellsList.Clear (); - + // Save a copy of the original, not the reference foreach (List cells in cellsList) { _originalCellsList.Add ([..cells]); From 52435e4943ccc615e115a311a9bb607681c4dfee Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 8 Oct 2024 22:13:03 +0100 Subject: [PATCH 23/24] Move Prompt method and all ColorPicker*.cs to ColorPicker.*.cs. --- Terminal.Gui/Drawing/ColorScheme.Colors.cs | 119 ----------------- .../{ColorPicker16.cs => ColorPicker.16.cs} | 0 Terminal.Gui/Views/ColorPicker.Prompt.cs | 123 ++++++++++++++++++ ...lorPickerStyle.cs => ColorPicker.Style.cs} | 0 Terminal.Gui/Views/ColorPicker.cs | 2 +- Terminal.Gui/Views/TextView.cs | 2 +- 6 files changed, 125 insertions(+), 121 deletions(-) rename Terminal.Gui/Views/{ColorPicker16.cs => ColorPicker.16.cs} (100%) create mode 100644 Terminal.Gui/Views/ColorPicker.Prompt.cs rename Terminal.Gui/Views/{ColorPickerStyle.cs => ColorPicker.Style.cs} (100%) diff --git a/Terminal.Gui/Drawing/ColorScheme.Colors.cs b/Terminal.Gui/Drawing/ColorScheme.Colors.cs index a7c827be5a..ca14a49868 100644 --- a/Terminal.Gui/Drawing/ColorScheme.Colors.cs +++ b/Terminal.Gui/Drawing/ColorScheme.Colors.cs @@ -173,123 +173,4 @@ public ColorScheme? this [string key] return ColorSchemes; } - - /// - /// Open a with two or , based on the - /// is false or true, respectively, for - /// and colors. - /// - /// The title to show in the dialog. - /// The current attribute used. - /// The new attribute. - /// if a new color was accepted, otherwise . - public static bool PromptForColors (string title, Attribute? currentAttribute, out Attribute newAttribute) - { - var accept = false; - - var d = new Dialog - { - Title = title, - Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)), - Height = 20 - }; - - var btnOk = new Button - { - X = Pos.Center () - 5, - Y = Application.Force16Colors ? 6 : 4, - Text = "Ok", - Width = Dim.Auto (), - IsDefault = true - }; - - btnOk.Accept += (s, e) => - { - accept = true; - e.Handled = true; - Application.RequestStop (); - }; - - var btnCancel = new Button - { - X = Pos.Center () + 5, - Y = 4, - Text = "Cancel", - Width = Dim.Auto () - }; - - btnCancel.Accept += (s, e) => - { - e.Handled = true; - Application.RequestStop (); - }; - - d.Add (btnOk); - d.Add (btnCancel); - - d.AddButton (btnOk); - d.AddButton (btnCancel); - - View cpForeground; - - if (Application.Force16Colors) - { - cpForeground = new ColorPicker16 - { - SelectedColor = currentAttribute!.Value.Foreground.GetClosestNamedColor16 (), - Width = Dim.Fill (), - BorderStyle = LineStyle.Single, - Title = "Foreground" - }; - } - else - { - cpForeground = new ColorPicker - { - SelectedColor = currentAttribute!.Value.Foreground, - Width = Dim.Fill (), - Style = new () { ShowColorName = true, ShowTextFields = true }, - BorderStyle = LineStyle.Single, - Title = "Foreground" - }; - ((ColorPicker)cpForeground).ApplyStyleChanges (); - } - - View cpBackground; - - if (Application.Force16Colors) - { - cpBackground = new ColorPicker16 - { - SelectedColor = currentAttribute!.Value.Background.GetClosestNamedColor16 (), - Y = Pos.Bottom (cpForeground) + 1, - Width = Dim.Fill (), - BorderStyle = LineStyle.Single, - Title = "Background" - }; - } - else - { - cpBackground = new ColorPicker - { - SelectedColor = currentAttribute!.Value.Background, - Width = Dim.Fill (), - Y = Pos.Bottom (cpForeground) + 1, - Style = new () { ShowColorName = true, ShowTextFields = true }, - BorderStyle = LineStyle.Single, - Title = "Background" - }; - ((ColorPicker)cpBackground).ApplyStyleChanges (); - } - - d.Add (cpForeground, cpBackground); - - Application.Run (d); - d.Dispose (); - Color newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor; - Color newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor; - newAttribute = new (newForeColor, newBackColor); - - return accept; - } } diff --git a/Terminal.Gui/Views/ColorPicker16.cs b/Terminal.Gui/Views/ColorPicker.16.cs similarity index 100% rename from Terminal.Gui/Views/ColorPicker16.cs rename to Terminal.Gui/Views/ColorPicker.16.cs diff --git a/Terminal.Gui/Views/ColorPicker.Prompt.cs b/Terminal.Gui/Views/ColorPicker.Prompt.cs new file mode 100644 index 0000000000..95798899ad --- /dev/null +++ b/Terminal.Gui/Views/ColorPicker.Prompt.cs @@ -0,0 +1,123 @@ +namespace Terminal.Gui; + +public partial class ColorPicker +{ + /// + /// Open a with two or , based on the + /// is false or true, respectively, for + /// and colors. + /// + /// The title to show in the dialog. + /// The current attribute used. + /// The new attribute. + /// if a new color was accepted, otherwise . + public static bool Prompt (string title, Attribute? currentAttribute, out Attribute newAttribute) + { + var accept = false; + + var d = new Dialog + { + Title = title, + Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)), + Height = 20 + }; + + var btnOk = new Button + { + X = Pos.Center () - 5, + Y = Application.Force16Colors ? 6 : 4, + Text = "Ok", + Width = Dim.Auto (), + IsDefault = true + }; + + btnOk.Accept += (s, e) => + { + accept = true; + e.Handled = true; + Application.RequestStop (); + }; + + var btnCancel = new Button + { + X = Pos.Center () + 5, + Y = 4, + Text = "Cancel", + Width = Dim.Auto () + }; + + btnCancel.Accept += (s, e) => + { + e.Handled = true; + Application.RequestStop (); + }; + + d.Add (btnOk); + d.Add (btnCancel); + + d.AddButton (btnOk); + d.AddButton (btnCancel); + + View cpForeground; + + if (Application.Force16Colors) + { + cpForeground = new ColorPicker16 + { + SelectedColor = currentAttribute!.Value.Foreground.GetClosestNamedColor16 (), + Width = Dim.Fill (), + BorderStyle = LineStyle.Single, + Title = "Foreground" + }; + } + else + { + cpForeground = new ColorPicker + { + SelectedColor = currentAttribute!.Value.Foreground, + Width = Dim.Fill (), + Style = new () { ShowColorName = true, ShowTextFields = true }, + BorderStyle = LineStyle.Single, + Title = "Foreground" + }; + ((ColorPicker)cpForeground).ApplyStyleChanges (); + } + + View cpBackground; + + if (Application.Force16Colors) + { + cpBackground = new ColorPicker16 + { + SelectedColor = currentAttribute!.Value.Background.GetClosestNamedColor16 (), + Y = Pos.Bottom (cpForeground) + 1, + Width = Dim.Fill (), + BorderStyle = LineStyle.Single, + Title = "Background" + }; + } + else + { + cpBackground = new ColorPicker + { + SelectedColor = currentAttribute!.Value.Background, + Width = Dim.Fill (), + Y = Pos.Bottom (cpForeground) + 1, + Style = new () { ShowColorName = true, ShowTextFields = true }, + BorderStyle = LineStyle.Single, + Title = "Background" + }; + ((ColorPicker)cpBackground).ApplyStyleChanges (); + } + + d.Add (cpForeground, cpBackground); + + Application.Run (d); + d.Dispose (); + Color newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor; + Color newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor; + newAttribute = new (newForeColor, newBackColor); + + return accept; + } +} diff --git a/Terminal.Gui/Views/ColorPickerStyle.cs b/Terminal.Gui/Views/ColorPicker.Style.cs similarity index 100% rename from Terminal.Gui/Views/ColorPickerStyle.cs rename to Terminal.Gui/Views/ColorPicker.Style.cs diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs index 9c5cec83be..18aeaa0e5c 100644 --- a/Terminal.Gui/Views/ColorPicker.cs +++ b/Terminal.Gui/Views/ColorPicker.cs @@ -7,7 +7,7 @@ namespace Terminal.Gui; /// /// True color picker using HSL /// -public class ColorPicker : View +public partial class ColorPicker : View { /// /// Creates a new instance of . Use diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 0ba83778b5..517de8063a 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2894,7 +2894,7 @@ internal void ApplyCellsAttribute (Attribute attribute) /// public void PromptForColors () { - if (!Colors.PromptForColors ( + if (!ColorPicker.Prompt ( "Colors", GetSelectedCellAttribute (), out Attribute newAttribute From a6234da33409d948a9080ea002a109308b28417a Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 9 Oct 2024 12:40:47 +0100 Subject: [PATCH 24/24] Fix merge errors. --- Terminal.Gui/Views/ColorPicker.Prompt.cs | 8 ++++---- Terminal.Gui/Views/TextView.cs | 2 +- UnitTests/Views/TextViewTests.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/Views/ColorPicker.Prompt.cs b/Terminal.Gui/Views/ColorPicker.Prompt.cs index 95798899ad..3f2372db9a 100644 --- a/Terminal.Gui/Views/ColorPicker.Prompt.cs +++ b/Terminal.Gui/Views/ColorPicker.Prompt.cs @@ -31,10 +31,10 @@ public static bool Prompt (string title, Attribute? currentAttribute, out Attrib IsDefault = true }; - btnOk.Accept += (s, e) => + btnOk.Accepting += (s, e) => { accept = true; - e.Handled = true; + e.Cancel = true; Application.RequestStop (); }; @@ -46,9 +46,9 @@ public static bool Prompt (string title, Attribute? currentAttribute, out Attrib Width = Dim.Auto () }; - btnCancel.Accept += (s, e) => + btnCancel.Accepting += (s, e) => { - e.Handled = true; + e.Cancel = true; Application.RequestStop (); }; diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 3ee5455bb0..c34e9220bd 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2859,7 +2859,7 @@ internal void ApplyCellsAttribute (Attribute attribute) } GetSelectedRegion (); - Selecting = false; + IsSelecting = false; _historyText.Add ( [.. selectedCellsOriginal], diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 853f38643b..a05af9ef07 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -8954,7 +8954,7 @@ public void Cell_LoadCells_InheritsPreviousAttribute () tv.SelectionStartRow = 0; Assert.Equal ($"TopLevel{Environment.NewLine}Base{Environment.NewLine}Dialog", tv.SelectedText); tv.Copy (); - tv.Selecting = false; + tv.IsSelecting = false; tv.CursorPosition = new (2, 4); tv.Paste (); Application.Refresh (); @@ -8989,7 +8989,7 @@ public void Cell_LoadCells_InheritsPreviousAttribute () tv.SelectedText ); tv.Copy (); - tv.Selecting = false; + tv.IsSelecting = false; tv.CursorPosition = new (2, 4); tv.Paste (); Application.Refresh ();