#nullable disable namespace Terminal.Gui.Views; /// /// A button View that can be pressed with the mouse or keyboard. /// /// /// /// The Button will raise the event when the user presses , /// Enter, or Space /// or clicks on the button with the mouse. /// /// Use to change the hot key specifier from the default of ('_'). /// /// Button can act as the default handler for all peer-Views. See /// . /// /// /// Set to to have the /// event /// invoked repeatedly while the button is pressed. /// /// public class Button : View, IDesignable { private static ShadowStyle _defaultShadow = ShadowStyle.Opaque; // Resources/config.json overrides private static MouseState _defaultHighlightStates = MouseState.In | MouseState.Pressed | MouseState.PressedOutside; // Resources/config.json overrides private readonly Rune _leftBracket; private readonly Rune _leftDefault; private readonly Rune _rightBracket; private readonly Rune _rightDefault; private bool _isDefault; /// /// Gets or sets whether s are shown with a shadow effect by default. /// [ConfigurationProperty (Scope = typeof (ThemeScope))] public static ShadowStyle DefaultShadow { get => _defaultShadow; set => _defaultShadow = value; } /// /// Gets or sets the default Highlight Style. /// [ConfigurationProperty (Scope = typeof (ThemeScope))] public static MouseState DefaultHighlightStates { get => _defaultHighlightStates; set => _defaultHighlightStates = value; } /// Initializes a new instance of . public Button () { base.TextAlignment = Alignment.Center; base.VerticalTextAlignment = Alignment.Center; _leftBracket = Glyphs.LeftBracket; _rightBracket = Glyphs.RightBracket; _leftDefault = Glyphs.LeftDefaultIndicator; _rightDefault = Glyphs.RightDefaultIndicator; Height = Dim.Auto (DimAutoStyle.Text); Width = Dim.Auto (DimAutoStyle.Text); CanFocus = true; AddCommand (Command.HotKey, HandleHotKeyCommand); KeyBindings.Remove (Key.Space); KeyBindings.Add (Key.Space, Command.HotKey); KeyBindings.Remove (Key.Enter); KeyBindings.Add (Key.Enter, Command.HotKey); // Replace default Activate binding with HotKey for mouse clicks MouseBindings.Clear (); MouseBindings.Add (MouseFlags.Button1Clicked, Command.HotKey); MouseBindings.Add (MouseFlags.Button2Clicked, Command.HotKey); MouseBindings.Add (MouseFlags.Button3Clicked, Command.HotKey); MouseBindings.Add (MouseFlags.Button4Clicked, Command.HotKey); MouseBindings.Add (MouseFlags.Button1Clicked | MouseFlags.ButtonCtrl, Command.HotKey); TitleChanged += Button_TitleChanged; base.ShadowStyle = DefaultShadow; HighlightStates = DefaultHighlightStates; if (MouseHeldDown != null) { MouseHeldDown.MouseIsHeldDownTick += (_,_) => RaiseAccepting (null); } } private bool? HandleHotKeyCommand (ICommandContext commandContext) { bool cachedIsDefault = IsDefault; // Supports "Swap Default" in Buttons scenario where IsDefault changes if (RaiseActivating (commandContext) is true) { return true; } bool? handled = RaiseAccepting (commandContext); if (handled == true) { return true; } SetFocus (); // TODO: If `IsDefault` were a property on `View` *any* View could work this way. That's theoretical as // TODO: no use-case has been identified for any View other than Button to act like this. // If Accept was not handled... if (cachedIsDefault && SuperView is { }) { return SuperView.InvokeCommand (Command.Accept); } return false; } private void Button_TitleChanged (object sender, EventArgs e) { base.Text = e.Value; TextFormatter.HotKeySpecifier = HotKeySpecifier; } /// public override string Text { get => Title; set => base.Text = Title = value; } /// public override Rune HotKeySpecifier { get => base.HotKeySpecifier; set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value; } /// /// Gets or sets whether the will act as the default handler for /// commands on the . /// /// /// /// If : /// /// /// - The Button will display an indicator that it is the default Button. /// /// /// - When clicked, if the Accepting event is not handled, will be /// invoked on the SuperView. /// /// /// - If a peer-View receives and does not handle it, the command will be passed to /// the /// first Button in the SuperView that has set to . See /// for more information. /// /// public bool IsDefault { get => _isDefault; set { if (_isDefault == value) { return; } _isDefault = value; UpdateTextFormatterText (); SetNeedsLayout (); } } /// /// Gets or sets whether the Button will show decorations or not. If the glyphs that normally /// bracket the Button Title and the indicator will not be shown. /// public bool NoDecorations { get; set; } /// /// Gets or sets whether the Button will include padding on each side of the Title. /// public bool NoPadding { get; set; } /// public override Point? PositionCursor () { if (HotKey.IsValid && Text != "") { for (var i = 0; i < TextFormatter.Text.GetRuneCount (); i++) { if (TextFormatter.Text [i] == Text [0]) { Move (i, 0); return null; // Don't show the cursor } } } return base.PositionCursor (); } /// protected override void UpdateTextFormatterText () { base.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}"; } } } /// public bool EnableForDesign () { Title = "_Button"; return true; } }