namespace Terminal.Gui.Views; /// /// A status bar is a that snaps to the bottom of a displaying set of /// s. The should be context sensitive. This means, if the main menu /// and an open text editor are visible, the items probably shown will be ~F1~ Help ~F2~ Save ~F3~ Load. While a dialog /// to ask a file to load is executed, the remaining commands will probably be ~F1~ Help. So for each context must be a /// new instance of a status bar. /// public class StatusBar : Bar, IDesignable { /// public StatusBar () : this ([]) { } /// public StatusBar (IEnumerable shortcuts) : base (shortcuts) { TabStop = TabBehavior.NoStop; Orientation = Orientation.Horizontal; Y = Pos.AnchorEnd (); Width = Dim.Fill (); Height = Dim.Auto (DimAutoStyle.Content, 1); if (Border is { }) { Border.LineStyle = DefaultSeparatorLineStyle; } SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Menu); ConfigurationManager.Applied += OnConfigurationManagerApplied; SuperViewChanged += OnSuperViewChanged; } private void OnSuperViewChanged (object? sender, SuperViewChangedEventArgs e) { if (SuperView is null) { // BUGBUG: This is a hack for avoiding a race condition in ConfigurationManager.Apply // BUGBUG: For some reason in some unit tests, when Top is disposed, MenuBar.Dispose does not get called. // BUGBUG: Yet, the MenuBar does get Removed from Top (and it's SuperView set to null). // BUGBUG: Related: https://github.com/gui-cs/Terminal.Gui/issues/4021 ConfigurationManager.Applied -= OnConfigurationManagerApplied; } } private void OnConfigurationManagerApplied (object? sender, ConfigurationManagerEventArgs e) { if (Border is { }) { Border.LineStyle = DefaultSeparatorLineStyle; } } /// /// Gets or sets the default Line Style for the separators between the shortcuts of the StatusBar. /// [ConfigurationProperty (Scope = typeof (ThemeScope))] public static LineStyle DefaultSeparatorLineStyle { get; set; } = LineStyle.Single; /// protected override void OnSubViewLayout (LayoutEventArgs args) { for (int index = 0; index < SubViews.Count; index++) { View barItem = SubViews.ElementAt (index); barItem.BorderStyle = BorderStyle; if (barItem.Border is { }) { barItem.Border!.Thickness = index == SubViews.Count - 1 ? new Thickness (0, 0, 0, 0) : new Thickness (0, 0, 1, 0); } if (barItem is Shortcut shortcut) { shortcut.Orientation = Orientation.Horizontal; } } base.OnSubViewLayout (args); } /// protected override void OnSubViewAdded (View subView) { subView.CanFocus = false; if (subView is Shortcut shortcut) { // TODO: not happy about using AlignmentModes for this. Too implied. // TODO: instead, add a property (a style enum?) to Shortcut to control this shortcut.AlignmentModes = AlignmentModes.EndToStart; } } /// bool IDesignable.EnableForDesign () { var shortcut = new Shortcut { Text = "Quit", Title = "Q_uit", Key = Key.Z.WithCtrl, }; Add (shortcut); shortcut = new Shortcut { Text = "Help Text", Title = "Help", Key = Key.F1, }; Add (shortcut); shortcut = new Shortcut { Title = "_Show/Hide", Key = Key.F10, CommandView = new CheckBox { CanFocus = false, Text = "_Show/Hide" }, }; Add (shortcut); var button1 = new Button { Text = "I'll Hide", // Visible = false }; button1.Accepting += OnButtonClicked; Add (button1); #pragma warning disable TGUI001 shortcut.Accepting += (_, e) => { button1.Visible = !button1.Visible; button1.Enabled = button1.Visible; e.Handled = false; }; #pragma warning restore TGUI001 Add (new Label { HotKeySpecifier = new Rune ('_'), Text = "Fo_cusLabel", CanFocus = true }); var button2 = new Button { Text = "Or me!", }; button2.Accepting += (s, e) => App?.RequestStop (); Add (button2); return true; void OnButtonClicked (object? sender, EventArgs? e) { MessageBox.Query ("Hi", $"You clicked {sender}"); } } /// protected override void Dispose (bool disposing) { base.Dispose (disposing); SuperViewChanged -= OnSuperViewChanged; ConfigurationManager.Applied -= OnConfigurationManagerApplied; } }