From 2a3808a7c058aa4d2bde5ad42e6a584a6bda8de3 Mon Sep 17 00:00:00 2001 From: Tig Date: Wed, 12 Jun 2024 13:31:40 -0700 Subject: [PATCH] Enabled CanFocus for COmmandView --- Terminal.Gui/Views/Shortcut.cs | 266 ++++++++++++++++--------------- UICatalog/Scenarios/Shortcuts.cs | 46 ++++-- 2 files changed, 170 insertions(+), 142 deletions(-) diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index c850a600a..296932398 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -7,7 +7,8 @@ namespace Terminal.Gui; // TODO: I tried `BarItem` but that's not great either as it implies it can only be used in `Bar`s. /// -/// Displays a command, help text, and a key binding. When the key is pressed, the command will be invoked. Useful for displaying a command in such as a +/// Displays a command, help text, and a key binding. When the key is pressed, the command will be invoked. Useful for +/// displaying a command in such as a /// menu, toolbar, or status bar. /// /// @@ -17,14 +18,17 @@ namespace Terminal.Gui; /// event to be fired /// /// -/// If is , the command +/// If is , the +/// command /// be invoked regardless of what View has focus, enabling an application-wide keyboard shortcut. /// /// -/// A Shortcut displays the command text on the left side, the help text in the middle, and the key binding on the right side. +/// A Shortcut displays the command text on the left side, the help text in the middle, and the key binding on the +/// right side. /// /// -/// The command text can be set by setting the 's Text property or by setting . +/// The command text can be set by setting the 's Text property or by setting +/// . /// /// /// The help text can be set by setting the property or by setting . @@ -48,28 +52,23 @@ public class Shortcut : View Width = GetWidthDimAuto (); Height = Dim.Auto (DimAutoStyle.Content, 1); - AddCommand (Gui.Command.HotKey, OnAccept); - AddCommand (Gui.Command.Accept, OnAccept); - KeyBindings.Add (KeyCode.Space, Gui.Command.Accept); - KeyBindings.Add (KeyCode.Enter, Gui.Command.Accept); + AddCommand (Command.HotKey, OnAccept); + AddCommand (Command.Accept, OnAccept); + KeyBindings.Add (KeyCode.Space, Command.Accept); + KeyBindings.Add (KeyCode.Enter, Command.Accept); TitleChanged += Shortcut_TitleChanged; // This needs to be set before CommandView is set - CommandView = new View (); + CommandView = new (); HelpView.Id = "_helpView"; HelpView.CanFocus = false; SetHelpViewDefaultLayout (); Add (HelpView); - - // HelpView.TextAlignment = Alignment.End; HelpView.MouseClick += Shortcut_MouseClick; KeyView.Id = "_keyView"; - - // Only the Shortcut should be able to have focus, not any subviews KeyView.CanFocus = false; - SetKeyViewDefaultLayout (); Add (KeyView); @@ -85,14 +84,18 @@ public class Shortcut : View void OnInitialized (object sender, EventArgs e) { + SuperViewRendersLineCanvas = true; + Border.ShowTitle = false; + ShowHide (); // Force Width to DimAuto to calculate natural width and then set it back Dim savedDim = Width; Width = GetWidthDimAuto (); - _naturalWidth = Frame.Width; + _minimumDimAutoWidth = Frame.Width; Width = savedDim; + // Set KeyView's colors to show "hot" if (ColorScheme != null) { var cs = new ColorScheme (ColorScheme) @@ -105,11 +108,11 @@ public class Shortcut : View } } + // Helper to set Width consistently Dim GetWidthDimAuto () { return Dim.Auto (DimAutoStyle.Content, maximumContentDim: Dim.Func (() => PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width))); } - } // When one of the subviews is "empty" we don't want to show it. So we @@ -118,27 +121,32 @@ public class Shortcut : View private void ShowHide () { RemoveAll (); + if (!string.IsNullOrEmpty (CommandView.Text)) { Add (CommandView); } + if (!string.IsNullOrEmpty (HelpView.Text)) { Add (HelpView); } + if (Key != Key.Empty) { Add (KeyView); } } - private int? _naturalWidth; + // This is used to calculate the minimum width of the Shortcut when the width is NOT Dim.Auto + private int? _minimumDimAutoWidth; + // When layout starts, we need to adjust the layout of the HelpView and KeyView private void OnLayoutStarted (object sender, LayoutEventArgs e) { if (Width is DimAuto widthAuto) { - _naturalWidth = Frame.Width; + _minimumDimAutoWidth = Frame.Width; } else { @@ -150,9 +158,9 @@ public class Shortcut : View int currentWidth = Frame.Width; // If our width is smaller than the natural then reduce width of HelpView. - if (currentWidth < _naturalWidth) + if (currentWidth < _minimumDimAutoWidth) { - int delta = _naturalWidth.Value - currentWidth; + int delta = _minimumDimAutoWidth.Value - currentWidth; int maxHelpWidth = int.Max (0, HelpView.Text.GetColumns () + 2 - delta); switch (maxHelpWidth) @@ -235,6 +243,7 @@ public class Shortcut : View { // When the Shortcut is clicked, we want to invoke the Command and Set focus var view = sender as View; + if (view != CommandView) { CommandView.InvokeCommand (Command.Accept); @@ -263,34 +272,6 @@ public class Shortcut : View e.Handled = true; } - /// - public override ColorScheme ColorScheme - { - get - { - if (base.ColorScheme == null) - { - return SuperView?.ColorScheme ?? base.ColorScheme; - } - - return base.ColorScheme; - } - set - { - base.ColorScheme = value; - - if (ColorScheme != null) - { - var cs = new ColorScheme (ColorScheme) - { - Normal = ColorScheme.HotNormal, - HotNormal = ColorScheme.Normal - }; - KeyView.ColorScheme = cs; - } - } - } - #region Command private View _commandView = new (); @@ -386,7 +367,7 @@ public class Shortcut : View _commandView.TextChanged += CommandViewTextChanged; SetHelpViewDefaultLayout (); - SetKeyViewDefaultLayout(); + SetKeyViewDefaultLayout (); ShowHide (); UpdateKeyBinding (); @@ -440,10 +421,9 @@ public class Shortcut : View HelpView.X = Pos.Align (Alignment.End, AlignmentModes.IgnoreFirstOrLast); HelpView.Y = 0; //Pos.Center (), HelpView.Width = Dim.Auto (DimAutoStyle.Text); - HelpView.Height = Dim.Height(CommandView); + HelpView.Height = Dim.Height (CommandView); HelpView.Visible = true; HelpView.VerticalTextAlignment = Alignment.Center; - } /// @@ -531,8 +511,8 @@ public class Shortcut : View public View KeyView { get; } = new (); private int _minimumKeyViewSize; + /// - /// /// public int MinimumKeyViewSize { @@ -543,9 +523,10 @@ public class Shortcut : View { //return; } + _minimumKeyViewSize = value; - SetKeyViewDefaultLayout(); - CommandView.SetNeedsLayout(); + SetKeyViewDefaultLayout (); + CommandView.SetNeedsLayout (); HelpView.SetNeedsLayout (); KeyView.SetNeedsLayout (); SetSubViewNeedsDisplay (); @@ -558,34 +539,31 @@ public class Shortcut : View { KeyView.Margin.Thickness = new (1, 0, 1, 0); KeyView.X = Pos.Align (Alignment.End, AlignmentModes.IgnoreFirstOrLast); + //KeyView.Y = Pos.Center (); - KeyView.Width = Dim.Auto (DimAutoStyle.Text, minimumContentDim: Dim.Func(GetMinimumKeyViewSize)); - KeyView.Height = Dim.Height(CommandView); + KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize)); + KeyView.Height = Dim.Height (CommandView); KeyView.Visible = true; + // Right align the text in the keyview KeyView.TextAlignment = Alignment.End; KeyView.VerticalTextAlignment = Alignment.Center; + KeyView.KeyBindings.Clear (); } private void UpdateKeyBinding () { - if (KeyBindingScope == KeyBindingScope.Application) - { - // return; - } - if (Key != null) { - // CommandView holds our command/keybinding - // Add a key binding for this command to this Shortcut - - CommandView.KeyBindings.Remove (Key); - CommandView.KeyBindings.Add (Key, KeyBindingScope, Command.Accept); + KeyBindings.Remove (Key); + KeyBindings.Add (Key, KeyBindingScope, Command.Accept); } } #endregion Key + #region Accept Handling + /// /// The event fired when the command is received. This /// occurs if the user clicks on the Shortcut or presses . @@ -599,39 +577,26 @@ public class Shortcut : View /// protected new bool? OnAccept () { - // TODO: This is not completely thought through. + var handled = true; - if (Key == null || Key == Key.Empty) + switch (KeyBindingScope) { - return false; + case KeyBindingScope.Application: + break; + + case KeyBindingScope.Focused: + // TODO: Figure this out + handled = false; + + break; + case KeyBindingScope.HotKey: + handled = _commandView.InvokeCommand (Command.HotKey) == true; + handled = false; + + break; } - var handled = false; - var keyCopy = new Key (Key); - - //switch (KeyBindingScope) - //{ - // case KeyBindingScope.Application: - // // Simulate a key down to invoke the Application scoped key binding - // handled = Application.OnKeyDown (keyCopy); - - // break; - // case KeyBindingScope.Focused: - // handled = InvokeCommand (Command.Value) == true; - // handled = false; - - // break; - // case KeyBindingScope.HotKey: - // if (Command.HasValue) - // { - // //handled = _commandView.InvokeCommand (Gui.Command.HotKey) == true; - // //handled = false; - // } - - // break; - //} - - //if (handled == false) + if (handled == false) { var args = new HandledEventArgs (); Accept?.Invoke (this, args); @@ -641,25 +606,56 @@ public class Shortcut : View return true; } + #endregion Accept Handling + + #region Focus + + /// + public override ColorScheme ColorScheme + { + get + { + if (base.ColorScheme == null) + { + return SuperView?.ColorScheme ?? base.ColorScheme; + } + + return base.ColorScheme; + } + set + { + base.ColorScheme = value; + + if (CommandView.CanFocus) + { + CommandView.ColorScheme = SuperView?.ColorScheme ?? ColorScheme; + } + + if (ColorScheme != null) + { + var cs = new ColorScheme (ColorScheme) + { + Normal = ColorScheme.HotNormal, + HotNormal = ColorScheme.Normal + }; + KeyView.ColorScheme = cs; + } + + Border.ColorScheme = SuperView?.ColorScheme ?? ColorScheme; + } + } + /// public override bool OnEnter (View view) { - // TODO: This is a hack. Need to refine this. - var cs = new ColorScheme (ColorScheme) + if (SuperView is { }) { - Normal = ColorScheme.Focus, - HotNormal = ColorScheme.HotFocus - }; - - // _container.ColorScheme = cs; - - cs = new (ColorScheme) - { - Normal = ColorScheme.HotFocus, - HotNormal = ColorScheme.Focus - }; - - //KeyView.ColorScheme = cs; + ColorScheme = new (SuperView?.ColorScheme) + { + Normal = SuperView.ColorScheme.Focus, + HotNormal = SuperView.ColorScheme.HotFocus + }; + } return base.OnEnter (view); } @@ -667,24 +663,42 @@ public class Shortcut : View /// public override bool OnLeave (View view) { - // TODO: This is a hack. Need to refine this. - var cs = new ColorScheme (ColorScheme) + ColorScheme = null; + + return base.OnLeave (view); + if (SuperView is { }) { - Normal = ColorScheme.Normal, - HotNormal = ColorScheme.HotNormal - }; - - // _container.ColorScheme = cs; - - cs = new (ColorScheme) - { - Normal = ColorScheme.HotNormal, - HotNormal = ColorScheme.Normal - }; - - //KeyView.ColorScheme = cs; + ColorScheme = new (SuperView?.ColorScheme) + { + Normal = SuperView.ColorScheme.Normal, + HotNormal = SuperView.ColorScheme.HotNormal + }; + } return base.OnLeave (view); } -} + #endregion Focus + + /// + protected override void Dispose (bool disposing) + { + if (disposing) + { + if (CommandView?.IsAdded == false) + { + CommandView.Dispose (); + } + if (HelpView?.IsAdded == false) + { + HelpView.Dispose (); + } + if (KeyView?.IsAdded == false) + { + KeyView.Dispose (); + } + } + base.Dispose (disposing); + + } +} diff --git a/UICatalog/Scenarios/Shortcuts.cs b/UICatalog/Scenarios/Shortcuts.cs index 0a26298bb..9bcc7426f 100644 --- a/UICatalog/Scenarios/Shortcuts.cs +++ b/UICatalog/Scenarios/Shortcuts.cs @@ -50,13 +50,13 @@ public class Shortcuts : Scenario KeyBindingScope = KeyBindingScope.Application, BorderStyle = LineStyle.Dotted }; - shortcut1.Border.Thickness = new (1, 0, 1, 0); + shortcut1.Border.Thickness = new (1, 1, 1, 1); Application.Top.Add (shortcut1); var shortcut2 = new Shortcut { X = 20, - Y = Pos.Bottom (shortcut1), + Y = Pos.Bottom (shortcut1) - 1, Width = Dim.Width (shortcut1), Key = Key.F2, Text = "Width is ^", @@ -78,7 +78,7 @@ public class Shortcuts : Scenario shortcut2.Accept += (o, args) => { // Cycle to next item. If at end, set 0 - if (((RadioGroup)shortcut2.CommandView).SelectedItem < ((RadioGroup)shortcut2.CommandView).RadioLabels.Length-1) + if (((RadioGroup)shortcut2.CommandView).SelectedItem < ((RadioGroup)shortcut2.CommandView).RadioLabels.Length - 1) { ((RadioGroup)shortcut2.CommandView).SelectedItem++; } @@ -87,7 +87,7 @@ public class Shortcuts : Scenario ((RadioGroup)shortcut2.CommandView).SelectedItem = 0; } }; - shortcut2.Border.Thickness = new (1, 0, 1, 0); + shortcut2.Border.Thickness = new (1, 1, 1, 1); Application.Top.Add (shortcut2); var shortcut3 = new Shortcut @@ -101,7 +101,8 @@ public class Shortcuts : Scenario KeyBindingScope = KeyBindingScope.HotKey, BorderStyle = LineStyle.Dotted }; - shortcut3.Border.Thickness = new (1, 0, 1, 0); + shortcut3.CommandView.CanFocus = true; + shortcut3.Border.Thickness = new (1, 1, 1, 0); ((CheckBox)shortcut3.CommandView).Toggled += (s, e) => { @@ -135,23 +136,24 @@ public class Shortcuts : Scenario Width = Dim.Width (shortcut3), CommandView = new Button { - Title = "_Button" + Title = "B_utton", }, HelpText = "Width is Fill", Key = Key.K, KeyBindingScope = KeyBindingScope.HotKey, BorderStyle = LineStyle.Dotted }; - + Button button = (Button)shortcut4.CommandView; shortcut4.CommandView.Accept += Button_Clicked; - shortcut4.Border.Thickness = new (1, 0, 1, 0); + shortcut4.CommandView.CanFocus = true; + shortcut4.Border.Thickness = new (1, 0, 1,0); Application.Top.Add (shortcut4); var shortcut5 = new Shortcut { X = 20, - Y = Pos.Bottom (shortcut4), + Y = Pos.Bottom (shortcut4) , Width = Dim.Width (shortcut4), Title = "Fi_ve", @@ -160,14 +162,14 @@ public class Shortcuts : Scenario KeyBindingScope = KeyBindingScope.HotKey, BorderStyle = LineStyle.Dotted }; - shortcut5.Border.Thickness = new (1, 0, 1, 0); + shortcut5.Border.Thickness = new (1, 0, 1, 1); Application.Top.Add (shortcut5); var shortcutSlider = new Shortcut { X = 20, - Y = Pos.Bottom (shortcut5), + Y = Pos.Bottom (shortcut5) - 1, Key = Key.F5, HelpText = "Width is Fill", Width = Dim.Width (shortcut5), @@ -181,9 +183,9 @@ public class Shortcuts : Scenario } }; - ((Slider)shortcutSlider.CommandView).Options = new() { new () { Legend = "A" }, new () { Legend = "B" }, new () { Legend = "C" } }; + ((Slider)shortcutSlider.CommandView).Options = new () { new () { Legend = "A" }, new () { Legend = "B" }, new () { Legend = "C" } }; ((Slider)shortcutSlider.CommandView).SetOption (0); - shortcutSlider.Border.Thickness = new (1, 0, 1, 0); + shortcutSlider.Border.Thickness = new (1, 1, 1, 1); ((Slider)shortcutSlider.CommandView).OptionsChanged += (o, args) => { @@ -193,20 +195,32 @@ public class Shortcuts : Scenario Application.Top.Add (shortcutSlider); + var shortcut6 = new Shortcut + { + X = 20, + Y = Pos.Bottom (shortcutSlider) - 1, + Width = Dim.Width (shortcutSlider), + + Title = "_No Key", + HelpText = "Keyless", + BorderStyle = LineStyle.Dotted + }; + shortcut6.Border.Thickness = new (1, 1, 1, 1); + + Application.Top.Add (shortcut6); + foreach (View sh in Application.Top.Subviews.Where (v => v is Shortcut)!) { if (sh is Shortcut shortcut) { shortcut.Accept += (o, args) => { + var x = button; eventSource.Add ($"Accept: {shortcut!.CommandView.Text}"); eventLog.MoveDown (); }; } } - - //shortcut1.SetFocus (); - //View.Diagnostics = ViewDiagnosticFlags.Ruler; } private void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }