diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index fd6b2c8f5..0f9174665 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -153,7 +153,7 @@ namespace Terminal.Gui { /// typically in containers such as and to set the scheme that is used by all the /// views contained inside. /// - public class ColorScheme { + public class ColorScheme : IEquatable { Attribute _normal; Attribute _focus; Attribute _hotNormal; @@ -313,6 +313,42 @@ namespace Terminal.Gui { preparingScheme = false; return attribute; } + + public override bool Equals (object obj) + { + return Equals (obj as ColorScheme); + } + + public bool Equals (ColorScheme other) + { + return other != null && + EqualityComparer.Default.Equals (_normal, other._normal) && + EqualityComparer.Default.Equals (_focus, other._focus) && + EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) && + EqualityComparer.Default.Equals (_hotFocus, other._hotFocus) && + EqualityComparer.Default.Equals (_disabled, other._disabled); + } + + public override int GetHashCode () + { + int hashCode = -1242460230; + hashCode = hashCode * -1521134295 + _normal.GetHashCode (); + hashCode = hashCode * -1521134295 + _focus.GetHashCode (); + hashCode = hashCode * -1521134295 + _hotNormal.GetHashCode (); + hashCode = hashCode * -1521134295 + _hotFocus.GetHashCode (); + hashCode = hashCode * -1521134295 + _disabled.GetHashCode (); + return hashCode; + } + + public static bool operator == (ColorScheme left, ColorScheme right) + { + return EqualityComparer.Default.Equals (left, right); + } + + public static bool operator != (ColorScheme left, ColorScheme right) + { + return !(left == right); + } } /// diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 5ed1d2ac6..9bedceaa8 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -12,12 +12,11 @@ using System; using NStack; using System.Linq; using System.Collections.Generic; -using System.Reflection; namespace Terminal.Gui { /// - /// A has a title, an associated help text, and an action to execute on activation. + /// A has a title, an associated help text, and an action to execute on activation. /// public class MenuItem { @@ -112,7 +111,39 @@ namespace Terminal.Gui { return CanExecute == null ? true : CanExecute (); } - internal int Width => Title.Length + Help.Length + 1 + 2; + internal int Width => Title.Length + Help.Length + 1 + 2 + + (Checked || CheckType.HasFlag (MenuItemCheckType.Checked) || CheckType.HasFlag (MenuItemCheckType.Radio) ? 2 : 0); + + /// + /// Sets or gets whether the shows a check indicator or not. See . + /// + public bool Checked { set; get; } + + /// + /// Specifies how a shows selection state. + /// + [Flags] + public enum MenuItemCheckType : uint { + /// + /// The menu item will be shown normally, with no check indicator. + /// + NoCheck = 0b_0000_0000, + + /// + /// The menu item will indicate checked/un-checked state (see . + /// + Checked = 0b_0000_0001, + + /// + /// The menu item is part of a menu radio group (see and will indicate selected state. + /// + Radio = 0b_0000_0010, + }; + + /// + /// Sets or gets the type selection indicator the menu item will be displayed with. + /// + public MenuItemCheckType CheckType { get; set; } /// /// Gets or sets the parent for this @@ -308,11 +339,30 @@ namespace Terminal.Gui { continue; } + ustring textToDraw; + var checkChar = (char)0x25cf; + var uncheckedChar = (char)0x25cc; + + if (item.CheckType.HasFlag (MenuItem.MenuItemCheckType.Checked)) { + checkChar = (char)0x221a; + uncheckedChar = ' '; + } + + // Support Checked even though CHeckType wasn't set + if (item.Checked) { + textToDraw = checkChar + " " + item.Title; + } else if (item.CheckType.HasFlag (MenuItem.MenuItemCheckType.Checked) || + item.CheckType.HasFlag (MenuItem.MenuItemCheckType.Radio)) { + textToDraw = uncheckedChar + " " + item.Title; + } else { + textToDraw = item.Title; + } + Move (2, i + 1); if (!item.IsEnabled ()) - DrawHotString (item.Title, ColorScheme.Disabled, ColorScheme.Disabled); + DrawHotString (textToDraw, ColorScheme.Disabled, ColorScheme.Disabled); else - DrawHotString (item.Title, + DrawHotString (textToDraw, i == current ? ColorScheme.HotFocus : ColorScheme.HotNormal, i == current ? ColorScheme.Focus : ColorScheme.Normal); @@ -1172,8 +1222,7 @@ namespace Terminal.Gui { Application.GrabMouse (current); } else if (IsMenuOpen && (me.View is MenuBar || me.View is Menu)) { Application.GrabMouse (me.View); - } - else { + } else { handled = false; return false; } diff --git a/UICatalog/Scenario.cs b/UICatalog/Scenario.cs index bdfef607b..89cdcdd72 100644 --- a/UICatalog/Scenario.cs +++ b/UICatalog/Scenario.cs @@ -63,7 +63,8 @@ namespace UICatalog { /// the Scenario picker UI. /// Override to provide any behavior needed. /// - /// + /// The Toplevel created by the UI Catalog host. + /// The colorscheme to use. /// /// /// Thg base implementation calls , sets to the passed in , creates a for and adds it to . @@ -72,7 +73,7 @@ namespace UICatalog { /// Overrides that do not call the base., must call before creating any views or calling other Terminal.Gui APIs. /// /// - public virtual void Init(Toplevel top) + public virtual void Init(Toplevel top, ColorScheme colorScheme) { Application.Init (); @@ -85,7 +86,8 @@ namespace UICatalog { X = 0, Y = 0, Width = Dim.Fill (), - Height = Dim.Fill () + Height = Dim.Fill (), + ColorScheme = colorScheme, }; Top.Add (Win); } diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index e74821e0f..4c02fe634 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -36,7 +36,7 @@ namespace UICatalog { TextField _hText; int _hVal = 0; - public override void Init (Toplevel top) + public override void Init (Toplevel top, ColorScheme colorScheme) { Application.Init (); diff --git a/UICatalog/Scenarios/Clipping.cs b/UICatalog/Scenarios/Clipping.cs index 5b2ceb74d..2817ebac7 100644 --- a/UICatalog/Scenarios/Clipping.cs +++ b/UICatalog/Scenarios/Clipping.cs @@ -7,7 +7,7 @@ namespace UICatalog { class Clipping : Scenario { - public override void Init (Toplevel top) + public override void Init (Toplevel top, ColorScheme colorScheme) { Application.Init (); diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index db8414193..932a5beea 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -14,13 +14,14 @@ namespace UICatalog { private TextView _textView; private bool _saved = true; - public override void Init (Toplevel top) + public override void Init (Toplevel top, ColorScheme colorScheme) { - base.Init (top); - } + Application.Init (); + Top = top; + if (Top == null) { + Top = Application.Top; + } - public override void Setup () - { var menu = new MenuBar (new MenuBarItem [] { new MenuBarItem ("_File", new MenuItem [] { new MenuItem ("_New", "", () => New()), @@ -50,7 +51,8 @@ namespace UICatalog { X = 0, Y = 1, Width = Dim.Fill (), - Height = Dim.Fill () + Height = Dim.Fill (), + ColorScheme = colorScheme, }; Top.Add (Win); @@ -59,7 +61,7 @@ namespace UICatalog { Y = 0, Width = Dim.Fill (), Height = Dim.Fill (), - + }; LoadFile (); @@ -67,6 +69,10 @@ namespace UICatalog { Win.Add (_textView); } + public override void Setup () + { + } + private void New () { Win.Title = _fileName = "Untitled"; diff --git a/UICatalog/Scenarios/Keys.cs b/UICatalog/Scenarios/Keys.cs index 7688dafb5..53c2f88ec 100644 --- a/UICatalog/Scenarios/Keys.cs +++ b/UICatalog/Scenarios/Keys.cs @@ -48,7 +48,7 @@ namespace UICatalog { } } - public override void Init (Toplevel top) + public override void Init (Toplevel top, ColorScheme colorScheme) { Application.Init (); Top = top; @@ -57,7 +57,8 @@ namespace UICatalog { X = 0, Y = 0, Width = Dim.Fill (), - Height = Dim.Fill () + Height = Dim.Fill (), + ColorScheme = colorScheme, }; Top.Add (Win); } @@ -100,13 +101,18 @@ namespace UICatalog { Y = Pos.Top (editLabel) + 4, }; Win.Add (keyLogLabel); - + var fakeKeyPress = new KeyEvent (Key.ControlA, new KeyModifiers () { + Alt = true, + Ctrl = true, + Shift = true + }); + var maxLogEntry = $"Key{"",-5}: {fakeKeyPress}".Length; var yOffset = (Top == Application.Top ? 1 : 6); var keyStrokelist = new List (); var keyStrokeListView = new ListView (keyStrokelist) { X = 0, Y = Pos.Top (keyLogLabel) + yOffset, - Width = Dim.Percent (40), + Width = maxLogEntry, Height = Dim.Fill (), }; keyStrokeListView.ColorScheme = Colors.TopLevel; @@ -119,11 +125,12 @@ namespace UICatalog { }; Win.Add (processKeyLogLabel); + maxLogEntry = $"{fakeKeyPress}".Length; yOffset = (Top == Application.Top ? 1 : 6); var processKeyListView = new ListView (_processKeyList) { X = Pos.Left (processKeyLogLabel), Y = Pos.Top (processKeyLogLabel) + yOffset, - Width = Dim.Percent (60), + Width = maxLogEntry, Height = Dim.Fill (), }; processKeyListView.ColorScheme = Colors.TopLevel; @@ -141,7 +148,7 @@ namespace UICatalog { var processHotKeyListView = new ListView (_processHotKeyList) { X = Pos.Left (processHotKeyLogLabel), Y = Pos.Top (processHotKeyLogLabel) + yOffset, - Width = Dim.Percent (50), + Width = maxLogEntry, Height = Dim.Fill (), }; processHotKeyListView.ColorScheme = Colors.TopLevel; @@ -159,7 +166,7 @@ namespace UICatalog { var processColdKeyListView = new ListView (_processColdKeyList) { X = Pos.Left (processColdKeyLogLabel), Y = Pos.Top (processColdKeyLogLabel) + yOffset, - Width = Dim.Fill (), + Width = maxLogEntry, Height = Dim.Fill (), }; diff --git a/UICatalog/Scenarios/SystemConsole.cs b/UICatalog/Scenarios/SystemConsole.cs index a27ce5789..c3d0513fe 100644 --- a/UICatalog/Scenarios/SystemConsole.cs +++ b/UICatalog/Scenarios/SystemConsole.cs @@ -5,10 +5,10 @@ namespace UICatalog { [ScenarioCategory ("Bug Repro")] [ScenarioCategory ("Console")] class UseSystemConsole : Scenario { - public override void Init (Toplevel top) + public override void Init (Toplevel top, ColorScheme colorScheme) { Application.UseSystemConsole = true; - base.Init (top); + base.Init (top, colorScheme); } public override void RequestStop () diff --git a/UICatalog/Scenarios/WindowsAndFrameViews.cs b/UICatalog/Scenarios/WindowsAndFrameViews.cs index 4ffbf2c3c..03b23a639 100644 --- a/UICatalog/Scenarios/WindowsAndFrameViews.cs +++ b/UICatalog/Scenarios/WindowsAndFrameViews.cs @@ -8,7 +8,7 @@ namespace UICatalog { [ScenarioCategory ("Layout")] [ScenarioCategory ("Bug Repro")] class WindowsAndFrameViews : Scenario { - public override void Init (Toplevel top) + public override void Init (Toplevel top, ColorScheme colorScheme) { Application.Init (); diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index f6132761f..08f1851c0 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -71,7 +71,7 @@ namespace UICatalog { var item = _scenarios.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals (args [0], StringComparison.OrdinalIgnoreCase)); _runningScenario = (Scenario)Activator.CreateInstance (_scenarios [item]); Application.Init (); - _runningScenario.Init (Application.Top); + _runningScenario.Init (Application.Top, _baseColorScheme); _runningScenario.Setup (); _runningScenario.Run (); _runningScenario = null; @@ -82,7 +82,7 @@ namespace UICatalog { while (scenario != null) { Application.UseSystemConsole = _useSystemConsole; Application.Init (); - scenario.Init (Application.Top); + scenario.Init (Application.Top, _baseColorScheme); scenario.Setup (); scenario.Run (); scenario = GetScenarioToRun (); @@ -123,56 +123,83 @@ namespace UICatalog { return _runningScenario; } - static MenuItem CheckedMenuMenuItem(ustring menuItem, Action action, Func checkFunction) + static MenuItem [] CreateDiagnosticMenuItems () { - var mi = new MenuItem (); - mi.Title = $"{(checkFunction () ? (char)0x25cf : (char)0x25cc)} {menuItem}"; - mi.Action = () => { - action?.Invoke (); - mi.Title = $"{(checkFunction () ? (char)0x25cf : (char)0x25cc)} {menuItem}"; + MenuItem CheckedMenuMenuItem (ustring menuItem, Action action, Func checkFunction) + { + var mi = new MenuItem (); + mi.Title = menuItem; + mi.CheckType |= MenuItem.MenuItemCheckType.Checked; + mi.Checked = checkFunction (); + mi.Action = () => { + action?.Invoke (); + mi.Title = menuItem; + mi.Checked = checkFunction (); + }; + return mi; + } + + return new MenuItem [] { + CheckedMenuMenuItem ("Use _System Console", + () => { + _useSystemConsole = !_useSystemConsole; + }, + () => _useSystemConsole), + CheckedMenuMenuItem ("Diagnostics: _Frame Padding", + () => { + ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FramePadding; + _top.SetNeedsDisplay (); + }, + () => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FramePadding) == ConsoleDriver.DiagnosticFlags.FramePadding), + CheckedMenuMenuItem ("Diagnostics: Frame _Ruler", + () => { + ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler; + _top.SetNeedsDisplay (); + }, + () => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler), }; - return mi; } - - static MenuItem RadioMenuItem (ustring menuItem, Action action, Func checkFunction) + static void SetColorScheme () { - var mi = new MenuItem (); - mi.Title = $"{(checkFunction () ? (char)0x25cf : (char)0x25cc)} {menuItem}"; - mi.Action = () => { - action?.Invoke (); - mi.Title = $"{(checkFunction () ? (char)0x25cf : (char)0x25cc)} {menuItem}"; - }; - return mi; + _leftPane.ColorScheme = _baseColorScheme; + _rightPane.ColorScheme = _baseColorScheme; + _top?.SetNeedsDisplay (); } - static ColorScheme _defaultColorScheme = Colors.ColorSchemes.Values.First (); - static MenuItem[] GetColorSchemeMenuItems () + + static ColorScheme _baseColorScheme; + static MenuItem [] CreateColorSchemeMenuItems () { - var menuItems = Colors.ColorSchemes.Select (s => RadioMenuItem (s.Key, () => { - _defaultColorScheme = s.Value; - // SetSelection (s.Key, s.Value); - }, () => _defaultColorScheme == s.Value)).ToArray (); - - //void SetSelection (string key, ColorScheme cs) - //{ - // foreach(var menu in menuItems) { - // menu.Title = $"{(cs == _defaultColorScheme ? (char)0x25cf : (char)0x25cc)} {key}"; - // } - //} - - return menuItems; + List menuItems = new List (); + foreach (var sc in Colors.ColorSchemes) { + var item = new MenuItem (); + item.Title = sc.Key; + item.CheckType |= MenuItem.MenuItemCheckType.Radio; + item.Checked = sc.Value == _baseColorScheme; + item.Action += () => { + _baseColorScheme = sc.Value; + SetColorScheme (); + foreach (var menuItem in menuItems) { + menuItem.Checked = menuItem.Title.Equals (sc.Key) && sc.Value == _baseColorScheme; + } + }; + menuItems.Add (item); + } + return menuItems.ToArray (); } - /// /// Create all controls. This gets called once and the controls remain with their state between Sceanrio runs. /// private static void Setup () { + // Set this here because not initilzied until driver is loaded + _baseColorScheme = Colors.Base; + StringBuilder aboutMessage = new StringBuilder (); aboutMessage.AppendLine ("UI Catalog is a comprehensive sample library for Terminal.Gui"); aboutMessage.AppendLine (""); - aboutMessage.AppendLine ($"Version: {typeof(UICatalogApp).Assembly.GetName ().Version}"); + aboutMessage.AppendLine ($"Version: {typeof (UICatalogApp).Assembly.GetName ().Version}"); aboutMessage.AppendLine ($"Using Terminal.Gui Version: {typeof (Terminal.Gui.Application).Assembly.GetName ().Version}"); aboutMessage.AppendLine (""); @@ -180,27 +207,8 @@ namespace UICatalog { new MenuBarItem ("_File", new MenuItem [] { new MenuItem ("_Quit", "", () => Application.RequestStop() ) }), - new MenuBarItem ("_Settings", new MenuItem [] { - new MenuItem("Primary Color Scheme", new MenuBarItem(GetColorSchemeMenuItems())), - CheckedMenuMenuItem ("Use _System Console", - () => { - _useSystemConsole = !_useSystemConsole; - }, - () => _useSystemConsole), - CheckedMenuMenuItem ("Diagnostics: _Frame Padding", - () => { - ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FramePadding; - _top.SetNeedsDisplay (); - }, - () => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FramePadding) == ConsoleDriver.DiagnosticFlags.FramePadding), - CheckedMenuMenuItem ("Diagnostics: Frame _Ruler", - () => { - ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler; - _top.SetNeedsDisplay (); - }, - () => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler), - - }), + new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()), + new MenuBarItem ("_Diagostics", CreateDiagnosticMenuItems()), new MenuBarItem ("_About...", "About this app", () => MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "Ok")), }); @@ -272,6 +280,8 @@ namespace UICatalog { _numlock, _scrolllock }); + + SetColorScheme (); } private static void _scenarioListView_OpenSelectedItem (EventArgs e)