From 43c958842e6dfeb2004cfe84cf7066457ccc6a57 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 8 Jun 2020 01:50:33 +0100 Subject: [PATCH 01/23] Resolves Button Text Alignment with Unicode chars. --- Terminal.Gui/Core/View.cs | 65 +++++++++++++++++++++++++++++++++- Terminal.Gui/Views/Button.cs | 50 +++++++++----------------- UICatalog/Scenarios/Buttons.cs | 12 ++++++- 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 6ac03c572..1909bae31 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1450,6 +1450,70 @@ namespace Terminal.Gui { OnLayoutComplete (new LayoutEventArgs () { OldBounds = oldBounds }); } + /// + /// A generic virtual method at the level of View to manipulate any hot-keys. + /// + /// The text to manipulate. + /// The hot-key to look for. + /// The returning hot-key position. + /// The character immediately to the right relative to the hot-key position + /// + public virtual ustring GetTextFromHotKey(ustring text, Rune hotKey, out int hotPos, out Rune showHotKey) + { + Rune hot_key = (Rune)0; + int hot_pos = -1; + ustring shown_text = text; + + // Use first hot_key char passed into 'hotKey'. + int i = 0; + foreach (Rune c in shown_text) { + if ((char)c != 0xFFFD) { + if (c == hotKey) { + hot_pos = i; + } else if (hot_pos > -1) { + hot_key = (char)c; + break; + } + } + i++; + } + + if (hot_pos == -1) { + // Use first upper-case char if there are no hot-key in the text. + i = 0; + foreach (Rune c in shown_text) { + if (Rune.IsUpper (c)) { + hot_key = (char)c; + hot_pos = i; + break; + } + i++; + } + } else { + // Use char after 'hotKey' + ustring start = ""; + i = 0; + foreach (Rune c in shown_text) { + start += ustring.Make (c); + i++; + if (i == hot_pos) + break; + } + var st = shown_text; + shown_text = start; + i = 0; + foreach (Rune c in st) { + i++; + if (i > hot_pos + 1) { + shown_text += ustring.Make (c); + } + } + } + hotPos = hot_pos; + showHotKey = hot_key; + return shown_text; + } + /// /// Pretty prints the View /// @@ -1505,7 +1569,6 @@ namespace Terminal.Gui { return false; } - /// /// Method invoked when a mouse event is generated /// diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index db47043d4..bff8d0a44 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -168,26 +168,7 @@ namespace Terminal.Gui { else shown_text = ustring.Make (_leftBracket) + " " + text + " " + ustring.Make (_rightBracket); - hot_key = (Rune)0; - hot_pos = shown_text.IndexOf ('_'); - - if (hot_pos == -1) { - // Use first upper-case char - int i = 0; - foreach (Rune c in shown_text) { - if (Rune.IsUpper (c)) { - hot_key = c; - hot_pos = i; - break; - } - i++; - } - } else { - // Use char after '_' - var start = shown_text [0, hot_pos]; - shown_text = start + shown_text [hot_pos + 1, shown_text.Length]; - hot_key = Char.ToUpper((char)shown_text [hot_pos]); - } + shown_text = GetTextFromHotKey (shown_text, '_', out hot_pos, out hot_key); SetNeedsDisplay (); } @@ -207,30 +188,30 @@ namespace Terminal.Gui { if (Frame.Width > shown_text.Length + 1) { switch (TextAlignment) { case TextAlignment.Left: - caption += new string (' ', Frame.Width - caption.Length); + caption += new string (' ', Frame.Width - caption.RuneCount); break; case TextAlignment.Right: - start = Frame.Width - caption.Length; - caption = $"{new string (' ', Frame.Width - caption.Length)}{caption}"; + start = Frame.Width - caption.RuneCount; + caption = $"{new string (' ', Frame.Width - caption.RuneCount)}{caption}"; if (c_hot_pos > -1) { c_hot_pos += start; } break; case TextAlignment.Centered: - start = Frame.Width / 2 - caption.Length / 2; - caption = $"{new string (' ', start)}{caption}{new string (' ', Frame.Width - caption.Length - start)}"; + start = Frame.Width / 2 - caption.RuneCount / 2; + caption = $"{new string (' ', start)}{caption}{new string (' ', Frame.Width - caption.RuneCount - start)}"; if (c_hot_pos > -1) { c_hot_pos += start; } break; case TextAlignment.Justified: - var words = caption.ToString ().Split (new string [] { " " }, StringSplitOptions.RemoveEmptyEntries); - var wLen = GetWordsLength (words); - var space = (Frame.Width - wLen) / (caption.Length - wLen); + var words = caption.Split (" "); + var wLen = GetWordsLength (words, out int runeCount); + var space = (Frame.Width - runeCount) / (caption.Length - wLen); caption = ""; for (int i = 0; i < words.Length; i++) { if (i == words.Length - 1) { - caption += new string (' ', Frame.Width - caption.Length - 1); + caption += new string (' ', Frame.Width - caption.RuneCount - 1); caption += words [i]; } else { caption += words [i]; @@ -240,7 +221,7 @@ namespace Terminal.Gui { } } if (c_hot_pos > -1) { - c_hot_pos += space - 1; + c_hot_pos += space - 1 + (wLen - runeCount == 0 ? 0 : wLen - runeCount + 1); } break; } @@ -255,14 +236,15 @@ namespace Terminal.Gui { } } - int GetWordsLength (string[] words) + int GetWordsLength (ustring [] words, out int runeCount) { int length = 0; - + int rCount = 0; for (int i = 0; i < words.Length; i++) { length += words [i].Length; + rCount += words [i].RuneCount; } - + runeCount = rCount; return length; } @@ -274,7 +256,7 @@ namespace Terminal.Gui { bool CheckKey (KeyEvent key) { - if (Char.ToUpper ((char)key.KeyValue) == hot_key) { + if ((char)key.KeyValue == hot_key) { this.SuperView.SetFocus (this); Clicked?.Invoke (); return true; diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs index 08c219d16..e6e4bff6c 100644 --- a/UICatalog/Scenarios/Buttons.cs +++ b/UICatalog/Scenarios/Buttons.cs @@ -154,7 +154,7 @@ namespace UICatalog { absoluteFrame.Add (moveBtnA); // Demonstrates how changing the View.Frame property can SIZE Views (#583) - var sizeBtnA = new Button (0, 2, "Size This Button via Frame") { + var sizeBtnA = new Button (0, 2, " ~  s  gui.cs   master ↑10 = Со_хранить") { ColorScheme = Colors.Error, }; sizeBtnA.Clicked = () => { @@ -233,6 +233,16 @@ namespace UICatalog { moveHotKeyBtn.Text = MoveHotkey (moveHotKeyBtn.Text); }; Win.Add (moveHotKeyBtn); + + var moveUnicodeHotKeyBtn = new Button (" ~  s  gui.cs   master ↑10 = Сохранить") { + X = Pos.Right (moveHotKeyBtn) + 6, + Y = Pos.Bottom (radioGroup) + 1, + ColorScheme = Colors.TopLevel, + }; + moveUnicodeHotKeyBtn.Clicked = () => { + moveUnicodeHotKeyBtn.Text = MoveHotkey (moveUnicodeHotKeyBtn.Text); + }; + Win.Add (moveUnicodeHotKeyBtn); } } } From 199e3aa1137cf43ef9a178caebf9a02ad5803282 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sat, 6 Jun 2020 17:27:52 -0600 Subject: [PATCH 02/23] double spacing --- UICatalog/Scenarios/CharacterMap.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index ddd47c44d..ba5ca1d45 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -16,12 +16,18 @@ namespace UICatalog { class CharacterMap : Scenario { public override void Setup () { - var charMap = new CharMap () { X = 0, Y = 0, Width = CharMap.RowWidth + 2, Height = Dim.Fill(), Start = 0x2500, - ColorScheme = Colors.Dialog}; + var charMap = new CharMap () { + X = 0, + Y = 0, + Width = CharMap.RowWidth + 2, + Height = Dim.Fill (), + Start = 0x2500, + ColorScheme = Colors.Dialog + }; Win.Add (charMap); - Button CreateBlock(Window win, ustring title, int start, int end, View align) + Button CreateBlock (Window win, ustring title, int start, int end, View align) { var button = new Button ($"{title} (U+{start:x5}-{end:x5})") { X = Pos.X (align), @@ -99,14 +105,14 @@ namespace UICatalog { Move (viewport.X + RowHeaderWidth + 1 + (header * 3), 0); Driver.AddStr ($" {header:x} "); } - for (int row = 0; row < viewport.Height - 1; row++) { + for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y += 2) { int val = (-viewport.Y + row) * 16; if (val < MaxCodePointVal) { var rowLabel = $"U+{val / 16:x4}x"; - Move (0, row + 1); + Move (0, y + 1); Driver.AddStr (rowLabel); for (int col = 0; col < 16; col++) { - Move (viewport.X + RowHeaderWidth + 1 + (col * 3), 0 + row + 1); + Move (viewport.X + RowHeaderWidth + 1 + (col * 3), 0 + y + 1); Driver.AddStr ($" {(char)((-viewport.Y + row) * 16 + col)} "); } } From 157255bc1b7fa0e2855bc9fa67f574020a906966 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 15:12:25 -0600 Subject: [PATCH 03/23] enabled better characters --- Example/demo.cs | 2 +- .../CursesDriver/CursesDriver.cs | 14 ++ Terminal.Gui/ConsoleDrivers/NetDriver.cs | 14 ++ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 14 ++ Terminal.Gui/Core/ConsoleDriver.cs | 34 ++++ Terminal.Gui/Core/View.cs | 2 +- Terminal.Gui/Views/Menu.cs | 14 +- Terminal.Gui/Views/RadioGroup.cs | 158 +++++++++++------- Terminal.Gui/Views/ScrollView.cs | 8 +- UICatalog/Scenarios/AllViewsTester.cs | 8 +- UICatalog/Scenarios/Buttons.cs | 6 +- UICatalog/Scenarios/CharacterMap.cs | 66 ++++---- UICatalog/Scenarios/MessageBoxes.cs | 2 +- UICatalog/Scenarios/Unicode.cs | 2 +- UICatalog/UICatalog.csproj | 1 + 15 files changed, 227 insertions(+), 118 deletions(-) diff --git a/Example/demo.cs b/Example/demo.cs index 0a78162ae..71efe5457 100644 --- a/Example/demo.cs +++ b/Example/demo.cs @@ -168,7 +168,7 @@ static class Demo { passText, new FrameView (new Rect (3, 10, 25, 6), "Options"){ new CheckBox (1, 0, "Remember me"), - new RadioGroup (1, 2, new [] { "_Personal", "_Company" }), + new RadioGroup (1, 2, new ustring [] { "_Personal", "_Company" }), }, new ListView (new Rect (59, 6, 16, 4), new string [] { "First row", diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 02ad65005..6a133064b 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -531,6 +531,20 @@ namespace Terminal.Gui { RightTee = Curses.ACS_RTEE; TopTee = Curses.ACS_TTEE; BottomTee = Curses.ACS_BTEE; + Checked = '\u221a'; + UnChecked = ' '; + Selected = '\u25cf'; + UnSelected = '\u25cc'; + RightArrow = Curses.ACS_RARROW; + LeftArrow = Curses.ACS_LARROW; + UpArrow = Curses.ACS_UARROW; + DownArrow = Curses.ACS_DARROW; + LeftDefaultIndicator = '\u25e6'; + RightDefaultIndicator = '\u25e6'; + LeftBracket = '['; + RightBracket = ']'; + OnMeterSegment = '\u258c'; + OffMeterSegement = ' '; Colors.TopLevel = new ColorScheme (); Colors.Base = new ColorScheme (); diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 3d0cd8645..91a39b3bf 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -164,6 +164,20 @@ namespace Terminal.Gui { RightTee = '\u2524'; TopTee = '\u22a4'; BottomTee = '\u22a5'; + Checked = '\u221a'; + UnChecked = ' '; + Selected = '\u25cf'; + UnSelected = '\u25cc'; + RightArrow = '\u25ba'; + LeftArrow = '\u25c4'; + UpArrow = '\u25b2'; + DownArrow = '\u25bc'; + LeftDefaultIndicator = '\u25e6'; + RightDefaultIndicator = '\u25e6'; + LeftBracket = '['; + RightBracket = ']'; + OnMeterSegment = '\u258c'; + OffMeterSegement = ' '; } public override Attribute MakeAttribute (Color fore, Color back) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index d8049817a..075cb8092 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -497,6 +497,20 @@ namespace Terminal.Gui { RightTee = '\u2524'; TopTee = '\u22a4'; BottomTee = '\u22a5'; + Checked = '\u221a'; + UnChecked = ' '; + Selected = '\u25cf'; + UnSelected = '\u25cc'; + RightArrow = '\u25ba'; + LeftArrow = '\u25c4'; + UpArrow = '\u25b2'; + DownArrow = '\u25bc'; + LeftDefaultIndicator = '\u25e6'; + RightDefaultIndicator = '\u25e6'; + LeftBracket = '['; + RightBracket = ']'; + OnMeterSegment = '\u258c'; + OffMeterSegement = ' '; } [StructLayout (LayoutKind.Sequential)] diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 8da5690fb..7a09f2004 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -948,6 +948,40 @@ namespace Terminal.Gui { /// public Rune BottomTee; + /// + /// Checkmark. + /// + public Rune Checked; + + /// + /// Un-checked checkmark. + /// + public Rune UnChecked; + + /// + /// Selected mark. + /// + public Rune Selected; + + /// + /// Un-selected selected mark. + /// + public Rune UnSelected; + + public Rune RightArrow; + public Rune LeftArrow; + public Rune DownArrow; + public Rune UpArrow; + + public Rune LeftDefaultIndicator; + public Rune RightDefaultIndicator; + + public Rune LeftBracket; + public Rune RightBracket; + + public Rune OnMeterSegment; + public Rune OffMeterSegement; + /// /// Make the attribute for the foreground and background colors. /// diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 1909bae31..113fb8113 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -960,7 +960,7 @@ namespace Terminal.Gui { Application.CurrentView = view; // Clip the sub-view - var savedClip = ClipToBounds (); + var savedClip = view.ClipToBounds (); // Draw the subview // Use the view's bounds (view-relative; Location will always be (0,0) because diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index a081ce62b..f48a390c9 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -329,7 +329,7 @@ namespace Terminal.Gui { if (item == null) Driver.AddRune (Driver.HLine); else if (p == Frame.Width - 3 && barItems.Children [i].SubMenu != null) - Driver.AddRune ('>'); + Driver.AddRune (Driver.RightArrow); else Driver.AddRune (' '); @@ -340,20 +340,20 @@ namespace Terminal.Gui { } ustring textToDraw; - var checkChar = (char)0x25cf; - var uncheckedChar = (char)0x25cc; + var checkChar = Driver.Selected; + var uncheckedChar = Driver.UnSelected; if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked)) { - checkChar = (char)0x221a; - uncheckedChar = ' '; + checkChar = Driver.Checked; + uncheckedChar = Driver.UnChecked; } // Support Checked even though CHeckType wasn't set if (item.Checked) { - textToDraw = checkChar + " " + item.Title; + textToDraw = ustring.Make(new Rune [] { checkChar, ' ' }) + item.Title; } else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) || item.CheckType.HasFlag (MenuItemCheckStyle.Radio)) { - textToDraw = uncheckedChar + " " + item.Title; + textToDraw = ustring.Make (new Rune [] { uncheckedChar, ' ' }) + item.Title; } else { textToDraw = item.Title; } diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index 8db6c07ea..24a0b8992 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -1,4 +1,8 @@ -using System; +using NStack; +using System; +using System.Collections.Generic; +using System.Linq; + namespace Terminal.Gui { /// /// shows a group of radio labels, only one of those can be selected at a given time @@ -6,65 +10,44 @@ namespace Terminal.Gui { public class RadioGroup : View { int selected, cursor; - /// - /// Initializes a new instance of the class using layout. - /// - /// Boundaries for the radio group. - /// The radio labels; an array of strings that can contain hotkeys using an underscore before the letter. - /// The index of item to be selected, the value is clamped to the number of items. - public RadioGroup (Rect rect, string [] radioLabels, int selected = 0) : base (rect) + void Init(Rect rect, ustring [] radioLabels, int selected) { + if (radioLabels == null) { + this.radioLabels = new List(); + } else { + this.radioLabels = radioLabels.ToList (); + } + this.selected = selected; - this.radioLabels = radioLabels; + SetWidthHeight (this.radioLabels); CanFocus = true; } - /// - /// The location of the cursor in the - /// - public int Cursor { - get => cursor; - set { - if (cursor < 0 || cursor >= radioLabels.Length) - return; - cursor = value; - SetNeedsDisplay (); - } - } /// /// Initializes a new instance of the class using layout. /// - public RadioGroup () : this (radioLabels: new string [] { }) { } + public RadioGroup () : this (radioLabels: new ustring [] { }) { } /// /// Initializes a new instance of the class using layout. /// /// The radio labels; an array of strings that can contain hotkeys using an underscore before the letter. /// The index of the item to be selected, the value is clamped to the number of items. - public RadioGroup (string [] radioLabels, int selected = 0) : base () + public RadioGroup (ustring [] radioLabels, int selected = 0) : base () { - SetWidthHeight(radioLabels); - - this.selected = selected; - this.radioLabels = radioLabels; - CanFocus = true; + Init (Rect.Empty, radioLabels, selected); } - void SetWidthHeight(string[] radioLabels) + /// + /// Initializes a new instance of the class using layout. + /// + /// Boundaries for the radio group. + /// The radio labels; an array of strings that can contain hotkeys using an underscore before the letter. + /// The index of item to be selected, the value is clamped to the number of items. + public RadioGroup (Rect rect, ustring [] radioLabels, int selected = 0) : base (rect) { - var r = MakeRect(0, 0, radioLabels); - Width = r.Width; - Height = radioLabels.Length; - } - - static Rect MakeRect (int x, int y, string [] radioLabels) - { - int width = 0; - - foreach (var s in radioLabels) - width = Math.Max (s.Length + 4, width); - return new Rect (x, y, width, radioLabels.Length); + Init (rect, radioLabels, selected); } /// @@ -75,44 +58,89 @@ namespace Terminal.Gui { /// The y coordinate. /// The radio labels; an array of strings that can contain hotkeys using an underscore before the letter. /// The item to be selected, the value is clamped to the number of items. - public RadioGroup (int x, int y, string [] radioLabels, int selected = 0) : this (MakeRect (x, y, radioLabels), radioLabels, selected) { } + public RadioGroup (int x, int y, ustring [] radioLabels, int selected = 0) : + this (MakeRect (x, y, radioLabels != null ? radioLabels.ToList() : null), radioLabels, selected) { } - string [] radioLabels; + /// + /// The location of the cursor in the + /// + public int Cursor { + get => cursor; + set { + if (cursor < 0 || cursor >= radioLabels.Count) + return; + cursor = value; + SetNeedsDisplay (); + } + } + + void SetWidthHeight (List radioLabels) + { + var r = MakeRect(0, 0, radioLabels); + if (LayoutStyle == LayoutStyle.Computed) { + Width = r.Width; + Height = radioLabels.Count; + } else { + Frame = new Rect (Frame.Location, new Size (r.Width, radioLabels.Count)); + } + } + + static Rect MakeRect (int x, int y, List radioLabels) + { + int width = 0; + + if (radioLabels == null) { + return new Rect (x, y, width, 0); + } + + foreach (var s in radioLabels) + width = Math.Max (s.Length + 3, width); + return new Rect (x, y, width, radioLabels.Count); + } + + + List radioLabels = new List (); /// /// The radio labels to display /// /// The radio labels. - public string [] RadioLabels { - get => radioLabels; + public ustring [] RadioLabels { + get => radioLabels.ToArray(); set { - Update(value); - radioLabels = value; - selected = 0; + var prevCount = radioLabels.Count; + radioLabels = value.ToList (); + if (prevCount != radioLabels.Count) { + SetWidthHeight (radioLabels); + } + Selected = 0; cursor = 0; SetNeedsDisplay (); } } - void Update(string [] newRadioLabels) - { - for (int i = 0; i < radioLabels.Length; i++) { - Move(0, i); - Driver.SetAttribute(ColorScheme.Normal); - Driver.AddStr(new string(' ', radioLabels[i].Length + 4)); - } - if (newRadioLabels.Length != radioLabels.Length) { - SetWidthHeight(newRadioLabels); - } - } + //// Redraws the RadioGroup + //void Update(List newRadioLabels) + //{ + // for (int i = 0; i < radioLabels.Count; i++) { + // Move(0, i); + // Driver.SetAttribute(ColorScheme.Normal); + // Driver.AddStr(ustring.Make(new string (' ', radioLabels[i].Length + 4))); + // } + // if (newRadioLabels.Count != radioLabels.Count) { + // SetWidthHeight(newRadioLabels); + // } + //} /// public override void Redraw (Rect bounds) { - for (int i = 0; i < radioLabels.Length; i++) { + Driver.SetAttribute (ColorScheme.Normal); + Clear (); + for (int i = 0; i < radioLabels.Count; i++) { Move (0, i); Driver.SetAttribute (ColorScheme.Normal); - Driver.AddStr (i == selected ? "(o) " : "( ) "); + Driver.AddStr (ustring.Make(new Rune[] { (i == selected ? Driver.Selected : Driver.UnSelected), ' '})); DrawHotString (radioLabels [i], HasFocus && i == cursor, ColorScheme); } base.Redraw (bounds); @@ -121,11 +149,11 @@ namespace Terminal.Gui { /// public override void PositionCursor () { - Move (1, cursor); + Move (0, cursor); } /// - /// Invoked when the selected radio label has changed + /// Invoked when the selected radio label has changed. The passed int indicates the newly selected item. /// public Action SelectedItemChanged; @@ -183,7 +211,7 @@ namespace Terminal.Gui { } break; case Key.CursorDown: - if (cursor + 1 < radioLabels.Length) { + if (cursor + 1 < radioLabels.Count) { cursor++; SetNeedsDisplay (); return true; @@ -204,7 +232,7 @@ namespace Terminal.Gui { SuperView.SetFocus (this); - if (me.Y < radioLabels.Length) { + if (me.Y < radioLabels.Count) { cursor = Selected = me.Y; SetNeedsDisplay (); } diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index 0857380bb..bc539112d 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -156,9 +156,9 @@ namespace Terminal.Gui { var by2 = (position + bh) * bh / Size; Move (col, 0); - Driver.AddRune ('^'); + Driver.AddRune (Driver.UpArrow); Move (col, Bounds.Height - 1); - Driver.AddRune ('v'); + Driver.AddRune (Driver.DownArrow); for (int y = 0; y < bh; y++) { Move (col, y + 1); if (y < by1 - 1 || y > by2) @@ -204,7 +204,7 @@ namespace Terminal.Gui { var bx2 = (position + bw) * bw / Size; Move (0, row); - Driver.AddRune ('<'); + Driver.AddRune (Driver.LeftArrow); for (int x = 0; x < bw; x++) { @@ -224,7 +224,7 @@ namespace Terminal.Gui { } Driver.AddRune (special); } - Driver.AddRune ('>'); + Driver.AddRune (Driver.RightArrow); } } } diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index 4c02fe634..fceb45e6a 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -117,7 +117,7 @@ namespace UICatalog { }; _settingsPane.Add (_computedCheckBox); - var radioItems = new [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" }; + var radioItems = new ustring [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" }; _locationFrame = new FrameView ("Location (Pos)") { X = Pos.Left (_computedCheckBox), Y = Pos.Bottom (_computedCheckBox), @@ -146,7 +146,7 @@ namespace UICatalog { _locationFrame.Add (_xRadioGroup); - radioItems = new [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" }; + radioItems = new ustring [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" }; label = new Label ("y:") { X = Pos.Right (_xRadioGroup) + 1, Y = 0 }; _locationFrame.Add (label); _yText = new TextField ($"{_yVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 }; @@ -173,7 +173,7 @@ namespace UICatalog { Width = 40, }; - radioItems = new [] { "Percent(width)", "Fill(width)", "Sized(width)" }; + radioItems = new ustring [] { "Percent(width)", "Fill(width)", "Sized(width)" }; label = new Label ("width:") { X = 0, Y = 0 }; _sizeFrame.Add (label); _wRadioGroup = new RadioGroup (radioItems) { @@ -193,7 +193,7 @@ namespace UICatalog { _sizeFrame.Add (_wText); _sizeFrame.Add (_wRadioGroup); - radioItems = new [] { "Percent(height)", "Fill(height)", "Sized(height)" }; + radioItems = new ustring [] { "Percent(height)", "Fill(height)", "Sized(height)" }; label = new Label ("height:") { X = Pos.Right (_wRadioGroup) + 1, Y = 0 }; _sizeFrame.Add (label); _hText = new TextField ($"{_hVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 }; diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs index e6e4bff6c..b336e94b4 100644 --- a/UICatalog/Scenarios/Buttons.cs +++ b/UICatalog/Scenarios/Buttons.cs @@ -111,7 +111,7 @@ namespace UICatalog { Win.Add (computedFrame); // Demonstrates how changing the View.Frame property can move Views - var moveBtn = new Button ("Move This Button via Pos") { + var moveBtn = new Button ("Move This \u263b Button _via Pos") { X = 0, Y = Pos.Center() - 1, Width = 30, @@ -124,7 +124,7 @@ namespace UICatalog { computedFrame.Add (moveBtn); // Demonstrates how changing the View.Frame property can SIZE Views (#583) - var sizeBtn = new Button ("Size This Button via Pos") { + var sizeBtn = new Button ("Size This \u263a Button _via Pos") { X = 0, Y = Pos.Center () + 1, Width = 30, @@ -168,7 +168,7 @@ namespace UICatalog { }; Win.Add (label); - var radioGroup = new RadioGroup (new [] { "Left", "Right", "Centered", "Justified" }) { + var radioGroup = new RadioGroup (new ustring [] { "Left", "Right", "Centered", "Justified" }) { X = 4, Y = Pos.Bottom (label) + 1, Selected = 2, diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index ba5ca1d45..7cb6ae4e2 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -1,5 +1,6 @@ using NStack; using System.Collections.Generic; +using System.Linq; using System.Text; using Terminal.Gui; @@ -26,36 +27,40 @@ namespace UICatalog { }; Win.Add (charMap); + var label = new Label ("Jump To Unicode Block:") { X = Pos.Right (charMap) + 1, Y = Pos.Y (charMap) }; + Win.Add (label); - Button CreateBlock (Window win, ustring title, int start, int end, View align) + (ustring radioLabel, int start, int end) CreateRadio (ustring title, int start, int end) { - var button = new Button ($"{title} (U+{start:x5}-{end:x5})") { - X = Pos.X (align), - Y = Pos.Bottom (align), - Clicked = () => { - charMap.Start = start; - }, - }; - win.Add (button); - return button; + return ($"{title} (U+{start:x5}-{end:x5})", start, end); + } + + var radioItems = new (ustring radioLabel, int start, int end) [] { + CreateRadio("Currency Symbols", 0x20A0, 0x20CF), + CreateRadio("Letterlike Symbols", 0x2100, 0x214F), + CreateRadio("Arrows", 0x2190, 0x21ff), + CreateRadio("Mathematical symbols", 0x2200, 0x22ff), + CreateRadio("Miscellaneous Technical", 0x2300, 0x23ff), + CreateRadio("Box Drawing & Geometric Shapes", 0x2500, 0x25ff), + CreateRadio("Miscellaneous Symbols", 0x2600, 0x26ff), + CreateRadio("Dingbats", 0x2700, 0x27ff), + CreateRadio("Braille", 0x2800, 0x28ff), + CreateRadio("Miscellaneous Symbols and Arrows", 0x2b00, 0x2bff), + CreateRadio("Alphabetic Presentation Forms", 0xFB00, 0xFb4f), + CreateRadio("Cuneiform Numbers and Punctuation", 0x12400, 0x1240f), + CreateRadio("Chess Symbols", 0x1FA00, 0x1FA0f), + CreateRadio("End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal), }; - var label = new Label ("Unicode Blocks:") { X = Pos.Right (charMap) + 2, Y = Pos.Y (charMap) }; - Win.Add (label); - var button = CreateBlock (Win, "Currency Symbols", 0x20A0, 0x20CF, label); - button = CreateBlock (Win, "Letterlike Symbols", 0x2100, 0x214F, button); - button = CreateBlock (Win, "Arrows", 0x2190, 0x21ff, button); - button = CreateBlock (Win, "Mathematical symbols", 0x2200, 0x22ff, button); - button = CreateBlock (Win, "Miscellaneous Technical", 0x2300, 0x23ff, button); - button = CreateBlock (Win, "Box Drawing & Geometric Shapes", 0x2500, 0x25ff, button); - button = CreateBlock (Win, "Miscellaneous Symbols", 0x2600, 0x26ff, button); - button = CreateBlock (Win, "Dingbats", 0x2700, 0x27ff, button); - button = CreateBlock (Win, "Braille", 0x2800, 0x28ff, button); - button = CreateBlock (Win, "Miscellaneous Symbols and Arrows", 0x2b00, 0x2bff, button); - button = CreateBlock (Win, "Alphabetic Presentation Forms", 0xFB00, 0xFb4f, button); - button = CreateBlock (Win, "Cuneiform Numbers and Punctuation[1", 0x12400, 0x1240f, button); - button = CreateBlock (Win, "Chess Symbols", 0x1FA00, 0x1FA0f, button); - button = CreateBlock (Win, "End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal, button); + var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ()); + jumpList.X = Pos.X (label); + jumpList.Y = Pos.Bottom (label); + jumpList.Width = Dim.Fill (); + jumpList.SelectedItemChanged = (selected) => { + charMap.Start = radioItems[selected].start; + }; + + Win.Add (jumpList); } } @@ -70,7 +75,6 @@ namespace UICatalog { set { _start = value; ContentOffset = new Point (0, _start / 16); - SetNeedsDisplay (); } } @@ -80,7 +84,7 @@ namespace UICatalog { // Row Header + space + (space + char + space) public static int RowHeaderWidth => $"U+{MaxCodePointVal:x5}".Length; - public static int RowWidth => RowHeaderWidth + 1 + (" c ".Length * 16); + public static int RowWidth => RowHeaderWidth + (" c".Length * 16); public CharMap () { @@ -102,7 +106,7 @@ namespace UICatalog { private void CharMap_DrawContent (Rect viewport) { for (int header = 0; header < 16; header++) { - Move (viewport.X + RowHeaderWidth + 1 + (header * 3), 0); + Move (viewport.X + RowHeaderWidth + (header * 2), 0); Driver.AddStr ($" {header:x} "); } for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y += 2) { @@ -112,8 +116,8 @@ namespace UICatalog { Move (0, y + 1); Driver.AddStr (rowLabel); for (int col = 0; col < 16; col++) { - Move (viewport.X + RowHeaderWidth + 1 + (col * 3), 0 + y + 1); - Driver.AddStr ($" {(char)((-viewport.Y + row) * 16 + col)} "); + Move (viewport.X + RowHeaderWidth + (col * 2), 0 + y + 1); + Driver.AddStr ($" {(char)((-viewport.Y + row) * 16 + col)}"); } } } diff --git a/UICatalog/Scenarios/MessageBoxes.cs b/UICatalog/Scenarios/MessageBoxes.cs index 24dafb292..eaf1061a4 100644 --- a/UICatalog/Scenarios/MessageBoxes.cs +++ b/UICatalog/Scenarios/MessageBoxes.cs @@ -118,7 +118,7 @@ namespace UICatalog { TextAlignment = Terminal.Gui.TextAlignment.Right, }; frame.Add (label); - var styleRadioGroup = new RadioGroup (new [] { "_Query", "_Error" } ) { + var styleRadioGroup = new RadioGroup (new ustring [] { "_Query", "_Error" } ) { X = Pos.Right (label) + 1, Y = Pos.Top (label), }; diff --git a/UICatalog/Scenarios/Unicode.cs b/UICatalog/Scenarios/Unicode.cs index 8bf6a9f7e..b5fef5471 100644 --- a/UICatalog/Scenarios/Unicode.cs +++ b/UICatalog/Scenarios/Unicode.cs @@ -69,7 +69,7 @@ namespace UICatalog { label = new Label ("RadioGroup:") { X = Pos.X (label), Y = Pos.Bottom (listView) + 1 }; Win.Add (label); - var radioGroup = new RadioGroup (new [] { "item #1", " ~  s  gui.cs   master ↑10", "Со_хранить" }, selected: 0) { + var radioGroup = new RadioGroup (new ustring [] { "item #1", " ~  s  gui.cs   master ↑10", "Со_хранить" }, selected: 0) { X = 15, Y = Pos.Y (label), Width = Dim.Percent (60), diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index c7135c9c4..328fc3749 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -5,6 +5,7 @@ netcoreapp3.1 UICatalog.UICatalogApp 1.0.0.1 + 8.0 From 83ed0fa47267ba4c300d092bfbfe080f906453db Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 15:19:47 -0600 Subject: [PATCH 04/23] API docs --- Terminal.Gui/Core/ConsoleDriver.cs | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 7a09f2004..5d7205bac 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -968,18 +968,54 @@ namespace Terminal.Gui { /// public Rune UnSelected; + /// + /// Right Arrow. + /// public Rune RightArrow; + + /// + /// Left Arrow. + /// public Rune LeftArrow; + + /// + /// Down Arrow. + /// public Rune DownArrow; + + /// + /// Up Arrow. + /// public Rune UpArrow; + /// + /// Left indicator for default action (e.g. '<' for ). + /// public Rune LeftDefaultIndicator; + + /// + /// Right indicator for default action (e.g. '>' for ). + /// public Rune RightDefaultIndicator; + /// + /// Left frame/bracket (e.g. '[' for ). + /// public Rune LeftBracket; + + /// + /// Right frame/bracket (e.g. ']' for ). + /// public Rune RightBracket; + /// + /// On Segment indicator for meter views (e.g. . + /// public Rune OnMeterSegment; + + /// + /// Off Segment indicator for meter views (e.g. . + /// public Rune OffMeterSegement; /// From 2e5f28b70da4bcb9030ef38fafa4ad2204a1de13 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 15:20:43 -0600 Subject: [PATCH 05/23] API docs --- Terminal.Gui/Core/ConsoleDriver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 5d7205bac..77f61f1e2 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -989,12 +989,12 @@ namespace Terminal.Gui { public Rune UpArrow; /// - /// Left indicator for default action (e.g. '<' for ). + /// Left indicator for default action (e.g. for ). /// public Rune LeftDefaultIndicator; /// - /// Right indicator for default action (e.g. '>' for ). + /// Right indicator for default action (e.g. for ). /// public Rune RightDefaultIndicator; From ad278c8a6d24e23c50cce22dbd2108d22d2e3dd8 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 15:35:10 -0600 Subject: [PATCH 06/23] fix scrollview characters --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 075cb8092..6397967e0 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -487,16 +487,16 @@ namespace Terminal.Gui { HLine = '\u2500'; VLine = '\u2502'; - Stipple = '\u2592'; - Diamond = '\u25c6'; + Stipple = '\u2591'; + Diamond = '\u25ca'; ULCorner = '\u250C'; LLCorner = '\u2514'; URCorner = '\u2510'; LRCorner = '\u2518'; LeftTee = '\u251c'; RightTee = '\u2524'; - TopTee = '\u22a4'; - BottomTee = '\u22a5'; + TopTee = '\u252c'; + BottomTee = '\u2534'; Checked = '\u221a'; UnChecked = ' '; Selected = '\u25cf'; From 1b8abd142146800832c64279ec62b515862e7fee Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 16:00:39 -0600 Subject: [PATCH 07/23] RadioGroup event fix --- Terminal.Gui/Views/RadioGroup.cs | 58 ++++++++++++++++++++++----- UICatalog/Scenarios/AllViewsTester.cs | 16 ++++---- UICatalog/Scenarios/Buttons.cs | 6 +-- UICatalog/Scenarios/CharacterMap.cs | 4 +- UICatalog/Scenarios/MessageBoxes.cs | 2 +- 5 files changed, 62 insertions(+), 24 deletions(-) diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index 24a0b8992..bc6efb85a 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -8,7 +8,8 @@ namespace Terminal.Gui { /// shows a group of radio labels, only one of those can be selected at a given time /// public class RadioGroup : View { - int selected, cursor; + int selected = -1; + int cursor; void Init(Rect rect, ustring [] radioLabels, int selected) { @@ -113,7 +114,7 @@ namespace Terminal.Gui { if (prevCount != radioLabels.Count) { SetWidthHeight (radioLabels); } - Selected = 0; + SelectedItem = 0; cursor = 0; SetNeedsDisplay (); } @@ -152,24 +153,61 @@ namespace Terminal.Gui { Move (0, cursor); } + // TODO: Make this a global class /// - /// Invoked when the selected radio label has changed. The passed int indicates the newly selected item. + /// Event arguments for the SelectedItemChagned event. /// - public Action SelectedItemChanged; + public class SelectedItemChangedEventArgs : EventArgs { + /// + /// Gets the index of the item that was previously selected. -1 if there was no previous selection. + /// + public int PreviousSelectedItem { get; } + + /// + /// Gets the index of the item that is now selected. -1 if there is no selection. + /// + public int SelectedItem { get; } + + /// + /// Initializes a new class. + /// + /// + /// + public SelectedItemChangedEventArgs(int selectedItem, int previousSelectedItem) + { + PreviousSelectedItem = previousSelectedItem; + SelectedItem = selectedItem; + } + } + + /// + /// Invoked when the selected radio label has changed. + /// + public Action SelectedItemChanged; /// /// The currently selected item from the list of radio labels /// /// The selected. - public int Selected { + public int SelectedItem { get => selected; set { - selected = value; - SelectedItemChanged?.Invoke (selected); + OnSelectedItemChanged (value, SelectedItem); SetNeedsDisplay (); } } + /// + /// Called whenever the current selected item changes. Invokes the event. + /// + /// + /// + public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem) + { + selected = selectedItem; + SelectedItemChanged?.Invoke (new SelectedItemChangedEventArgs (selectedItem, previousSelectedItem)); + } + /// public override bool ProcessColdKey (KeyEvent kb) { @@ -184,7 +222,7 @@ namespace Terminal.Gui { nextIsHot = true; else { if (nextIsHot && c == key) { - Selected = i; + SelectedItem = i; cursor = i; if (!HasFocus) SuperView.SetFocus (this); @@ -218,7 +256,7 @@ namespace Terminal.Gui { } break; case Key.Space: - Selected = cursor; + SelectedItem = cursor; return true; } return base.ProcessKey (kb); @@ -233,7 +271,7 @@ namespace Terminal.Gui { SuperView.SetFocus (this); if (me.Y < radioLabels.Count) { - cursor = Selected = me.Y; + cursor = SelectedItem = me.Y; SetNeedsDisplay (); } return true; diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index fceb45e6a..ff689f3b2 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -235,7 +235,7 @@ namespace UICatalog { return; } try { - switch (_xRadioGroup.Selected) { + switch (_xRadioGroup.SelectedItem) { case 0: view.X = Pos.Percent (_xVal); break; @@ -250,7 +250,7 @@ namespace UICatalog { break; } - switch (_yRadioGroup.Selected) { + switch (_yRadioGroup.SelectedItem) { case 0: view.Y = Pos.Percent (_yVal); break; @@ -265,7 +265,7 @@ namespace UICatalog { break; } - switch (_wRadioGroup.Selected) { + switch (_wRadioGroup.SelectedItem) { case 0: view.Width = Dim.Percent (_wVal); break; @@ -277,7 +277,7 @@ namespace UICatalog { break; } - switch (_hRadioGroup.Selected) { + switch (_hRadioGroup.SelectedItem) { case 0: view.Height = Dim.Percent (_hVal); break; @@ -301,15 +301,15 @@ namespace UICatalog { { var x = view.X.ToString (); var y = view.Y.ToString (); - _xRadioGroup.Selected = posNames.IndexOf (posNames.Where (s => x.Contains (s)).First ()); - _yRadioGroup.Selected = posNames.IndexOf (posNames.Where (s => y.Contains (s)).First ()); + _xRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => x.Contains (s)).First ()); + _yRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => y.Contains (s)).First ()); _xText.Text = $"{view.Frame.X}"; _yText.Text = $"{view.Frame.Y}"; var w = view.Width.ToString (); var h = view.Height.ToString (); - _wRadioGroup.Selected = dimNames.IndexOf (dimNames.Where (s => w.Contains (s)).First ()); - _hRadioGroup.Selected = dimNames.IndexOf (dimNames.Where (s => h.Contains (s)).First ()); + _wRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => w.Contains (s)).First ()); + _hRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => h.Contains (s)).First ()); _wText.Text = $"{view.Frame.Width}"; _hText.Text = $"{view.Frame.Height}"; } diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs index b336e94b4..9895fc41a 100644 --- a/UICatalog/Scenarios/Buttons.cs +++ b/UICatalog/Scenarios/Buttons.cs @@ -171,9 +171,9 @@ namespace UICatalog { var radioGroup = new RadioGroup (new ustring [] { "Left", "Right", "Centered", "Justified" }) { X = 4, Y = Pos.Bottom (label) + 1, - Selected = 2, - SelectedItemChanged = (selected) => { - switch (selected) { + SelectedItem = 2, + SelectedItemChanged = (args) => { + switch (args.SelectedItem) { case 0: moveBtn.TextAlignment = TextAlignment.Left; sizeBtn.TextAlignment = TextAlignment.Left; diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 7cb6ae4e2..60d30ee83 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -56,8 +56,8 @@ namespace UICatalog { jumpList.X = Pos.X (label); jumpList.Y = Pos.Bottom (label); jumpList.Width = Dim.Fill (); - jumpList.SelectedItemChanged = (selected) => { - charMap.Start = radioItems[selected].start; + jumpList.SelectedItemChanged = (args) => { + charMap.Start = radioItems[args.SelectedItem].start; }; Win.Add (jumpList); diff --git a/UICatalog/Scenarios/MessageBoxes.cs b/UICatalog/Scenarios/MessageBoxes.cs index eaf1061a4..8075ba51a 100644 --- a/UICatalog/Scenarios/MessageBoxes.cs +++ b/UICatalog/Scenarios/MessageBoxes.cs @@ -158,7 +158,7 @@ namespace UICatalog { for (int i = 0; i < numButtons; i++) { btns.Add(btnText[i % 10]); } - if (styleRadioGroup.Selected == 0) { + if (styleRadioGroup.SelectedItem == 0) { buttonPressedLabel.Text = $"{MessageBox.Query (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), btns.ToArray ())}"; } else { buttonPressedLabel.Text = $"{MessageBox.ErrorQuery (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), btns.ToArray ())}"; From da0ddd88a6c788b0c5fb8ebaef4313881f147692 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 16:05:06 -0600 Subject: [PATCH 08/23] simplified name --- Terminal.Gui/Views/RadioGroup.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index bc6efb85a..7bcd7aaee 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -157,7 +157,7 @@ namespace Terminal.Gui { /// /// Event arguments for the SelectedItemChagned event. /// - public class SelectedItemChangedEventArgs : EventArgs { + public class SelectedItemChangedArgs : EventArgs { /// /// Gets the index of the item that was previously selected. -1 if there was no previous selection. /// @@ -169,11 +169,11 @@ namespace Terminal.Gui { public int SelectedItem { get; } /// - /// Initializes a new class. + /// Initializes a new class. /// /// /// - public SelectedItemChangedEventArgs(int selectedItem, int previousSelectedItem) + public SelectedItemChangedArgs(int selectedItem, int previousSelectedItem) { PreviousSelectedItem = previousSelectedItem; SelectedItem = selectedItem; @@ -183,7 +183,7 @@ namespace Terminal.Gui { /// /// Invoked when the selected radio label has changed. /// - public Action SelectedItemChanged; + public Action SelectedItemChanged; /// /// The currently selected item from the list of radio labels @@ -205,7 +205,7 @@ namespace Terminal.Gui { public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem) { selected = selectedItem; - SelectedItemChanged?.Invoke (new SelectedItemChangedEventArgs (selectedItem, previousSelectedItem)); + SelectedItemChanged?.Invoke (new SelectedItemChangedArgs (selectedItem, previousSelectedItem)); } /// From fa0c030024c75e9e1543bbaeadb8ee6260c1fa2a Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 16:27:17 -0600 Subject: [PATCH 09/23] updated rel notes --- Terminal.Gui/Terminal.Gui.csproj | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 795bb538a..c0540e484 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -93,6 +93,13 @@ * Fixes #421 Now builds on Linux with "dotnet build". (Thanks @AArnott!) * MenuItem now supports checked/selected items. (Thanks @tig!) * Label no longer incorreclty displays formfeed char. (Thanks @tig!) + * Fixes #645 - RadioGroup now supports unicode. (Thanks @tig!) + * Fixes #573 - RadioGroup supports Computed Layout. (Thanks @tig!) + * RadioGroup now uses a single, good looking, glyph. (Thanks @tig!) + * RadioGroup now supportrs the Action-based event pattern correctly. BREAKING CHANGE. (Thanks @tig!) + * ConsoleDriver and Drivers have new standard glyph definitions for things like right arrow. (Thanks @tig!) + * ScrollView updated to use pretty glyphs. (Thanks @tig!) + * Menubar now uses pretty arrow glyph for sub-menus. (Thanks @tig!) 0.81: * Fix ncurses engine for macOS/Linux, it works again From 181f72e7c86e46719b4deb9be477b997d4d923a4 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 21:48:49 -0600 Subject: [PATCH 10/23] Enabling GitHub Actions for Unit Tests --- .github/workflows/dotnet-core.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/dotnet-core.yml diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml new file mode 100644 index 000000000..f734ea7b5 --- /dev/null +++ b/.github/workflows/dotnet-core.yml @@ -0,0 +1,25 @@ +name: .NET Core + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.101 + - name: Install dependencies + run: dotnet restore + - name: Build + run: dotnet build --configuration Release --no-restore + - name: Test + run: dotnet test --no-restore --verbosity normal From 407208ce65635deedda940f79e352b597ccce55e Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 21:44:03 -0600 Subject: [PATCH 11/23] first unit tests --- .../ConsoleDrivers/MockDriver/MockConsole.cs | 1661 +++++++++++++++++ .../ConsoleDrivers/MockDriver/MockDriver.cs | 440 +++++ Terminal.Gui/Core/Application.cs | 21 +- Terminal.Gui/Core/Toplevel.cs | 2 +- Terminal.sln | 12 +- UnitTests/ApplicationTests.cs | 195 ++ UnitTests/ConsoleDriverTests.cs | 67 + UnitTests/UnitTests.csproj | 20 + UnitTests/xunit.runner.json | 5 + 9 files changed, 2416 insertions(+), 7 deletions(-) create mode 100644 Terminal.Gui/ConsoleDrivers/MockDriver/MockConsole.cs create mode 100644 Terminal.Gui/ConsoleDrivers/MockDriver/MockDriver.cs create mode 100644 UnitTests/ApplicationTests.cs create mode 100644 UnitTests/ConsoleDriverTests.cs create mode 100644 UnitTests/UnitTests.csproj create mode 100644 UnitTests/xunit.runner.json diff --git a/Terminal.Gui/ConsoleDrivers/MockDriver/MockConsole.cs b/Terminal.Gui/ConsoleDrivers/MockDriver/MockConsole.cs new file mode 100644 index 000000000..ffb3b0c32 --- /dev/null +++ b/Terminal.Gui/ConsoleDrivers/MockDriver/MockConsole.cs @@ -0,0 +1,1661 @@ +// +// MockConsole.cs: A mock .NET Windows Console API implementaiton for unit tests. +// +// Authors: +// Charlie Kindel (github.com/tig) +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Terminal.Gui { + /// + /// + /// + public static class MockConsole { + + // + // Summary: + // Gets or sets the width of the console window. + // + // Returns: + // The width of the console window measured in columns. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight + // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight + // property plus the value of the System.Console.WindowTop property is greater than + // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth + // property or the value of the System.Console.WindowHeight property is greater + // than the largest possible window width or height for the current screen resolution + // and console font. + // + // T:System.IO.IOException: + // Error reading or writing information. + public static int WindowWidth { get; set; } = 80; + // + // Summary: + // Gets a value that indicates whether output has been redirected from the standard + // output stream. + // + // Returns: + // true if output is redirected; otherwise, false. + public static bool IsOutputRedirected { get; } + // + // Summary: + // Gets a value that indicates whether the error output stream has been redirected + // from the standard error stream. + // + // Returns: + // true if error output is redirected; otherwise, false. + public static bool IsErrorRedirected { get; } + // + // Summary: + // Gets the standard input stream. + // + // Returns: + // A System.IO.TextReader that represents the standard input stream. + public static TextReader In { get; } + // + // Summary: + // Gets the standard output stream. + // + // Returns: + // A System.IO.TextWriter that represents the standard output stream. + public static TextWriter Out { get; } + // + // Summary: + // Gets the standard error output stream. + // + // Returns: + // A System.IO.TextWriter that represents the standard error output stream. + public static TextWriter Error { get; } + // + // Summary: + // Gets or sets the encoding the console uses to read input. + // + // Returns: + // The encoding used to read console input. + // + // Exceptions: + // T:System.ArgumentNullException: + // The property value in a set operation is null. + // + // T:System.IO.IOException: + // An error occurred during the execution of this operation. + // + // T:System.Security.SecurityException: + // Your application does not have permission to perform this operation. + public static Encoding InputEncoding { get; set; } + // + // Summary: + // Gets or sets the encoding the console uses to write output. + // + // Returns: + // The encoding used to write console output. + // + // Exceptions: + // T:System.ArgumentNullException: + // The property value in a set operation is null. + // + // T:System.IO.IOException: + // An error occurred during the execution of this operation. + // + // T:System.Security.SecurityException: + // Your application does not have permission to perform this operation. + public static Encoding OutputEncoding { get; set; } + // + // Summary: + // Gets or sets the background color of the console. + // + // Returns: + // A value that specifies the background color of the console; that is, the color + // that appears behind each character. The default is black. + // + // Exceptions: + // T:System.ArgumentException: + // The color specified in a set operation is not a valid member of System.ConsoleColor. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static ConsoleColor BackgroundColor { get; set; } = _defaultBackgroundColor; + static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black; + + // + // Summary: + // Gets or sets the foreground color of the console. + // + // Returns: + // A System.ConsoleColor that specifies the foreground color of the console; that + // is, the color of each character that is displayed. The default is gray. + // + // Exceptions: + // T:System.ArgumentException: + // The color specified in a set operation is not a valid member of System.ConsoleColor. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static ConsoleColor ForegroundColor { get; set; } = _defaultForegroundColor; + static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray; + // + // Summary: + // Gets or sets the height of the buffer area. + // + // Returns: + // The current height, in rows, of the buffer area. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than or equal to zero.-or- The value in + // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value + // in a set operation is less than System.Console.WindowTop + System.Console.WindowHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static int BufferHeight { get; set; } = 25; + // + // Summary: + // Gets or sets the width of the buffer area. + // + // Returns: + // The current width, in columns, of the buffer area. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than or equal to zero.-or- The value in + // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value + // in a set operation is less than System.Console.WindowLeft + System.Console.WindowWidth. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static int BufferWidth { get; set; } = 80; + // + // Summary: + // Gets or sets the height of the console window area. + // + // Returns: + // The height of the console window measured in rows. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight + // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight + // property plus the value of the System.Console.WindowTop property is greater than + // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth + // property or the value of the System.Console.WindowHeight property is greater + // than the largest possible window width or height for the current screen resolution + // and console font. + // + // T:System.IO.IOException: + // Error reading or writing information. + public static int WindowHeight { get; set; } = 25; + // + // Summary: + // Gets or sets a value indicating whether the combination of the System.ConsoleModifiers.Control + // modifier key and System.ConsoleKey.C console key (Ctrl+C) is treated as ordinary + // input or as an interruption that is handled by the operating system. + // + // Returns: + // true if Ctrl+C is treated as ordinary input; otherwise, false. + // + // Exceptions: + // T:System.IO.IOException: + // Unable to get or set the input mode of the console input buffer. + public static bool TreatControlCAsInput { get; set; } + // + // Summary: + // Gets the largest possible number of console window columns, based on the current + // font and screen resolution. + // + // Returns: + // The width of the largest possible console window measured in columns. + public static int LargestWindowWidth { get; } + // + // Summary: + // Gets the largest possible number of console window rows, based on the current + // font and screen resolution. + // + // Returns: + // The height of the largest possible console window measured in rows. + public static int LargestWindowHeight { get; } + // + // Summary: + // Gets or sets the leftmost position of the console window area relative to the + // screen buffer. + // + // Returns: + // The leftmost console window position measured in columns. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // In a set operation, the value to be assigned is less than zero.-or-As a result + // of the assignment, System.Console.WindowLeft plus System.Console.WindowWidth + // would exceed System.Console.BufferWidth. + // + // T:System.IO.IOException: + // Error reading or writing information. + public static int WindowLeft { get; set; } + // + // Summary: + // Gets or sets the top position of the console window area relative to the screen + // buffer. + // + // Returns: + // The uppermost console window position measured in rows. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // In a set operation, the value to be assigned is less than zero.-or-As a result + // of the assignment, System.Console.WindowTop plus System.Console.WindowHeight + // would exceed System.Console.BufferHeight. + // + // T:System.IO.IOException: + // Error reading or writing information. + public static int WindowTop { get; set; } + // + // Summary: + // Gets or sets the column position of the cursor within the buffer area. + // + // Returns: + // The current position, in columns, of the cursor. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than zero.-or- The value in a set operation + // is greater than or equal to System.Console.BufferWidth. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static int CursorLeft { get; set; } + // + // Summary: + // Gets or sets the row position of the cursor within the buffer area. + // + // Returns: + // The current position, in rows, of the cursor. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than zero.-or- The value in a set operation + // is greater than or equal to System.Console.BufferHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static int CursorTop { get; set; } + // + // Summary: + // Gets or sets the height of the cursor within a character cell. + // + // Returns: + // The size of the cursor expressed as a percentage of the height of a character + // cell. The property value ranges from 1 to 100. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value specified in a set operation is less than 1 or greater than 100. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static int CursorSize { get; set; } + // + // Summary: + // Gets or sets a value indicating whether the cursor is visible. + // + // Returns: + // true if the cursor is visible; otherwise, false. + // + // Exceptions: + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static bool CursorVisible { get; set; } + // + // Summary: + // Gets or sets the title to display in the console title bar. + // + // Returns: + // The string to be displayed in the title bar of the console. The maximum length + // of the title string is 24500 characters. + // + // Exceptions: + // T:System.InvalidOperationException: + // In a get operation, the retrieved title is longer than 24500 characters. + // + // T:System.ArgumentOutOfRangeException: + // In a set operation, the specified title is longer than 24500 characters. + // + // T:System.ArgumentNullException: + // In a set operation, the specified title is null. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static string Title { get; set; } + // + // Summary: + // Gets a value indicating whether a key press is available in the input stream. + // + // Returns: + // true if a key press is available; otherwise, false. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.InvalidOperationException: + // Standard input is redirected to a file instead of the keyboard. + public static bool KeyAvailable { get; } + // + // Summary: + // Gets a value indicating whether the NUM LOCK keyboard toggle is turned on or + // turned off. + // + // Returns: + // true if NUM LOCK is turned on; false if NUM LOCK is turned off. + public static bool NumberLock { get; } + // + // Summary: + // Gets a value indicating whether the CAPS LOCK keyboard toggle is turned on or + // turned off. + // + // Returns: + // true if CAPS LOCK is turned on; false if CAPS LOCK is turned off. + public static bool CapsLock { get; } + // + // Summary: + // Gets a value that indicates whether input has been redirected from the standard + // input stream. + // + // Returns: + // true if input is redirected; otherwise, false. + public static bool IsInputRedirected { get; } + + // + // Summary: + // Occurs when the System.ConsoleModifiers.Control modifier key (Ctrl) and either + // the System.ConsoleKey.C console key (C) or the Break key are pressed simultaneously + // (Ctrl+C or Ctrl+Break). + //public static event ConsoleCancelEventHandler CancelKeyPress; + + // + // Summary: + // Plays the sound of a beep through the console speaker. + // + // Exceptions: + // T:System.Security.HostProtectionException: + // This method was executed on a server, such as SQL Server, that does not permit + // access to a user interface. + public static void Beep () + { + throw new NotImplementedException (); + } + // + // Summary: + // Plays the sound of a beep of a specified frequency and duration through the console + // speaker. + // + // Parameters: + // frequency: + // The frequency of the beep, ranging from 37 to 32767 hertz. + // + // duration: + // The duration of the beep measured in milliseconds. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // frequency is less than 37 or more than 32767 hertz.-or- duration is less than + // or equal to zero. + // + // T:System.Security.HostProtectionException: + // This method was executed on a server, such as SQL Server, that does not permit + // access to the console. + public static void Beep (int frequency, int duration) + { + throw new NotImplementedException (); + } + // + // Summary: + // Clears the console buffer and corresponding console window of display information. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Clear () + { + _buffer = new char [WindowWidth, WindowHeight]; + SetCursorPosition (0, 0); + } + + static char [,] _buffer = new char [WindowWidth, WindowHeight]; + + // + // Summary: + // Copies a specified source area of the screen buffer to a specified destination + // area. + // + // Parameters: + // sourceLeft: + // The leftmost column of the source area. + // + // sourceTop: + // The topmost row of the source area. + // + // sourceWidth: + // The number of columns in the source area. + // + // sourceHeight: + // The number of rows in the source area. + // + // targetLeft: + // The leftmost column of the destination area. + // + // targetTop: + // The topmost row of the destination area. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft + // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop + // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight + // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth + // is greater than or equal to System.Console.BufferWidth. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Copies a specified source area of the screen buffer to a specified destination + // area. + // + // Parameters: + // sourceLeft: + // The leftmost column of the source area. + // + // sourceTop: + // The topmost row of the source area. + // + // sourceWidth: + // The number of columns in the source area. + // + // sourceHeight: + // The number of rows in the source area. + // + // targetLeft: + // The leftmost column of the destination area. + // + // targetTop: + // The topmost row of the destination area. + // + // sourceChar: + // The character used to fill the source area. + // + // sourceForeColor: + // The foreground color used to fill the source area. + // + // sourceBackColor: + // The background color used to fill the source area. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft + // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop + // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight + // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth + // is greater than or equal to System.Console.BufferWidth. + // + // T:System.ArgumentException: + // One or both of the color parameters is not a member of the System.ConsoleColor + // enumeration. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Acquires the standard error stream. + // + // Returns: + // The standard error stream. + public static Stream OpenStandardError () + { + throw new NotImplementedException (); + } + + // + // Summary: + // Acquires the standard error stream, which is set to a specified buffer size. + // + // Parameters: + // bufferSize: + // The internal stream buffer size. + // + // Returns: + // The standard error stream. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. + public static Stream OpenStandardError (int bufferSize) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Acquires the standard input stream, which is set to a specified buffer size. + // + // Parameters: + // bufferSize: + // The internal stream buffer size. + // + // Returns: + // The standard input stream. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. + public static Stream OpenStandardInput (int bufferSize) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Acquires the standard input stream. + // + // Returns: + // The standard input stream. + public static Stream OpenStandardInput () + { + throw new NotImplementedException (); + } + + // + // Summary: + // Acquires the standard output stream, which is set to a specified buffer size. + // + // Parameters: + // bufferSize: + // The internal stream buffer size. + // + // Returns: + // The standard output stream. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. + public static Stream OpenStandardOutput (int bufferSize) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Acquires the standard output stream. + // + // Returns: + // The standard output stream. + public static Stream OpenStandardOutput () + { + throw new NotImplementedException (); + } + + // + // Summary: + // Reads the next character from the standard input stream. + // + // Returns: + // The next character from the input stream, or negative one (-1) if there are currently + // no more characters to be read. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static int Read () + { + throw new NotImplementedException (); + } + + // + // Summary: + // Obtains the next character or function key pressed by the user. The pressed key + // is optionally displayed in the console window. + // + // Parameters: + // intercept: + // Determines whether to display the pressed key in the console window. true to + // not display the pressed key; otherwise, false. + // + // Returns: + // An object that describes the System.ConsoleKey constant and Unicode character, + // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo + // object also describes, in a bitwise combination of System.ConsoleModifiers values, + // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously + // with the console key. + // + // Exceptions: + // T:System.InvalidOperationException: + // The System.Console.In property is redirected from some stream other than the + // console. + //[SecuritySafeCritical] + public static ConsoleKeyInfo ReadKey (bool intercept) + { + if (MockKeyPresses.Count > 0) { + return MockKeyPresses.Pop(); + } else { + return new ConsoleKeyInfo ('~', ConsoleKey.Oem3, false,false,false); + } + } + + public static Stack MockKeyPresses = new Stack (); + + // + // Summary: + // Obtains the next character or function key pressed by the user. The pressed key + // is displayed in the console window. + // + // Returns: + // An object that describes the System.ConsoleKey constant and Unicode character, + // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo + // object also describes, in a bitwise combination of System.ConsoleModifiers values, + // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously + // with the console key. + // + // Exceptions: + // T:System.InvalidOperationException: + // The System.Console.In property is redirected from some stream other than the + // console. + public static ConsoleKeyInfo ReadKey () + { + throw new NotImplementedException (); + } + + // + // Summary: + // Reads the next line of characters from the standard input stream. + // + // Returns: + // The next line of characters from the input stream, or null if no more lines are + // available. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.OutOfMemoryException: + // There is insufficient memory to allocate a buffer for the returned string. + // + // T:System.ArgumentOutOfRangeException: + // The number of characters in the next line of characters is greater than System.Int32.MaxValue. + public static string ReadLine () + { + throw new NotImplementedException (); + } + + // + // Summary: + // Sets the foreground and background console colors to their defaults. + // + // Exceptions: + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + public static void ResetColor () + { + BackgroundColor = _defaultBackgroundColor; + ForegroundColor = _defaultForegroundColor; + } + + // + // Summary: + // Sets the height and width of the screen buffer area to the specified values. + // + // Parameters: + // width: + // The width of the buffer area measured in columns. + // + // height: + // The height of the buffer area measured in rows. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // height or width is less than or equal to zero.-or- height or width is greater + // than or equal to System.Int16.MaxValue.-or- width is less than System.Console.WindowLeft + // + System.Console.WindowWidth.-or- height is less than System.Console.WindowTop + // + System.Console.WindowHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + public static void SetBufferSize (int width, int height) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Sets the position of the cursor. + // + // Parameters: + // left: + // The column position of the cursor. Columns are numbered from left to right starting + // at 0. + // + // top: + // The row position of the cursor. Rows are numbered from top to bottom starting + // at 0. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // left or top is less than zero.-or- left is greater than or equal to System.Console.BufferWidth.-or- + // top is greater than or equal to System.Console.BufferHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + public static void SetCursorPosition (int left, int top) + { + CursorLeft = left; + CursorTop = top; + } + + // + // Summary: + // Sets the System.Console.Error property to the specified System.IO.TextWriter + // object. + // + // Parameters: + // newError: + // A stream that is the new standard error output. + // + // Exceptions: + // T:System.ArgumentNullException: + // newError is null. + // + // T:System.Security.SecurityException: + // The caller does not have the required permission. + //[SecuritySafeCritical] + public static void SetError (TextWriter newError) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Sets the System.Console.In property to the specified System.IO.TextReader object. + // + // Parameters: + // newIn: + // A stream that is the new standard input. + // + // Exceptions: + // T:System.ArgumentNullException: + // newIn is null. + // + // T:System.Security.SecurityException: + // The caller does not have the required permission. + //[SecuritySafeCritical] + public static void SetIn (TextReader newIn) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Sets the System.Console.Out property to the specified System.IO.TextWriter object. + // + // Parameters: + // newOut: + // A stream that is the new standard output. + // + // Exceptions: + // T:System.ArgumentNullException: + // newOut is null. + // + // T:System.Security.SecurityException: + // The caller does not have the required permission. + //[SecuritySafeCritical] + public static void SetOut (TextWriter newOut) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Sets the position of the console window relative to the screen buffer. + // + // Parameters: + // left: + // The column position of the upper left corner of the console window. + // + // top: + // The row position of the upper left corner of the console window. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // left or top is less than zero.-or- left + System.Console.WindowWidth is greater + // than System.Console.BufferWidth.-or- top + System.Console.WindowHeight is greater + // than System.Console.BufferHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + public static void SetWindowPosition (int left, int top) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Sets the height and width of the console window to the specified values. + // + // Parameters: + // width: + // The width of the console window measured in columns. + // + // height: + // The height of the console window measured in rows. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // width or height is less than or equal to zero.-or- width plus System.Console.WindowLeft + // or height plus System.Console.WindowTop is greater than or equal to System.Int16.MaxValue. + // -or- width or height is greater than the largest possible window width or height + // for the current screen resolution and console font. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + public static void SetWindowSize (int width, int height) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the specified string value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (string value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified object to the standard output + // stream. + // + // Parameters: + // value: + // The value to write, or null. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (object value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified 64-bit unsigned integer value + // to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + //[CLSCompliant (false)] + public static void Write (ulong value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified 64-bit signed integer value to + // the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (long value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified objects to the standard output + // stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // The first object to write using format. + // + // arg1: + // The second object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + public static void Write (string format, object arg0, object arg1) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified 32-bit signed integer value to + // the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (int value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified object to the standard output + // stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // An object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + public static void Write (string format, object arg0) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified 32-bit unsigned integer value + // to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + //[CLSCompliant (false)] + public static void Write (uint value) + { + throw new NotImplementedException (); + } + + //[CLSCompliant (false)] + public static void Write (string format, object arg0, object arg1, object arg2, object arg3) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified array of objects to the standard + // output stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg: + // An array of objects to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format or arg is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + public static void Write (string format, params object [] arg) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified Boolean value to the standard + // output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (bool value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the specified Unicode character value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (char value) + { + _buffer [CursorLeft, CursorTop] = value; + } + + // + // Summary: + // Writes the specified array of Unicode characters to the standard output stream. + // + // Parameters: + // buffer: + // A Unicode character array. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (char [] buffer) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the specified subarray of Unicode characters to the standard output stream. + // + // Parameters: + // buffer: + // An array of Unicode characters. + // + // index: + // The starting position in buffer. + // + // count: + // The number of characters to write. + // + // Exceptions: + // T:System.ArgumentNullException: + // buffer is null. + // + // T:System.ArgumentOutOfRangeException: + // index or count is less than zero. + // + // T:System.ArgumentException: + // index plus count specify a position that is not within buffer. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (char [] buffer, int index, int count) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified objects to the standard output + // stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // The first object to write using format. + // + // arg1: + // The second object to write using format. + // + // arg2: + // The third object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + public static void Write (string format, object arg0, object arg1, object arg2) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified System.Decimal value to the standard + // output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (decimal value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified single-precision floating-point + // value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (float value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified double-precision floating-point + // value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void Write (double value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the current line terminator to the standard output stream. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine () + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified single-precision floating-point + // value, followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (float value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified 32-bit signed integer value, + // followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (int value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified 32-bit unsigned integer value, + // followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + //[CLSCompliant (false)] + public static void WriteLine (uint value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified 64-bit signed integer value, + // followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (long value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified 64-bit unsigned integer value, + // followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + //[CLSCompliant (false)] + public static void WriteLine (ulong value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified object, followed by the current + // line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (object value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the specified string value, followed by the current line terminator, to + // the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (string value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified object, followed by the current + // line terminator, to the standard output stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // An object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + public static void WriteLine (string format, object arg0) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified objects, followed by the current + // line terminator, to the standard output stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // The first object to write using format. + // + // arg1: + // The second object to write using format. + // + // arg2: + // The third object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + public static void WriteLine (string format, object arg0, object arg1, object arg2) + { + throw new NotImplementedException (); + } + + //[CLSCompliant (false)] + public static void WriteLine (string format, object arg0, object arg1, object arg2, object arg3) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified array of objects, followed by + // the current line terminator, to the standard output stream using the specified + // format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg: + // An array of objects to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format or arg is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + public static void WriteLine (string format, params object [] arg) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the specified subarray of Unicode characters, followed by the current + // line terminator, to the standard output stream. + // + // Parameters: + // buffer: + // An array of Unicode characters. + // + // index: + // The starting position in buffer. + // + // count: + // The number of characters to write. + // + // Exceptions: + // T:System.ArgumentNullException: + // buffer is null. + // + // T:System.ArgumentOutOfRangeException: + // index or count is less than zero. + // + // T:System.ArgumentException: + // index plus count specify a position that is not within buffer. + // + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (char [] buffer, int index, int count) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified System.Decimal value, followed + // by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (decimal value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the specified array of Unicode characters, followed by the current line + // terminator, to the standard output stream. + // + // Parameters: + // buffer: + // A Unicode character array. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (char [] buffer) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the specified Unicode character, followed by the current line terminator, + // value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (char value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified Boolean value, followed by the + // current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (bool value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified objects, followed by the current + // line terminator, to the standard output stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // The first object to write using format. + // + // arg1: + // The second object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + public static void WriteLine (string format, object arg0, object arg1) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified double-precision floating-point + // value, followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + public static void WriteLine (double value) + { + throw new NotImplementedException (); + } + + } +} diff --git a/Terminal.Gui/ConsoleDrivers/MockDriver/MockDriver.cs b/Terminal.Gui/ConsoleDrivers/MockDriver/MockDriver.cs new file mode 100644 index 000000000..c236cd0b9 --- /dev/null +++ b/Terminal.Gui/ConsoleDrivers/MockDriver/MockDriver.cs @@ -0,0 +1,440 @@ +// +// MockDriver.cs: A mock ConsoleDriver for unit tests. +// +// Authors: +// Charlie Kindel (github.com/tig) +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using NStack; + +namespace Terminal.Gui { + public class MockDriver : ConsoleDriver, IMainLoopDriver { + int cols, rows; + public override int Cols => cols; + public override int Rows => rows; + + // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag + int [,,] contents; + bool [] dirtyLine; + + void UpdateOffscreen () + { + int cols = Cols; + int rows = Rows; + + contents = new int [rows, cols, 3]; + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + contents [r, c, 0] = ' '; + contents [r, c, 1] = MakeColor (ConsoleColor.Gray, ConsoleColor.Black); + contents [r, c, 2] = 0; + } + } + dirtyLine = new bool [rows]; + for (int row = 0; row < rows; row++) + dirtyLine [row] = true; + } + + static bool sync = false; + + public MockDriver () + { + cols = MockConsole.WindowWidth; + rows = MockConsole.WindowHeight; // - 1; + UpdateOffscreen (); + } + + bool needMove; + // Current row, and current col, tracked by Move/AddCh only + int ccol, crow; + public override void Move (int col, int row) + { + ccol = col; + crow = row; + + if (Clip.Contains (col, row)) { + MockConsole.CursorTop = row; + MockConsole.CursorLeft = col; + needMove = false; + } else { + MockConsole.CursorTop = Clip.Y; + MockConsole.CursorLeft = Clip.X; + needMove = true; + } + + } + + public override void AddRune (Rune rune) + { + if (Clip.Contains (ccol, crow)) { + if (needMove) { + //MockConsole.CursorLeft = ccol; + //MockConsole.CursorTop = crow; + needMove = false; + } + contents [crow, ccol, 0] = (int)(uint)rune; + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 1; + dirtyLine [crow] = true; + } else + needMove = true; + ccol++; + //if (ccol == Cols) { + // ccol = 0; + // if (crow + 1 < Rows) + // crow++; + //} + if (sync) + UpdateScreen (); + } + + public override void AddStr (ustring str) + { + foreach (var rune in str) + AddRune (rune); + } + + public override void End () + { + MockConsole.ResetColor (); + MockConsole.Clear (); + } + + static Attribute MakeColor (ConsoleColor f, ConsoleColor b) + { + // Encode the colors into the int value. + return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) }; + } + + + public override void Init (Action terminalResized) + { + Colors.TopLevel = new ColorScheme (); + Colors.Base = new ColorScheme (); + Colors.Dialog = new ColorScheme (); + Colors.Menu = new ColorScheme (); + Colors.Error = new ColorScheme (); + Clip = new Rect (0, 0, Cols, Rows); + + Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black); + Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan); + Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black); + Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan); + + Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Blue); + Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan); + Colors.Base.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Blue); + Colors.Base.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan); + + // Focused, + // Selected, Hot: Yellow on Black + // Selected, text: white on black + // Unselected, hot: yellow on cyan + // unselected, text: same as unfocused + Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black); + Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black); + Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan); + Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan); + Colors.Menu.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Cyan); + + Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray); + Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan); + Colors.Dialog.HotNormal = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray); + Colors.Dialog.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Cyan); + + Colors.Error.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Red); + Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray); + Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red); + Colors.Error.HotFocus = Colors.Error.HotNormal; + + HLine = '\u2500'; + VLine = '\u2502'; + Stipple = '\u2592'; + Diamond = '\u25c6'; + ULCorner = '\u250C'; + LLCorner = '\u2514'; + URCorner = '\u2510'; + LRCorner = '\u2518'; + LeftTee = '\u251c'; + RightTee = '\u2524'; + TopTee = '\u22a4'; + BottomTee = '\u22a5'; + Checked = '\u221a'; + UnChecked = ' '; + Selected = '\u25cf'; + UnSelected = '\u25cc'; + RightArrow = '\u25ba'; + LeftArrow = '\u25c4'; + UpArrow = '\u25b2'; + DownArrow = '\u25bc'; + LeftDefaultIndicator = '\u25e6'; + RightDefaultIndicator = '\u25e6'; + LeftBracket = '['; + RightBracket = ']'; + OnMeterSegment = '\u258c'; + OffMeterSegement = ' '; + + //MockConsole.Clear (); + } + + public override Attribute MakeAttribute (Color fore, Color back) + { + return MakeColor ((ConsoleColor)fore, (ConsoleColor)back); + } + + int redrawColor = -1; + void SetColor (int color) + { + redrawColor = color; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains (color & 0xffff)) { + MockConsole.BackgroundColor = (ConsoleColor)(color & 0xffff); + } + if (values.Contains ((color >> 16) & 0xffff)) { + MockConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff); + } + } + + public override void UpdateScreen () + { + int rows = Rows; + int cols = Cols; + + MockConsole.CursorTop = 0; + MockConsole.CursorLeft = 0; + for (int row = 0; row < rows; row++) { + dirtyLine [row] = false; + for (int col = 0; col < cols; col++) { + contents [row, col, 2] = 0; + var color = contents [row, col, 1]; + if (color != redrawColor) + SetColor (color); + MockConsole.Write ((char)contents [row, col, 0]); + } + } + } + + public override void Refresh () + { + int rows = Rows; + int cols = Cols; + + var savedRow = MockConsole.CursorTop; + var savedCol = MockConsole.CursorLeft; + for (int row = 0; row < rows; row++) { + if (!dirtyLine [row]) + continue; + dirtyLine [row] = false; + for (int col = 0; col < cols; col++) { + if (contents [row, col, 2] != 1) + continue; + + MockConsole.CursorTop = row; + MockConsole.CursorLeft = col; + for (; col < cols && contents [row, col, 2] == 1; col++) { + var color = contents [row, col, 1]; + if (color != redrawColor) + SetColor (color); + + MockConsole.Write ((char)contents [row, col, 0]); + contents [row, col, 2] = 0; + } + } + } + MockConsole.CursorTop = savedRow; + MockConsole.CursorLeft = savedCol; + } + + public override void UpdateCursor () + { + // + } + + public override void StartReportingMouseMoves () + { + } + + public override void StopReportingMouseMoves () + { + } + + public override void Suspend () + { + } + + int currentAttribute; + public override void SetAttribute (Attribute c) + { + currentAttribute = c.value; + } + + Key MapKey (ConsoleKeyInfo keyInfo) + { + switch (keyInfo.Key) { + case ConsoleKey.Escape: + return Key.Esc; + case ConsoleKey.Tab: + return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; + case ConsoleKey.Home: + return Key.Home; + case ConsoleKey.End: + return Key.End; + case ConsoleKey.LeftArrow: + return Key.CursorLeft; + case ConsoleKey.RightArrow: + return Key.CursorRight; + case ConsoleKey.UpArrow: + return Key.CursorUp; + case ConsoleKey.DownArrow: + return Key.CursorDown; + case ConsoleKey.PageUp: + return Key.PageUp; + case ConsoleKey.PageDown: + return Key.PageDown; + case ConsoleKey.Enter: + return Key.Enter; + case ConsoleKey.Spacebar: + return Key.Space; + case ConsoleKey.Backspace: + return Key.Backspace; + case ConsoleKey.Delete: + return Key.Delete; + + case ConsoleKey.Oem1: + case ConsoleKey.Oem2: + case ConsoleKey.Oem3: + case ConsoleKey.Oem4: + case ConsoleKey.Oem5: + case ConsoleKey.Oem6: + case ConsoleKey.Oem7: + case ConsoleKey.Oem8: + case ConsoleKey.Oem102: + case ConsoleKey.OemPeriod: + case ConsoleKey.OemComma: + case ConsoleKey.OemPlus: + case ConsoleKey.OemMinus: + return (Key)((uint)keyInfo.KeyChar); + } + + var key = keyInfo.Key; + if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { + var delta = key - ConsoleKey.A; + if (keyInfo.Modifiers == ConsoleModifiers.Control) + return (Key)((uint)Key.ControlA + delta); + if (keyInfo.Modifiers == ConsoleModifiers.Alt) + return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta)); + if (keyInfo.Modifiers == ConsoleModifiers.Shift) + return (Key)((uint)'A' + delta); + else + return (Key)((uint)'a' + delta); + } + if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { + var delta = key - ConsoleKey.D0; + if (keyInfo.Modifiers == ConsoleModifiers.Alt) + return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta)); + if (keyInfo.Modifiers == ConsoleModifiers.Shift) + return (Key)((uint)keyInfo.KeyChar); + return (Key)((uint)'0' + delta); + } + if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) { + var delta = key - ConsoleKey.F1; + + return (Key)((int)Key.F1 + delta); + } + return (Key)(0xffffffff); + } + + KeyModifiers keyModifiers = new KeyModifiers (); + + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called + (mainLoop.Driver as MockDriver).WindowsKeyPressed = delegate (ConsoleKeyInfo consoleKey) { + var map = MapKey (consoleKey); + if (map == (Key)0xffffffff) + return; + keyHandler (new KeyEvent (map, keyModifiers)); + keyUpHandler (new KeyEvent (map, keyModifiers)); + }; + } + + public override void SetColors (ConsoleColor foreground, ConsoleColor background) + { + throw new NotImplementedException (); + } + + public override void SetColors (short foregroundColorId, short backgroundColorId) + { + throw new NotImplementedException (); + } + + public override void CookMouse () + { + } + + public override void UncookMouse () + { + } + + AutoResetEvent keyReady = new AutoResetEvent (false); + AutoResetEvent waitForProbe = new AutoResetEvent (false); + ConsoleKeyInfo? windowsKeyResult = null; + public Action WindowsKeyPressed; + MainLoop mainLoop; + + void WindowsKeyReader () + { + while (true) { + waitForProbe.WaitOne (); + windowsKeyResult = MockConsole.ReadKey (true); + keyReady.Set (); + } + } + + void IMainLoopDriver.Setup (MainLoop mainLoop) + { + this.mainLoop = mainLoop; + Thread readThread = new Thread (WindowsKeyReader); + readThread.Start (); + } + + void IMainLoopDriver.Wakeup () + { + } + + bool IMainLoopDriver.EventsPending (bool wait) + { + long now = DateTime.UtcNow.Ticks; + + int waitTimeout; + if (mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) + return true; + } else + waitTimeout = -1; + + if (!wait) + waitTimeout = 0; + + windowsKeyResult = null; + waitForProbe.Set (); + keyReady.WaitOne (waitTimeout); + return windowsKeyResult.HasValue; + } + + void IMainLoopDriver.MainIteration () + { + if (windowsKeyResult.HasValue) { + if (WindowsKeyPressed != null) + WindowsKeyPressed (windowsKeyResult.Value); + windowsKeyResult = null; + } + } + } +} \ No newline at end of file diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 9b728dc7c..53aff5e82 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -157,21 +157,28 @@ namespace Terminal.Gui { /// Creates a and assigns it to and /// /// - public static void Init () => Init (() => Toplevel.Create ()); + public static void Init (ConsoleDriver driver = null) => Init (() => Toplevel.Create (), driver); internal static bool _initialized = false; /// /// Initializes the Terminal.Gui application /// - static void Init (Func topLevelFactory) + static void Init (Func topLevelFactory, ConsoleDriver driver = null) { if (_initialized) return; + // This supports Unit Tests and the passing of a mock driver/loopdriver + if (driver != null) { + Driver = driver; + Driver.Init (TerminalResized); + MainLoop = new MainLoop ((IMainLoopDriver)driver); + SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop)); + } + if (Driver == null) { var p = Environment.OSVersion.Platform; IMainLoopDriver mainLoopDriver; - if (UseSystemConsole) { mainLoopDriver = new NetMainLoop (); Driver = new NetDriver (); @@ -199,7 +206,11 @@ namespace Terminal.Gui { public class RunState : IDisposable { internal bool closeDriver = true; - internal RunState (Toplevel view) + /// + /// Initializes a new class. + /// + /// + public RunState (Toplevel view) { Toplevel = view; } @@ -476,7 +487,7 @@ namespace Terminal.Gui { } /// - /// Shutdown an application initialized with + /// Shutdown an application initialized with /// /// /// trueCloses the application.falseCloses toplevels only. public static void Shutdown (bool closeDriver = true) diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index ec041fa62..7c5db8966 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -20,7 +20,7 @@ namespace Terminal.Gui { /// been called (which sets the property to false). /// /// - /// A Toplevel is created when an application initialzies Terminal.Gui by callling . + /// A Toplevel is created when an application initialzies Terminal.Gui by callling . /// The application Toplevel can be accessed via . Additional Toplevels can be created /// and run (e.g. s. To run a Toplevel, create the and /// call . diff --git a/Terminal.sln b/Terminal.sln index c5b9ac6bc..6df6d7c65 100644 --- a/Terminal.sln +++ b/Terminal.sln @@ -6,10 +6,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "Example\Example. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui", "Terminal.Gui\Terminal.Gui.csproj", "{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UICatalog", "UICatalog\UICatalog.csproj", "{88979F89-9A42-448F-AE3E-3060145F6375}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{8B901EDE-8974-4820-B100-5226917E2990}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -50,6 +52,14 @@ Global {88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.Build.0 = Release|Any CPU {88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.ActiveCfg = Release|Any CPU {88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.Build.0 = Release|Any CPU + {8B901EDE-8974-4820-B100-5226917E2990}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B901EDE-8974-4820-B100-5226917E2990}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B901EDE-8974-4820-B100-5226917E2990}.Debug|x86.ActiveCfg = Debug|Any CPU + {8B901EDE-8974-4820-B100-5226917E2990}.Debug|x86.Build.0 = Debug|Any CPU + {8B901EDE-8974-4820-B100-5226917E2990}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B901EDE-8974-4820-B100-5226917E2990}.Release|Any CPU.Build.0 = Release|Any CPU + {8B901EDE-8974-4820-B100-5226917E2990}.Release|x86.ActiveCfg = Release|Any CPU + {8B901EDE-8974-4820-B100-5226917E2990}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs new file mode 100644 index 000000000..1676cdb93 --- /dev/null +++ b/UnitTests/ApplicationTests.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Terminal.Gui; +using Xunit; + +// Alais Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.MockConsole; + +// Since Application is a singleton we can't run tests in parallel +[assembly: CollectionBehavior (DisableTestParallelization = true)] + +namespace Terminal.Gui { + public class ApplicationTests { + [Fact] + public void TestInitShutdown () + { + Assert.Null (Application.Current); + Assert.Null (Application.CurrentView); + Assert.Null (Application.Top); + Assert.Null (Application.MainLoop); + Assert.Null (Application.Driver); + + Application.Init (new MockDriver ()); + Assert.NotNull (Application.Current); + Assert.NotNull (Application.CurrentView); + Assert.NotNull (Application.Top); + Assert.NotNull (Application.MainLoop); + Assert.NotNull (Application.Driver); + + // MockDriver is always 80x25 + Assert.Equal (80, Application.Driver.Cols); + Assert.Equal (25, Application.Driver.Rows); + + Application.Shutdown (true); + Assert.Null (Application.Current); + Assert.Null (Application.CurrentView); + Assert.Null (Application.Top); + Assert.Null (Application.MainLoop); + Assert.Null (Application.Driver); + } + + [Fact] + public void TestNewRunState () + { + var rs = new Application.RunState (null); + Assert.NotNull (rs); + + // Should not throw because Toplevel was null + rs.Dispose (); + + var top = new Toplevel (); + rs = new Application.RunState (top); + Assert.NotNull (rs); + + // Should throw because there's no stack + Assert.Throws (() => rs.Dispose ()); + } + + [Fact] + public void TestBeginEnd () + { + // Setup Mock driver + Application.Init (new MockDriver ()); + Assert.NotNull (Application.Driver); + + // Test null Toplevel + Assert.Throws (() => Application.Begin (null)); + + var top = new Toplevel (); + var rs = Application.Begin (top); + Assert.NotNull (rs); + Assert.Equal (top, Application.Current); + Application.End (rs, true); + + Assert.Null (Application.Current); + Assert.Null (Application.CurrentView); + Assert.Null (Application.Top); + Assert.Null (Application.MainLoop); + Assert.Null (Application.Driver); + + Application.Shutdown (true); + } + + [Fact] + public void TestRequestStop () + { + // Setup Mock driver + Application.Init (new MockDriver ()); + Assert.NotNull (Application.Driver); + + var top = new Toplevel (); + var rs = Application.Begin (top); + Assert.NotNull (rs); + Assert.Equal (top, Application.Current); + + Application.Iteration = () => { + Application.RequestStop (); + }; + + Application.Run (top, true); + + Application.Shutdown (true); + Assert.Null (Application.Current); + Assert.Null (Application.CurrentView); + Assert.Null (Application.Top); + Assert.Null (Application.MainLoop); + Assert.Null (Application.Driver); + } + + [Fact] + public void TestRunningFalse () + { + // Setup Mock driver + Application.Init (new MockDriver ()); + Assert.NotNull (Application.Driver); + + var top = new Toplevel (); + var rs = Application.Begin (top); + Assert.NotNull (rs); + Assert.Equal (top, Application.Current); + + Application.Iteration = () => { + top.Running = false; + }; + + Application.Run (top, true); + + Application.Shutdown (true); + Assert.Null (Application.Current); + Assert.Null (Application.CurrentView); + Assert.Null (Application.Top); + Assert.Null (Application.MainLoop); + Assert.Null (Application.Driver); + } + + + [Fact] + public void TestKeyUp () + { + // Setup Mock driver + Application.Init (new MockDriver ()); + Assert.NotNull (Application.Driver); + + // Setup some fake kepresses (This) + var input = "Tests"; + + // Put a control-q in at the end + Console.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true)); + foreach (var c in input.Reverse()) { + if (char.IsLetter (c)) { + Console.MockKeyPresses.Push (new ConsoleKeyInfo (char.ToLower (c), (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false)); + } + else + { + Console.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false)); + } + } + + int stackSize = Console.MockKeyPresses.Count; + + int iterations = 0; + Application.Iteration = () => { + iterations++; + }; + + int keyUps = 0; + var output = string.Empty; + Application.Top.KeyUp += (View.KeyEventEventArgs args) => { + if (args.KeyEvent.Key != Key.ControlQ) { + output += (char)args.KeyEvent.KeyValue; + } + keyUps++; + }; + + Application.Run (Application.Top, true); + + // Input string should match output + Assert.Equal (input, output); + + // # of key up events should match stack size + Assert.Equal (stackSize, keyUps); + + // # of key up events should match # of iterations + Assert.Equal (stackSize, iterations); + + Application.Shutdown (true); + Assert.Null (Application.Current); + Assert.Null (Application.CurrentView); + Assert.Null (Application.Top); + Assert.Null (Application.MainLoop); + Assert.Null (Application.Driver); + } + } +} diff --git a/UnitTests/ConsoleDriverTests.cs b/UnitTests/ConsoleDriverTests.cs new file mode 100644 index 000000000..b2b040e70 --- /dev/null +++ b/UnitTests/ConsoleDriverTests.cs @@ -0,0 +1,67 @@ +using System; +using Terminal.Gui; +using Xunit; + +// Alais Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.MockConsole; + +namespace Terminal.Gui { + public class ConsoleDriverTests { + [Fact] + public void TestInit () + { + var driver = new MockDriver (); + driver.Init (() => { }); + + Assert.Equal (80, Console.BufferWidth); + Assert.Equal (25, Console.BufferHeight); + + // MockDriver is always 80x25 + Assert.Equal (Console.BufferWidth, driver.Cols); + Assert.Equal (Console.BufferHeight, driver.Rows); + driver.End (); + } + + [Fact] + public void TestEnd () + { + var driver = new MockDriver (); + driver.Init (() => { }); + + MockConsole.ForegroundColor = ConsoleColor.Red; + Assert.Equal (ConsoleColor.Red, Console.ForegroundColor); + + MockConsole.BackgroundColor = ConsoleColor.Green; + Assert.Equal (ConsoleColor.Green, Console.BackgroundColor); + driver.Move (2, 3); + Assert.Equal (2, Console.CursorLeft); + Assert.Equal (3, Console.CursorTop); + + driver.End (); + Assert.Equal (0, Console.CursorLeft); + Assert.Equal (0, Console.CursorTop); + Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor); + Assert.Equal (ConsoleColor.Black, Console.BackgroundColor); + } + + [Fact] + public void TestSetColors () + { + var driver = new MockDriver (); + driver.Init (() => { }); + Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor); + Assert.Equal (ConsoleColor.Black, Console.BackgroundColor); + + Console.ForegroundColor = ConsoleColor.Red; + Assert.Equal (ConsoleColor.Red, Console.ForegroundColor); + + Console.BackgroundColor = ConsoleColor.Green; + Assert.Equal (ConsoleColor.Green, Console.BackgroundColor); + + Console.ResetColor (); + Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor); + Assert.Equal (ConsoleColor.Black, Console.BackgroundColor); + driver.End (); + } + } +} diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj new file mode 100644 index 000000000..9b10745f5 --- /dev/null +++ b/UnitTests/UnitTests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + false + + + + + + + + + + + + + + + diff --git a/UnitTests/xunit.runner.json b/UnitTests/xunit.runner.json new file mode 100644 index 000000000..e810a9725 --- /dev/null +++ b/UnitTests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeTestCollections": false, + "parallelizeAssembly": false +} From 68322ca0d4c4a93462cfede5746922c03f78b1d6 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 21:50:39 -0600 Subject: [PATCH 12/23] disabled travis ci/cd in favor of github actions --- .travis.yml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e9171c2e2..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: csharp -solution: Terminal.sln -script: - - tsh_version=1.4.0-pre1 - - msbuild /t:Restore $TRAVIS_BUILD_DIR/Terminal.sln - - cd $TRAVIS_BUILD_DIR/ - - msbuild /p:Configuration=Release Terminal.sln -mono: "6.8.0" -dotnet: "3.1.201" -os: "osx" From 80a6acc55b11976571a5fa0683024dcbdd6bcad7 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 22:06:02 -0600 Subject: [PATCH 13/23] removed warnings --- .../ConsoleDrivers/MockDriver/MockConsole.cs | 332 +++++++++++++++++- .../ConsoleDrivers/MockDriver/MockDriver.cs | 89 ++++- 2 files changed, 413 insertions(+), 8 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/MockDriver/MockConsole.cs b/Terminal.Gui/ConsoleDrivers/MockDriver/MockConsole.cs index ffb3b0c32..d69b52ea9 100644 --- a/Terminal.Gui/ConsoleDrivers/MockDriver/MockConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/MockDriver/MockConsole.cs @@ -36,6 +36,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // Error reading or writing information. + /// + /// + /// public static int WindowWidth { get; set; } = 80; // // Summary: @@ -44,6 +47,9 @@ namespace Terminal.Gui { // // Returns: // true if output is redirected; otherwise, false. + /// + /// + /// public static bool IsOutputRedirected { get; } // // Summary: @@ -52,6 +58,9 @@ namespace Terminal.Gui { // // Returns: // true if error output is redirected; otherwise, false. + /// + /// + /// public static bool IsErrorRedirected { get; } // // Summary: @@ -59,6 +68,9 @@ namespace Terminal.Gui { // // Returns: // A System.IO.TextReader that represents the standard input stream. + /// + /// + /// public static TextReader In { get; } // // Summary: @@ -66,6 +78,9 @@ namespace Terminal.Gui { // // Returns: // A System.IO.TextWriter that represents the standard output stream. + /// + /// + /// public static TextWriter Out { get; } // // Summary: @@ -73,6 +88,9 @@ namespace Terminal.Gui { // // Returns: // A System.IO.TextWriter that represents the standard error output stream. + /// + /// + /// public static TextWriter Error { get; } // // Summary: @@ -90,6 +108,9 @@ namespace Terminal.Gui { // // T:System.Security.SecurityException: // Your application does not have permission to perform this operation. + /// + /// + /// public static Encoding InputEncoding { get; set; } // // Summary: @@ -107,6 +128,9 @@ namespace Terminal.Gui { // // T:System.Security.SecurityException: // Your application does not have permission to perform this operation. + /// + /// + /// public static Encoding OutputEncoding { get; set; } // // Summary: @@ -171,6 +195,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static int BufferHeight { get; set; } = 25; // // Summary: @@ -190,6 +217,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static int BufferWidth { get; set; } = 80; // // Summary: @@ -210,6 +240,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // Error reading or writing information. + /// + /// + /// public static int WindowHeight { get; set; } = 25; // // Summary: @@ -223,6 +256,9 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // Unable to get or set the input mode of the console input buffer. + /// + /// + /// public static bool TreatControlCAsInput { get; set; } // // Summary: @@ -231,6 +267,9 @@ namespace Terminal.Gui { // // Returns: // The width of the largest possible console window measured in columns. + /// + /// + /// public static int LargestWindowWidth { get; } // // Summary: @@ -239,6 +278,9 @@ namespace Terminal.Gui { // // Returns: // The height of the largest possible console window measured in rows. + /// + /// + /// public static int LargestWindowHeight { get; } // // Summary: @@ -256,6 +298,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // Error reading or writing information. + /// + /// + /// public static int WindowLeft { get; set; } // // Summary: @@ -273,6 +318,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // Error reading or writing information. + /// + /// + /// public static int WindowTop { get; set; } // // Summary: @@ -291,6 +339,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static int CursorLeft { get; set; } // // Summary: @@ -309,6 +360,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static int CursorTop { get; set; } // // Summary: @@ -327,6 +381,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static int CursorSize { get; set; } // // Summary: @@ -341,6 +398,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static bool CursorVisible { get; set; } // // Summary: @@ -362,6 +422,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static string Title { get; set; } // // Summary: @@ -376,6 +439,9 @@ namespace Terminal.Gui { // // T:System.InvalidOperationException: // Standard input is redirected to a file instead of the keyboard. + /// + /// + /// public static bool KeyAvailable { get; } // // Summary: @@ -384,6 +450,9 @@ namespace Terminal.Gui { // // Returns: // true if NUM LOCK is turned on; false if NUM LOCK is turned off. + /// + /// + /// public static bool NumberLock { get; } // // Summary: @@ -392,6 +461,9 @@ namespace Terminal.Gui { // // Returns: // true if CAPS LOCK is turned on; false if CAPS LOCK is turned off. + /// + /// + /// public static bool CapsLock { get; } // // Summary: @@ -400,15 +472,11 @@ namespace Terminal.Gui { // // Returns: // true if input is redirected; otherwise, false. + /// + /// + /// public static bool IsInputRedirected { get; } - // - // Summary: - // Occurs when the System.ConsoleModifiers.Control modifier key (Ctrl) and either - // the System.ConsoleKey.C console key (C) or the Break key are pressed simultaneously - // (Ctrl+C or Ctrl+Break). - //public static event ConsoleCancelEventHandler CancelKeyPress; - // // Summary: // Plays the sound of a beep through the console speaker. @@ -417,6 +485,9 @@ namespace Terminal.Gui { // T:System.Security.HostProtectionException: // This method was executed on a server, such as SQL Server, that does not permit // access to a user interface. + /// + /// + /// public static void Beep () { throw new NotImplementedException (); @@ -441,6 +512,9 @@ namespace Terminal.Gui { // T:System.Security.HostProtectionException: // This method was executed on a server, such as SQL Server, that does not permit // access to the console. + /// + /// + /// public static void Beep (int frequency, int duration) { throw new NotImplementedException (); @@ -452,6 +526,9 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static void Clear () { _buffer = new char [WindowWidth, WindowHeight]; @@ -497,6 +574,9 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop) { throw new NotImplementedException (); @@ -553,6 +633,9 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[SecuritySafeCritical] + /// + /// + /// public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor) { throw new NotImplementedException (); @@ -564,6 +647,9 @@ namespace Terminal.Gui { // // Returns: // The standard error stream. + /// + /// + /// public static Stream OpenStandardError () { throw new NotImplementedException (); @@ -583,6 +669,9 @@ namespace Terminal.Gui { // Exceptions: // T:System.ArgumentOutOfRangeException: // bufferSize is less than or equal to zero. + /// + /// + /// public static Stream OpenStandardError (int bufferSize) { throw new NotImplementedException (); @@ -602,6 +691,9 @@ namespace Terminal.Gui { // Exceptions: // T:System.ArgumentOutOfRangeException: // bufferSize is less than or equal to zero. + /// + /// + /// public static Stream OpenStandardInput (int bufferSize) { throw new NotImplementedException (); @@ -613,6 +705,9 @@ namespace Terminal.Gui { // // Returns: // The standard input stream. + /// + /// + /// public static Stream OpenStandardInput () { throw new NotImplementedException (); @@ -632,6 +727,9 @@ namespace Terminal.Gui { // Exceptions: // T:System.ArgumentOutOfRangeException: // bufferSize is less than or equal to zero. + /// + /// + /// public static Stream OpenStandardOutput (int bufferSize) { throw new NotImplementedException (); @@ -643,6 +741,9 @@ namespace Terminal.Gui { // // Returns: // The standard output stream. + /// + /// + /// public static Stream OpenStandardOutput () { throw new NotImplementedException (); @@ -659,6 +760,9 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static int Read () { throw new NotImplementedException (); @@ -686,6 +790,9 @@ namespace Terminal.Gui { // The System.Console.In property is redirected from some stream other than the // console. //[SecuritySafeCritical] + /// + /// + /// public static ConsoleKeyInfo ReadKey (bool intercept) { if (MockKeyPresses.Count > 0) { @@ -695,6 +802,9 @@ namespace Terminal.Gui { } } + /// + /// + /// public static Stack MockKeyPresses = new Stack (); // @@ -713,6 +823,9 @@ namespace Terminal.Gui { // T:System.InvalidOperationException: // The System.Console.In property is redirected from some stream other than the // console. + /// + /// + /// public static ConsoleKeyInfo ReadKey () { throw new NotImplementedException (); @@ -735,6 +848,9 @@ namespace Terminal.Gui { // // T:System.ArgumentOutOfRangeException: // The number of characters in the next line of characters is greater than System.Int32.MaxValue. + /// + /// + /// public static string ReadLine () { throw new NotImplementedException (); @@ -751,6 +867,9 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[SecuritySafeCritical] + /// + /// + /// public static void ResetColor () { BackgroundColor = _defaultBackgroundColor; @@ -781,6 +900,9 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[SecuritySafeCritical] + /// + /// + /// public static void SetBufferSize (int width, int height) { throw new NotImplementedException (); @@ -810,6 +932,9 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[SecuritySafeCritical] + /// + /// + /// public static void SetCursorPosition (int left, int top) { CursorLeft = left; @@ -832,6 +957,9 @@ namespace Terminal.Gui { // T:System.Security.SecurityException: // The caller does not have the required permission. //[SecuritySafeCritical] + /// + /// + /// public static void SetError (TextWriter newError) { throw new NotImplementedException (); @@ -852,6 +980,9 @@ namespace Terminal.Gui { // T:System.Security.SecurityException: // The caller does not have the required permission. //[SecuritySafeCritical] + /// + /// + /// public static void SetIn (TextReader newIn) { throw new NotImplementedException (); @@ -872,6 +1003,10 @@ namespace Terminal.Gui { // T:System.Security.SecurityException: // The caller does not have the required permission. //[SecuritySafeCritical] + /// + /// + /// + /// public static void SetOut (TextWriter newOut) { throw new NotImplementedException (); @@ -900,6 +1035,11 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[SecuritySafeCritical] + /// + /// + /// + /// + /// public static void SetWindowPosition (int left, int top) { throw new NotImplementedException (); @@ -929,6 +1069,11 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[SecuritySafeCritical] + /// + /// + /// + /// + /// public static void SetWindowSize (int width, int height) { throw new NotImplementedException (); @@ -945,6 +1090,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (string value) { throw new NotImplementedException (); @@ -962,6 +1111,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (object value) { throw new NotImplementedException (); @@ -980,6 +1133,10 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[CLSCompliant (false)] + /// + /// + /// + /// public static void Write (ulong value) { throw new NotImplementedException (); @@ -997,6 +1154,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (long value) { throw new NotImplementedException (); @@ -1026,6 +1187,12 @@ namespace Terminal.Gui { // // T:System.FormatException: // The format specification in format is invalid. + /// + /// + /// + /// + /// + /// public static void Write (string format, object arg0, object arg1) { throw new NotImplementedException (); @@ -1043,6 +1210,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (int value) { throw new NotImplementedException (); @@ -1069,6 +1240,11 @@ namespace Terminal.Gui { // // T:System.FormatException: // The format specification in format is invalid. + /// + /// + /// + /// + /// public static void Write (string format, object arg0) { throw new NotImplementedException (); @@ -1087,12 +1263,24 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[CLSCompliant (false)] + /// + /// + /// + /// public static void Write (uint value) { throw new NotImplementedException (); } //[CLSCompliant (false)] + /// + /// + /// + /// + /// + /// + /// + /// public static void Write (string format, object arg0, object arg1, object arg2, object arg3) { throw new NotImplementedException (); @@ -1119,6 +1307,11 @@ namespace Terminal.Gui { // // T:System.FormatException: // The format specification in format is invalid. + /// + /// + /// + /// + /// public static void Write (string format, params object [] arg) { throw new NotImplementedException (); @@ -1136,6 +1329,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (bool value) { throw new NotImplementedException (); @@ -1152,6 +1349,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (char value) { _buffer [CursorLeft, CursorTop] = value; @@ -1168,6 +1369,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (char [] buffer) { throw new NotImplementedException (); @@ -1199,6 +1404,12 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// + /// + /// public static void Write (char [] buffer, int index, int count) { throw new NotImplementedException (); @@ -1231,6 +1442,13 @@ namespace Terminal.Gui { // // T:System.FormatException: // The format specification in format is invalid. + /// + /// + /// + /// + /// + /// + /// public static void Write (string format, object arg0, object arg1, object arg2) { throw new NotImplementedException (); @@ -1248,6 +1466,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (decimal value) { throw new NotImplementedException (); @@ -1265,6 +1487,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (float value) { throw new NotImplementedException (); @@ -1282,6 +1508,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void Write (double value) { throw new NotImplementedException (); @@ -1294,6 +1524,9 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// public static void WriteLine () { throw new NotImplementedException (); @@ -1311,6 +1544,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (float value) { throw new NotImplementedException (); @@ -1328,6 +1565,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (int value) { throw new NotImplementedException (); @@ -1346,6 +1587,10 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[CLSCompliant (false)] + /// + /// + /// + /// public static void WriteLine (uint value) { throw new NotImplementedException (); @@ -1363,6 +1608,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (long value) { throw new NotImplementedException (); @@ -1381,6 +1630,10 @@ namespace Terminal.Gui { // T:System.IO.IOException: // An I/O error occurred. //[CLSCompliant (false)] + /// + /// + /// + /// public static void WriteLine (ulong value) { throw new NotImplementedException (); @@ -1398,6 +1651,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (object value) { throw new NotImplementedException (); @@ -1415,6 +1672,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (string value) { throw new NotImplementedException (); @@ -1441,6 +1702,11 @@ namespace Terminal.Gui { // // T:System.FormatException: // The format specification in format is invalid. + /// + /// + /// + /// + /// public static void WriteLine (string format, object arg0) { throw new NotImplementedException (); @@ -1473,12 +1739,27 @@ namespace Terminal.Gui { // // T:System.FormatException: // The format specification in format is invalid. + /// + /// + /// + /// + /// + /// + /// public static void WriteLine (string format, object arg0, object arg1, object arg2) { throw new NotImplementedException (); } //[CLSCompliant (false)] + /// + /// + /// + /// + /// + /// + /// + /// public static void WriteLine (string format, object arg0, object arg1, object arg2, object arg3) { throw new NotImplementedException (); @@ -1506,6 +1787,11 @@ namespace Terminal.Gui { // // T:System.FormatException: // The format specification in format is invalid. + /// + /// + /// + /// + /// public static void WriteLine (string format, params object [] arg) { throw new NotImplementedException (); @@ -1538,6 +1824,12 @@ namespace Terminal.Gui { // // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// + /// + /// public static void WriteLine (char [] buffer, int index, int count) { throw new NotImplementedException (); @@ -1555,6 +1847,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (decimal value) { throw new NotImplementedException (); @@ -1572,6 +1868,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (char [] buffer) { throw new NotImplementedException (); @@ -1589,6 +1889,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (char value) { throw new NotImplementedException (); @@ -1606,6 +1910,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (bool value) { throw new NotImplementedException (); @@ -1635,6 +1943,12 @@ namespace Terminal.Gui { // // T:System.FormatException: // The format specification in format is invalid. + /// + /// + /// + /// + /// + /// public static void WriteLine (string format, object arg0, object arg1) { throw new NotImplementedException (); @@ -1652,6 +1966,10 @@ namespace Terminal.Gui { // Exceptions: // T:System.IO.IOException: // An I/O error occurred. + /// + /// + /// + /// public static void WriteLine (double value) { throw new NotImplementedException (); diff --git a/Terminal.Gui/ConsoleDrivers/MockDriver/MockDriver.cs b/Terminal.Gui/ConsoleDrivers/MockDriver/MockDriver.cs index c236cd0b9..8442b5c29 100644 --- a/Terminal.Gui/ConsoleDrivers/MockDriver/MockDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/MockDriver/MockDriver.cs @@ -11,9 +11,18 @@ using System.Threading; using NStack; namespace Terminal.Gui { + /// + /// Implements a mock ConsoleDriver for unit testing + /// public class MockDriver : ConsoleDriver, IMainLoopDriver { int cols, rows; + /// + /// + /// public override int Cols => cols; + /// + /// + /// public override int Rows => rows; // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag @@ -40,6 +49,9 @@ namespace Terminal.Gui { static bool sync = false; + /// + /// + /// public MockDriver () { cols = MockConsole.WindowWidth; @@ -50,6 +62,11 @@ namespace Terminal.Gui { bool needMove; // Current row, and current col, tracked by Move/AddCh only int ccol, crow; + /// + /// + /// + /// + /// public override void Move (int col, int row) { ccol = col; @@ -67,6 +84,10 @@ namespace Terminal.Gui { } + /// + /// + /// + /// public override void AddRune (Rune rune) { if (Clip.Contains (ccol, crow)) { @@ -91,12 +112,19 @@ namespace Terminal.Gui { UpdateScreen (); } + /// + /// + /// + /// public override void AddStr (ustring str) { foreach (var rune in str) AddRune (rune); } + /// + /// + /// public override void End () { MockConsole.ResetColor (); @@ -109,7 +137,10 @@ namespace Terminal.Gui { return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) }; } - + /// + /// + /// + /// public override void Init (Action terminalResized) { Colors.TopLevel = new ColorScheme (); @@ -180,6 +211,12 @@ namespace Terminal.Gui { //MockConsole.Clear (); } + /// + /// + /// + /// + /// + /// public override Attribute MakeAttribute (Color fore, Color back) { return MakeColor ((ConsoleColor)fore, (ConsoleColor)back); @@ -200,6 +237,9 @@ namespace Terminal.Gui { } } + /// + /// + /// public override void UpdateScreen () { int rows = Rows; @@ -219,6 +259,9 @@ namespace Terminal.Gui { } } + /// + /// + /// public override void Refresh () { int rows = Rows; @@ -250,24 +293,40 @@ namespace Terminal.Gui { MockConsole.CursorLeft = savedCol; } + /// + /// + /// public override void UpdateCursor () { // } + /// + /// + /// public override void StartReportingMouseMoves () { } + /// + /// + /// public override void StopReportingMouseMoves () { } + /// + /// + /// public override void Suspend () { } int currentAttribute; + /// + /// + /// + /// public override void SetAttribute (Attribute c) { currentAttribute = c.value; @@ -351,6 +410,14 @@ namespace Terminal.Gui { KeyModifiers keyModifiers = new KeyModifiers (); + /// + /// + /// + /// + /// + /// + /// + /// public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called @@ -363,20 +430,36 @@ namespace Terminal.Gui { }; } + /// + /// + /// + /// + /// public override void SetColors (ConsoleColor foreground, ConsoleColor background) { throw new NotImplementedException (); } + /// + /// + /// + /// + /// public override void SetColors (short foregroundColorId, short backgroundColorId) { throw new NotImplementedException (); } + /// + /// + /// public override void CookMouse () { } + /// + /// + /// public override void UncookMouse () { } @@ -384,6 +467,10 @@ namespace Terminal.Gui { AutoResetEvent keyReady = new AutoResetEvent (false); AutoResetEvent waitForProbe = new AutoResetEvent (false); ConsoleKeyInfo? windowsKeyResult = null; + + /// + /// + /// public Action WindowsKeyPressed; MainLoop mainLoop; From 9d7a1c4ce575290ba13f2486370adece98008cff Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 22:09:05 -0600 Subject: [PATCH 14/23] Updated readme with new Github Action Status --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 883915e83..1482ad0fa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/migueldeicaza/gui.cs.svg?branch=master)](https://travis-ci.org/migueldeicaza/gui.cs) +[![Actions Status](https://github.com/{owner}/{repo}/workflows/{workflow_name}/badge.svg)](https://github.com/{owner}/{repo}/actions) [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui) [![Downloads](https://img.shields.io/nuget/dt/Terminal.Gui)](https://www.nuget.org/packages/Terminal.Gui) [![License](https://img.shields.io/github/license/migueldeicaza/gui.cs.svg)](LICENSE) From 19a993bb8a049ae751d1bacb7e5cdc81ccf63c3c Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 22:10:53 -0600 Subject: [PATCH 15/23] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1482ad0fa..bbfd9fe28 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Actions Status](https://github.com/{owner}/{repo}/workflows/{workflow_name}/badge.svg)](https://github.com/{owner}/{repo}/actions) +![.NET Core](https://github.com/migueldeicaza/gui.cs/workflows/.NET%20Core/badge.svg?branch=master) [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui) [![Downloads](https://img.shields.io/nuget/dt/Terminal.Gui)](https://www.nuget.org/packages/Terminal.Gui) [![License](https://img.shields.io/github/license/migueldeicaza/gui.cs.svg)](LICENSE) From 3fbaacb28ad1f9c9edb4f557020c94032c535131 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 22:18:39 -0600 Subject: [PATCH 16/23] added readme for unit tests --- UnitTests/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 UnitTests/README.md diff --git a/UnitTests/README.md b/UnitTests/README.md new file mode 100644 index 000000000..64f34caec --- /dev/null +++ b/UnitTests/README.md @@ -0,0 +1,14 @@ +# Automated Unit Tests + +`Terminal.Gui` uses [xunit](https://xunit.net/) for automated unit tests run automatically with Github Actions. + +## Notes + +* Running tests in parallel is disabled because `Application` is a singleton. Do not change those settings. + +## Guidelines for Adding More Tests + +1. Do. Please. Add lots. +2. Structure the tests by class. Name the test classes in the form of `ClassNameTests` and the file `ClassNameTests.cs`. +3. The test functions themselves should have descriptive names like `TestBeginEnd`. +4. IMPORTANT: Remember `Application` is a static class (singleton). You must clean up after your tests by calling `Application.Shutdown`. From 225fe09f0e4870d955c721aa0bbf2239b6c2bf97 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 22:20:12 -0600 Subject: [PATCH 17/23] added readme for unit tests --- UnitTests/README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/UnitTests/README.md b/UnitTests/README.md index 64f34caec..27b7dcccd 100644 --- a/UnitTests/README.md +++ b/UnitTests/README.md @@ -2,13 +2,4 @@ `Terminal.Gui` uses [xunit](https://xunit.net/) for automated unit tests run automatically with Github Actions. -## Notes - -* Running tests in parallel is disabled because `Application` is a singleton. Do not change those settings. - -## Guidelines for Adding More Tests - -1. Do. Please. Add lots. -2. Structure the tests by class. Name the test classes in the form of `ClassNameTests` and the file `ClassNameTests.cs`. -3. The test functions themselves should have descriptive names like `TestBeginEnd`. -4. IMPORTANT: Remember `Application` is a static class (singleton). You must clean up after your tests by calling `Application.Shutdown`. +See the [Testing wiki](https://github.com/migueldeicaza/gui.cs/wiki/Testing) for details on how to add more tests. From cf6bb2a9ec56c90c024c79d66602f6a41009b90b Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 22:26:35 -0600 Subject: [PATCH 18/23] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad0e5f20e..b3d92d8ef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -95,6 +95,8 @@ See also: https://www.codeproject.com/Articles/20550/C-Event-Implementation-Fund ## Examples & Tests -**Terminal.Gui** does an automated unit or regression test suite ([please feel free to help build one](https://github.com/migueldeicaza/gui.cs/issues/566)). Until such a suite is available, the best we have is the [UICatalog](https://github.com/migueldeicaza/gui.cs/tree/master/UICatalog) sample app. +**Terminal.Gui** has an automated unit or regression test suite. See the [Testing wiki](https://github.com/migueldeicaza/gui.cs/wiki/Testing) + +In addition [UI Catalog](https://github.com/migueldeicaza/gui.cs/tree/master/UICatalog) is a great sample app for manual testing. When adding new functionality, fixing bugs, or changing things, please either add a new `Scenario` to **UICatalog** or update an existing `Scenario` to fully illustrate your work and provide a test-case. From 30a37ad7956c9ad4c10428eab6d5dbbbef10596b Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 23:10:09 -0600 Subject: [PATCH 19/23] added Responder unit tests --- UnitTests/ResponderTests.cs | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 UnitTests/ResponderTests.cs diff --git a/UnitTests/ResponderTests.cs b/UnitTests/ResponderTests.cs new file mode 100644 index 000000000..3d8680604 --- /dev/null +++ b/UnitTests/ResponderTests.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Terminal.Gui; +using Xunit; + +// Alais Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.MockConsole; + +namespace Terminal.Gui { + public class ResponderTests { + [Fact] + public void TestNew () + { + var r = new Responder (); + Assert.NotNull (r); + Assert.Equal ("Terminal.Gui.Responder", r.ToString ()); + Assert.False (r.CanFocus); + Assert.False (r.HasFocus); + } + + [Fact] + public void TestMethods () + { + var r = new Responder (); + + Assert.False (r.ProcessKey (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.ProcessHotKey (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.ProcessColdKey (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.OnKeyDown (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.OnKeyUp (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.MouseEvent (new MouseEvent () { Flags = MouseFlags.AllEvents })); + Assert.False (r.OnMouseEnter (new MouseEvent () { Flags = MouseFlags.AllEvents })); + Assert.False (r.OnMouseLeave (new MouseEvent () { Flags = MouseFlags.AllEvents })); + Assert.False (r.OnEnter ()); + Assert.False (r.OnLeave ()); + } + } +} From 36ecedefb3c9e7b997201a7cf2d53eb7a25d6dd3 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 7 Jun 2020 23:56:52 -0600 Subject: [PATCH 20/23] added View and DimPos tests --- UnitTests/DimPosTests.cs | 127 +++++++++++++++++++++++++++++++++++++++ UnitTests/ViewTests.cs | 111 ++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 UnitTests/DimPosTests.cs create mode 100644 UnitTests/ViewTests.cs diff --git a/UnitTests/DimPosTests.cs b/UnitTests/DimPosTests.cs new file mode 100644 index 000000000..175ed3967 --- /dev/null +++ b/UnitTests/DimPosTests.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using Terminal.Gui; +using Xunit; + +// Alais Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.MockConsole; + +namespace Terminal.Gui { + public class DimPosTests { + [Fact] + public void TestNew () + { + var pos = new Pos (); + Assert.Equal ("Terminal.Gui.Pos", pos.ToString ()); + } + + [Fact] + public void TestAnchorEnd () + { + var pos = Pos.AnchorEnd (); + Assert.Equal ("Pos.AnchorEnd(margin=0)", pos.ToString ()); + + pos = Pos.AnchorEnd (5); + Assert.Equal ("Pos.AnchorEnd(margin=5)", pos.ToString ()); + } + + [Fact] + public void TestAt () + { + var pos = Pos.At (0); + Assert.Equal ("Pos.Absolute(0)", pos.ToString ()); + + pos = Pos.At (5); + Assert.Equal ("Pos.Absolute(5)", pos.ToString ()); + + //Assert.Throws (() => pos = Pos.At (-1)); + } + + [Fact] + public void TestLeft () + { + var pos = Pos.Left (null); + Assert.Throws (() => pos.ToString ()); + + pos = Pos.Left (new View ()); + Assert.Equal ("Pos.View(side=x, target=View()({X=0,Y=0,Width=0,Height=0})", pos.ToString ()); + + pos = Pos.Left (new View (new Rect (1, 2, 3, 4))); + Assert.Equal ("Pos.View(side=x, target=View()({X=1,Y=2,Width=3,Height=4})", pos.ToString ()); + } + + [Fact] + public void TestTop () + { + var pos = Pos.Top (null); + Assert.Throws (() => pos.ToString ()); + + pos = Pos.Top (new View ()); + Assert.Equal ("Pos.View(side=y, target=View()({X=0,Y=0,Width=0,Height=0})", pos.ToString ()); + + pos = Pos.Top (new View (new Rect (1, 2, 3, 4))); + Assert.Equal ("Pos.View(side=y, target=View()({X=1,Y=2,Width=3,Height=4})", pos.ToString ()); + } + + [Fact] + public void TestRight () + { + var pos = Pos.Right (null); + Assert.Throws (() => pos.ToString ()); + + pos = Pos.Right (new View ()); + Assert.Equal ("Pos.View(side=right, target=View()({X=0,Y=0,Width=0,Height=0})", pos.ToString ()); + + pos = Pos.Right (new View (new Rect (1, 2, 3, 4))); + Assert.Equal ("Pos.View(side=right, target=View()({X=1,Y=2,Width=3,Height=4})", pos.ToString ()); + } + + [Fact] + public void TestBottom () + { + var pos = Pos.Bottom (null); + Assert.Throws (() => pos.ToString ()); + + pos = Pos.Bottom (new View ()); + Assert.Equal ("Pos.View(side=bottom, target=View()({X=0,Y=0,Width=0,Height=0})", pos.ToString ()); + + pos = Pos.Bottom (new View (new Rect (1, 2, 3, 4))); + Assert.Equal ("Pos.View(side=bottom, target=View()({X=1,Y=2,Width=3,Height=4})", pos.ToString ()); + + //Assert.Throws (() => pos = Pos.Bottom (new View (new Rect (0, 0, -3, -4)))); + + } + + [Fact] + public void TestCenter () + { + var pos = Pos.Center (); + Assert.Equal ("Pos.Center", pos.ToString ()); + } + + [Fact] + public void TestPercent () + { + var pos = Pos.Percent (0); + Assert.Equal ("Pos.Factor(0)", pos.ToString ()); + + pos = Pos.Percent (0.5F); + Assert.Equal ("Pos.Factor(0.005)", pos.ToString ()); + + pos = Pos.Percent (100); + Assert.Equal ("Pos.Factor(1)", pos.ToString ()); + + Assert.Throws (() => pos = Pos.Percent (-1)); + Assert.Throws (() => pos = Pos.Percent (101)); + Assert.Throws (() => pos = Pos.Percent (100.0001F)); + Assert.Throws (() => pos = Pos.Percent (1000001)); + } + + // TODO: Test operators + + // TODO: Test Dim + } +} diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs new file mode 100644 index 000000000..5fe17f35f --- /dev/null +++ b/UnitTests/ViewTests.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using Terminal.Gui; +using Xunit; + +// Alais Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.MockConsole; + +namespace Terminal.Gui { + public class ViewTests { + [Fact] + public void TestNew () + { + // Parameterless + var r = new View (); + Assert.NotNull (r); + Assert.Equal (LayoutStyle.Computed, r.LayoutStyle); + Assert.Equal ("View()({X=0,Y=0,Width=0,Height=0})", r.ToString ()); + Assert.False (r.CanFocus); + Assert.False (r.HasFocus); + Assert.Equal (new Rect (0, 0, 0, 0), r.Bounds); + Assert.Equal (new Rect (0, 0, 0, 0), r.Frame); + Assert.Null (r.Focused); + Assert.Null (r.ColorScheme); + Assert.Equal (Dim.Sized (0), r.Height); + Assert.Equal (Dim.Sized (0), r.Height); + // BUGBUG: Pos needs eqality implemented + //Assert.Equal (Pos.At (0), r.X); + //Assert.Equal (Pos.At (0), r.Y); + Assert.False (r.IsCurrentTop); + Assert.Empty (r.Id); + Assert.Empty (r.Subviews); + Assert.False (r.WantContinuousButtonPressed); + Assert.False (r.WantMousePositionReports); + Assert.Null (r.GetEnumerator().Current); + Assert.Null (r.SuperView); + Assert.Null (r.MostFocused); + + // Empty Rect + r = new View (Rect.Empty); + Assert.NotNull (r); + Assert.Equal (LayoutStyle.Absolute, r.LayoutStyle); + Assert.Equal ("View()({X=0,Y=0,Width=0,Height=0})", r.ToString ()); + Assert.False (r.CanFocus); + Assert.False (r.HasFocus); + Assert.Equal (new Rect (0, 0, 0, 0), r.Bounds); + Assert.Equal (new Rect (0, 0, 0, 0), r.Frame); + Assert.Null (r.Focused); + Assert.Null (r.ColorScheme); + Assert.Null (r.Height); + Assert.Null (r.Height); + Assert.Null (r.X); + Assert.Null (r.Y); + Assert.False (r.IsCurrentTop); + Assert.Empty (r.Id); + Assert.Empty (r.Subviews); + Assert.False (r.WantContinuousButtonPressed); + Assert.False (r.WantMousePositionReports); + Assert.Null (r.GetEnumerator ().Current); + Assert.Null (r.SuperView); + Assert.Null (r.MostFocused); + + // Rect with values + r = new View (new Rect(1, 2, 3, 4)); + Assert.NotNull (r); + Assert.Equal (LayoutStyle.Absolute, r.LayoutStyle); + Assert.Equal ("View()({X=1,Y=2,Width=3,Height=4})", r.ToString ()); + Assert.False (r.CanFocus); + Assert.False (r.HasFocus); + Assert.Equal (new Rect (0, 0, 3, 4), r.Bounds); + Assert.Equal (new Rect (1, 2, 3, 4), r.Frame); + Assert.Null (r.Focused); + Assert.Null (r.ColorScheme); + Assert.Null (r.Height); + Assert.Null (r.Height); + Assert.Null (r.X); + Assert.Null (r.Y); + Assert.False (r.IsCurrentTop); + Assert.Empty (r.Id); + Assert.Empty (r.Subviews); + Assert.False (r.WantContinuousButtonPressed); + Assert.False (r.WantMousePositionReports); + Assert.Null (r.GetEnumerator ().Current); + Assert.Null (r.SuperView); + Assert.Null (r.MostFocused); + + } + + [Fact] + public void TestMethods () + { + var r = new View (); + + Assert.False (r.ProcessKey (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.ProcessHotKey (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.ProcessColdKey (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.OnKeyDown (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.OnKeyUp (new KeyEvent () { Key = Key.Unknown })); + Assert.False (r.MouseEvent (new MouseEvent () { Flags = MouseFlags.AllEvents })); + Assert.False (r.OnMouseEnter (new MouseEvent () { Flags = MouseFlags.AllEvents })); + Assert.False (r.OnMouseLeave (new MouseEvent () { Flags = MouseFlags.AllEvents })); + Assert.False (r.OnEnter ()); + Assert.False (r.OnLeave ()); + + // TODO: Add more + } + } +} From b884ea4ce34899118cb1b241cad63c5f5512ca24 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 8 Jun 2020 22:05:33 +0100 Subject: [PATCH 21/23] Changed as suggest. --- Terminal.Gui/Views/Button.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index bff8d0a44..148f86ea3 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -156,10 +156,10 @@ namespace Terminal.Gui { } } - Rune _leftBracket = new Rune ('['); - Rune _rightBracket = new Rune (']'); - Rune _leftDefault = new Rune ('<'); - Rune _rightDefault = new Rune ('>'); + Rune _leftBracket = new Rune (Driver.LeftBracket); + Rune _rightBracket = new Rune (Driver.RightBracket); + Rune _leftDefault = new Rune (Driver.LeftDefaultIndicator); + Rune _rightDefault = new Rune (Driver.RightDefaultIndicator); internal void Update () { From 15e38550c58942ff2604c3ca34abd2de78aa3944 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 8 Jun 2020 23:39:31 +0100 Subject: [PATCH 22/23] Fixed a layout bug that preventing the redrawing on Pos and Dim changes. Now the TextAlignment is working well. I move them to the view for a generic uses to others views. --- Terminal.Gui/Core/View.cs | 120 +++++++++++++++++++++++++++++++-- Terminal.Gui/Views/Button.cs | 66 ++---------------- Terminal.Gui/Views/Label.cs | 22 ------ UICatalog/Scenarios/Buttons.cs | 93 ++++++++++++++----------- 4 files changed, 173 insertions(+), 128 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 113fb8113..c612d16b8 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -17,6 +17,27 @@ using System.Linq; using NStack; namespace Terminal.Gui { + /// + /// Text alignment enumeration, controls how text is displayed. + /// + public enum TextAlignment { + /// + /// Aligns the text to the left of the frame. + /// + Left, + /// + /// Aligns the text to the right side of the frame. + /// + Right, + /// + /// Centers the text in the frame. + /// + Centered, + /// + /// Shows the text as justified text in the frame. + /// + Justified + } /// /// Determines the LayoutStyle for a view, if Absolute, during LayoutSubviews, the @@ -299,6 +320,7 @@ namespace Terminal.Gui { set { x = value; SetNeedsLayout (); + SetNeedsDisplay (frame); } } @@ -314,6 +336,7 @@ namespace Terminal.Gui { set { y = value; SetNeedsLayout (); + SetNeedsDisplay (frame); } } @@ -331,6 +354,7 @@ namespace Terminal.Gui { set { width = value; SetNeedsLayout (); + SetNeedsDisplay (frame); } } @@ -344,6 +368,7 @@ namespace Terminal.Gui { set { height = value; SetNeedsLayout (); + SetNeedsDisplay (frame); } } @@ -1457,7 +1482,7 @@ namespace Terminal.Gui { /// The hot-key to look for. /// The returning hot-key position. /// The character immediately to the right relative to the hot-key position - /// + /// It aims to facilitate the preparation for procedures. public virtual ustring GetTextFromHotKey(ustring text, Rune hotKey, out int hotPos, out Rune showHotKey) { Rune hot_key = (Rune)0; @@ -1471,7 +1496,7 @@ namespace Terminal.Gui { if (c == hotKey) { hot_pos = i; } else if (hot_pos > -1) { - hot_key = (char)c; + hot_key = c; break; } } @@ -1482,10 +1507,12 @@ namespace Terminal.Gui { // Use first upper-case char if there are no hot-key in the text. i = 0; foreach (Rune c in shown_text) { - if (Rune.IsUpper (c)) { - hot_key = (char)c; - hot_pos = i; - break; + if ((char)c != 0xFFFD) { + if (Rune.IsUpper (c)) { + hot_key = c; + hot_pos = i; + break; + } } i++; } @@ -1514,6 +1541,87 @@ namespace Terminal.Gui { return shown_text; } + /// + /// A generic virtual method at the level of View to manipulate any hot-keys with process. + /// + /// The text to manipulate to align. + /// The passed in hot-key position. + /// The returning hot-key position. + /// The to align to. + /// It performs the process to the caller. + public virtual ustring GetTextAlignment (ustring shown_text, int hot_pos, out int c_hot_pos, TextAlignment textAlignment) + { + int start; + var caption = shown_text; + c_hot_pos = hot_pos; + + if (Frame.Width > shown_text.Length + 1) { + switch (textAlignment) { + case TextAlignment.Left: + caption += new string (' ', Frame.Width - caption.RuneCount); + break; + case TextAlignment.Right: + start = Frame.Width - caption.RuneCount; + caption = $"{new string (' ', Frame.Width - caption.RuneCount)}{caption}"; + if (c_hot_pos > -1) { + c_hot_pos += start; + } + break; + case TextAlignment.Centered: + start = Frame.Width / 2 - caption.RuneCount / 2; + caption = $"{new string (' ', start)}{caption}{new string (' ', Frame.Width - caption.RuneCount - start)}"; + if (c_hot_pos > -1) { + c_hot_pos += start; + } + break; + case TextAlignment.Justified: + var words = caption.Split (" "); + var wLen = GetWordsLength (words, c_hot_pos, out int runeCount, out int w_hot_pos); + var space = (Frame.Width - runeCount) / (caption.Length - wLen); + caption = ""; + for (int i = 0; i < words.Length; i++) { + if (i == words.Length - 1) { + caption += new string (' ', Frame.Width - caption.RuneCount - 1); + caption += words [i]; + } else { + caption += words [i]; + } + if (i < words.Length - 1) { + caption += new string (' ', space); + } + } + if (c_hot_pos > -1) { + if (wLen - runeCount == 0) { + c_hot_pos += (wLen - runeCount == 0 ? w_hot_pos * (space) - space - w_hot_pos + 1 : space + wLen - runeCount); + } else { + c_hot_pos += space + wLen - runeCount; + } + } + break; + } + } + + return caption; + } + + int GetWordsLength (ustring [] words, int hotPos, out int runeCount, out int wordHotPos) + { + int length = 0; + int rCount = 0; + int wHotPos = -1; + for (int i = 0; i < words.Length; i++) { + if (wHotPos == -1 && rCount + words [i].RuneCount >= hotPos) + wHotPos = i; + length += words [i].Length; + rCount += words [i].RuneCount; + } + if (wHotPos == -1 && hotPos > -1) + wHotPos = words.Length; + runeCount = rCount; + wordHotPos = wHotPos; + return length; + } + /// /// Pretty prints the View /// diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 148f86ea3..2d87e2dd1 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -6,6 +6,7 @@ // using System; +using System.Net.Sockets; using NStack; namespace Terminal.Gui { @@ -137,9 +138,7 @@ namespace Terminal.Gui { } set { - if (text?.Length != value?.Length) { - SetWidthHeight (value, is_default); - } + SetWidthHeight (value, is_default); text = value; Update (); } @@ -152,7 +151,7 @@ namespace Terminal.Gui { get => textAlignment; set { textAlignment = value; - SetNeedsDisplay (); + Update (); } } @@ -181,51 +180,8 @@ namespace Terminal.Gui { Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal); Move (0, 0); - var caption = shown_text; - c_hot_pos = hot_pos; - int start; - - if (Frame.Width > shown_text.Length + 1) { - switch (TextAlignment) { - case TextAlignment.Left: - caption += new string (' ', Frame.Width - caption.RuneCount); - break; - case TextAlignment.Right: - start = Frame.Width - caption.RuneCount; - caption = $"{new string (' ', Frame.Width - caption.RuneCount)}{caption}"; - if (c_hot_pos > -1) { - c_hot_pos += start; - } - break; - case TextAlignment.Centered: - start = Frame.Width / 2 - caption.RuneCount / 2; - caption = $"{new string (' ', start)}{caption}{new string (' ', Frame.Width - caption.RuneCount - start)}"; - if (c_hot_pos > -1) { - c_hot_pos += start; - } - break; - case TextAlignment.Justified: - var words = caption.Split (" "); - var wLen = GetWordsLength (words, out int runeCount); - var space = (Frame.Width - runeCount) / (caption.Length - wLen); - caption = ""; - for (int i = 0; i < words.Length; i++) { - if (i == words.Length - 1) { - caption += new string (' ', Frame.Width - caption.RuneCount - 1); - caption += words [i]; - } else { - caption += words [i]; - } - if (i < words.Length - 1) { - caption += new string (' ', space); - } - } - if (c_hot_pos > -1) { - c_hot_pos += space - 1 + (wLen - runeCount == 0 ? 0 : wLen - runeCount + 1); - } - break; - } - } + var caption = GetTextAlignment (shown_text, hot_pos, out int s_hot_pos, TextAlignment); + c_hot_pos = s_hot_pos; Driver.AddStr (caption); @@ -236,18 +192,6 @@ namespace Terminal.Gui { } } - int GetWordsLength (ustring [] words, out int runeCount) - { - int length = 0; - int rCount = 0; - for (int i = 0; i < words.Length; i++) { - length += words [i].Length; - rCount += words [i].RuneCount; - } - runeCount = rCount; - return length; - } - /// public override void PositionCursor () { diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs index 88d81bf6d..c387441b3 100644 --- a/Terminal.Gui/Views/Label.cs +++ b/Terminal.Gui/Views/Label.cs @@ -11,28 +11,6 @@ using System.Linq; using NStack; namespace Terminal.Gui { - /// - /// Text alignment enumeration, controls how text is displayed. - /// - public enum TextAlignment { - /// - /// Aligns the text to the left of the frame. - /// - Left, - /// - /// Aligns the text to the right side of the frame. - /// - Right, - /// - /// Centers the text in the frame. - /// - Centered, - /// - /// Shows the text as justified text in the frame. - /// - Justified - } - /// /// The Label displays a string at a given position and supports multiple lines separted by newline characters. /// diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs index 9895fc41a..29f70cc01 100644 --- a/UICatalog/Scenarios/Buttons.cs +++ b/UICatalog/Scenarios/Buttons.cs @@ -89,7 +89,7 @@ namespace UICatalog { textChanger.Clicked = () => textChanger.Text += "!"; Win.Add (button = new Button ("Lets see if this will move as \"Text Changer\" grows") { - X = Pos.Right(textChanger) + 2, + X = Pos.Right (textChanger) + 2, Y = Pos.Y (textChanger), }); @@ -105,7 +105,7 @@ namespace UICatalog { var computedFrame = new FrameView ("Computed Layout") { X = 0, Y = Pos.Bottom (removeButton) + 1, - Width = Dim.Percent(50), + Width = Dim.Percent (50), Height = 5 }; Win.Add (computedFrame); @@ -113,7 +113,7 @@ namespace UICatalog { // Demonstrates how changing the View.Frame property can move Views var moveBtn = new Button ("Move This \u263b Button _via Pos") { X = 0, - Y = Pos.Center() - 1, + Y = Pos.Center () - 1, Width = 30, ColorScheme = Colors.Error, }; @@ -124,7 +124,7 @@ namespace UICatalog { computedFrame.Add (moveBtn); // Demonstrates how changing the View.Frame property can SIZE Views (#583) - var sizeBtn = new Button ("Size This \u263a Button _via Pos") { + var sizeBtn = new Button ("Size This Button _via Pos") { X = 0, Y = Pos.Center () + 1, Width = 30, @@ -132,14 +132,14 @@ namespace UICatalog { }; sizeBtn.Clicked = () => { sizeBtn.Width = sizeBtn.Frame.Width + 5; - computedFrame.LayoutSubviews (); // BUGBUG: This call should not be needed. View.X is not causing relayout correctly + //computedFrame.LayoutSubviews (); // FIXED: This call should not be needed. View.X is not causing relayout correctly }; computedFrame.Add (sizeBtn); var absoluteFrame = new FrameView ("Absolute Layout") { - X = Pos.Right(computedFrame), + X = Pos.Right (computedFrame), Y = Pos.Bottom (removeButton) + 1, - Width = Dim.Fill(), + Width = Dim.Fill (), Height = 5 }; Win.Add (absoluteFrame); @@ -172,34 +172,6 @@ namespace UICatalog { X = 4, Y = Pos.Bottom (label) + 1, SelectedItem = 2, - SelectedItemChanged = (args) => { - switch (args.SelectedItem) { - case 0: - moveBtn.TextAlignment = TextAlignment.Left; - sizeBtn.TextAlignment = TextAlignment.Left; - moveBtnA.TextAlignment = TextAlignment.Left; - sizeBtnA.TextAlignment = TextAlignment.Left; - break; - case 1: - moveBtn.TextAlignment = TextAlignment.Right; - sizeBtn.TextAlignment = TextAlignment.Right; - moveBtnA.TextAlignment = TextAlignment.Right; - sizeBtnA.TextAlignment = TextAlignment.Right; - break; - case 2: - moveBtn.TextAlignment = TextAlignment.Centered; - sizeBtn.TextAlignment = TextAlignment.Centered; - moveBtnA.TextAlignment = TextAlignment.Centered; - sizeBtnA.TextAlignment = TextAlignment.Centered; - break; - case 3: - moveBtn.TextAlignment = TextAlignment.Justified; - sizeBtn.TextAlignment = TextAlignment.Justified; - moveBtnA.TextAlignment = TextAlignment.Justified; - sizeBtnA.TextAlignment = TextAlignment.Justified; - break; - } - } }; Win.Add (radioGroup); @@ -208,7 +180,9 @@ namespace UICatalog { { // Remove the '_' var i = txt.IndexOf ('_'); - var start = txt [0, i]; + ustring start = ""; + if (i > -1) + start = txt [0, i]; txt = start + txt [i + 1, txt.Length]; // Move over one or go to start @@ -224,9 +198,11 @@ namespace UICatalog { return txt; } - var moveHotKeyBtn = new Button ("Click to Change th_is Button's Hotkey") { + var mhkb = "Click to Change th_is Button's Hotkey"; + var moveHotKeyBtn = new Button (mhkb) { X = 2, Y = Pos.Bottom (radioGroup) + 1, + Width = mhkb.Length + 10, ColorScheme = Colors.TopLevel, }; moveHotKeyBtn.Clicked = () => { @@ -234,15 +210,54 @@ namespace UICatalog { }; Win.Add (moveHotKeyBtn); - var moveUnicodeHotKeyBtn = new Button (" ~  s  gui.cs   master ↑10 = Сохранить") { + var muhkb = " ~  s  gui.cs   master ↑10 = Сохранить"; + var moveUnicodeHotKeyBtn = new Button (muhkb) { X = Pos.Right (moveHotKeyBtn) + 6, Y = Pos.Bottom (radioGroup) + 1, + Width = muhkb.Length + 30, ColorScheme = Colors.TopLevel, }; moveUnicodeHotKeyBtn.Clicked = () => { moveUnicodeHotKeyBtn.Text = MoveHotkey (moveUnicodeHotKeyBtn.Text); }; Win.Add (moveUnicodeHotKeyBtn); + + radioGroup.SelectedItemChanged += (args) => { + switch (args.SelectedItem) { + case 0: + moveBtn.TextAlignment = TextAlignment.Left; + sizeBtn.TextAlignment = TextAlignment.Left; + moveBtnA.TextAlignment = TextAlignment.Left; + sizeBtnA.TextAlignment = TextAlignment.Left; + moveHotKeyBtn.TextAlignment = TextAlignment.Left; + moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Left; + break; + case 1: + moveBtn.TextAlignment = TextAlignment.Right; + sizeBtn.TextAlignment = TextAlignment.Right; + moveBtnA.TextAlignment = TextAlignment.Right; + sizeBtnA.TextAlignment = TextAlignment.Right; + moveHotKeyBtn.TextAlignment = TextAlignment.Right; + moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Right; + break; + case 2: + moveBtn.TextAlignment = TextAlignment.Centered; + sizeBtn.TextAlignment = TextAlignment.Centered; + moveBtnA.TextAlignment = TextAlignment.Centered; + sizeBtnA.TextAlignment = TextAlignment.Centered; + moveHotKeyBtn.TextAlignment = TextAlignment.Centered; + moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Centered; + break; + case 3: + moveBtn.TextAlignment = TextAlignment.Justified; + sizeBtn.TextAlignment = TextAlignment.Justified; + moveBtnA.TextAlignment = TextAlignment.Justified; + sizeBtnA.TextAlignment = TextAlignment.Justified; + moveHotKeyBtn.TextAlignment = TextAlignment.Justified; + moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Justified; + break; + } + }; } } -} +} \ No newline at end of file From e5b777845deb59c9e10dcbefd59164e491c614f2 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 8 Jun 2020 23:45:31 +0100 Subject: [PATCH 23/23] Removed System.Net.Sockets. --- Terminal.Gui/Views/Button.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 2d87e2dd1..b9e475338 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -6,7 +6,6 @@ // using System; -using System.Net.Sockets; using NStack; namespace Terminal.Gui {