From 428103036134fab57b26edef16da88b36972a460 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 5 Mar 2022 02:05:27 +0000 Subject: [PATCH] Changed Button Text property to use override instead of new. (#1622) * Changed Button Text property to use override instead of new. * Fixing HotKey behavior. --- Terminal.Gui/Core/View.cs | 4 +- Terminal.Gui/Views/Button.cs | 93 +++++++++++++++++++++++++++--------- UnitTests/ButtonTests.cs | 68 ++++++++++++++++++++++---- 3 files changed, 130 insertions(+), 35 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 501dceed7..650e333a5 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -186,12 +186,12 @@ namespace Terminal.Gui { /// /// Gets or sets the HotKey defined for this view. A user pressing HotKey on the keyboard while this view has focus will cause the Clicked event to fire. /// - public Key HotKey { get => textFormatter.HotKey; set => textFormatter.HotKey = value; } + public virtual Key HotKey { get => textFormatter.HotKey; set => textFormatter.HotKey = value; } /// /// Gets or sets the specifier character for the hotkey (e.g. '_'). Set to '\xffff' to disable hotkey support for this View instance. The default is '\xffff'. /// - public Rune HotKeySpecifier { get => textFormatter.HotKeySpecifier; set => textFormatter.HotKeySpecifier = value; } + public virtual Rune HotKeySpecifier { get => textFormatter.HotKeySpecifier; set => textFormatter.HotKeySpecifier = value; } /// /// This is the global setting that can be used as a global shortcut to invoke an action if provided. diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index ae50617a8..e48f4bdef 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -33,6 +33,7 @@ namespace Terminal.Gui { public class Button : View { ustring text; bool is_default; + TextFormatter textFormatter = new TextFormatter (); /// /// Initializes a new instance of using layout. @@ -96,6 +97,8 @@ namespace Terminal.Gui { Rune _rightBracket; Rune _leftDefault; Rune _rightDefault; + private Key hotKey = Key.Null; + private Rune hotKeySpecifier; void Initialize (ustring text, bool is_default) { @@ -113,8 +116,6 @@ namespace Terminal.Gui { this.text = text ?? string.Empty; Update (); - HotKeyChanged += Button_HotKeyChanged; - // Things this view knows how to do AddCommand (Command.Accept, () => AcceptKey ()); @@ -126,27 +127,17 @@ namespace Terminal.Gui { } } - private void Button_HotKeyChanged (Key obj) - { - if (HotKey != Key.Null) { - if (ContainsKeyBinding (obj)) { - ReplaceKeyBinding (Key.Space | obj, Key.Space | HotKey); - } else { - AddKeyBinding (Key.Space | HotKey, Command.Accept); - } - } - } - - /// - /// The text displayed by this . - /// - public new ustring Text { + /// > + public override ustring Text { get { return text; } - set { text = value; + TextFormatter.FindHotKey (text, HotKeySpecifier, true, out _, out Key hk); + if (hotKey != hk) { + HotKey = hk; + } Update (); } } @@ -163,14 +154,51 @@ namespace Terminal.Gui { } } + /// + public override Key HotKey { + get => hotKey; + set { + if (hotKey != value) { + var v = value == Key.Unknown ? Key.Null : value; + if (ContainsKeyBinding (Key.Space | hotKey)) { + if (v == Key.Null) { + ClearKeybinding (Key.Space | hotKey); + } else { + ReplaceKeyBinding (Key.Space | hotKey, Key.Space | v); + } + } else if (v != Key.Null) { + AddKeyBinding (Key.Space | v, Command.Accept); + } + hotKey = v; + } + } + } + + /// + public override Rune HotKeySpecifier { + get => hotKeySpecifier; + set { + hotKeySpecifier = textFormatter.HotKeySpecifier = value; + } + } + + /// + public override bool AutoSize { + get => base.AutoSize; + set { + base.AutoSize = value; + Update (); + } + } + internal void Update () { if (IsDefault) - base.Text = ustring.Make (_leftBracket) + ustring.Make (_leftDefault) + " " + text + " " + ustring.Make (_rightDefault) + ustring.Make (_rightBracket); + textFormatter.Text = ustring.Make (_leftBracket) + ustring.Make (_leftDefault) + " " + text + " " + ustring.Make (_rightDefault) + ustring.Make (_rightBracket); else - base.Text = ustring.Make (_leftBracket) + " " + text + " " + ustring.Make (_rightBracket); + textFormatter.Text = ustring.Make (_leftBracket) + " " + text + " " + ustring.Make (_rightBracket); - int w = base.Text.RuneCount - (base.Text.Contains (HotKeySpecifier) ? 1 : 0); + int w = textFormatter.Text.RuneCount - (textFormatter.Text.Contains (HotKeySpecifier) ? 1 : 0); GetCurrentWidth (out int cWidth); var canSetWidth = SetWidth (w, out int rWidth); if (canSetWidth && (cWidth < rWidth || AutoSize)) { @@ -194,6 +222,25 @@ namespace Terminal.Gui { SetNeedsDisplay (); } + /// + public override void Redraw (Rect bounds) + { + if (ColorScheme != null) { + Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal); + } + + if (Border != null) { + Border.DrawContent (this); + } + + if (!ustring.IsNullOrEmpty (textFormatter.Text)) { + Clear (); + textFormatter.NeedsFormat = true; + textFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? ColorScheme.Focus : GetNormalColor (), + HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled); + } + } + /// public override bool ProcessHotKey (KeyEvent kb) { @@ -294,8 +341,8 @@ namespace Terminal.Gui { public override void PositionCursor () { if (HotKey == Key.Unknown && text != "") { - for (int i = 0; i < base.Text.RuneCount; i++) { - if (base.Text [i] == text [0]) { + for (int i = 0; i < textFormatter.Text.RuneCount; i++) { + if (textFormatter.Text [i] == text [0]) { Move (i, 0); return; } diff --git a/UnitTests/ButtonTests.cs b/UnitTests/ButtonTests.cs index 74936741a..153e7ef33 100644 --- a/UnitTests/ButtonTests.cs +++ b/UnitTests/ButtonTests.cs @@ -7,12 +7,14 @@ using Xunit; namespace Terminal.Gui.Views { public class ButtonTests { - [Fact] + [Fact, AutoInitShutdown] public void Constructors_Defaults () { var btn = new Button (); Assert.Equal (string.Empty, btn.Text); - Assert.Equal ("[ ]", btn.GetType ().BaseType.GetProperty ("Text").GetValue (btn).ToString ()); + Application.Top.Add (btn); + btn.Redraw (btn.Bounds); + Assert.Equal ("[ ]", GetContents (btn.Bounds.Width)); Assert.False (btn.IsDefault); Assert.Equal (TextAlignment.Centered, btn.TextAlignment); Assert.Equal ('_', btn.HotKeySpecifier); @@ -22,23 +24,36 @@ namespace Terminal.Gui.Views { btn = new Button ("Test", true); Assert.Equal ("Test", btn.Text); - Assert.Equal ("[< Test >]", btn.GetType ().BaseType.GetProperty ("Text").GetValue (btn).ToString ()); + Application.Top.Add (btn); + btn.Redraw (btn.Bounds); + Assert.Equal ("[◦ Test ◦]", GetContents (btn.Bounds.Width)); Assert.True (btn.IsDefault); Assert.Equal (TextAlignment.Centered, btn.TextAlignment); Assert.Equal ('_', btn.HotKeySpecifier); Assert.True (btn.CanFocus); Assert.Equal (new Rect (0, 0, 10, 1), btn.Frame); - Assert.Equal (Key.Null, btn.HotKey); + Assert.Equal (Key.T, btn.HotKey); btn = new Button (3, 4, "Test", true); Assert.Equal ("Test", btn.Text); - Assert.Equal ("[< Test >]", btn.GetType ().BaseType.GetProperty ("Text").GetValue (btn).ToString ()); + Application.Top.Add (btn); + btn.Redraw (btn.Bounds); + Assert.Equal ("[◦ Test ◦]", GetContents (btn.Bounds.Width)); Assert.True (btn.IsDefault); Assert.Equal (TextAlignment.Centered, btn.TextAlignment); Assert.Equal ('_', btn.HotKeySpecifier); Assert.True (btn.CanFocus); Assert.Equal (new Rect (3, 4, 10, 1), btn.Frame); - Assert.Equal (Key.Null, btn.HotKey); + Assert.Equal (Key.T, btn.HotKey); + } + + private string GetContents (int width) + { + string output = ""; + for (int i = 0; i < width; i++) { + output += (char)Application.Driver.Contents [0, i, 0]; + } + return output; } [Fact] @@ -75,9 +90,15 @@ namespace Terminal.Gui.Views { clicked = false; Assert.True (btn.ProcessKey (new KeyEvent ((Key)'t', new KeyModifiers ()))); Assert.True (clicked); + clicked = false; + Assert.True (btn.ProcessKey (new KeyEvent (Key.Space | btn.HotKey, new KeyModifiers ()))); + Assert.True (clicked); + btn.Text = "Te_st"; + clicked = false; + Assert.True (btn.ProcessKey (new KeyEvent (Key.Space | btn.HotKey, new KeyModifiers ()))); + Assert.True (clicked); } - [Fact] [AutoInitShutdown] public void ChangeHotKey () @@ -114,18 +135,18 @@ namespace Terminal.Gui.Views { btn.Clicked += () => pressed++; // The Button class supports the Accept command - Assert.Contains(Command.Accept,btn.GetSupportedCommands ()); + Assert.Contains (Command.Accept, btn.GetSupportedCommands ()); Application.Top.Add (btn); Application.Begin (Application.Top); // default keybinding is Enter which results in keypress - Application.Driver.SendKeys ('\n',ConsoleKey.Enter,false,false,false); + Application.Driver.SendKeys ('\n', ConsoleKey.Enter, false, false, false); Assert.Equal (1, pressed); // remove the default keybinding (Enter) btn.ClearKeybinding (Command.Accept); - + // After clearing the default keystroke the Enter button no longer does anything for the Button Application.Driver.SendKeys ('\n', ConsoleKey.Enter, false, false, false); Assert.Equal (1, pressed); @@ -137,5 +158,32 @@ namespace Terminal.Gui.Views { Application.Driver.SendKeys ('b', ConsoleKey.B, false, false, false); Assert.Equal (2, pressed); } + + [Fact] + public void TestAssignTextToButton () + { + View b = new Button (); + b.Text = "heya"; + Assert.Equal ("heya", b.Text); + + // with cast + Assert.Equal ("heya", ((Button)b).Text); + } + + [Fact] + public void Setting_Empty_Text_Sets_HoKey_To_KeyNull () + { + var btn = new Button ("Test"); + Assert.Equal ("Test", btn.Text); + Assert.Equal (Key.T, btn.HotKey); + + btn.Text = string.Empty; + Assert.Equal ("", btn.Text); + Assert.Equal (Key.Null, btn.HotKey); + + btn.Text = "Te_st"; + Assert.Equal ("Te_st", btn.Text); + Assert.Equal (Key.S, btn.HotKey); + } } }