瀏覽代碼

Merge branch 'v2_develop' of tig:gui-cs/Terminal.Gui into v2_develop

Tig 9 月之前
父節點
當前提交
4cae9ef5ac
共有 37 個文件被更改,包括 1679 次插入1046 次删除
  1. 3 3
      .github/workflows/publish.yml
  2. 210 43
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  3. 152 6
      Terminal.Gui/Drawing/Cell.cs
  4. 8 8
      Terminal.Gui/Drawing/CellEventArgs.cs
  5. 1 1
      Terminal.Gui/Drawing/LineCanvas.cs
  6. 1 1
      Terminal.Gui/Drawing/StraightLine.cs
  7. 9 0
      Terminal.Gui/Resources/Strings.Designer.cs
  8. 15 12
      Terminal.Gui/Resources/Strings.fr-FR.resx
  9. 58 55
      Terminal.Gui/Resources/Strings.ja-JP.resx
  10. 16 13
      Terminal.Gui/Resources/Strings.pt-PT.resx
  11. 264 261
      Terminal.Gui/Resources/Strings.resx
  12. 9 6
      Terminal.Gui/Resources/Strings.zh-Hans.resx
  13. 2 2
      Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs
  14. 2 2
      Terminal.Gui/Views/AutocompleteFilepathContext.cs
  15. 0 0
      Terminal.Gui/Views/ColorPicker.16.cs
  16. 123 0
      Terminal.Gui/Views/ColorPicker.Prompt.cs
  17. 0 0
      Terminal.Gui/Views/ColorPicker.Style.cs
  18. 1 1
      Terminal.Gui/Views/ColorPicker.cs
  19. 3 3
      Terminal.Gui/Views/HistoryTextItemEventArgs.cs
  20. 16 16
      Terminal.Gui/Views/TextField.cs
  21. 265 238
      Terminal.Gui/Views/TextView.cs
  22. 9 9
      Terminal.Gui/Views/TreeView/Branch.cs
  23. 4 4
      Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs
  24. 9 1
      UICatalog/Scenarios/Editor.cs
  25. 23 9
      UICatalog/Scenarios/LineDrawing.cs
  26. 1 1
      UICatalog/Scenarios/ProgressBarStyles.cs
  27. 31 28
      UICatalog/Scenarios/SyntaxHighlighting.cs
  28. 7 9
      UICatalog/Scenarios/TreeViewFileSystem.cs
  29. 1 0
      UnitTests/Configuration/ThemeTests.cs
  30. 1 1
      UnitTests/ConsoleDrivers/DriverColorTests.cs
  31. 52 0
      UnitTests/Drawing/CellTests.cs
  32. 1 1
      UnitTests/Text/AutocompleteTests.cs
  33. 1 0
      UnitTests/View/Mouse/MouseTests.cs
  34. 0 294
      UnitTests/Views/RuneCellTests.cs
  35. 14 0
      UnitTests/Views/TextFieldTests.cs
  36. 360 11
      UnitTests/Views/TextViewTests.cs
  37. 7 7
      UnitTests/Views/TreeViewTests.cs

+ 3 - 3
.github/workflows/publish.yml

@@ -43,11 +43,11 @@ jobs:
     - name: Build Release
       run: |
         dotnet-gitversion /updateprojectfiles
-        dotnet build --no-incremental --nologo --force --configuration Release
-        dotnet test --configuration Release
+        dotnet build Terminal.Gui/Terminal.Gui.csproj --no-incremental --nologo --force --configuration Release
+        dotnet test Terminal.Gui/Terminal.Gui.csproj --configuration Release
 
     - name: Pack
-      run: dotnet pack -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}' 
+      run: dotnet pack Terminal.Gui/Terminal.Gui.csproj -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}' 
 
     # - name: Test to generate Code Coverage Report
     #   run: |

+ 210 - 43
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -39,7 +39,7 @@ internal class CursesDriver : ConsoleDriver
         }
     }
 
-    public override bool SupportsTrueColor => false;
+    public override bool SupportsTrueColor => true;
 
     /// <inheritdoc/>
     public override bool EnsureCursorVisibility () { return false; }
@@ -200,8 +200,12 @@ internal class CursesDriver : ConsoleDriver
         if (!RunningUnitTests)
         {
             Platform.Suspend ();
-            Curses.Window.Standard.redrawwin ();
-            Curses.refresh ();
+
+            if (Force16Colors)
+            {
+                Curses.Window.Standard.redrawwin ();
+                Curses.refresh ();
+            }
         }
 
         StartReportingMouseMoves ();
@@ -214,74 +218,232 @@ internal class CursesDriver : ConsoleDriver
         if (!RunningUnitTests && Col >= 0 && Col < Cols && Row >= 0 && Row < Rows)
         {
             Curses.move (Row, Col);
-            Curses.raw ();
-            Curses.noecho ();
-            Curses.refresh ();
+
+            if (Force16Colors)
+            {
+                Curses.raw ();
+                Curses.noecho ();
+                Curses.refresh ();
+            }
         }
     }
 
-
     public override void UpdateScreen ()
     {
-        for (var row = 0; row < Rows; row++)
+        if (Force16Colors)
         {
-            if (!_dirtyLines [row])
+            for (var row = 0; row < Rows; row++)
             {
-                continue;
-            }
-
-            _dirtyLines [row] = false;
-
-            for (var col = 0; col < Cols; col++)
-            {
-                if (Contents [row, col].IsDirty == false)
+                if (!_dirtyLines [row])
                 {
                     continue;
                 }
 
-                if (RunningUnitTests)
+                _dirtyLines [row] = false;
+
+                for (var col = 0; col < Cols; col++)
                 {
-                    // In unit tests, we don't want to actually write to the screen.
-                    continue;
-                }
+                    if (Contents [row, col].IsDirty == false)
+                    {
+                        continue;
+                    }
 
-                Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().PlatformColor);
+                    if (RunningUnitTests)
+                    {
+                        // In unit tests, we don't want to actually write to the screen.
+                        continue;
+                    }
 
-                Rune rune = Contents [row, col].Rune;
+                    Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().PlatformColor);
 
-                if (rune.IsBmp)
-                {
-                    // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell.
-                    if (rune.GetColumns () < 2)
+                    Rune rune = Contents [row, col].Rune;
+
+                    if (rune.IsBmp)
                     {
-                        Curses.mvaddch (row, col, rune.Value);
+                        // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell.
+                        if (rune.GetColumns () < 2)
+                        {
+                            Curses.mvaddch (row, col, rune.Value);
+                        }
+                        else /*if (col + 1 < Cols)*/
+                        {
+                            Curses.mvaddwstr (row, col, rune.ToString ());
+                        }
                     }
-                    else /*if (col + 1 < Cols)*/
+                    else
                     {
                         Curses.mvaddwstr (row, col, rune.ToString ());
+
+                        if (rune.GetColumns () > 1 && col + 1 < Cols)
+                        {
+                            // TODO: This is a hack to deal with non-BMP and wide characters.
+                            //col++;
+                            Curses.mvaddch (row, ++col, '*');
+                        }
                     }
                 }
-                else
+            }
+
+            if (!RunningUnitTests)
+            {
+                Curses.move (Row, Col);
+                _window.wrefresh ();
+            }
+        }
+        else
+        {
+            if (RunningUnitTests
+                || Console.WindowHeight < 1
+                || Contents.Length != Rows * Cols
+                || Rows != Console.WindowHeight)
+            {
+                return;
+            }
+
+            var top = 0;
+            var left = 0;
+            int rows = Rows;
+            int cols = Cols;
+            var output = new StringBuilder ();
+            Attribute? redrawAttr = null;
+            int lastCol = -1;
+
+            CursorVisibility? savedVisibility = _currentCursorVisibility;
+            SetCursorVisibility (CursorVisibility.Invisible);
+
+            for (int row = top; row < rows; row++)
+            {
+                if (Console.WindowHeight < 1)
+                {
+                    return;
+                }
+
+                if (!_dirtyLines [row])
+                {
+                    continue;
+                }
+
+                if (!SetCursorPosition (0, row))
                 {
-                    Curses.mvaddwstr (row, col, rune.ToString ());
+                    return;
+                }
+
+                _dirtyLines [row] = false;
+                output.Clear ();
 
-                    if (rune.GetColumns () > 1 && col + 1 < Cols)
+                for (int col = left; col < cols; col++)
+                {
+                    lastCol = -1;
+                    var outputWidth = 0;
+
+                    for (; col < cols; col++)
                     {
-                        // TODO: This is a hack to deal with non-BMP and wide characters.
-                        //col++;
-                        Curses.mvaddch (row, ++col, '*');
+                        if (!Contents [row, col].IsDirty)
+                        {
+                            if (output.Length > 0)
+                            {
+                                WriteToConsole (output, ref lastCol, row, ref outputWidth);
+                            }
+                            else if (lastCol == -1)
+                            {
+                                lastCol = col;
+                            }
+
+                            if (lastCol + 1 < cols)
+                            {
+                                lastCol++;
+                            }
+
+                            continue;
+                        }
+
+                        if (lastCol == -1)
+                        {
+                            lastCol = col;
+                        }
+
+                        Attribute attr = Contents [row, col].Attribute.Value;
+
+                        // Performance: Only send the escape sequence if the attribute has changed.
+                        if (attr != redrawAttr)
+                        {
+                            redrawAttr = attr;
+
+                            output.Append (
+                                           EscSeqUtils.CSI_SetForegroundColorRGB (
+                                                                                  attr.Foreground.R,
+                                                                                  attr.Foreground.G,
+                                                                                  attr.Foreground.B
+                                                                                 )
+                                          );
+
+                            output.Append (
+                                           EscSeqUtils.CSI_SetBackgroundColorRGB (
+                                                                                  attr.Background.R,
+                                                                                  attr.Background.G,
+                                                                                  attr.Background.B
+                                                                                 )
+                                          );
+                        }
+
+                        outputWidth++;
+                        Rune rune = Contents [row, col].Rune;
+                        output.Append (rune);
+
+                        if (Contents [row, col].CombiningMarks.Count > 0)
+                        {
+                            // AtlasEngine does not support NON-NORMALIZED combining marks in a way
+                            // compatible with the driver architecture. Any CMs (except in the first col)
+                            // are correctly combined with the base char, but are ALSO treated as 1 column
+                            // width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é  ]`.
+                            // 
+                            // For now, we just ignore the list of CMs.
+                            //foreach (var combMark in Contents [row, col].CombiningMarks) {
+                            //	output.Append (combMark);
+                            //}
+                            // WriteToConsole (output, ref lastCol, row, ref outputWidth);
+                        }
+                        else if (rune.IsSurrogatePair () && rune.GetColumns () < 2)
+                        {
+                            WriteToConsole (output, ref lastCol, row, ref outputWidth);
+                            SetCursorPosition (col - 1, row);
+                        }
+
+                        Contents [row, col].IsDirty = false;
                     }
                 }
+
+                if (output.Length > 0)
+                {
+                    SetCursorPosition (lastCol, row);
+                    Console.Write (output);
+                }
             }
-        }
 
-        if (!RunningUnitTests)
-        {
-            Curses.move (Row, Col);
-            _window.wrefresh ();
+            SetCursorPosition (0, 0);
+
+            _currentCursorVisibility = savedVisibility;
+
+            void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
+            {
+                SetCursorPosition (lastCol, row);
+                Console.Write (output);
+                output.Clear ();
+                lastCol += outputWidth;
+                outputWidth = 0;
+            }
         }
     }
 
+    private bool SetCursorPosition (int col, int row)
+    {
+        // + 1 is needed because non-Windows is based on 1 instead of 0 and
+        // Console.CursorTop/CursorLeft isn't reliable.
+        Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1));
+
+        return true;
+    }
+
     internal override void End ()
     {
         StopReportingMouseMoves ();
@@ -405,7 +567,11 @@ internal class CursesDriver : ConsoleDriver
         if (!RunningUnitTests)
         {
             Curses.CheckWinChange ();
-            Curses.refresh ();
+
+            if (Force16Colors)
+            {
+                Curses.refresh ();
+            }
         }
 
         return new MainLoop (_mainLoopDriver);
@@ -852,7 +1018,8 @@ internal class CursesDriver : ConsoleDriver
     /// <returns></returns>
     private static Attribute MakeColor (short foreground, short background)
     {
-        var v = (short)((ushort)foreground | (background << 4));
+        //var v = (short)((ushort)foreground | (background << 4));
+        var v = (short)(((ushort)(foreground & 0xffff) << 16) | (background & 0xffff));
 
         // TODO: for TrueColor - Use InitExtendedPair
         Curses.InitColorPair (v, foreground, background);
@@ -872,7 +1039,7 @@ internal class CursesDriver : ConsoleDriver
     /// </remarks>
     public override Attribute MakeColor (in Color foreground, in Color background)
     {
-        if (!RunningUnitTests)
+        if (!RunningUnitTests && Force16Colors)
         {
             return MakeColor (
                               ColorNameToCursesColorNumber (foreground.GetClosestNamedColor16 ()),

+ 152 - 6
Terminal.Gui/Drawing/Cell.cs

@@ -4,19 +4,18 @@
 ///     Represents a single row/column in a Terminal.Gui rendering surface (e.g. <see cref="LineCanvas"/> and
 ///     <see cref="ConsoleDriver"/>).
 /// </summary>
-public record struct Cell ()
+public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Rune Rune = default)
 {
-
     /// <summary>The attributes to use when drawing the Glyph.</summary>
-    public Attribute? Attribute { get; set; } = null;
+    public Attribute? Attribute { get; set; } = Attribute;
 
     /// <summary>
     ///     Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.Cell"/> has been modified since the
     ///     last time it was drawn.
     /// </summary>
-    public bool IsDirty { get; set; } = false;
+    public bool IsDirty { get; set; } = IsDirty;
 
-    private Rune _rune = default;
+    private Rune _rune = Rune;
 
     /// <summary>The character to display. If <see cref="Rune"/> is <see langword="null"/>, then <see cref="Rune"/> is ignored.</summary>
     public Rune Rune
@@ -29,6 +28,8 @@ public record struct Cell ()
         }
     }
 
+    private List<Rune> _combiningMarks;
+
     /// <summary>
     ///     The combining marks for <see cref="Rune"/> that when combined makes this Cell a combining sequence. If
     ///     <see cref="CombiningMarks"/> empty, then <see cref="CombiningMarks"/> is ignored.
@@ -37,8 +38,153 @@ public record struct Cell ()
     ///     Only valid in the rare case where <see cref="Rune"/> is a combining sequence that could not be normalized to a
     ///     single Rune.
     /// </remarks>
-    internal List<Rune> CombiningMarks { get; } = new ();
+    internal List<Rune> CombiningMarks
+    {
+        get => _combiningMarks ?? [];
+        private set => _combiningMarks = value ?? [];
+    }
 
     /// <inheritdoc/>
     public override string ToString () { return $"[{Rune}, {Attribute}]"; }
+
+    /// <summary>Converts the string into a <see cref="List{Cell}"/>.</summary>
+    /// <param name="str">The string to convert.</param>
+    /// <param name="attribute">The <see cref="Gui.ColorScheme"/> to use.</param>
+    /// <returns></returns>
+    public static List<Cell> ToCellList (string str, Attribute? attribute = null)
+    {
+        List<Cell> cells = new ();
+
+        foreach (Rune rune in str.EnumerateRunes ())
+        {
+            cells.Add (new () { Rune = rune, Attribute = attribute });
+        }
+
+        return cells;
+    }
+
+    /// <summary>
+    ///     Splits a string into a List that will contain a <see cref="List{Cell}"/> for each line.
+    /// </summary>
+    /// <param name="content">The string content.</param>
+    /// <param name="attribute">The color scheme.</param>
+    /// <returns>A <see cref="List{Cell}"/> for each line.</returns>
+    public static List<List<Cell>> StringToLinesOfCells (string content, Attribute? attribute = null)
+    {
+        List<Cell> cells = content.EnumerateRunes ()
+                                  .Select (x => new Cell { Rune = x, Attribute = attribute })
+                                  .ToList ();
+
+        return SplitNewLines (cells);
+    }
+
+    /// <summary>Converts a <see cref="Cell"/> generic collection into a string.</summary>
+    /// <param name="cells">The enumerable cell to convert.</param>
+    /// <returns></returns>
+    public static string ToString (IEnumerable<Cell> cells)
+    {
+        var str = string.Empty;
+
+        foreach (Cell cell in cells)
+        {
+            str += cell.Rune.ToString ();
+        }
+
+        return str;
+    }
+
+    /// <summary>Converts a <see cref="List{Cell}"/> generic collection into a string.</summary>
+    /// <param name="cellsList">The enumerable cell to convert.</param>
+    /// <returns></returns>
+    public static string ToString (List<List<Cell>> cellsList)
+    {
+        var str = string.Empty;
+
+        for (var i = 0; i < cellsList.Count; i++)
+        {
+            IEnumerable<Cell> 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<Cell> StringToCells (string str, Attribute? attribute = null)
+    {
+        List<Cell> cells = [];
+
+        foreach (Rune rune in str.ToRunes ())
+        {
+            cells.Add (new () { Rune = rune, Attribute = attribute });
+        }
+
+        return cells;
+    }
+
+    internal static List<Cell> ToCells (IEnumerable<Rune> runes, Attribute? attribute = null)
+    {
+        List<Cell> cells = new ();
+
+        foreach (Rune rune in runes)
+        {
+            cells.Add (new () { Rune = rune, Attribute = attribute });
+        }
+
+        return cells;
+    }
+
+    private static List<List<Cell>> SplitNewLines (List<Cell> cells)
+    {
+        List<List<Cell>> 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;
+    }
+
+    /// <summary>
+    ///     Splits a rune cell list into a List that will contain a <see cref="List{Cell}"/> for each line.
+    /// </summary>
+    /// <param name="cells">The cells list.</param>
+    /// <returns></returns>
+    public static List<List<Cell>> ToCells (List<Cell> cells) { return SplitNewLines (cells); }
 }

+ 8 - 8
Terminal.Gui/Views/RuneCellEventArgs.cs → Terminal.Gui/Drawing/CellEventArgs.cs

@@ -1,27 +1,27 @@
 namespace Terminal.Gui;
 
-/// <summary>Args for events that relate to a specific <see cref="RuneCell"/>.</summary>
-public class RuneCellEventArgs
+/// <summary>Args for events that relate to a specific <see cref="Cell"/>.</summary>
+public record struct CellEventArgs
 {
-    /// <summary>Creates a new instance of the <see cref="RuneCellEventArgs"/> class.</summary>
+    /// <summary>Creates a new instance of the <see cref="CellEventArgs"/> class.</summary>
     /// <param name="line">The line.</param>
     /// <param name="col">The col index.</param>
     /// <param name="unwrappedPosition">The unwrapped row and col index.</param>
-    public RuneCellEventArgs (List<RuneCell> line, int col, (int Row, int Col) unwrappedPosition)
+    public CellEventArgs (List<Cell> line, int col, (int Row, int Col) unwrappedPosition)
     {
         Line = line;
         Col = col;
         UnwrappedPosition = unwrappedPosition;
     }
 
-    /// <summary>The index of the RuneCell in the line.</summary>
+    /// <summary>The index of the Cell in the line.</summary>
     public int Col { get; }
 
-    /// <summary>The list of runes the RuneCell is part of.</summary>
-    public List<RuneCell> Line { get; }
+    /// <summary>The list of runes the Cell is part of.</summary>
+    public List<Cell> Line { get; }
 
     /// <summary>
-    ///     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.
     /// </summary>
     public (int Row, int Col) UnwrappedPosition { get; }

+ 1 - 1
Terminal.Gui/Drawing/LineCanvas.cs

@@ -138,7 +138,7 @@ public class LineCanvas : IDisposable
         int length,
         Orientation orientation,
         LineStyle style,
-        Attribute? attribute = default
+        Attribute? attribute = null
     )
     {
         _cachedViewport = Rectangle.Empty;

+ 1 - 1
Terminal.Gui/Drawing/StraightLine.cs

@@ -16,7 +16,7 @@ public class StraightLine
         int length,
         Orientation orientation,
         LineStyle style,
-        Attribute? attribute = default
+        Attribute? attribute = null
     )
     {
         Start = start;

+ 9 - 0
Terminal.Gui/Resources/Strings.Designer.cs

@@ -1437,6 +1437,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Co_lors.
+        /// </summary>
+        internal static string ctxColors {
+            get {
+                return ResourceManager.GetString("ctxColors", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to _Copy.
         /// </summary>

+ 15 - 12
Terminal.Gui/Resources/Strings.fr-FR.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>Tout _sélectionner</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>_Tout supprimer</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
     <value>_Copier</value>
   </data>
   <data name="ctxCut" xml:space="preserve">
     <value>Co_uper</value>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>_Tout supprimer</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
     <value>C_oller</value>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>_Rétablir</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>Tout _sélectionner</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
     <value>_Annuler</value>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>_Rétablir</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
     <value>_Dossier</value>
   </data>
@@ -168,16 +168,19 @@
   <data name="wzNext" xml:space="preserve">
     <value>Prochai_n...</value>
   </data>
+  <data name="btnOpen" xml:space="preserve">
+    <value>Ouvrir</value>
+  </data>
   <data name="btnSave" xml:space="preserve">
     <value>Enregistrer</value>
   </data>
   <data name="btnSaveAs" xml:space="preserve">
     <value>E_nregistrer sous</value>
   </data>
-  <data name="btnOpen" xml:space="preserve">
-    <value>Ouvrir</value>
-  </data>
   <data name="dpTitle" xml:space="preserve">
     <value>Sélecteur de Date</value>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Cou_leurs</value>
+  </data>
 </root>

+ 58 - 55
Terminal.Gui/Resources/Strings.ja-JP.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>全て選択 (_S)</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>全て削除 (_D)</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
     <value>コピー (_C)</value>
   </data>
   <data name="ctxCut" xml:space="preserve">
     <value>切り取り (_T)</value>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>全て削除 (_D)</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
     <value>貼り付け (_P)</value>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>やり直し (_R)</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>全て選択 (_S)</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
     <value>元に戻す (_U)</value>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>やり直し (_R)</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
     <value>ディレクトリ</value>
   </data>
@@ -171,44 +171,47 @@
   <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>同じ名前のディレクトリはすでに存在しました</value>
   </data>
-  <data name="fdDeleteBody" xml:space="preserve">
-    <value>“{0}”を削除もよろしいですか?この操作は元に戻りません</value>
-  </data>
-  <data name="fdType" xml:space="preserve">
-    <value>タイプ</value>
+  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したディレクトリを選択してください</value>
   </data>
-  <data name="fdSize" xml:space="preserve">
-    <value>サイズ</value>
+  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
+    <value>同じ名前のファイルはすでに存在しました</value>
   </data>
-  <data name="fdPathCaption" xml:space="preserve">
-    <value>パスを入力</value>
+  <data name="fdFileMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したファイルを選択してください</value>
   </data>
   <data name="fdFilename" xml:space="preserve">
     <value>ファイル名</value>
   </data>
-  <data name="fdNewTitle" xml:space="preserve">
-    <value>新規ディレクトリ</value>
-  </data>
-  <data name="btnNo" xml:space="preserve">
-    <value>いいえ (_N)</value>
-  </data>
-  <data name="btnYes" xml:space="preserve">
-    <value>はい (_Y)</value>
+  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したファイルまたはディレクトリを選択してください</value>
   </data>
   <data name="fdModified" xml:space="preserve">
     <value>変更日時</value>
   </data>
-  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したファイルまたはディレクトリを選択してください</value>
+  <data name="fdPathCaption" xml:space="preserve">
+    <value>パスを入力</value>
   </data>
-  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したディレクトリを選択してください</value>
+  <data name="fdSearchCaption" xml:space="preserve">
+    <value>検索を入力</value>
   </data>
-  <data name="fdFileMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したファイルを選択してください</value>
+  <data name="fdSize" xml:space="preserve">
+    <value>サイズ</value>
   </data>
-  <data name="fdRenamePrompt" xml:space="preserve">
-    <value>名前:</value>
+  <data name="fdType" xml:space="preserve">
+    <value>タイプ</value>
+  </data>
+  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
+    <value>ファイルタイプが間違っでいます</value>
+  </data>
+  <data name="fdAnyFiles" xml:space="preserve">
+    <value>任意ファイル</value>
+  </data>
+  <data name="fdDeleteBody" xml:space="preserve">
+    <value>“{0}”を削除もよろしいですか?この操作は元に戻りません</value>
+  </data>
+  <data name="fdDeleteFailedTitle" xml:space="preserve">
+    <value>削除失敗</value>
   </data>
   <data name="fdDeleteTitle" xml:space="preserve">
     <value>{0} を削除</value>
@@ -216,35 +219,26 @@
   <data name="fdNewFailed" xml:space="preserve">
     <value>新規失敗</value>
   </data>
-  <data name="fdExisting" xml:space="preserve">
-    <value>既存</value>
+  <data name="fdNewTitle" xml:space="preserve">
+    <value>新規ディレクトリ</value>
   </data>
-  <data name="fdRenameTitle" xml:space="preserve">
-    <value>名前を変更</value>
+  <data name="btnNo" xml:space="preserve">
+    <value>いいえ (_N)</value>
   </data>
   <data name="fdRenameFailedTitle" xml:space="preserve">
     <value>変更失敗</value>
   </data>
-  <data name="fdDeleteFailedTitle" xml:space="preserve">
-    <value>削除失敗</value>
-  </data>
-  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
-    <value>同じ名前のファイルはすでに存在しました</value>
-  </data>
-  <data name="fdSearchCaption" xml:space="preserve">
-    <value>検索を入力</value>
-  </data>
-  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
-    <value>ファイルタイプが間違っでいます</value>
+  <data name="fdRenamePrompt" xml:space="preserve">
+    <value>名前:</value>
   </data>
-  <data name="fdAnyFiles" xml:space="preserve">
-    <value>任意ファイル</value>
+  <data name="fdRenameTitle" xml:space="preserve">
+    <value>名前を変更</value>
   </data>
-  <data name="btnCancel" xml:space="preserve">
-    <value>キャンセル (_C)</value>
+  <data name="btnYes" xml:space="preserve">
+    <value>はい (_Y)</value>
   </data>
-  <data name="btnOk" xml:space="preserve">
-    <value>OK (_O)</value>
+  <data name="fdExisting" xml:space="preserve">
+    <value>既存</value>
   </data>
   <data name="btnOpen" xml:space="preserve">
     <value>開く (_O)</value>
@@ -255,6 +249,12 @@
   <data name="btnSaveAs" xml:space="preserve">
     <value>名前を付けて保存 (_S)</value>
   </data>
+  <data name="btnOk" xml:space="preserve">
+    <value>OK (_O)</value>
+  </data>
+  <data name="btnCancel" xml:space="preserve">
+    <value>キャンセル (_C)</value>
+  </data>
   <data name="fdCtxDelete" xml:space="preserve">
     <value>削除 (_D)</value>
   </data>
@@ -276,4 +276,7 @@
   <data name="dpTitle" xml:space="preserve">
     <value>日付ピッカー</value>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>絵の具 (_L)</value>
+  </data>
 </root>

+ 16 - 13
Terminal.Gui/Resources/Strings.pt-PT.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>_Selecionar Tudo</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>_Apagar Tudo</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
     <value>_Copiar</value>
   </data>
   <data name="ctxCut" xml:space="preserve">
     <value>Cor_tar</value>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>_Apagar Tudo</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
     <value>Co_lar</value>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>_Refazer</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>_Selecionar Tudo</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
     <value>_Desfazer</value>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>_Refazer</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
     <value>Diretório</value>
   </data>
@@ -168,16 +168,19 @@
   <data name="wzNext" xml:space="preserve">
     <value>S_eguir...</value>
   </data>
-  <data name="btnSaveAs" xml:space="preserve">
-    <value>Guardar como</value>
+  <data name="btnOpen" xml:space="preserve">
+    <value>Abrir</value>
   </data>
   <data name="btnSave" xml:space="preserve">
     <value>Guardar</value>
   </data>
-  <data name="btnOpen" xml:space="preserve">
-    <value>Abrir</value>
+  <data name="btnSaveAs" xml:space="preserve">
+    <value>Guardar como</value>
   </data>
   <data name="dpTitle" xml:space="preserve">
     <value>Seletor de Data</value>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Co_res</value>
+  </data>
 </root>

+ 264 - 261
Terminal.Gui/Resources/Strings.resx

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <root>
-    <!-- 
+  <!-- 
     Microsoft ResX Schema 
     
     Version 2.0
@@ -59,663 +59,666 @@
             : using a System.ComponentModel.TypeConverter
             : and then encoded with base64 encoding.
     -->
-    <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-        <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-        <xsd:element name="root" msdata:IsDataSet="true">
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
             <xsd:complexType>
-                <xsd:choice maxOccurs="unbounded">
-                    <xsd:element name="metadata">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" use="required" type="xsd:string" />
-                            <xsd:attribute name="type" type="xsd:string" />
-                            <xsd:attribute name="mimetype" type="xsd:string" />
-                            <xsd:attribute ref="xml:space" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="assembly">
-                        <xsd:complexType>
-                            <xsd:attribute name="alias" type="xsd:string" />
-                            <xsd:attribute name="name" type="xsd:string" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="data">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-                            <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-                            <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-                            <xsd:attribute ref="xml:space" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="resheader">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" type="xsd:string" use="required" />
-                        </xsd:complexType>
-                    </xsd:element>
-                </xsd:choice>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
             </xsd:complexType>
-        </xsd:element>
-    </xsd:schema>
-    <resheader name="resmimetype">
-        <value>text/microsoft-resx</value>
-    </resheader>
-    <resheader name="version">
-        <value>2.0</value>
-    </resheader>
-    <resheader name="reader">
-        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-    </resheader>
-    <resheader name="writer">
-        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-    </resheader>
-    <data name="ctxSelectAll" xml:space="preserve">
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
     <value>_Select All</value>
   </data>
-    <data name="ctxDeleteAll" xml:space="preserve">
+  <data name="ctxDeleteAll" xml:space="preserve">
     <value>_Delete All</value>
   </data>
-    <data name="ctxCopy" xml:space="preserve">
+  <data name="ctxCopy" xml:space="preserve">
     <value>_Copy</value>
   </data>
-    <data name="ctxCut" xml:space="preserve">
+  <data name="ctxCut" xml:space="preserve">
     <value>Cu_t</value>
   </data>
-    <data name="ctxPaste" xml:space="preserve">
+  <data name="ctxPaste" xml:space="preserve">
     <value>_Paste</value>
   </data>
-    <data name="ctxUndo" xml:space="preserve">
+  <data name="ctxUndo" xml:space="preserve">
     <value>_Undo</value>
   </data>
-    <data name="ctxRedo" xml:space="preserve">
+  <data name="ctxRedo" xml:space="preserve">
     <value>_Redo</value>
   </data>
-    <data name="fdDirectory" xml:space="preserve">
+  <data name="fdDirectory" xml:space="preserve">
     <value>Directory</value>
   </data>
-    <data name="fdFile" xml:space="preserve">
+  <data name="fdFile" xml:space="preserve">
     <value>File</value>
   </data>
-    <data name="fdSave" xml:space="preserve">
+  <data name="fdSave" xml:space="preserve">
     <value>Save</value>
   </data>
-    <data name="fdSaveAs" xml:space="preserve">
+  <data name="fdSaveAs" xml:space="preserve">
     <value>Save as</value>
   </data>
-    <data name="fdOpen" xml:space="preserve">
+  <data name="fdOpen" xml:space="preserve">
     <value>Open</value>
   </data>
-    <data name="fdSelectFolder" xml:space="preserve">
+  <data name="fdSelectFolder" xml:space="preserve">
     <value>Select folder</value>
   </data>
-    <data name="fdSelectMixed" xml:space="preserve">
+  <data name="fdSelectMixed" xml:space="preserve">
     <value>Select Mixed</value>
   </data>
-    <data name="wzBack" xml:space="preserve">
+  <data name="wzBack" xml:space="preserve">
     <value>_Back</value>
   </data>
-    <data name="wzFinish" xml:space="preserve">
+  <data name="wzFinish" xml:space="preserve">
     <value>Fi_nish</value>
   </data>
-    <data name="wzNext" xml:space="preserve">
+  <data name="wzNext" xml:space="preserve">
     <value>_Next...</value>
   </data>
-    <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
+  <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>Directory already exists with that name</value>
     <comment>When trying to save a file with a name already taken by a directory</comment>
   </data>
-    <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
+  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
     <value>Must select an existing directory</value>
   </data>
-    <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
+  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
     <value>File already exists with that name</value>
   </data>
-    <data name="fdFileMustExistFeedback" xml:space="preserve">
+  <data name="fdFileMustExistFeedback" xml:space="preserve">
     <value>Must select an existing file</value>
     <comment>When trying to save a directory with a name already used by a file</comment>
   </data>
-    <data name="fdFilename" xml:space="preserve">
+  <data name="fdFilename" xml:space="preserve">
     <value>Filename</value>
   </data>
-    <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
+  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
     <value>Must select an existing file or directory</value>
   </data>
-    <data name="fdModified" xml:space="preserve">
+  <data name="fdModified" xml:space="preserve">
     <value>Modified</value>
   </data>
-    <data name="fdPathCaption" xml:space="preserve">
+  <data name="fdPathCaption" xml:space="preserve">
     <value>Enter Path</value>
   </data>
-    <data name="fdSearchCaption" xml:space="preserve">
+  <data name="fdSearchCaption" xml:space="preserve">
     <value>Enter Search</value>
   </data>
-    <data name="fdSize" xml:space="preserve">
+  <data name="fdSize" xml:space="preserve">
     <value>Size</value>
   </data>
-    <data name="fdType" xml:space="preserve">
+  <data name="fdType" xml:space="preserve">
     <value>Type</value>
   </data>
-    <data name="fdWrongFileTypeFeedback" xml:space="preserve">
+  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
     <value>Wrong file type</value>
     <comment>When trying to open/save a file that does not match the provided filter (e.g. csv)</comment>
   </data>
-    <data name="fdAnyFiles" xml:space="preserve">
+  <data name="fdAnyFiles" xml:space="preserve">
     <value>Any Files</value>
     <comment>Describes an AllowedType that matches anything</comment>
   </data>
-    <data name="fdDeleteBody" xml:space="preserve">
+  <data name="fdDeleteBody" xml:space="preserve">
     <value>Are you sure you want to delete '{0}'? This operation is permanent</value>
   </data>
-    <data name="fdDeleteFailedTitle" xml:space="preserve">
+  <data name="fdDeleteFailedTitle" xml:space="preserve">
     <value>Delete Failed</value>
   </data>
-    <data name="fdDeleteTitle" xml:space="preserve">
+  <data name="fdDeleteTitle" xml:space="preserve">
     <value>Delete {0}</value>
   </data>
-    <data name="fdNewFailed" xml:space="preserve">
+  <data name="fdNewFailed" xml:space="preserve">
     <value>New Failed</value>
   </data>
-    <data name="fdNewTitle" xml:space="preserve">
+  <data name="fdNewTitle" xml:space="preserve">
     <value>New Folder</value>
   </data>
-    <data name="btnNo" xml:space="preserve">
+  <data name="btnNo" xml:space="preserve">
     <value>_No</value>
   </data>
-    <data name="fdRenameFailedTitle" xml:space="preserve">
+  <data name="fdRenameFailedTitle" xml:space="preserve">
     <value>Rename Failed</value>
   </data>
-    <data name="fdRenamePrompt" xml:space="preserve">
+  <data name="fdRenamePrompt" xml:space="preserve">
     <value>Name:</value>
   </data>
-    <data name="fdRenameTitle" xml:space="preserve">
+  <data name="fdRenameTitle" xml:space="preserve">
     <value>Rename</value>
   </data>
-    <data name="btnYes" xml:space="preserve">
+  <data name="btnYes" xml:space="preserve">
     <value>_Yes</value>
   </data>
-    <data name="fdExisting" xml:space="preserve">
+  <data name="fdExisting" xml:space="preserve">
     <value>Existing</value>
   </data>
-    <data name="btnOpen" xml:space="preserve">
+  <data name="btnOpen" xml:space="preserve">
     <value>O_pen</value>
   </data>
-    <data name="btnSave" xml:space="preserve">
+  <data name="btnSave" xml:space="preserve">
     <value>_Save</value>
   </data>
-    <data name="btnSaveAs" xml:space="preserve">
+  <data name="btnSaveAs" xml:space="preserve">
     <value>Save _as</value>
   </data>
-    <data name="btnOk" xml:space="preserve">
+  <data name="btnOk" xml:space="preserve">
     <value>_OK</value>
   </data>
-    <data name="btnCancel" xml:space="preserve">
+  <data name="btnCancel" xml:space="preserve">
     <value>_Cancel</value>
   </data>
-    <data name="fdCtxDelete" xml:space="preserve">
+  <data name="fdCtxDelete" xml:space="preserve">
     <value>_Delete</value>
   </data>
-    <data name="fdCtxHide" xml:space="preserve">
+  <data name="fdCtxHide" xml:space="preserve">
     <value>_Hide {0}</value>
   </data>
-    <data name="fdCtxNew" xml:space="preserve">
+  <data name="fdCtxNew" xml:space="preserve">
     <value>_New</value>
   </data>
-    <data name="fdCtxRename" xml:space="preserve">
+  <data name="fdCtxRename" xml:space="preserve">
     <value>_Rename</value>
   </data>
-    <data name="fdCtxSortAsc" xml:space="preserve">
+  <data name="fdCtxSortAsc" xml:space="preserve">
     <value>_Sort {0} ASC</value>
   </data>
-    <data name="fdCtxSortDesc" xml:space="preserve">
+  <data name="fdCtxSortDesc" xml:space="preserve">
     <value>_Sort {0} DESC</value>
   </data>
-    <data name="dpTitle" xml:space="preserve">
+  <data name="dpTitle" xml:space="preserve">
     <value>Date Picker</value>
   </data>
-    <data name="#F0F8FF" xml:space="preserve">
+  <data name="#F0F8FF" xml:space="preserve">
     <value>AliceBlue</value>
   </data>
-    <data name="#FAEBD7" xml:space="preserve">
+  <data name="#FAEBD7" xml:space="preserve">
     <value>AntiqueWhite</value>
   </data>
-    <data name="#7FFFD4" xml:space="preserve">
+  <data name="#7FFFD4" xml:space="preserve">
     <value>Aquamarine</value>
   </data>
-    <data name="#F0FFFF" xml:space="preserve">
+  <data name="#F0FFFF" xml:space="preserve">
     <value>Azure</value>
   </data>
-    <data name="#F5F5DC" xml:space="preserve">
+  <data name="#F5F5DC" xml:space="preserve">
     <value>Beige</value>
   </data>
-    <data name="#FFE4C4" xml:space="preserve">
+  <data name="#FFE4C4" xml:space="preserve">
     <value>Bisque</value>
   </data>
-    <data name="#000000" xml:space="preserve">
+  <data name="#000000" xml:space="preserve">
     <value>Black</value>
   </data>
-    <data name="#FFEBCD" xml:space="preserve">
+  <data name="#FFEBCD" xml:space="preserve">
     <value>BlanchedAlmond</value>
   </data>
-    <data name="#0000FF" xml:space="preserve">
+  <data name="#0000FF" xml:space="preserve">
     <value>Blue</value>
   </data>
-    <data name="#8A2BE2" xml:space="preserve">
+  <data name="#8A2BE2" xml:space="preserve">
     <value>BlueViolet</value>
   </data>
-    <data name="#A52A2A" xml:space="preserve">
+  <data name="#A52A2A" xml:space="preserve">
     <value>Brown</value>
   </data>
-    <data name="#DEB887" xml:space="preserve">
+  <data name="#DEB887" xml:space="preserve">
     <value>BurlyWood</value>
   </data>
-    <data name="#5F9EA0" xml:space="preserve">
+  <data name="#5F9EA0" xml:space="preserve">
     <value>CadetBlue</value>
   </data>
-    <data name="#7FFF00" xml:space="preserve">
+  <data name="#7FFF00" xml:space="preserve">
     <value>Chartreuse</value>
   </data>
-    <data name="#D2691E" xml:space="preserve">
+  <data name="#D2691E" xml:space="preserve">
     <value>Chocolate</value>
   </data>
-    <data name="#FF7F50" xml:space="preserve">
+  <data name="#FF7F50" xml:space="preserve">
     <value>Coral</value>
   </data>
-    <data name="#6495ED" xml:space="preserve">
+  <data name="#6495ED" xml:space="preserve">
     <value>CornflowerBlue</value>
   </data>
-    <data name="#FFF8DC" xml:space="preserve">
+  <data name="#FFF8DC" xml:space="preserve">
     <value>Cornsilk</value>
   </data>
-    <data name="#DC143C" xml:space="preserve">
+  <data name="#DC143C" xml:space="preserve">
     <value>Crimson</value>
   </data>
-    <data name="#00FFFF" xml:space="preserve">
+  <data name="#00FFFF" xml:space="preserve">
     <value>Cyan</value>
   </data>
-    <data name="#00008B" xml:space="preserve">
+  <data name="#00008B" xml:space="preserve">
     <value>DarkBlue</value>
   </data>
-    <data name="#008B8B" xml:space="preserve">
+  <data name="#008B8B" xml:space="preserve">
     <value>DarkCyan</value>
   </data>
-    <data name="#B8860B" xml:space="preserve">
+  <data name="#B8860B" xml:space="preserve">
     <value>DarkGoldenRod</value>
   </data>
-    <data name="#A9A9A9" xml:space="preserve">
+  <data name="#A9A9A9" xml:space="preserve">
     <value>DarkGrey</value>
   </data>
-    <data name="#006400" xml:space="preserve">
+  <data name="#006400" xml:space="preserve">
     <value>DarkGreen</value>
   </data>
-    <data name="#BDB76B" xml:space="preserve">
+  <data name="#BDB76B" xml:space="preserve">
     <value>DarkKhaki</value>
   </data>
-    <data name="#8B008B" xml:space="preserve">
+  <data name="#8B008B" xml:space="preserve">
     <value>DarkMagenta</value>
   </data>
-    <data name="#556B2F" xml:space="preserve">
+  <data name="#556B2F" xml:space="preserve">
     <value>DarkOliveGreen</value>
   </data>
-    <data name="#FF8C00" xml:space="preserve">
+  <data name="#FF8C00" xml:space="preserve">
     <value>DarkOrange</value>
   </data>
-    <data name="#9932CC" xml:space="preserve">
+  <data name="#9932CC" xml:space="preserve">
     <value>DarkOrchid</value>
   </data>
-    <data name="#8B0000" xml:space="preserve">
+  <data name="#8B0000" xml:space="preserve">
     <value>DarkRed</value>
   </data>
-    <data name="#E9967A" xml:space="preserve">
+  <data name="#E9967A" xml:space="preserve">
     <value>DarkSalmon</value>
   </data>
-    <data name="#8FBC8F" xml:space="preserve">
+  <data name="#8FBC8F" xml:space="preserve">
     <value>DarkSeaGreen</value>
   </data>
-    <data name="#483D8B" xml:space="preserve">
+  <data name="#483D8B" xml:space="preserve">
     <value>DarkSlateBlue</value>
   </data>
-    <data name="#2F4F4F" xml:space="preserve">
+  <data name="#2F4F4F" xml:space="preserve">
     <value>DarkSlateGrey</value>
   </data>
-    <data name="#00CED1" xml:space="preserve">
+  <data name="#00CED1" xml:space="preserve">
     <value>DarkTurquoise</value>
   </data>
-    <data name="#9400D3" xml:space="preserve">
+  <data name="#9400D3" xml:space="preserve">
     <value>DarkViolet</value>
   </data>
-    <data name="#FF1493" xml:space="preserve">
+  <data name="#FF1493" xml:space="preserve">
     <value>DeepPink</value>
   </data>
-    <data name="#00BFFF" xml:space="preserve">
+  <data name="#00BFFF" xml:space="preserve">
     <value>DeepSkyBlue</value>
   </data>
-    <data name="#696969" xml:space="preserve">
+  <data name="#696969" xml:space="preserve">
     <value>DimGray</value>
   </data>
-    <data name="#1E90FF" xml:space="preserve">
+  <data name="#1E90FF" xml:space="preserve">
     <value>DodgerBlue</value>
   </data>
-    <data name="#B22222" xml:space="preserve">
+  <data name="#B22222" xml:space="preserve">
     <value>FireBrick</value>
   </data>
-    <data name="#FFFAF0" xml:space="preserve">
+  <data name="#FFFAF0" xml:space="preserve">
     <value>FloralWhite</value>
   </data>
-    <data name="#228B22" xml:space="preserve">
+  <data name="#228B22" xml:space="preserve">
     <value>ForestGreen</value>
   </data>
-    <data name="#DCDCDC" xml:space="preserve">
+  <data name="#DCDCDC" xml:space="preserve">
     <value>Gainsboro</value>
   </data>
-    <data name="#F8F8FF" xml:space="preserve">
+  <data name="#F8F8FF" xml:space="preserve">
     <value>GhostWhite</value>
   </data>
-    <data name="#FFD700" xml:space="preserve">
+  <data name="#FFD700" xml:space="preserve">
     <value>Gold</value>
   </data>
-    <data name="#DAA520" xml:space="preserve">
+  <data name="#DAA520" xml:space="preserve">
     <value>GoldenRod</value>
   </data>
-    <data name="#808080" xml:space="preserve">
+  <data name="#808080" xml:space="preserve">
     <value>Gray</value>
   </data>
-    <data name="#008000" xml:space="preserve">
+  <data name="#008000" xml:space="preserve">
     <value>Green</value>
   </data>
-    <data name="#ADFF2F" xml:space="preserve">
+  <data name="#ADFF2F" xml:space="preserve">
     <value>GreenYellow</value>
   </data>
-    <data name="#F0FFF0" xml:space="preserve">
+  <data name="#F0FFF0" xml:space="preserve">
     <value>HoneyDew</value>
   </data>
-    <data name="#FF69B4" xml:space="preserve">
+  <data name="#FF69B4" xml:space="preserve">
     <value>HotPink</value>
   </data>
-    <data name="#CD5C5C" xml:space="preserve">
+  <data name="#CD5C5C" xml:space="preserve">
     <value>IndianRed</value>
   </data>
-    <data name="#4B0082" xml:space="preserve">
+  <data name="#4B0082" xml:space="preserve">
     <value>Indigo</value>
   </data>
-    <data name="#FFFFF0" xml:space="preserve">
+  <data name="#FFFFF0" xml:space="preserve">
     <value>Ivory</value>
   </data>
-    <data name="#F0E68C" xml:space="preserve">
+  <data name="#F0E68C" xml:space="preserve">
     <value>Khaki</value>
   </data>
-    <data name="#E6E6FA" xml:space="preserve">
+  <data name="#E6E6FA" xml:space="preserve">
     <value>Lavender</value>
   </data>
-    <data name="#FFF0F5" xml:space="preserve">
+  <data name="#FFF0F5" xml:space="preserve">
     <value>LavenderBlush</value>
   </data>
-    <data name="#7CFC00" xml:space="preserve">
+  <data name="#7CFC00" xml:space="preserve">
     <value>LawnGreen</value>
   </data>
-    <data name="#FFFACD" xml:space="preserve">
+  <data name="#FFFACD" xml:space="preserve">
     <value>LemonChiffon</value>
   </data>
-    <data name="#ADD8E6" xml:space="preserve">
+  <data name="#ADD8E6" xml:space="preserve">
     <value>LightBlue</value>
   </data>
-    <data name="#F08080" xml:space="preserve">
+  <data name="#F08080" xml:space="preserve">
     <value>LightCoral</value>
   </data>
-    <data name="#E0FFFF" xml:space="preserve">
+  <data name="#E0FFFF" xml:space="preserve">
     <value>LightCyan</value>
   </data>
-    <data name="#FAFAD2" xml:space="preserve">
+  <data name="#FAFAD2" xml:space="preserve">
     <value>LightGoldenRodYellow</value>
   </data>
-    <data name="#D3D3D3" xml:space="preserve">
+  <data name="#D3D3D3" xml:space="preserve">
     <value>LightGray</value>
   </data>
-    <data name="#90EE90" xml:space="preserve">
+  <data name="#90EE90" xml:space="preserve">
     <value>LightGreen</value>
   </data>
-    <data name="#FFB6C1" xml:space="preserve">
+  <data name="#FFB6C1" xml:space="preserve">
     <value>LightPink</value>
   </data>
-    <data name="#FFA07A" xml:space="preserve">
+  <data name="#FFA07A" xml:space="preserve">
     <value>LightSalmon</value>
   </data>
-    <data name="#20B2AA" xml:space="preserve">
+  <data name="#20B2AA" xml:space="preserve">
     <value>LightSeaGreen</value>
   </data>
-    <data name="#87CEFA" xml:space="preserve">
+  <data name="#87CEFA" xml:space="preserve">
     <value>LightSkyBlue</value>
   </data>
-    <data name="#778899" xml:space="preserve">
+  <data name="#778899" xml:space="preserve">
     <value>LightSlateGrey</value>
   </data>
-    <data name="#B0C4DE" xml:space="preserve">
+  <data name="#B0C4DE" xml:space="preserve">
     <value>LightSteelBlue</value>
   </data>
-    <data name="#FFFFE0" xml:space="preserve">
+  <data name="#FFFFE0" xml:space="preserve">
     <value>LightYellow</value>
   </data>
-    <data name="#00FF00" xml:space="preserve">
+  <data name="#00FF00" xml:space="preserve">
     <value>Lime</value>
   </data>
-    <data name="#32CD32" xml:space="preserve">
+  <data name="#32CD32" xml:space="preserve">
     <value>LimeGreen</value>
   </data>
-    <data name="#FAF0E6" xml:space="preserve">
+  <data name="#FAF0E6" xml:space="preserve">
     <value>Linen</value>
   </data>
-    <data name="#FF00FF" xml:space="preserve">
+  <data name="#FF00FF" xml:space="preserve">
     <value>Magenta</value>
   </data>
-    <data name="#800000" xml:space="preserve">
+  <data name="#800000" xml:space="preserve">
     <value>Maroon</value>
   </data>
-    <data name="#66CDAA" xml:space="preserve">
+  <data name="#66CDAA" xml:space="preserve">
     <value>MediumAquaMarine</value>
   </data>
-    <data name="#0000CD" xml:space="preserve">
+  <data name="#0000CD" xml:space="preserve">
     <value>MediumBlue</value>
   </data>
-    <data name="#BA55D3" xml:space="preserve">
+  <data name="#BA55D3" xml:space="preserve">
     <value>MediumOrchid</value>
   </data>
-    <data name="#9370DB" xml:space="preserve">
+  <data name="#9370DB" xml:space="preserve">
     <value>MediumPurple</value>
   </data>
-    <data name="#3CB371" xml:space="preserve">
+  <data name="#3CB371" xml:space="preserve">
     <value>MediumSeaGreen</value>
   </data>
-    <data name="#7B68EE" xml:space="preserve">
+  <data name="#7B68EE" xml:space="preserve">
     <value>MediumSlateBlue</value>
   </data>
-    <data name="#00FA9A" xml:space="preserve">
+  <data name="#00FA9A" xml:space="preserve">
     <value>MediumSpringGreen</value>
   </data>
-    <data name="#48D1CC" xml:space="preserve">
+  <data name="#48D1CC" xml:space="preserve">
     <value>MediumTurquoise</value>
   </data>
-    <data name="#C71585" xml:space="preserve">
+  <data name="#C71585" xml:space="preserve">
     <value>MediumVioletRed</value>
   </data>
-    <data name="#191970" xml:space="preserve">
+  <data name="#191970" xml:space="preserve">
     <value>MidnightBlue</value>
   </data>
-    <data name="#F5FFFA" xml:space="preserve">
+  <data name="#F5FFFA" xml:space="preserve">
     <value>MintCream</value>
   </data>
-    <data name="#FFE4E1" xml:space="preserve">
+  <data name="#FFE4E1" xml:space="preserve">
     <value>MistyRose</value>
   </data>
-    <data name="#FFE4B5" xml:space="preserve">
+  <data name="#FFE4B5" xml:space="preserve">
     <value>Moccasin</value>
   </data>
-    <data name="#FFDEAD" xml:space="preserve">
+  <data name="#FFDEAD" xml:space="preserve">
     <value>NavajoWhite</value>
   </data>
-    <data name="#000080" xml:space="preserve">
+  <data name="#000080" xml:space="preserve">
     <value>Navy</value>
   </data>
-    <data name="#FDF5E6" xml:space="preserve">
+  <data name="#FDF5E6" xml:space="preserve">
     <value>OldLace</value>
   </data>
-    <data name="#808000" xml:space="preserve">
+  <data name="#808000" xml:space="preserve">
     <value>Olive</value>
   </data>
-    <data name="#6B8E23" xml:space="preserve">
+  <data name="#6B8E23" xml:space="preserve">
     <value>OliveDrab</value>
   </data>
-    <data name="#FFA500" xml:space="preserve">
+  <data name="#FFA500" xml:space="preserve">
     <value>Orange</value>
   </data>
-    <data name="#FF4500" xml:space="preserve">
+  <data name="#FF4500" xml:space="preserve">
     <value>OrangeRed</value>
   </data>
-    <data name="#DA70D6" xml:space="preserve">
+  <data name="#DA70D6" xml:space="preserve">
     <value>Orchid</value>
   </data>
-    <data name="#EEE8AA" xml:space="preserve">
+  <data name="#EEE8AA" xml:space="preserve">
     <value>PaleGoldenRod</value>
   </data>
-    <data name="#98FB98" xml:space="preserve">
+  <data name="#98FB98" xml:space="preserve">
     <value>PaleGreen</value>
   </data>
-    <data name="#AFEEEE" xml:space="preserve">
+  <data name="#AFEEEE" xml:space="preserve">
     <value>PaleTurquoise</value>
   </data>
-    <data name="#DB7093" xml:space="preserve">
+  <data name="#DB7093" xml:space="preserve">
     <value>PaleVioletRed</value>
   </data>
-    <data name="#FFEFD5" xml:space="preserve">
+  <data name="#FFEFD5" xml:space="preserve">
     <value>PapayaWhip</value>
   </data>
-    <data name="#FFDAB9" xml:space="preserve">
+  <data name="#FFDAB9" xml:space="preserve">
     <value>PeachPuff</value>
   </data>
-    <data name="#CD853F" xml:space="preserve">
+  <data name="#CD853F" xml:space="preserve">
     <value>Peru</value>
   </data>
-    <data name="#FFC0CB" xml:space="preserve">
+  <data name="#FFC0CB" xml:space="preserve">
     <value>Pink</value>
   </data>
-    <data name="#DDA0DD" xml:space="preserve">
+  <data name="#DDA0DD" xml:space="preserve">
     <value>Plum</value>
   </data>
-    <data name="#B0E0E6" xml:space="preserve">
+  <data name="#B0E0E6" xml:space="preserve">
     <value>PowderBlue</value>
   </data>
-    <data name="#800080" xml:space="preserve">
+  <data name="#800080" xml:space="preserve">
     <value>Purple</value>
   </data>
-    <data name="#663399" xml:space="preserve">
+  <data name="#663399" xml:space="preserve">
     <value>RebeccaPurple</value>
   </data>
-    <data name="#FF0000" xml:space="preserve">
+  <data name="#FF0000" xml:space="preserve">
     <value>Red</value>
   </data>
-    <data name="#BC8F8F" xml:space="preserve">
+  <data name="#BC8F8F" xml:space="preserve">
     <value>RosyBrown</value>
   </data>
-    <data name="#4169E1" xml:space="preserve">
+  <data name="#4169E1" xml:space="preserve">
     <value>RoyalBlue</value>
   </data>
-    <data name="#8B4513" xml:space="preserve">
+  <data name="#8B4513" xml:space="preserve">
     <value>SaddleBrown</value>
   </data>
-    <data name="#FA8072" xml:space="preserve">
+  <data name="#FA8072" xml:space="preserve">
     <value>Salmon</value>
   </data>
-    <data name="#F4A460" xml:space="preserve">
+  <data name="#F4A460" xml:space="preserve">
     <value>SandyBrown</value>
   </data>
-    <data name="#2E8B57" xml:space="preserve">
+  <data name="#2E8B57" xml:space="preserve">
     <value>SeaGreen</value>
   </data>
-    <data name="#FFF5EE" xml:space="preserve">
+  <data name="#FFF5EE" xml:space="preserve">
     <value>SeaShell</value>
   </data>
-    <data name="#A0522D" xml:space="preserve">
+  <data name="#A0522D" xml:space="preserve">
     <value>Sienna</value>
   </data>
-    <data name="#C0C0C0" xml:space="preserve">
+  <data name="#C0C0C0" xml:space="preserve">
     <value>Silver</value>
   </data>
-    <data name="#87CEEB" xml:space="preserve">
+  <data name="#87CEEB" xml:space="preserve">
     <value>SkyBlue</value>
   </data>
-    <data name="#6A5ACD" xml:space="preserve">
+  <data name="#6A5ACD" xml:space="preserve">
     <value>SlateBlue</value>
   </data>
-    <data name="#708090" xml:space="preserve">
+  <data name="#708090" xml:space="preserve">
     <value>SlateGray</value>
   </data>
-    <data name="#FFFAFA" xml:space="preserve">
+  <data name="#FFFAFA" xml:space="preserve">
     <value>Snow</value>
   </data>
-    <data name="#00FF7F" xml:space="preserve">
+  <data name="#00FF7F" xml:space="preserve">
     <value>SpringGreen</value>
   </data>
-    <data name="#4682B4" xml:space="preserve">
+  <data name="#4682B4" xml:space="preserve">
     <value>SteelBlue</value>
   </data>
-    <data name="#D2B48C" xml:space="preserve">
+  <data name="#D2B48C" xml:space="preserve">
     <value>Tan</value>
   </data>
-    <data name="#008080" xml:space="preserve">
+  <data name="#008080" xml:space="preserve">
     <value>Teal</value>
   </data>
-    <data name="#D8BFD8" xml:space="preserve">
+  <data name="#D8BFD8" xml:space="preserve">
     <value>Thistle</value>
   </data>
-    <data name="#FF6347" xml:space="preserve">
+  <data name="#FF6347" xml:space="preserve">
     <value>Tomato</value>
   </data>
-    <data name="#40E0D0" xml:space="preserve">
+  <data name="#40E0D0" xml:space="preserve">
     <value>Turquoise</value>
   </data>
-    <data name="#EE82EE" xml:space="preserve">
+  <data name="#EE82EE" xml:space="preserve">
     <value>Violet</value>
   </data>
-    <data name="#F5DEB3" xml:space="preserve">
+  <data name="#F5DEB3" xml:space="preserve">
     <value>Wheat</value>
   </data>
-    <data name="#FFFFFF" xml:space="preserve">
+  <data name="#FFFFFF" xml:space="preserve">
     <value>White</value>
   </data>
-    <data name="#F5F5F5" xml:space="preserve">
+  <data name="#F5F5F5" xml:space="preserve">
     <value>WhiteSmoke</value>
   </data>
-    <data name="#FFFF00" xml:space="preserve">
+  <data name="#FFFF00" xml:space="preserve">
     <value>Yellow</value>
   </data>
-    <data name="#9ACD32" xml:space="preserve">
+  <data name="#9ACD32" xml:space="preserve">
     <value>YellowGreen</value>
   </data>
-    <data name="#3B78FF" xml:space="preserve">
+  <data name="#3B78FF" xml:space="preserve">
     <value>BrightBlue</value>
   </data>
-    <data name="#61D6D6" xml:space="preserve">
+  <data name="#61D6D6" xml:space="preserve">
     <value>BrightCyan</value>
   </data>
-    <data name="#E74856" xml:space="preserve">
+  <data name="#E74856" xml:space="preserve">
     <value>BrightRed</value>
-</data>
-    <data name="#16C60C" xml:space="preserve">
+  </data>
+  <data name="#16C60C" xml:space="preserve">
     <value>BrightGreen</value>
-</data>
-    <data name="#B4009E" xml:space="preserve">
+  </data>
+  <data name="#B4009E" xml:space="preserve">
     <value>BrightMagenta</value>
-</data>
-    <data name="#F9F1A5" xml:space="preserve">
+  </data>
+  <data name="#F9F1A5" xml:space="preserve">
     <value>BrightYellow</value>
-</data>
-    <data name="#767676" xml:space="preserve">
+  </data>
+  <data name="#767676" xml:space="preserve">
     <value>DarkGray</value>
-</data>
+  </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Co_lors</value>
+  </data>
 </root>

+ 9 - 6
Terminal.Gui/Resources/Strings.zh-Hans.resx

@@ -153,9 +153,6 @@
   <data name="fdOpen" xml:space="preserve">
     <value>打开</value>
   </data>
-  <data name="wzNext" xml:space="preserve">
-    <value>下一步 (_N)...</value>
-  </data>
   <data name="fdSelectFolder" xml:space="preserve">
     <value>选择文件夹 (_S)</value>
   </data>
@@ -168,6 +165,9 @@
   <data name="wzFinish" xml:space="preserve">
     <value>结束 (_N)</value>
   </data>
+  <data name="wzNext" xml:space="preserve">
+    <value>下一步 (_N)...</value>
+  </data>
   <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>已存在相同名称的目录</value>
   </data>
@@ -240,9 +240,6 @@
   <data name="fdExisting" xml:space="preserve">
     <value>已有</value>
   </data>
-  <data name="btnOk" xml:space="preserve">
-    <value>确定 (_O)</value>
-  </data>
   <data name="btnOpen" xml:space="preserve">
     <value>打开 (_O)</value>
   </data>
@@ -252,6 +249,9 @@
   <data name="btnSaveAs" xml:space="preserve">
     <value>另存为 (_S)</value>
   </data>
+  <data name="btnOk" xml:space="preserve">
+    <value>确定 (_O)</value>
+  </data>
   <data name="btnCancel" xml:space="preserve">
     <value>取消 (_C)</value>
   </data>
@@ -276,4 +276,7 @@
   <data name="dpTitle" xml:space="preserve">
     <value>日期选择器</value>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>旗帜 (_L)</value>
+  </data>
 </root>

+ 2 - 2
Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs

@@ -7,7 +7,7 @@ namespace Terminal.Gui;
 public class AutocompleteContext
 {
     /// <summary>Creates a new instance of the <see cref="AutocompleteContext"/> class</summary>
-    public AutocompleteContext (List<RuneCell> currentLine, int cursorPosition, bool canceled = false)
+    public AutocompleteContext (List<Cell> currentLine, int cursorPosition, bool canceled = false)
     {
         CurrentLine = currentLine;
         CursorPosition = cursorPosition;
@@ -18,7 +18,7 @@ public class AutocompleteContext
     public bool Canceled { get; set; }
 
     /// <summary>The text on the current line.</summary>
-    public List<RuneCell> CurrentLine { get; set; }
+    public List<Cell> CurrentLine { get; set; }
 
     /// <summary>The position of the input cursor within the <see cref="CurrentLine"/>.</summary>
     public int CursorPosition { get; set; }

+ 2 - 2
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 (Cell.ToCellList (currentLine), cursorPosition)
     {
         State = state;
     }
@@ -30,7 +30,7 @@ internal class FilepathSuggestionGenerator : ISuggestionGenerator
             return Enumerable.Empty<Suggestion> ();
         }
 
-        var path = TextModel.ToString (context.CurrentLine);
+        var path = Cell.ToString (context.CurrentLine);
         int last = path.LastIndexOfAny (FileDialog.Separators);
 
         if (string.IsNullOrWhiteSpace (path) || !Path.IsPathRooted (path))

+ 0 - 0
Terminal.Gui/Views/ColorPicker16.cs → Terminal.Gui/Views/ColorPicker.16.cs


+ 123 - 0
Terminal.Gui/Views/ColorPicker.Prompt.cs

@@ -0,0 +1,123 @@
+namespace Terminal.Gui;
+
+public partial class ColorPicker
+{
+    /// <summary>
+    ///     Open a <see cref="Dialog"/> with two <see cref="ColorPicker"/> or <see cref="ColorPicker16"/>, based on the
+    ///     <see cref="ConsoleDriver.Force16Colors"/> is false or true, respectively, for <see cref="Attribute.Foreground"/>
+    ///     and <see cref="Attribute.Background"/> colors.
+    /// </summary>
+    /// <param name="title">The title to show in the dialog.</param>
+    /// <param name="currentAttribute">The current attribute used.</param>
+    /// <param name="newAttribute">The new attribute.</param>
+    /// <returns><see langword="true"/> if a new color was accepted, otherwise <see langword="false"/>.</returns>
+    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.Accepting += (s, e) =>
+                        {
+                            accept = true;
+                            e.Cancel = true;
+                            Application.RequestStop ();
+                        };
+
+        var btnCancel = new Button
+        {
+            X = Pos.Center () + 5,
+            Y = 4,
+            Text = "Cancel",
+            Width = Dim.Auto ()
+        };
+
+        btnCancel.Accepting += (s, e) =>
+                            {
+                                e.Cancel = 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;
+    }
+}

+ 0 - 0
Terminal.Gui/Views/ColorPickerStyle.cs → Terminal.Gui/Views/ColorPicker.Style.cs


+ 1 - 1
Terminal.Gui/Views/ColorPicker.cs

@@ -7,7 +7,7 @@ namespace Terminal.Gui;
 /// <summary>
 ///     True color picker using HSL
 /// </summary>
-public class ColorPicker : View
+public partial class ColorPicker : View
 {
     /// <summary>
     ///     Creates a new instance of <see cref="ColorPicker"/>. Use

+ 3 - 3
Terminal.Gui/Views/HistoryTextItemEventArgs.cs

@@ -9,11 +9,11 @@ internal partial class HistoryText
         public Point CursorPosition;
         public Point FinalCursorPosition;
         public bool IsUndoing;
-        public List<List<RuneCell>> Lines;
+        public List<List<Cell>> Lines;
         public LineStatus LineStatus;
         public HistoryTextItemEventArgs RemovedOnAdded;
 
-        public HistoryTextItemEventArgs (List<List<RuneCell>> lines, Point curPos, LineStatus linesStatus)
+        public HistoryTextItemEventArgs (List<List<Cell>> lines, Point curPos, LineStatus linesStatus)
         {
             Lines = lines;
             CursorPosition = curPos;
@@ -22,7 +22,7 @@ internal partial class HistoryText
 
         public HistoryTextItemEventArgs (HistoryTextItemEventArgs historyTextItem)
         {
-            Lines = new List<List<RuneCell>> (historyTextItem.Lines);
+            Lines = new List<List<Cell>> (historyTextItem.Lines);
             CursorPosition = new Point (historyTextItem.CursorPosition.X, historyTextItem.CursorPosition.Y);
             LineStatus = historyTextItem.LineStatus;
         }

+ 16 - 16
Terminal.Gui/Views/TextField.cs

@@ -459,7 +459,7 @@ public class TextField : View
     ///     Indicates whatever the text was changed or not. <see langword="true"/> if the text was changed
     ///     <see langword="false"/> otherwise.
     /// </summary>
-    public bool IsDirty => _historyText.IsDirty (Text);
+    public bool IsDirty => _historyText.IsDirty ([Cell.StringToCells (Text)]);
 
     /// <summary>If set to true its not allow any changes in the text.</summary>
     public bool ReadOnly { get; set; }
@@ -541,12 +541,12 @@ public class TextField : View
             if (!Secret && !_historyText.IsFromHistory)
             {
                 _historyText.Add (
-                                  new() { TextModel.ToRuneCellList (oldText) },
+                                  new () { Cell.ToCellList (oldText) },
                                   new (_cursorPosition, 0)
                                  );
 
                 _historyText.Add (
-                                  new() { TextModel.ToRuneCells (_text) },
+                                  new () { Cell.ToCells (_text) },
                                   new (_cursorPosition, 0),
                                   HistoryText.LineStatus.Replaced
                                  );
@@ -589,7 +589,7 @@ public class TextField : View
     }
 
     /// <summary>Allows clearing the <see cref="HistoryText.HistoryTextItemEventArgs"/> items updating the original text.</summary>
-    public void ClearHistoryChanges () { _historyText.Clear (Text); }
+    public void ClearHistoryChanges () { _historyText.Clear ([Cell.StringToCells (Text)]); }
 
     /// <summary>Copy the selected text to the clipboard.</summary>
     public virtual void Copy ()
@@ -643,7 +643,7 @@ public class TextField : View
         }
 
         _historyText.Add (
-                          new() { TextModel.ToRuneCells (_text) },
+                          new () { Cell.ToCells (_text) },
                           new (_cursorPosition, 0)
                          );
 
@@ -697,7 +697,7 @@ public class TextField : View
         }
 
         _historyText.Add (
-                          new() { TextModel.ToRuneCells (_text) },
+                          new () { Cell.ToCells (_text) },
                           new (_cursorPosition, 0)
                          );
 
@@ -944,7 +944,7 @@ public class TextField : View
 
         int p = ScrollOffset;
         var col = 0;
-        int width = Frame.Width + OffSetBackground ();
+        int width = Viewport.Width + OffSetBackground ();
         int tcount = _text.Count;
         Attribute roc = GetReadOnlyColor ();
 
@@ -1130,10 +1130,10 @@ public class TextField : View
             }
 
             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);
@@ -1216,16 +1216,16 @@ public class TextField : View
             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
                                     );
@@ -1330,7 +1330,7 @@ public class TextField : View
 
     private void GenerateSuggestions ()
     {
-        List<RuneCell> currentLine = TextModel.ToRuneCellList (Text);
+        List<Cell> currentLine = Cell.ToCellList (Text);
         int cursorPosition = Math.Min (CursorPosition, currentLine.Count);
 
         Autocomplete.Context = new (
@@ -1378,7 +1378,7 @@ public class TextField : View
             return;
         }
 
-        Text = TextModel.ToString (obj?.Lines [obj.CursorPosition.Y]);
+        Text = Cell.ToString (obj?.Lines [obj.CursorPosition.Y]);
         CursorPosition = obj.CursorPosition.X;
         Adjust ();
     }
@@ -1386,7 +1386,7 @@ public class TextField : View
     private void InsertText (Key a, bool usePreTextChangedCursorPos)
     {
         _historyText.Add (
-                          new() { TextModel.ToRuneCells (_text) },
+                          new () { Cell.ToCells (_text) },
                           new (_cursorPosition, 0)
                          );
 

文件差異過大導致無法顯示
+ 265 - 238
Terminal.Gui/Views/TextView.cs


+ 9 - 9
Terminal.Gui/Views/TreeView/Branch.cs

@@ -75,7 +75,7 @@ internal class Branch<T> where T : class
     /// <param name="availableWidth"></param>
     public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, int availableWidth)
     {
-        List<RuneCell> cells = new ();
+        List<Cell> cells = new ();
         int? indexOfExpandCollapseSymbol = null;
         int indexOfModelText;
 
@@ -106,7 +106,7 @@ internal class Branch<T> where T : class
             }
             else
             {
-                cells.Add (NewRuneCell (attr, r));
+                cells.Add (NewCell (attr, r));
                 availableWidth -= r.GetColumns ();
             }
         }
@@ -148,7 +148,7 @@ internal class Branch<T> where T : class
         else
         {
             indexOfExpandCollapseSymbol = cells.Count;
-            cells.Add (NewRuneCell (attr, expansion));
+            cells.Add (NewCell (attr, expansion));
             availableWidth -= expansion.GetColumns ();
         }
 
@@ -211,7 +211,7 @@ internal class Branch<T> where T : class
         }
 
         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 @@ internal class Branch<T> where T : class
 
             cells.AddRange (
                             Enumerable.Repeat (
-                                               NewRuneCell (attr, new Rune (' ')),
+                                               NewCell (attr, new Rune (' ')),
                                                availableWidth
                                               )
                            );
@@ -229,7 +229,7 @@ internal class Branch<T> where T : class
         {
             Model = Model,
             Y = y,
-            RuneCells = cells,
+            Cells = cells,
             Tree = tree,
             IndexOfExpandCollapseSymbol =
                 indexOfExpandCollapseSymbol,
@@ -239,9 +239,9 @@ internal class Branch<T> where T : class
 
         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 @@ internal class Branch<T> where T : class
         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) }; }
 }

+ 4 - 4
Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs

@@ -12,16 +12,16 @@ public class DrawTreeViewLineEventArgs<T> where T : class
     public bool Handled { get; set; }
 
     /// <summary>
-    ///     If line contains a branch that can be expanded/collapsed then this is the index in <see cref="RuneCells"/> at
+    ///     If line contains a branch that can be expanded/collapsed then this is the index in <see cref="Cells"/> at
     ///     which the symbol is (or null for leaf elements).
     /// </summary>
     public int? IndexOfExpandCollapseSymbol { get; init; }
 
     /// <summary>
-    ///     The notional index in <see cref="RuneCells"/> which contains the first character of the
+    ///     The notional index in <see cref="Cells"/> which contains the first character of the
     ///     <see cref="TreeView{T}.AspectGetter"/> text (i.e. after all branch lines and expansion/collapse symbols).
     /// </summary>
-    /// <remarks>May be negative or outside of bounds of <see cref="RuneCells"/> if the view has been scrolled horizontally.</remarks>
+    /// <remarks>May be negative or outside of bounds of <see cref="Cells"/> if the view has been scrolled horizontally.</remarks>
     public int IndexOfModelText { get; init; }
 
     /// <summary>The object at this line in the tree</summary>
@@ -32,7 +32,7 @@ public class DrawTreeViewLineEventArgs<T> where T : class
     ///     respected.  You can modify these to change what is rendered.
     /// </summary>
     /// <remarks>Changing the length of this collection may result in corrupt rendering</remarks>
-    public List<RuneCell> RuneCells { get; init; }
+    public List<Cell> Cells { get; init; }
 
     /// <summary>The <see cref="TreeView{T}"/> that is performing the rendering.</summary>
     public TreeView<T> Tree { get; init; }

+ 9 - 1
UICatalog/Scenarios/Editor.cs

@@ -202,7 +202,15 @@ public class Editor : Scenario
                          CreateWrapChecked (),
                          CreateAutocomplete (),
                          CreateAllowsTabChecked (),
-                         CreateReadOnlyChecked ()
+                         CreateReadOnlyChecked (),
+                         new MenuItem (
+                                       "Colors",
+                                       "",
+                                       () => _textView.PromptForColors (),
+                                       null,
+                                       null,
+                                       KeyCode.CtrlMask | KeyCode.L
+                                      )
                      }
                     ),
                 new (

+ 23 - 9
UICatalog/Scenarios/LineDrawing.cs

@@ -135,13 +135,14 @@ public class LineDrawing : Scenario
         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 class LineDrawing : Scenario
         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;
     }

+ 1 - 1
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -35,7 +35,7 @@ public class ProgressBarStyles : Scenario
 
         var editor = new AdornmentsEditor ()
         {
-            AutoSelectViewToEdit = true
+            AutoSelectViewToEdit = false
         };
         app.Add (editor);
 

+ 31 - 28
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;
 
     /// <summary>
     ///     Reads an object instance from an Json file.
@@ -155,12 +155,12 @@ public class SyntaxHighlighting : Scenario
                          new (
                               "_Load Rune Cells",
                               "",
-                              () => ApplyLoadRuneCells ()
+                              () => ApplyLoadCells ()
                              ),
                          new (
                               "_Save Rune Cells",
                               "",
-                              () => SaveRuneCells ()
+                              () => SaveCells ()
                              ),
                          null,
                          new ("_Quit", "", () => Quit ())
@@ -231,11 +231,11 @@ public class SyntaxHighlighting : Scenario
         }
     }
 
-    private void ApplyLoadRuneCells ()
+    private void ApplyLoadCells ()
     {
         ClearAllEvents ();
 
-        List<RuneCell> runeCells = new ();
+        List<Cell> cells = new ();
 
         foreach (KeyValuePair<string, ColorScheme> color in Colors.ColorSchemes)
         {
@@ -243,21 +243,21 @@ public class SyntaxHighlighting : Scenario
 
             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<List<RuneCell>> cells = ReadFromJsonFile<List<List<RuneCell>>> (_path);
-            _textView.Load (cells);
+            //Reading the file
+            List<List<Cell>> fileCells = ReadFromJsonFile<List<List<Cell>>> (_path);
+            _textView.Load (fileCells);
         }
         else
         {
-            _textView.Load (runeCells);
+            _textView.Load (cells);
         }
 
         _textView.Autocomplete.SuggestionGenerator = new SingleWordSuggestionGenerator ();
@@ -267,11 +267,11 @@ public class SyntaxHighlighting : Scenario
     {
         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 @@ public class SyntaxHighlighting : Scenario
         _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 @@ public class SyntaxHighlighting : Scenario
 
         for (var y = 0; y < _textView.Lines; y++)
         {
-            List<RuneCell> line = _textView.GetLine (y);
+            List<Cell> 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 @@ public class SyntaxHighlighting : Scenario
 
     private void Quit () { Application.RequestStop (); }
 
-    private void SaveRuneCells ()
+    private void SaveCells ()
     {
         //Writing to file  
-        List<List<RuneCell>> cells = _textView.GetAllLines ();
+        List<List<Cell>> cells = _textView.GetAllLines ();
         WriteToJsonFile (_path, cells);
     }
 

+ 7 - 9
UICatalog/Scenarios/TreeViewFileSystem.cs

@@ -441,16 +441,14 @@ public class TreeViewFileSystem : Scenario
         {
             if (_iconProvider.UseNerdIcons || _iconProvider.UseUnicodeCharacters)
             {
-                if (e.IndexOfModelText > 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
+                                                   );
                 }
             }
         }

+ 1 - 0
UnitTests/Configuration/ThemeTests.cs

@@ -10,6 +10,7 @@ public class ThemeTests
         Converters = { new AttributeJsonConverter (), new ColorJsonConverter () }
     };
 
+    [Fact]
     [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void TestApply ()
     {

+ 1 - 1
UnitTests/ConsoleDrivers/DriverColorTests.cs

@@ -60,7 +60,7 @@ public class DriverColorTests
 
     //[InlineData (typeof (ANSIDriver), true)]
     [InlineData (typeof (WindowsDriver), true)]
-    [InlineData (typeof (CursesDriver), false)]
+    [InlineData (typeof (CursesDriver), true)]
     public void SupportsTrueColor_Defaults (Type driverType, bool expectedSetting)
     {
         var driver = (ConsoleDriver)Activator.CreateInstance (driverType);

+ 52 - 0
UnitTests/Drawing/CellTests.cs

@@ -0,0 +1,52 @@
+using System.Text;
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.DrawingTests;
+
+public class CellTests (ITestOutputHelper output)
+{
+    [Fact]
+    public void Constructor_Defaults ()
+    {
+        var c = new Cell ();
+        Assert.True (c is { });
+        Assert.Equal (0, c.Rune.Value);
+        Assert.Null (c.Attribute);
+    }
+
+    [Fact]
+    public void Equals_False ()
+    {
+        var c1 = new Cell ();
+
+        var c2 = new Cell
+        {
+            Rune = new ('a'), Attribute = new (Color.Red)
+        };
+        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 ToString_Override ()
+    {
+        var c1 = new Cell ();
+
+        var c2 = new Cell
+        {
+            Rune = new ('a'), Attribute = new (Color.Red)
+        };
+        Assert.Equal ("[\0, ]", c1.ToString ());
+
+        Assert.Equal (
+                      "[a, [Red,Red]]",
+                      c2.ToString ()
+                     );
+    }
+}

+ 1 - 1
UnitTests/Text/AutocompleteTests.cs

@@ -254,7 +254,7 @@ This an long line and against TextView.",
 
         ac.GenerateSuggestions (
                                 new (
-                                     TextModel.ToRuneCellList (tv.Text),
+                                     Cell.ToCellList (tv.Text),
                                      2
                                     )
                                );

+ 1 - 0
UnitTests/View/Mouse/MouseTests.cs

@@ -404,6 +404,7 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
         me.Handled = false;
 
         view.Dispose ();
+        Application.ResetState (ignoreDisposed: true);
     }
 
     [Theory]

+ 0 - 294
UnitTests/Views/RuneCellTests.cs

@@ -1,294 +0,0 @@
-using System.Text;
-using Xunit.Abstractions;
-
-namespace Terminal.Gui.ViewsTests;
-
-public class RuneCellTests (ITestOutputHelper output)
-{
-    [Fact]
-    public void Constructor_Defaults ()
-    {
-        var rc = new RuneCell ();
-        Assert.NotNull (rc);
-        Assert.Equal (0, rc.Rune.Value);
-        Assert.Null (rc.ColorScheme);
-    }
-
-    [Fact]
-    public void Equals_False ()
-    {
-        var rc1 = new RuneCell ();
-
-        var rc2 = new RuneCell
-        {
-            Rune = new ('a'), ColorScheme = new() { Normal = 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));
-    }
-
-    [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));
-    }
-
-    [Fact]
-    [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)]
-    public void RuneCell_LoadRuneCells_InheritsPreviousColorScheme ()
-    {
-        List<RuneCell> runeCells = new ();
-
-        foreach (KeyValuePair<string, ColorScheme> color in Colors.ColorSchemes)
-        {
-            string csName = color.Key;
-
-            foreach (Rune rune in csName.EnumerateRunes ())
-            {
-                runeCells.Add (new() { Rune = rune, ColorScheme = color.Value });
-            }
-
-            runeCells.Add (new() { Rune = (Rune)'\n', ColorScheme = color.Value });
-        }
-
-        TextView tv = CreateTextView ();
-        tv.Load (runeCells);
-        var top = new Toplevel ();
-        top.Add (tv);
-        RunState rs = Application.Begin (top);
-        Assert.True (tv.InheritsPreviousColorScheme);
-
-        var expectedText = @"
-TopLevel
-Base    
-Dialog  
-Menu    
-Error   ";
-        TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output);
-
-        Attribute [] attributes =
-        {
-            // 0
-            Colors.ColorSchemes ["TopLevel"].Focus,
-
-            // 1
-            Colors.ColorSchemes ["Base"].Focus,
-
-            // 2
-            Colors.ColorSchemes ["Dialog"].Focus,
-
-            // 3
-            Colors.ColorSchemes ["Menu"].Focus,
-
-            // 4
-            Colors.ColorSchemes ["Error"].Focus
-        };
-
-        var expectedColor = @"
-0000000000
-1111000000
-2222220000
-3333000000
-4444400000";
-        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.IsSelecting = false;
-        tv.CursorPosition = new (2, 4);
-        tv.Paste ();
-        Application.Refresh ();
-
-        expectedText = @"
-TopLevel  
-Base      
-Dialog    
-Menu      
-ErTopLevel
-Base      
-Dialogror ";
-        TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output);
-
-        expectedColor = @"
-0000000000
-1111000000
-2222220000
-3333000000
-4444444444
-4444000000
-4444444440";
-        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.IsSelecting = false;
-        tv.CursorPosition = new (2, 4);
-        tv.Paste ();
-        Application.Refresh ();
-
-        expectedText = @"
-TopLevel  
-Base      
-Dialog    
-Menu      
-ErTopLevel
-Base      
-Dialog    
-ror       ";
-        TestHelpers.AssertDriverContentsWithFrameAre (expectedText, output);
-
-        expectedColor = @"
-0000000000
-1111000000
-2222220000
-3333000000
-4444444444
-4444000000
-4444440000
-4440000000";
-        TestHelpers.AssertDriverAttributesAre (expectedColor, Application.Driver, attributes);
-
-        Application.End (rs);
-        top.Dispose ();
-    }
-
-    [Fact]
-    public void RuneCell_LoadRuneCells_Without_ColorScheme_Is_Never_Null ()
-    {
-        List<RuneCell> 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<RuneCell> line = tv.GetLine (i);
-
-            foreach (RuneCell rc in line)
-            {
-                Assert.NotNull (rc.ColorScheme);
-            }
-        }
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void RuneCellEventArgs_WordWrap_True ()
-    {
-        var eventCount = 0;
-
-        List<List<RuneCell>> text = new ()
-        {
-            TextModel.ToRuneCells (
-                                   "This is the first line.".ToRunes ()
-                                  ),
-            TextModel.ToRuneCells (
-                                   "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)
-        {
-            Assert.Equal (e.Line [e.Col], text [e.UnwrappedPosition.Row] [e.UnwrappedPosition.Col]);
-            eventCount++;
-        }
-
-        tv.Text = $"{TextModel.ToString (text [0])}\n{TextModel.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 ()
-    {
-        var rc1 = new RuneCell ();
-
-        var rc2 = new RuneCell
-        {
-            Rune = new ('a'), ColorScheme = new() { Normal = new (Color.Red) }
-        };
-        Assert.Equal ("U+0000 '\0'; null", rc1.ToString ());
-
-        Assert.Equal (
-                      "U+0061 'a'; Normal: [Red,Red]; Focus: [White,Black]; HotNormal: [White,Black]; HotFocus: [White,Black]; Disabled: [White,Black]",
-                      rc2.ToString ()
-                     );
-    }
-
-    // TODO: Move the tests below to View or Color - they test ColorScheme, not RuneCell primitives.
-    private TextView CreateTextView () { return new() { Width = 30, Height = 10 }; }
-}

+ 14 - 0
UnitTests/Views/TextFieldTests.cs

@@ -2108,4 +2108,18 @@ Les Miśerables",
         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 ();
+    }
 }

+ 360 - 11
UnitTests/Views/TextViewTests.cs

@@ -1160,7 +1160,7 @@ This is the second line.
             {
                 Assert.Throws<ArgumentException> (
                                                   () => ht.Add (
-                                                                new List<List<RuneCell>> { new () },
+                                                                new List<List<Cell>> { new () },
                                                                 Point.Empty,
                                                                 (HistoryText.LineStatus)ls
                                                                )
@@ -1168,7 +1168,7 @@ This is the second line.
             }
         }
 
-        Assert.Null (Record.Exception (() => ht.Add (new List<List<RuneCell>> { new () }, Point.Empty)));
+        Assert.Null (Record.Exception (() => ht.Add (new List<List<Cell>> { new () }, Point.Empty)));
     }
 
     [Fact]
@@ -4713,11 +4713,98 @@ This is the second line.
         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);
+
+        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);
+        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<List<Cell>> region, 0, 12, 1, 18);
+
+            foreach (List<Cell> cells in region)
+            {
+                foreach (Cell cell in cells)
+                {
+                    Assert.Null (cell.Attribute);
+                }
+            }
+        }
+
+        void AssertRedGreenAttribute ()
+        {
+            tv.GetRegion (out List<List<Cell>> region, 0, 12, 1, 18);
+
+            foreach (List<Cell> cells in region)
+            {
+                foreach (Cell cell in cells)
+                {
+                    Assert.Equal ("[Red,Green]", cell.Attribute.ToString ());
+                }
+            }
+        }
+    }
+
     [Fact]
     public void Internal_Tests ()
     {
         var txt = "This is a text.";
-        List<RuneCell> txtRunes = TextModel.StringToRuneCells (txt);
+        List<Cell> 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 +4844,8 @@ This is the second line.
         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, 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 +4869,14 @@ This is the second line.
         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 (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, TextModel.StringToRuneCells ("This is first line."));
-        tm.AddLine (1, TextModel.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 (TextModel.StringToRuneCells ("This really first line."), tm.GetLine (0));
-        Assert.Equal (TextModel.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]
@@ -6273,6 +6360,7 @@ This is the second line.
 
         //                                             TAB to jump between text fields.
         TestHelpers.AssertDriverAttributesAre ("0000000", Application.Driver, attributes);
+        Assert.Empty (_textView.SelectedCellsList);
 
         _textView.NewKeyDownEvent (Key.CursorRight.WithCtrl.WithShift);
 
@@ -6282,6 +6370,7 @@ This is the second line.
 
         //                                             TAB to jump between text fields.
         TestHelpers.AssertDriverAttributesAre ("1111000", Application.Driver, attributes);
+        Assert.Equal ("TAB ", Cell.ToString (_textView.SelectedCellsList [^1]));
         top.Dispose ();
     }
 
@@ -8699,4 +8788,264 @@ line.
         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 ();
+    }
+
+    [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<List<Cell>> 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<Cell> cells = [];
+
+        foreach (KeyValuePair<string, ColorScheme> 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.IsSelecting = 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.IsSelecting = 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<Cell> 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<Cell> line = tv.GetLine (i);
+
+            foreach (Cell c in line)
+            {
+                Assert.NotNull (c.Attribute);
+            }
+        }
+    }
+
+    private TextView CreateTextView () { return new () { Width = 30, Height = 10 }; }
+
 }

+ 7 - 7
UnitTests/Views/TreeViewTests.cs

@@ -970,10 +970,10 @@ public class TreeViewTests
         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);

部分文件因文件數量過多而無法顯示