// // Button.cs: Button control // // Authors: // Miguel de Icaza (miguel@gnome.org) // using System; using System.Text; namespace Terminal.Gui; /// /// Button is a that provides an item that invokes raises the event. /// /// /// /// Provides a button showing text that raises the event when clicked on with a mouse /// or when the user presses SPACE, ENTER, or the . The hot key is the first letter or /// digit following the first underscore ('_') /// in the button text. /// /// /// Use to change the hot key specifier from the default of ('_'). /// /// /// If no hot key specifier is found, the first uppercase letter encountered will be used as the hot key. /// /// /// When the button is configured as the default () and the user presses /// the ENTER key, if no other processes the key, the 's /// event will will be fired. /// /// public class Button : View { bool _isDefault; Rune _leftBracket; Rune _leftDefault; Rune _rightBracket; Rune _rightDefault; /// /// Initializes a new instance of using layout. /// /// /// The width of the is computed based on the /// text length. The height will always be 1. /// public Button () : this (string.Empty, false) { } /// /// Initializes a new instance of using layout. /// /// /// The width of the is computed based on the /// text length. The height will always be 1. /// /// The button's text /// /// If true, a special decoration is used, and the user pressing the enter key /// in a will implicitly activate this button. /// public Button (string text, bool is_default = false) : base (text) => SetInitialProperties (text, is_default); /// /// Initializes a new instance of using layout, based on the given /// text /// /// /// The width of the is computed based on the /// text length. The height will always be 1. /// /// X position where the button will be shown. /// Y position where the button will be shown. /// The button's text public Button (int x, int y, string text) : this (x, y, text, false) { } /// /// Initializes a new instance of using layout, based on the given /// text. /// /// /// The width of the is computed based on the /// text length. The height will always be 1. /// /// X position where the button will be shown. /// Y position where the button will be shown. /// The button's text /// /// If true, a special decoration is used, and the user pressing the enter key /// in a will implicitly activate this button. /// public Button (int x, int y, string text, bool is_default) : base (new Rect (x, y, text.GetRuneCount () + 4 + (is_default ? 2 : 0), 1), text) => SetInitialProperties (text, is_default); /// /// Gets or sets whether the is the default action to activate in a dialog. /// /// true if is default; otherwise, false. public bool IsDefault { get => _isDefault; set { _isDefault = value; UpdateTextFormatterText (); OnResizeNeeded (); } } /// /// /// public bool NoDecorations { get; set; } /// /// /// public bool NoPadding { get; set; } // TODO: v2 - Remove constructors with parameters /// /// Private helper to set the initial properties of the View that were provided via constructors. /// /// /// void SetInitialProperties (string text, bool is_default) { TextAlignment = TextAlignment.Centered; VerticalTextAlignment = VerticalTextAlignment.Middle; HotKeySpecifier = new Rune ('_'); _leftBracket = Glyphs.LeftBracket; _rightBracket = Glyphs.RightBracket; _leftDefault = Glyphs.LeftDefaultIndicator; _rightDefault = Glyphs.RightDefaultIndicator; CanFocus = true; AutoSize = true; _isDefault = is_default; Text = text ?? string.Empty; // Override default behavior of View // Command.Default sets focus AddCommand (Command.Accept, () => { OnClicked (); return true; }); KeyBindings.Add (Key.Space, Command.Default, Command.Accept); KeyBindings.Add (Key.Enter, Command.Default, Command.Accept); } /// protected override void UpdateTextFormatterText () { if (NoDecorations) { TextFormatter.Text = Text; } else if (IsDefault) { TextFormatter.Text = $"{_leftBracket}{_leftDefault} {Text} {_rightDefault}{_rightBracket}"; } else { if (NoPadding) { TextFormatter.Text = $"{_leftBracket}{Text}{_rightBracket}"; } else { TextFormatter.Text = $"{_leftBracket} {Text} {_rightBracket}"; } } } /// /// Virtual method to invoke the event. /// public virtual void OnClicked () => Clicked?.Invoke (this, EventArgs.Empty); /// /// The event fired when the user clicks the primary mouse button within the Bounds of this /// or if the user presses the action key while this view is focused. (TODO: IsDefault) /// /// /// Client code can hook up to this event, it is /// raised when the button is activated either with /// the mouse or the keyboard. /// public event EventHandler Clicked; /// public override bool MouseEvent (MouseEvent me) { if (me.Flags == MouseFlags.Button1Clicked) { if (CanFocus && Enabled) { if (!HasFocus) { SetFocus (); SetNeedsDisplay (); Draw (); } OnClicked (); } return true; } return false; } /// public override void PositionCursor () { if (HotKey.IsValid && Text != "") { for (var i = 0; i < TextFormatter.Text.GetRuneCount (); i++) { if (TextFormatter.Text [i] == Text [0]) { Move (i, 0); return; } } } base.PositionCursor (); } /// public override bool OnEnter (View view) { Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); return base.OnEnter (view); } }