From e40d871eb01ff73b3510f1dbb98e4516b24bfa19 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 4 Dec 2022 10:42:21 +0000 Subject: [PATCH 1/6] Merge TrueColorAttribute into Attribute (WIP) --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 14 +- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 4 +- Terminal.Gui/Core/ConsoleDriver.cs | 2177 +++++++++--------- UICatalog/Scenarios/TrueColors.cs | 18 +- UnitTests/TureColorAttributeTests.cs | 22 +- 5 files changed, 1139 insertions(+), 1096 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index c2ba8e1115..c5a18c3bff 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1526,19 +1526,19 @@ System.Text.StringBuilder WriteAttributes (Attribute attr) { System.Text.StringBuilder sb = new System.Text.StringBuilder (); - if ((UseTrueColor) && (attr is TrueColorAttribute tca)) { + if (UseTrueColor) { sb.Append (new [] { '\x1b', '[', '3', '8', ';', '2', ';' }); - sb.Append (tca.TrueColorForeground.Red); + sb.Append (attr.TrueColorForeground.Red); sb.Append (';'); - sb.Append (tca.TrueColorForeground.Green); + sb.Append (attr.TrueColorForeground.Green); sb.Append (';'); - sb.Append (tca.TrueColorForeground.Blue); + sb.Append (attr.TrueColorForeground.Blue); sb.Append (new [] { ';', '4', '8', ';', '2', ';' }); - sb.Append (tca.TrueColorBackground.Red); + sb.Append (attr.TrueColorBackground.Red); sb.Append (';'); - sb.Append (tca.TrueColorBackground.Green); + sb.Append (attr.TrueColorBackground.Green); sb.Append (';'); - sb.Append (tca.TrueColorBackground.Blue); + sb.Append (attr.TrueColorBackground.Blue); sb.Append ('m'); } else { const string CSI = "\x1b["; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index a52e08f7d1..2498db682c 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -111,7 +111,9 @@ private bool WriteConsoleTrueColorOutput(ExtendedCharInfo [] charInfoBuffer) if (attr != prev) { prev = attr; - if (attr is TrueColorAttribute tca) { + + // TODO: this is now always true + if (attr is Attribute tca) { stringBuilder.Append (SendTrueColorFg); stringBuilder.Append (tca.TrueColorForeground.Red); stringBuilder.Append (';'); diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 6deaea8a8f..da15ad0c8a 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -173,15 +173,24 @@ public class Attribute : IEquatable { /// /// The color attribute value. /// - public int Value { get; } + public int Value { get; private set; } /// /// The foreground color. /// - public Color Foreground { get; } + public Color Foreground { get; private set; } /// /// The background color. /// - public Color Background { get; } + public Color Background { get; private set; } + + /// + /// The foreground color as a . + /// + public TrueColor TrueColorForeground { get; private set; } + /// + /// The background color as a . + /// + public TrueColor TrueColorBackground { get; private set; } /// /// Initializes a new instance of the class with only the value passed to @@ -199,10 +208,12 @@ public Attribute (int value = 0) Value = value; Foreground = foreground; Background = background; + + PopulateTrueColorsFromConsoleColors (); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the class. /// /// Value. /// Foreground @@ -212,10 +223,12 @@ public Attribute (int value, Color foreground, Color background) Value = value; Foreground = foreground; Background = background; + + PopulateTrueColorsFromConsoleColors (); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the class. /// /// Foreground /// Background @@ -224,15 +237,33 @@ public Attribute (Color foreground, Color background) Value = Make (foreground, background).Value; Foreground = foreground; Background = background; + + PopulateTrueColorsFromConsoleColors (); } /// - /// Initializes a new instance of the struct + /// Initializes a new instance of the class /// with the same colors for the foreground and background. /// /// The color. public Attribute (Color color) : this (color, color) { } + /// + /// Initializes a new instance of the class. Populates + /// and but also + /// and (basic console colors) in case + /// driver does not support true color rendering. + /// + /// + /// + public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground) + { + TrueColorForeground = trueColorForeground; + TrueColorBackground = trueColorBackground; + + PopulateConsoleColorsFromTrueColors (); + } + /// public bool Equals (Attribute other) { @@ -281,1253 +312,1265 @@ public static Attribute Get () /// Default empty attribute. /// public static readonly Attribute Default = new Attribute (); - } - /// - /// Defines a true color attribute. - /// - public class TrueColorAttribute : Attribute { /// - /// Initializes a new instance of the struct. + /// Populates and based on + /// and console colors. /// - /// Foreground - /// Background - public TrueColorAttribute (TrueColor foreground, TrueColor background) - : base ((Color)TrueColor.GetCode4 (foreground), (Color)TrueColor.GetCode4 (background)) + private void PopulateTrueColorsFromConsoleColors () { - TrueColorForeground = foreground; - TrueColorBackground = background; + TrueColorForeground = ConsoleColorToTrueColor (Foreground); + TrueColorBackground = ConsoleColorToTrueColor (Background); } - /// - /// Initializes a new instance of the struct - /// with the same colors for the foreground and background. - /// - /// The color. - public TrueColorAttribute (TrueColor color) : this (color, color) { } + private TrueColor ConsoleColorToTrueColor (Color consoleColor) + { + switch(consoleColor) { + case Color.Black : return new TrueColor (0, 0, 0); + case Color.Blue : return new TrueColor (0, 0, 0x80); + case Color.Green : return new TrueColor (0, 0x80, 0); + case Color.Cyan : return new TrueColor (0, 0x80, 0x80); + case Color.Red : return new TrueColor (0x80, 0, 0); + case Color.Magenta : return new TrueColor (0x80, 0, 0x80); + case Color.Brown :return new TrueColor (0xA5, 0x2A, 0x2A); // TODO confirm this + case Color.Gray : return new TrueColor (0xC0, 0xC0, 0xC0); + case Color.DarkGray : return new TrueColor (0x80, 0x80, 0x80); + case Color.BrightBlue : return new TrueColor (0, 0, 0xFF); + case Color.BrightGreen : return new TrueColor (0, 0xFF, 0); + case Color.BrightCyan : return new TrueColor (0, 0xFF, 0xFF); + case Color.BrightRed : return new TrueColor (0xFF, 0, 0); + case Color.BrightMagenta : return new TrueColor (0xFF, 0, 0xFF); + case Color.BrightYellow : return new TrueColor (0xFF, 0xFF, 0); + case Color.White : return new TrueColor (0xFF, 0xFF, 0xFF); + default : throw new ArgumentOutOfRangeException (nameof (consoleColor)); + }; + } - /// - /// The foreground color. - /// - public TrueColor TrueColorForeground { get; } - /// - /// The background color. - /// - public TrueColor TrueColorBackground { get; } + private void PopulateConsoleColorsFromTrueColors () + { + Foreground = TrueColorToConsoleColor (TrueColorForeground); + Background = TrueColorToConsoleColor (TrueColorBackground); + } + + private Color TrueColorToConsoleColor (TrueColor trueColorForeground) + { + //TODO + throw new NotImplementedException (); + } } +/// +/// Color scheme definitions, they cover some common scenarios and are used +/// typically in containers such as and to set the scheme that is used by all the +/// views contained inside. +/// +public class ColorScheme : IEquatable { + Attribute _normal = Attribute.Default; + Attribute _focus = Attribute.Default; + Attribute _hotNormal = Attribute.Default; + Attribute _hotFocus = Attribute.Default; + Attribute _disabled = Attribute.Default; + internal string caller = ""; + /// - /// Color scheme definitions, they cover some common scenarios and are used - /// typically in containers such as and to set the scheme that is used by all the - /// views contained inside. + /// The default color for text, when the view is not focused. /// - public class ColorScheme : IEquatable { - Attribute _normal = Attribute.Default; - Attribute _focus = Attribute.Default; - Attribute _hotNormal = Attribute.Default; - Attribute _hotFocus = Attribute.Default; - Attribute _disabled = Attribute.Default; - internal string caller = ""; + public Attribute Normal { get { return _normal; } set { _normal = SetAttribute (value); } } - /// - /// The default color for text, when the view is not focused. - /// - public Attribute Normal { get { return _normal; } set { _normal = SetAttribute (value); } } + /// + /// The color for text when the view has the focus. + /// + public Attribute Focus { get { return _focus; } set { _focus = SetAttribute (value); } } - /// - /// The color for text when the view has the focus. - /// - public Attribute Focus { get { return _focus; } set { _focus = SetAttribute (value); } } + /// + /// The color for the hotkey when a view is not focused + /// + public Attribute HotNormal { get { return _hotNormal; } set { _hotNormal = SetAttribute (value); } } - /// - /// The color for the hotkey when a view is not focused - /// - public Attribute HotNormal { get { return _hotNormal; } set { _hotNormal = SetAttribute (value); } } + /// + /// The color for the hotkey when the view is focused. + /// + public Attribute HotFocus { get { return _hotFocus; } set { _hotFocus = SetAttribute (value); } } - /// - /// The color for the hotkey when the view is focused. - /// - public Attribute HotFocus { get { return _hotFocus; } set { _hotFocus = SetAttribute (value); } } + /// + /// The default color for text, when the view is disabled. + /// + public Attribute Disabled { get { return _disabled; } set { _disabled = SetAttribute (value); } } - /// - /// The default color for text, when the view is disabled. - /// - public Attribute Disabled { get { return _disabled; } set { _disabled = SetAttribute (value); } } + bool preparingScheme = false; + + Attribute SetAttribute (Attribute attribute, [CallerMemberName] string callerMemberName = null) + { + if (!Application._initialized && !preparingScheme) + return attribute; - bool preparingScheme = false; + if (preparingScheme) + return attribute; - Attribute SetAttribute (Attribute attribute, [CallerMemberName] string callerMemberName = null) - { - if (!Application._initialized && !preparingScheme) - return attribute; - - if (preparingScheme) - return attribute; - - preparingScheme = true; - switch (caller) { - case "TopLevel": - switch (callerMemberName) { - case "Normal": - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - break; - case "Focus": - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); - break; - case "HotNormal": - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - break; - case "HotFocus": - HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); - if (Focus?.Foreground != attribute.Background) - Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); - break; - } + preparingScheme = true; + switch (caller) { + case "TopLevel": + switch (callerMemberName) { + case "Normal": + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + break; + case "Focus": + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); + break; + case "HotFocus": + HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); + if (Focus?.Foreground != attribute.Background) + Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); break; + } + break; - case "Base": - switch (callerMemberName) { - case "Normal": - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - break; - case "Focus": - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); - break; - case "HotNormal": + case "Base": + switch (callerMemberName) { + case "Normal": + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + break; + case "Focus": + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); + Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + break; + case "HotFocus": + HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); + if (Focus?.Foreground != attribute.Background) + Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); + break; + } + break; + + case "Menu": + switch (callerMemberName) { + case "Normal": + if (Focus?.Background != attribute.Background) + Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus?.Background ?? default); + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + Disabled = Application.Driver.MakeAttribute (Disabled?.Foreground ?? default, attribute.Background); + break; + case "Focus": + Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal?.Background ?? default); + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": + if (Focus?.Background != attribute.Background) HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); - break; - case "HotFocus": - HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); - if (Focus?.Foreground != attribute.Background) - Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); - break; - } + Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + Disabled = Application.Driver.MakeAttribute (Disabled?.Foreground ?? default, attribute.Background); break; - - case "Menu": - switch (callerMemberName) { - case "Normal": - if (Focus?.Background != attribute.Background) - Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus?.Background ?? default); - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - Disabled = Application.Driver.MakeAttribute (Disabled?.Foreground ?? default, attribute.Background); - break; - case "Focus": - Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal?.Background ?? default); - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); - break; - case "HotNormal": - if (Focus?.Background != attribute.Background) - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); - Disabled = Application.Driver.MakeAttribute (Disabled?.Foreground ?? default, attribute.Background); - break; - case "HotFocus": - HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); - if (Focus?.Foreground != attribute.Background) - Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); - break; - case "Disabled": - if (Focus?.Background != attribute.Background) - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - break; - } + case "HotFocus": + HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); + if (Focus?.Foreground != attribute.Background) + Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); break; - - case "Dialog": - switch (callerMemberName) { - case "Normal": - if (Focus?.Background != attribute.Background) - Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus?.Background ?? default); - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - break; - case "Focus": - Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal?.Background ?? default); - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); - break; - case "HotNormal": - if (Focus?.Background != attribute.Background) - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - if (Normal?.Foreground != attribute.Background) - Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); - break; - case "HotFocus": - HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); - if (Focus?.Foreground != attribute.Background) - Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); - break; - } + case "Disabled": + if (Focus?.Background != attribute.Background) + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); + Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); break; - - case "Error": - switch (callerMemberName) { - case "Normal": - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); - break; - case "HotNormal": - case "HotFocus": - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, attribute.Background); + } + break; + + case "Dialog": + switch (callerMemberName) { + case "Normal": + if (Focus?.Background != attribute.Background) + Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus?.Background ?? default); + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + break; + case "Focus": + Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal?.Background ?? default); + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": + if (Focus?.Background != attribute.Background) + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); + if (Normal?.Foreground != attribute.Background) Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); - break; - } + break; + case "HotFocus": + HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); + if (Focus?.Foreground != attribute.Background) + Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); break; } - preparingScheme = false; - return attribute; - } + break; - /// - /// Compares two objects for equality. - /// - /// - /// true if the two objects are equal - public override bool Equals (object obj) - { - return Equals (obj as ColorScheme); + case "Error": + switch (callerMemberName) { + case "Normal": + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": + case "HotFocus": + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, attribute.Background); + Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + break; + } + break; } + preparingScheme = false; + return attribute; + } - /// - /// Compares two objects for equality. - /// - /// - /// true if the two objects are equal - 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); - } + /// + /// Compares two objects for equality. + /// + /// + /// true if the two objects are equal + public override bool Equals (object obj) + { + return Equals (obj as ColorScheme); + } - /// - /// Returns a hashcode for this instance. - /// - /// hashcode for this instance - 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; - } + /// + /// Compares two objects for equality. + /// + /// + /// true if the two objects are equal + 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); + } - /// - /// Compares two objects for equality. - /// - /// - /// - /// true if the two objects are equivalent - public static bool operator == (ColorScheme left, ColorScheme right) - { - return EqualityComparer.Default.Equals (left, right); - } + /// + /// Returns a hashcode for this instance. + /// + /// hashcode for this instance + 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; + } - /// - /// Compares two objects for inequality. - /// - /// - /// - /// true if the two objects are not equivalent - public static bool operator != (ColorScheme left, ColorScheme right) - { - return !(left == right); - } + /// + /// Compares two objects for equality. + /// + /// + /// + /// true if the two objects are equivalent + public static bool operator == (ColorScheme left, ColorScheme right) + { + return EqualityComparer.Default.Equals (left, right); } /// - /// The default s for the application. + /// Compares two objects for inequality. /// - public static class Colors { - static Colors () - { - // Use reflection to dynamically create the default set of ColorSchemes from the list defined - // by the class. - ColorSchemes = typeof (Colors).GetProperties () - .Where (p => p.PropertyType == typeof (ColorScheme)) - .Select (p => new KeyValuePair (p.Name, new ColorScheme ())) // (ColorScheme)p.GetValue (p))) - .ToDictionary (t => t.Key, t => t.Value); - } + /// + /// + /// true if the two objects are not equivalent + public static bool operator != (ColorScheme left, ColorScheme right) + { + return !(left == right); + } +} - /// - /// The application toplevel color scheme, for the default toplevel views. - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["TopLevel"]; - /// - /// - public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); } +/// +/// The default s for the application. +/// +public static class Colors { + static Colors () + { + // Use reflection to dynamically create the default set of ColorSchemes from the list defined + // by the class. + ColorSchemes = typeof (Colors).GetProperties () + .Where (p => p.PropertyType == typeof (ColorScheme)) + .Select (p => new KeyValuePair (p.Name, new ColorScheme ())) // (ColorScheme)p.GetValue (p))) + .ToDictionary (t => t.Key, t => t.Value); + } - /// - /// The base color scheme, for the default toplevel views. - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Base"]; - /// - /// - public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); } + /// + /// The application toplevel color scheme, for the default toplevel views. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["TopLevel"]; + /// + /// + public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); } - /// - /// The dialog color scheme, for standard popup dialog boxes - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Dialog"]; - /// - /// - public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); } + /// + /// The base color scheme, for the default toplevel views. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Base"]; + /// + /// + public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); } - /// - /// The menu bar color - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Menu"]; - /// - /// - public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); } + /// + /// The dialog color scheme, for standard popup dialog boxes + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Dialog"]; + /// + /// + public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); } - /// - /// The color scheme for showing errors. - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Error"]; - /// - /// - public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); } - - static ColorScheme GetColorScheme ([CallerMemberName] string callerMemberName = null) - { - return ColorSchemes [callerMemberName]; - } + /// + /// The menu bar color + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Menu"]; + /// + /// + public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); } - static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string callerMemberName = null) - { - ColorSchemes [callerMemberName] = colorScheme; - colorScheme.caller = callerMemberName; - } + /// + /// The color scheme for showing errors. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Error"]; + /// + /// + public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); } - /// - /// Provides the defined s. - /// - public static Dictionary ColorSchemes { get; } + static ColorScheme GetColorScheme ([CallerMemberName] string callerMemberName = null) + { + return ColorSchemes [callerMemberName]; + } + + static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string callerMemberName = null) + { + ColorSchemes [callerMemberName] = colorScheme; + colorScheme.caller = callerMemberName; } /// - /// Cursors Visibility that are displayed + /// Provides the defined s. /// - // - // Hexa value are set as 0xAABBCCDD where : - // - // AA stand for the TERMINFO DECSUSR parameter value to be used under Linux & MacOS - // BB stand for the NCurses curs_set parameter value to be used under Linux & MacOS - // CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows - // DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows - // - public enum CursorVisibility { - /// - /// Cursor caret has default - /// - /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking. - Default = 0x00010119, + public static Dictionary ColorSchemes { get; } +} - /// - /// Cursor caret is hidden - /// - Invisible = 0x03000019, +/// +/// Cursors Visibility that are displayed +/// +// +// Hexa value are set as 0xAABBCCDD where : +// +// AA stand for the TERMINFO DECSUSR parameter value to be used under Linux & MacOS +// BB stand for the NCurses curs_set parameter value to be used under Linux & MacOS +// CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows +// DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows +// +public enum CursorVisibility { + /// + /// Cursor caret has default + /// + /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking. + Default = 0x00010119, - /// - /// Cursor caret is normally shown as a blinking underline bar _ - /// - Underline = 0x03010119, + /// + /// Cursor caret is hidden + /// + Invisible = 0x03000019, - /// - /// Cursor caret is normally shown as a underline bar _ - /// - /// Under Windows, this is equivalent to - UnderlineFix = 0x04010119, + /// + /// Cursor caret is normally shown as a blinking underline bar _ + /// + Underline = 0x03010119, - /// - /// Cursor caret is displayed a blinking vertical bar | - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - Vertical = 0x05010119, + /// + /// Cursor caret is normally shown as a underline bar _ + /// + /// Under Windows, this is equivalent to + UnderlineFix = 0x04010119, - /// - /// Cursor caret is displayed a blinking vertical bar | - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - VerticalFix = 0x06010119, + /// + /// Cursor caret is displayed a blinking vertical bar | + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + Vertical = 0x05010119, - /// - /// Cursor caret is displayed as a blinking block ▉ - /// - Box = 0x01020164, + /// + /// Cursor caret is displayed a blinking vertical bar | + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + VerticalFix = 0x06010119, - /// - /// Cursor caret is displayed a block ▉ - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - BoxFix = 0x02020164, - } + /// + /// Cursor caret is displayed as a blinking block ▉ + /// + Box = 0x01020164, - ///// - ///// Special characters that can be drawn with - ///// - //public enum SpecialChar { - // /// - // /// Horizontal line character. - // /// - // HLine, - - // /// - // /// Vertical line character. - // /// - // VLine, - - // /// - // /// Stipple pattern - // /// - // Stipple, - - // /// - // /// Diamond character - // /// - // Diamond, - - // /// - // /// Upper left corner - // /// - // ULCorner, - - // /// - // /// Lower left corner - // /// - // LLCorner, - - // /// - // /// Upper right corner - // /// - // URCorner, - - // /// - // /// Lower right corner - // /// - // LRCorner, - - // /// - // /// Left tee - // /// - // LeftTee, - - // /// - // /// Right tee - // /// - // RightTee, - - // /// - // /// Top tee - // /// - // TopTee, - - // /// - // /// The bottom tee. - // /// - // BottomTee, - //} - - /// - /// ConsoleDriver is an abstract class that defines the requirements for a console driver. - /// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. - /// - public abstract class ConsoleDriver { - /// - /// The handler fired when the terminal is resized. - /// - protected Action TerminalResized; + /// + /// Cursor caret is displayed a block ▉ + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + BoxFix = 0x02020164, +} - /// - /// The current number of columns in the terminal. - /// - public abstract int Cols { get; } +///// +///// Special characters that can be drawn with +///// +//public enum SpecialChar { +// /// +// /// Horizontal line character. +// /// +// HLine, + +// /// +// /// Vertical line character. +// /// +// VLine, + +// /// +// /// Stipple pattern +// /// +// Stipple, + +// /// +// /// Diamond character +// /// +// Diamond, + +// /// +// /// Upper left corner +// /// +// ULCorner, + +// /// +// /// Lower left corner +// /// +// LLCorner, + +// /// +// /// Upper right corner +// /// +// URCorner, + +// /// +// /// Lower right corner +// /// +// LRCorner, + +// /// +// /// Left tee +// /// +// LeftTee, + +// /// +// /// Right tee +// /// +// RightTee, + +// /// +// /// Top tee +// /// +// TopTee, + +// /// +// /// The bottom tee. +// /// +// BottomTee, +//} + +/// +/// ConsoleDriver is an abstract class that defines the requirements for a console driver. +/// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. +/// +public abstract class ConsoleDriver { + /// + /// The handler fired when the terminal is resized. + /// + protected Action TerminalResized; - /// - /// The current number of rows in the terminal. - /// - public abstract int Rows { get; } + /// + /// The current number of columns in the terminal. + /// + public abstract int Cols { get; } - /// - /// The current left in the terminal. - /// - public abstract int Left { get; } + /// + /// The current number of rows in the terminal. + /// + public abstract int Rows { get; } - /// - /// The current top in the terminal. - /// - public abstract int Top { get; } + /// + /// The current left in the terminal. + /// + public abstract int Left { get; } - /// - /// Get the operation system clipboard. - /// - public abstract IClipboard Clipboard { get; } + /// + /// The current top in the terminal. + /// + public abstract int Top { get; } - /// - /// If false height is measured by the window height and thus no scrolling. - /// If true then height is measured by the buffer height, enabling scrolling. - /// - public abstract bool HeightAsBuffer { get; set; } + /// + /// Get the operation system clipboard. + /// + public abstract IClipboard Clipboard { get; } - /// - /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - /// - public virtual int [,,] Contents { get; } + /// + /// If false height is measured by the window height and thus no scrolling. + /// If true then height is measured by the buffer height, enabling scrolling. + /// + public abstract bool HeightAsBuffer { get; set; } + /// + /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag + /// + public virtual int [,,] Contents { get; } - /// - /// Determinates if the current console driver supports TrueColor output- - /// - public virtual bool SupportsTrueColorOutput { get => false; } - bool useTrueColor; + /// + /// Determinates if the current console driver supports TrueColor output- + /// + public virtual bool SupportsTrueColorOutput { get => false; } - /// - /// Controls the TureColor output mode. Can be only enabled if the underlying ConsoleDriver supports it. - /// Note this will be enabled automaticaly if supported. See also - /// - public bool UseTrueColor { - get => useTrueColor; - set => this.useTrueColor = value && SupportsTrueColorOutput; - } + bool useTrueColor; - /// - /// Initializes the driver - /// - /// Method to invoke when the terminal is resized. - public abstract void Init (Action terminalResized); - /// - /// Moves the cursor to the specified column and row. - /// - /// Column to move the cursor to. - /// Row to move the cursor to. - public abstract void Move (int col, int row); + /// + /// Controls the TureColor output mode. Can be only enabled if the underlying ConsoleDriver supports it. + /// Note this will be enabled automaticaly if supported. See also + /// + public bool UseTrueColor { + get => useTrueColor; + set => this.useTrueColor = value && SupportsTrueColorOutput; + } - /// - /// Adds the specified rune to the display at the current cursor position. - /// - /// Rune to add. - public abstract void AddRune (Rune rune); + /// + /// Initializes the driver + /// + /// Method to invoke when the terminal is resized. + public abstract void Init (Action terminalResized); + /// + /// Moves the cursor to the specified column and row. + /// + /// Column to move the cursor to. + /// Row to move the cursor to. + public abstract void Move (int col, int row); - /// - /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 - /// to equivalent, printable, Unicode chars. - /// - /// Rune to translate - /// - public static Rune MakePrintable (Rune c) - { - var controlChars = c & 0xFFFF; - if (controlChars <= 0x1F || controlChars >= 0X7F && controlChars <= 0x9F) { - // ASCII (C0) control characters. - // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) - return new Rune (controlChars + 0x2400); - } + /// + /// Adds the specified rune to the display at the current cursor position. + /// + /// Rune to add. + public abstract void AddRune (Rune rune); - return c; + /// + /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 + /// to equivalent, printable, Unicode chars. + /// + /// Rune to translate + /// + public static Rune MakePrintable (Rune c) + { + var controlChars = c & 0xFFFF; + if (controlChars <= 0x1F || controlChars >= 0X7F && controlChars <= 0x9F) { + // ASCII (C0) control characters. + // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) + return new Rune (controlChars + 0x2400); } - /// - /// Ensures that the column and line are in a valid range from the size of the driver. - /// - /// The column. - /// The row. - /// The clip. - /// trueif it's a valid range,falseotherwise. - public bool IsValidContent (int col, int row, Rect clip) => - col >= 0 && row >= 0 && col < Cols && row < Rows && clip.Contains (col, row); + return c; + } - /// - /// Adds the to the display at the cursor position. - /// - /// String. - public abstract void AddStr (ustring str); + /// + /// Ensures that the column and line are in a valid range from the size of the driver. + /// + /// The column. + /// The row. + /// The clip. + /// trueif it's a valid range,falseotherwise. + public bool IsValidContent (int col, int row, Rect clip) => + col >= 0 && row >= 0 && col < Cols && row < Rows && clip.Contains (col, row); - /// - /// Prepare the driver and set the key and mouse events handlers. - /// - /// The main loop. - /// The handler for ProcessKey - /// The handler for key down events - /// The handler for key up events - /// The handler for mouse events - public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler); + /// + /// Adds the to the display at the cursor position. + /// + /// String. + public abstract void AddStr (ustring str); - /// - /// Updates the screen to reflect all the changes that have been done to the display buffer - /// - public abstract void Refresh (); + /// + /// Prepare the driver and set the key and mouse events handlers. + /// + /// The main loop. + /// The handler for ProcessKey + /// The handler for key down events + /// The handler for key up events + /// The handler for mouse events + public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler); - /// - /// Updates the location of the cursor position - /// - public abstract void UpdateCursor (); + /// + /// Updates the screen to reflect all the changes that have been done to the display buffer + /// + public abstract void Refresh (); - /// - /// Retreive the cursor caret visibility - /// - /// The current - /// true upon success - public abstract bool GetCursorVisibility (out CursorVisibility visibility); + /// + /// Updates the location of the cursor position + /// + public abstract void UpdateCursor (); - /// - /// Change the cursor caret visibility - /// - /// The wished - /// true upon success - public abstract bool SetCursorVisibility (CursorVisibility visibility); + /// + /// Retreive the cursor caret visibility + /// + /// The current + /// true upon success + public abstract bool GetCursorVisibility (out CursorVisibility visibility); - /// - /// Ensure the cursor visibility - /// - /// true upon success - public abstract bool EnsureCursorVisibility (); + /// + /// Change the cursor caret visibility + /// + /// The wished + /// true upon success + public abstract bool SetCursorVisibility (CursorVisibility visibility); - /// - /// Ends the execution of the console driver. - /// - public abstract void End (); + /// + /// Ensure the cursor visibility + /// + /// true upon success + public abstract bool EnsureCursorVisibility (); - /// - /// Resizes the clip area when the screen is resized. - /// - public abstract void ResizeScreen (); + /// + /// Ends the execution of the console driver. + /// + public abstract void End (); - /// - /// Reset and recreate the contents and the driver buffer. - /// - public abstract void UpdateOffScreen (); + /// + /// Resizes the clip area when the screen is resized. + /// + public abstract void ResizeScreen (); - /// - /// Redraws the physical screen with the contents that have been queued up via any of the printing commands. - /// - public abstract void UpdateScreen (); + /// + /// Reset and recreate the contents and the driver buffer. + /// + public abstract void UpdateOffScreen (); - /// - /// Selects the specified attribute as the attribute to use for future calls to AddRune, AddString. - /// - /// C. - public abstract void SetAttribute (Attribute c); + /// + /// Redraws the physical screen with the contents that have been queued up via any of the printing commands. + /// + public abstract void UpdateScreen (); - /// - /// Set Colors from limit sets of colors. - /// - /// Foreground. - /// Background. - public abstract void SetColors (ConsoleColor foreground, ConsoleColor background); + /// + /// Selects the specified attribute as the attribute to use for future calls to AddRune, AddString. + /// + /// C. + public abstract void SetAttribute (Attribute c); - // Advanced uses - set colors to any pre-set pairs, you would need to init_color - // that independently with the R, G, B values. - /// - /// Advanced uses - set colors to any pre-set pairs, you would need to init_color - /// that independently with the R, G, B values. - /// - /// Foreground color identifier. - /// Background color identifier. - public abstract void SetColors (short foregroundColorId, short backgroundColorId); + /// + /// Set Colors from limit sets of colors. + /// + /// Foreground. + /// Background. + public abstract void SetColors (ConsoleColor foreground, ConsoleColor background); + // Advanced uses - set colors to any pre-set pairs, you would need to init_color + // that independently with the R, G, B values. + /// + /// Advanced uses - set colors to any pre-set pairs, you would need to init_color + /// that independently with the R, G, B values. + /// + /// Foreground color identifier. + /// Background color identifier. + public abstract void SetColors (short foregroundColorId, short backgroundColorId); + + /// + /// Gets the foreground and background colors based on the value. + /// + /// The value. + /// The foreground. + /// The background. + /// + public abstract bool GetColors (int value, out Color foreground, out Color background); + + /// + /// Allows sending keys without typing on a keyboard. + /// + /// The character key. + /// The key. + /// If shift key is sending. + /// If alt key is sending. + /// If control key is sending. + public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control); + + /// + /// Set the handler when the terminal is resized. + /// + /// + public void SetTerminalResized (Action terminalResized) + { + TerminalResized = terminalResized; + } + + /// + /// Draws the title for a Window-style view incorporating padding. + /// + /// Screen relative region where the frame will be drawn. + /// The title for the window. The title will only be drawn if title is not null or empty and paddingTop is greater than 0. + /// Number of columns to pad on the left (if 0 the border will not appear on the left). + /// Number of rows to pad on the top (if 0 the border and title will not appear on the top). + /// Number of columns to pad on the right (if 0 the border will not appear on the right). + /// Number of rows to pad on the bottom (if 0 the border will not appear on the bottom). + /// Not yet implemented. + /// + public virtual void DrawWindowTitle (Rect region, ustring title, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, TextAlignment textAlignment = TextAlignment.Left) + { + var width = region.Width - (paddingLeft + 2) * 2; + if (!ustring.IsNullOrEmpty (title) && width > 4 && region.Y + paddingTop <= region.Y + paddingBottom) { + Move (region.X + 1 + paddingLeft, region.Y + paddingTop); + AddRune (' '); + var str = title.Sum (r => Math.Max (Rune.ColumnWidth (r), 1)) >= width + ? TextFormatter.Format (title, width - 2, false, false) [0] : title; + AddStr (str); + AddRune (' '); + } + } + + /// + /// Enables diagnostic functions + /// + [Flags] + public enum DiagnosticFlags : uint { /// - /// Gets the foreground and background colors based on the value. + /// All diagnostics off /// - /// The value. - /// The foreground. - /// The background. - /// - public abstract bool GetColors (int value, out Color foreground, out Color background); - + Off = 0b_0000_0000, /// - /// Allows sending keys without typing on a keyboard. + /// When enabled, will draw a + /// ruler in the frame for any side with a padding value greater than 0. /// - /// The character key. - /// The key. - /// If shift key is sending. - /// If alt key is sending. - /// If control key is sending. - public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control); - + FrameRuler = 0b_0000_0001, /// - /// Set the handler when the terminal is resized. + /// When Enabled, will use + /// 'L', 'R', 'T', and 'B' for padding instead of ' '. /// - /// - public void SetTerminalResized (Action terminalResized) - { - TerminalResized = terminalResized; + FramePadding = 0b_0000_0010, + } + + /// + /// Set flags to enable/disable diagnostics. + /// + public static DiagnosticFlags Diagnostics { get; set; } + + /// + /// Draws a frame for a window with padding and an optional visible border inside the padding. + /// + /// Screen relative region where the frame will be drawn. + /// Number of columns to pad on the left (if 0 the border will not appear on the left). + /// Number of rows to pad on the top (if 0 the border and title will not appear on the top). + /// Number of columns to pad on the right (if 0 the border will not appear on the right). + /// Number of rows to pad on the bottom (if 0 the border will not appear on the bottom). + /// If set to true and any padding dimension is > 0 the border will be drawn. + /// If set to true it will clear the content area (the area inside the padding) with the current color, otherwise the content area will be left untouched. + /// The to be used if defined. + public virtual void DrawWindowFrame (Rect region, int paddingLeft = 0, int paddingTop = 0, int paddingRight = 0, + int paddingBottom = 0, bool border = true, bool fill = false, Border borderContent = null) + { + char clearChar = ' '; + char leftChar = clearChar; + char rightChar = clearChar; + char topChar = clearChar; + char bottomChar = clearChar; + + if ((Diagnostics & DiagnosticFlags.FramePadding) == DiagnosticFlags.FramePadding) { + leftChar = 'L'; + rightChar = 'R'; + topChar = 'T'; + bottomChar = 'B'; + clearChar = 'C'; } - /// - /// Draws the title for a Window-style view incorporating padding. - /// - /// Screen relative region where the frame will be drawn. - /// The title for the window. The title will only be drawn if title is not null or empty and paddingTop is greater than 0. - /// Number of columns to pad on the left (if 0 the border will not appear on the left). - /// Number of rows to pad on the top (if 0 the border and title will not appear on the top). - /// Number of columns to pad on the right (if 0 the border will not appear on the right). - /// Number of rows to pad on the bottom (if 0 the border will not appear on the bottom). - /// Not yet implemented. - /// - public virtual void DrawWindowTitle (Rect region, ustring title, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, TextAlignment textAlignment = TextAlignment.Left) + void AddRuneAt (int col, int row, Rune ch) { - var width = region.Width - (paddingLeft + 2) * 2; - if (!ustring.IsNullOrEmpty (title) && width > 4 && region.Y + paddingTop <= region.Y + paddingBottom) { - Move (region.X + 1 + paddingLeft, region.Y + paddingTop); - AddRune (' '); - var str = title.Sum (r => Math.Max (Rune.ColumnWidth (r), 1)) >= width - ? TextFormatter.Format (title, width - 2, false, false) [0] : title; - AddStr (str); - AddRune (' '); - } + Move (col, row); + AddRune (ch); } - /// - /// Enables diagnostic functions - /// - [Flags] - public enum DiagnosticFlags : uint { - /// - /// All diagnostics off - /// - Off = 0b_0000_0000, - /// - /// When enabled, will draw a - /// ruler in the frame for any side with a padding value greater than 0. - /// - FrameRuler = 0b_0000_0001, - /// - /// When Enabled, will use - /// 'L', 'R', 'T', and 'B' for padding instead of ' '. - /// - FramePadding = 0b_0000_0010, - } + // fwidth is count of hLine chars + int fwidth = (int)(region.Width - (paddingRight + paddingLeft)); - /// - /// Set flags to enable/disable diagnostics. - /// - public static DiagnosticFlags Diagnostics { get; set; } + // fheight is count of vLine chars + int fheight = (int)(region.Height - (paddingBottom + paddingTop)); - /// - /// Draws a frame for a window with padding and an optional visible border inside the padding. - /// - /// Screen relative region where the frame will be drawn. - /// Number of columns to pad on the left (if 0 the border will not appear on the left). - /// Number of rows to pad on the top (if 0 the border and title will not appear on the top). - /// Number of columns to pad on the right (if 0 the border will not appear on the right). - /// Number of rows to pad on the bottom (if 0 the border will not appear on the bottom). - /// If set to true and any padding dimension is > 0 the border will be drawn. - /// If set to true it will clear the content area (the area inside the padding) with the current color, otherwise the content area will be left untouched. - /// The to be used if defined. - public virtual void DrawWindowFrame (Rect region, int paddingLeft = 0, int paddingTop = 0, int paddingRight = 0, - int paddingBottom = 0, bool border = true, bool fill = false, Border borderContent = null) - { - char clearChar = ' '; - char leftChar = clearChar; - char rightChar = clearChar; - char topChar = clearChar; - char bottomChar = clearChar; - - if ((Diagnostics & DiagnosticFlags.FramePadding) == DiagnosticFlags.FramePadding) { - leftChar = 'L'; - rightChar = 'R'; - topChar = 'T'; - bottomChar = 'B'; - clearChar = 'C'; - } + // fleft is location of left frame line + int fleft = region.X + paddingLeft - 1; - void AddRuneAt (int col, int row, Rune ch) - { - Move (col, row); - AddRune (ch); - } + // fright is location of right frame line + int fright = fleft + fwidth + 1; - // fwidth is count of hLine chars - int fwidth = (int)(region.Width - (paddingRight + paddingLeft)); - - // fheight is count of vLine chars - int fheight = (int)(region.Height - (paddingBottom + paddingTop)); - - // fleft is location of left frame line - int fleft = region.X + paddingLeft - 1; - - // fright is location of right frame line - int fright = fleft + fwidth + 1; - - // ftop is location of top frame line - int ftop = region.Y + paddingTop - 1; - - // fbottom is location of bottom frame line - int fbottom = ftop + fheight + 1; - - var borderStyle = borderContent == null ? BorderStyle.Single : borderContent.BorderStyle; - - Rune hLine = default, vLine = default, - uRCorner = default, uLCorner = default, lLCorner = default, lRCorner = default; - - if (border) { - switch (borderStyle) { - case BorderStyle.None: - break; - case BorderStyle.Single: - hLine = HLine; - vLine = VLine; - uRCorner = URCorner; - uLCorner = ULCorner; - lLCorner = LLCorner; - lRCorner = LRCorner; - break; - case BorderStyle.Double: - hLine = HDLine; - vLine = VDLine; - uRCorner = URDCorner; - uLCorner = ULDCorner; - lLCorner = LLDCorner; - lRCorner = LRDCorner; - break; - case BorderStyle.Rounded: - hLine = HRLine; - vLine = VRLine; - uRCorner = URRCorner; - uLCorner = ULRCorner; - lLCorner = LLRCorner; - lRCorner = LRRCorner; - break; - } - } else { - hLine = vLine = uRCorner = uLCorner = lLCorner = lRCorner = clearChar; - } + // ftop is location of top frame line + int ftop = region.Y + paddingTop - 1; - // Outside top - if (paddingTop > 1) { - for (int r = region.Y; r < ftop; r++) { - for (int c = region.X; c < region.X + region.Width; c++) { - AddRuneAt (c, r, topChar); - } - } - } + // fbottom is location of bottom frame line + int fbottom = ftop + fheight + 1; - // Outside top-left - for (int c = region.X; c < fleft; c++) { - AddRuneAt (c, ftop, leftChar); - } + var borderStyle = borderContent == null ? BorderStyle.Single : borderContent.BorderStyle; - // Frame top-left corner - AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : leftChar); + Rune hLine = default, vLine = default, + uRCorner = default, uLCorner = default, lLCorner = default, lRCorner = default; - // Frame top - for (int c = fleft + 1; c < fleft + 1 + fwidth; c++) { - AddRuneAt (c, ftop, paddingTop > 0 ? hLine : topChar); + if (border) { + switch (borderStyle) { + case BorderStyle.None: + break; + case BorderStyle.Single: + hLine = HLine; + vLine = VLine; + uRCorner = URCorner; + uLCorner = ULCorner; + lLCorner = LLCorner; + lRCorner = LRCorner; + break; + case BorderStyle.Double: + hLine = HDLine; + vLine = VDLine; + uRCorner = URDCorner; + uLCorner = ULDCorner; + lLCorner = LLDCorner; + lRCorner = LRDCorner; + break; + case BorderStyle.Rounded: + hLine = HRLine; + vLine = VRLine; + uRCorner = URRCorner; + uLCorner = ULRCorner; + lLCorner = LLRCorner; + lRCorner = LRRCorner; + break; } + } else { + hLine = vLine = uRCorner = uLCorner = lLCorner = lRCorner = clearChar; + } - // Frame top-right corner - if (fright > fleft) { - AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : rightChar); + // Outside top + if (paddingTop > 1) { + for (int r = region.Y; r < ftop; r++) { + for (int c = region.X; c < region.X + region.Width; c++) { + AddRuneAt (c, r, topChar); + } } + } - // Outside top-right corner - for (int c = fright + 1; c < fright + paddingRight; c++) { - AddRuneAt (c, ftop, rightChar); - } + // Outside top-left + for (int c = region.X; c < fleft; c++) { + AddRuneAt (c, ftop, leftChar); + } - // Left, Fill, Right - if (fbottom > ftop) { - for (int r = ftop + 1; r < fbottom; r++) { - // Outside left - for (int c = region.X; c < fleft; c++) { - AddRuneAt (c, r, leftChar); - } + // Frame top-left corner + AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : leftChar); - // Frame left - AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : leftChar); + // Frame top + for (int c = fleft + 1; c < fleft + 1 + fwidth; c++) { + AddRuneAt (c, ftop, paddingTop > 0 ? hLine : topChar); + } - // Fill - if (fill) { - for (int x = fleft + 1; x < fright; x++) { - AddRuneAt (x, r, clearChar); - } - } + // Frame top-right corner + if (fright > fleft) { + AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : rightChar); + } - // Frame right - if (fright > fleft) { - var v = vLine; - if ((Diagnostics & DiagnosticFlags.FrameRuler) == DiagnosticFlags.FrameRuler) { - v = (char)(((int)'0') + ((r - ftop) % 10)); // vLine; - } - AddRuneAt (fright, r, paddingRight > 0 ? v : rightChar); - } + // Outside top-right corner + for (int c = fright + 1; c < fright + paddingRight; c++) { + AddRuneAt (c, ftop, rightChar); + } - // Outside right - for (int c = fright + 1; c < fright + paddingRight; c++) { - AddRuneAt (c, r, rightChar); - } + // Left, Fill, Right + if (fbottom > ftop) { + for (int r = ftop + 1; r < fbottom; r++) { + // Outside left + for (int c = region.X; c < fleft; c++) { + AddRuneAt (c, r, leftChar); } - // Outside Bottom - for (int c = region.X; c < region.X + region.Width; c++) { - AddRuneAt (c, fbottom, leftChar); - } + // Frame left + AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : leftChar); - // Frame bottom-left - AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : leftChar); + // Fill + if (fill) { + for (int x = fleft + 1; x < fright; x++) { + AddRuneAt (x, r, clearChar); + } + } + // Frame right if (fright > fleft) { - // Frame bottom - for (int c = fleft + 1; c < fright; c++) { - var h = hLine; - if ((Diagnostics & DiagnosticFlags.FrameRuler) == DiagnosticFlags.FrameRuler) { - h = (char)(((int)'0') + ((c - fleft) % 10)); // hLine; - } - AddRuneAt (c, fbottom, paddingBottom > 0 ? h : bottomChar); + var v = vLine; + if ((Diagnostics & DiagnosticFlags.FrameRuler) == DiagnosticFlags.FrameRuler) { + v = (char)(((int)'0') + ((r - ftop) % 10)); // vLine; } - - // Frame bottom-right - AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : rightChar); + AddRuneAt (fright, r, paddingRight > 0 ? v : rightChar); } // Outside right for (int c = fright + 1; c < fright + paddingRight; c++) { - AddRuneAt (c, fbottom, rightChar); + AddRuneAt (c, r, rightChar); } } - // Out bottom - ensure top is always drawn if we overlap - if (paddingBottom > 0) { - for (int r = fbottom + 1; r < fbottom + paddingBottom; r++) { - for (int c = region.X; c < region.X + region.Width; c++) { - AddRuneAt (c, r, bottomChar); + // Outside Bottom + for (int c = region.X; c < region.X + region.Width; c++) { + AddRuneAt (c, fbottom, leftChar); + } + + // Frame bottom-left + AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : leftChar); + + if (fright > fleft) { + // Frame bottom + for (int c = fleft + 1; c < fright; c++) { + var h = hLine; + if ((Diagnostics & DiagnosticFlags.FrameRuler) == DiagnosticFlags.FrameRuler) { + h = (char)(((int)'0') + ((c - fleft) % 10)); // hLine; } + AddRuneAt (c, fbottom, paddingBottom > 0 ? h : bottomChar); } + + // Frame bottom-right + AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : rightChar); + } + + // Outside right + for (int c = fright + 1; c < fright + paddingRight; c++) { + AddRuneAt (c, fbottom, rightChar); } } - /// - /// Draws a frame on the specified region with the specified padding around the frame. - /// - /// Screen relative region where the frame will be drawn. - /// Padding to add on the sides. - /// If set to true it will clear the contents with the current color, otherwise the contents will be left untouched. - /// This API has been superseded by . - /// This API is equivalent to calling DrawWindowFrame(Rect, p - 1, p - 1, p - 1, p - 1). In other words, - /// A padding value of 0 means there is actually a one cell border. - /// - public virtual void DrawFrame (Rect region, int padding, bool fill) - { - // DrawFrame assumes the border is always at least one row/col thick - // DrawWindowFrame assumes a padding of 0 means NO padding and no frame - DrawWindowFrame (new Rect (region.X, region.Y, region.Width, region.Height), - padding + 1, padding + 1, padding + 1, padding + 1, border: false, fill: fill); + // Out bottom - ensure top is always drawn if we overlap + if (paddingBottom > 0) { + for (int r = fbottom + 1; r < fbottom + paddingBottom; r++) { + for (int c = region.X; c < region.X + region.Width; c++) { + AddRuneAt (c, r, bottomChar); + } + } } + } + /// + /// Draws a frame on the specified region with the specified padding around the frame. + /// + /// Screen relative region where the frame will be drawn. + /// Padding to add on the sides. + /// If set to true it will clear the contents with the current color, otherwise the contents will be left untouched. + /// This API has been superseded by . + /// This API is equivalent to calling DrawWindowFrame(Rect, p - 1, p - 1, p - 1, p - 1). In other words, + /// A padding value of 0 means there is actually a one cell border. + /// + public virtual void DrawFrame (Rect region, int padding, bool fill) + { + // DrawFrame assumes the border is always at least one row/col thick + // DrawWindowFrame assumes a padding of 0 means NO padding and no frame + DrawWindowFrame (new Rect (region.X, region.Y, region.Width, region.Height), + padding + 1, padding + 1, padding + 1, padding + 1, border: false, fill: fill); + } - /// - /// Suspend the application, typically needs to save the state, suspend the app and upon return, reset the console driver. - /// - public abstract void Suspend (); - Rect clip; + /// + /// Suspend the application, typically needs to save the state, suspend the app and upon return, reset the console driver. + /// + public abstract void Suspend (); - /// - /// Controls the current clipping region that AddRune/AddStr is subject to. - /// - /// The clip. - public Rect Clip { - get => clip; - set => this.clip = value; - } + Rect clip; - /// - /// Start of mouse moves. - /// - public abstract void StartReportingMouseMoves (); + /// + /// Controls the current clipping region that AddRune/AddStr is subject to. + /// + /// The clip. + public Rect Clip { + get => clip; + set => this.clip = value; + } - /// - /// Stop reporting mouses moves. - /// - public abstract void StopReportingMouseMoves (); + /// + /// Start of mouse moves. + /// + public abstract void StartReportingMouseMoves (); - /// - /// Disables the cooked event processing from the mouse driver. At startup, it is assumed mouse events are cooked. - /// - public abstract void UncookMouse (); + /// + /// Stop reporting mouses moves. + /// + public abstract void StopReportingMouseMoves (); - /// - /// Enables the cooked event processing from the mouse driver - /// - public abstract void CookMouse (); + /// + /// Disables the cooked event processing from the mouse driver. At startup, it is assumed mouse events are cooked. + /// + public abstract void UncookMouse (); - /// - /// Horizontal line character. - /// - public Rune HLine = '\u2500'; + /// + /// Enables the cooked event processing from the mouse driver + /// + public abstract void CookMouse (); - /// - /// Vertical line character. - /// - public Rune VLine = '\u2502'; + /// + /// Horizontal line character. + /// + public Rune HLine = '\u2500'; - /// - /// Stipple pattern - /// - public Rune Stipple = '\u2591'; + /// + /// Vertical line character. + /// + public Rune VLine = '\u2502'; - /// - /// Diamond character - /// - public Rune Diamond = '\u25ca'; + /// + /// Stipple pattern + /// + public Rune Stipple = '\u2591'; - /// - /// Upper left corner - /// - public Rune ULCorner = '\u250C'; + /// + /// Diamond character + /// + public Rune Diamond = '\u25ca'; - /// - /// Lower left corner - /// - public Rune LLCorner = '\u2514'; + /// + /// Upper left corner + /// + public Rune ULCorner = '\u250C'; - /// - /// Upper right corner - /// - public Rune URCorner = '\u2510'; + /// + /// Lower left corner + /// + public Rune LLCorner = '\u2514'; - /// - /// Lower right corner - /// - public Rune LRCorner = '\u2518'; + /// + /// Upper right corner + /// + public Rune URCorner = '\u2510'; - /// - /// Left tee - /// - public Rune LeftTee = '\u251c'; + /// + /// Lower right corner + /// + public Rune LRCorner = '\u2518'; - /// - /// Right tee - /// - public Rune RightTee = '\u2524'; + /// + /// Left tee + /// + public Rune LeftTee = '\u251c'; - /// - /// Top tee - /// - public Rune TopTee = '\u252c'; + /// + /// Right tee + /// + public Rune RightTee = '\u2524'; - /// - /// The bottom tee. - /// - public Rune BottomTee = '\u2534'; + /// + /// Top tee + /// + public Rune TopTee = '\u252c'; - /// - /// Checkmark. - /// - public Rune Checked = '\u221a'; + /// + /// The bottom tee. + /// + public Rune BottomTee = '\u2534'; - /// - /// Un-checked checkmark. - /// - public Rune UnChecked = '\u2574'; + /// + /// Checkmark. + /// + public Rune Checked = '\u221a'; - /// - /// Selected mark. - /// - public Rune Selected = '\u25cf'; + /// + /// Un-checked checkmark. + /// + public Rune UnChecked = '\u2574'; - /// - /// Un-selected selected mark. - /// - public Rune UnSelected = '\u25cc'; + /// + /// Selected mark. + /// + public Rune Selected = '\u25cf'; - /// - /// Right Arrow. - /// - public Rune RightArrow = '\u25ba'; + /// + /// Un-selected selected mark. + /// + public Rune UnSelected = '\u25cc'; - /// - /// Left Arrow. - /// - public Rune LeftArrow = '\u25c4'; + /// + /// Right Arrow. + /// + public Rune RightArrow = '\u25ba'; - /// - /// Down Arrow. - /// - public Rune DownArrow = '\u25bc'; + /// + /// Left Arrow. + /// + public Rune LeftArrow = '\u25c4'; - /// - /// Up Arrow. - /// - public Rune UpArrow = '\u25b2'; + /// + /// Down Arrow. + /// + public Rune DownArrow = '\u25bc'; - /// - /// Left indicator for default action (e.g. for ). - /// - public Rune LeftDefaultIndicator = '\u25e6'; + /// + /// Up Arrow. + /// + public Rune UpArrow = '\u25b2'; - /// - /// Right indicator for default action (e.g. for ). - /// - public Rune RightDefaultIndicator = '\u25e6'; + /// + /// Left indicator for default action (e.g. for ). + /// + public Rune LeftDefaultIndicator = '\u25e6'; - /// - /// Left frame/bracket (e.g. '[' for ). - /// - public Rune LeftBracket = '['; + /// + /// Right indicator for default action (e.g. for ). + /// + public Rune RightDefaultIndicator = '\u25e6'; - /// - /// Right frame/bracket (e.g. ']' for ). - /// - public Rune RightBracket = ']'; + /// + /// Left frame/bracket (e.g. '[' for ). + /// + public Rune LeftBracket = '['; - /// - /// Blocks Segment indicator for meter views (e.g. . - /// - public Rune BlocksMeterSegment = '\u258c'; + /// + /// Right frame/bracket (e.g. ']' for ). + /// + public Rune RightBracket = ']'; - /// - /// Continuous Segment indicator for meter views (e.g. . - /// - public Rune ContinuousMeterSegment = '\u2588'; + /// + /// Blocks Segment indicator for meter views (e.g. . + /// + public Rune BlocksMeterSegment = '\u258c'; - /// - /// Horizontal double line character. - /// - public Rune HDLine = '\u2550'; + /// + /// Continuous Segment indicator for meter views (e.g. . + /// + public Rune ContinuousMeterSegment = '\u2588'; - /// - /// Vertical double line character. - /// - public Rune VDLine = '\u2551'; + /// + /// Horizontal double line character. + /// + public Rune HDLine = '\u2550'; - /// - /// Upper left double corner - /// - public Rune ULDCorner = '\u2554'; + /// + /// Vertical double line character. + /// + public Rune VDLine = '\u2551'; - /// - /// Lower left double corner - /// - public Rune LLDCorner = '\u255a'; + /// + /// Upper left double corner + /// + public Rune ULDCorner = '\u2554'; - /// - /// Upper right double corner - /// - public Rune URDCorner = '\u2557'; + /// + /// Lower left double corner + /// + public Rune LLDCorner = '\u255a'; - /// - /// Lower right double corner - /// - public Rune LRDCorner = '\u255d'; + /// + /// Upper right double corner + /// + public Rune URDCorner = '\u2557'; - /// - /// Horizontal line character for rounded corners. - /// - public Rune HRLine = '\u2500'; + /// + /// Lower right double corner + /// + public Rune LRDCorner = '\u255d'; - /// - /// Vertical line character for rounded corners. - /// - public Rune VRLine = '\u2502'; + /// + /// Horizontal line character for rounded corners. + /// + public Rune HRLine = '\u2500'; - /// - /// Upper left rounded corner - /// - public Rune ULRCorner = '\u256d'; + /// + /// Vertical line character for rounded corners. + /// + public Rune VRLine = '\u2502'; - /// - /// Lower left rounded corner - /// - public Rune LLRCorner = '\u2570'; + /// + /// Upper left rounded corner + /// + public Rune ULRCorner = '\u256d'; - /// - /// Upper right rounded corner - /// - public Rune URRCorner = '\u256e'; + /// + /// Lower left rounded corner + /// + public Rune LLRCorner = '\u2570'; - /// - /// Lower right rounded corner - /// - public Rune LRRCorner = '\u256f'; + /// + /// Upper right rounded corner + /// + public Rune URRCorner = '\u256e'; - /// - /// Make the attribute for the foreground and background colors. - /// - /// Foreground. - /// Background. - /// - public abstract Attribute MakeAttribute (Color fore, Color back); + /// + /// Lower right rounded corner + /// + public Rune LRRCorner = '\u256f'; - /// - /// Gets the current . - /// - /// The current attribute. - public abstract Attribute GetAttribute (); + /// + /// Make the attribute for the foreground and background colors. + /// + /// Foreground. + /// Background. + /// + public abstract Attribute MakeAttribute (Color fore, Color back); - /// - /// Make the for the . - /// - /// The foreground color. - /// The background color. - /// The attribute for the foreground and background colors. - public abstract Attribute MakeColor (Color foreground, Color background); + /// + /// Gets the current . + /// + /// The current attribute. + public abstract Attribute GetAttribute (); - /// - /// Create all with the for the console driver. - /// - /// Flag indicating if colors are supported. - public void CreateColors (bool hasColors = true) - { - Colors.TopLevel = new ColorScheme (); - Colors.Base = new ColorScheme (); - Colors.Dialog = new ColorScheme (); - Colors.Menu = new ColorScheme (); - Colors.Error = new ColorScheme (); - - if (!hasColors) { - return; - } + /// + /// Make the for the . + /// + /// The foreground color. + /// The background color. + /// The attribute for the foreground and background colors. + public abstract Attribute MakeColor (Color foreground, Color background); - Colors.TopLevel.Normal = MakeColor (Color.BrightGreen, Color.Black); - Colors.TopLevel.Focus = MakeColor (Color.White, Color.Cyan); - Colors.TopLevel.HotNormal = MakeColor (Color.Brown, Color.Black); - Colors.TopLevel.HotFocus = MakeColor (Color.Blue, Color.Cyan); - Colors.TopLevel.Disabled = MakeColor (Color.DarkGray, Color.Black); - - Colors.Base.Normal = MakeColor (Color.White, Color.Blue); - Colors.Base.Focus = MakeColor (Color.Black, Color.Gray); - Colors.Base.HotNormal = MakeColor (Color.BrightCyan, Color.Blue); - Colors.Base.HotFocus = MakeColor (Color.BrightBlue, Color.Gray); - Colors.Base.Disabled = MakeColor (Color.DarkGray, Color.Blue); - - Colors.Dialog.Normal = MakeColor (Color.Black, Color.Gray); - Colors.Dialog.Focus = MakeColor (Color.White, Color.DarkGray); - Colors.Dialog.HotNormal = MakeColor (Color.Blue, Color.Gray); - Colors.Dialog.HotFocus = MakeColor (Color.BrightYellow, Color.DarkGray); - Colors.Dialog.Disabled = MakeColor (Color.Gray, Color.DarkGray); - - Colors.Menu.Normal = MakeColor (Color.White, Color.DarkGray); - Colors.Menu.Focus = MakeColor (Color.White, Color.Black); - Colors.Menu.HotNormal = MakeColor (Color.BrightYellow, Color.DarkGray); - Colors.Menu.HotFocus = MakeColor (Color.BrightYellow, Color.Black); - Colors.Menu.Disabled = MakeColor (Color.Gray, Color.DarkGray); - - Colors.Error.Normal = MakeColor (Color.Red, Color.White); - Colors.Error.Focus = MakeColor (Color.Black, Color.BrightRed); - Colors.Error.HotNormal = MakeColor (Color.Black, Color.White); - Colors.Error.HotFocus = MakeColor (Color.White, Color.BrightRed); - Colors.Error.Disabled = MakeColor (Color.DarkGray, Color.White); + /// + /// Create all with the for the console driver. + /// + /// Flag indicating if colors are supported. + public void CreateColors (bool hasColors = true) + { + Colors.TopLevel = new ColorScheme (); + Colors.Base = new ColorScheme (); + Colors.Dialog = new ColorScheme (); + Colors.Menu = new ColorScheme (); + Colors.Error = new ColorScheme (); + + if (!hasColors) { + return; } + + Colors.TopLevel.Normal = MakeColor (Color.BrightGreen, Color.Black); + Colors.TopLevel.Focus = MakeColor (Color.White, Color.Cyan); + Colors.TopLevel.HotNormal = MakeColor (Color.Brown, Color.Black); + Colors.TopLevel.HotFocus = MakeColor (Color.Blue, Color.Cyan); + Colors.TopLevel.Disabled = MakeColor (Color.DarkGray, Color.Black); + + Colors.Base.Normal = MakeColor (Color.White, Color.Blue); + Colors.Base.Focus = MakeColor (Color.Black, Color.Gray); + Colors.Base.HotNormal = MakeColor (Color.BrightCyan, Color.Blue); + Colors.Base.HotFocus = MakeColor (Color.BrightBlue, Color.Gray); + Colors.Base.Disabled = MakeColor (Color.DarkGray, Color.Blue); + + Colors.Dialog.Normal = MakeColor (Color.Black, Color.Gray); + Colors.Dialog.Focus = MakeColor (Color.White, Color.DarkGray); + Colors.Dialog.HotNormal = MakeColor (Color.Blue, Color.Gray); + Colors.Dialog.HotFocus = MakeColor (Color.BrightYellow, Color.DarkGray); + Colors.Dialog.Disabled = MakeColor (Color.Gray, Color.DarkGray); + + Colors.Menu.Normal = MakeColor (Color.White, Color.DarkGray); + Colors.Menu.Focus = MakeColor (Color.White, Color.Black); + Colors.Menu.HotNormal = MakeColor (Color.BrightYellow, Color.DarkGray); + Colors.Menu.HotFocus = MakeColor (Color.BrightYellow, Color.Black); + Colors.Menu.Disabled = MakeColor (Color.Gray, Color.DarkGray); + + Colors.Error.Normal = MakeColor (Color.Red, Color.White); + Colors.Error.Focus = MakeColor (Color.Black, Color.BrightRed); + Colors.Error.HotNormal = MakeColor (Color.Black, Color.White); + Colors.Error.HotFocus = MakeColor (Color.White, Color.BrightRed); + Colors.Error.Disabled = MakeColor (Color.DarkGray, Color.White); } } +} diff --git a/UICatalog/Scenarios/TrueColors.cs b/UICatalog/Scenarios/TrueColors.cs index fc2fce3cb4..f294b6ac82 100644 --- a/UICatalog/Scenarios/TrueColors.cs +++ b/UICatalog/Scenarios/TrueColors.cs @@ -81,16 +81,11 @@ public override void Setup () Win.Add (lblBlue); Application.RootMouseEvent = (e) => { + var normal = e.View.GetNormalColor (); if (e.View != null) { - if (e.View.GetNormalColor () is TrueColorAttribute colorAttribute) { - lblRed.Text = colorAttribute.TrueColorForeground.Red.ToString(); - lblGreen.Text = colorAttribute.TrueColorForeground.Green.ToString (); - lblBlue.Text = colorAttribute.TrueColorForeground.Blue.ToString (); - } else { - lblRed.Text = "na"; - lblGreen.Text = "na"; - lblBlue.Text = "na"; - } + lblRed.Text = normal.TrueColorForeground.Red.ToString(); + lblGreen.Text = normal.TrueColorForeground.Green.ToString (); + lblBlue.Text = normal.TrueColorForeground.Blue.ToString (); } }; } @@ -106,7 +101,10 @@ private void SetupGradient (string name, int x, ref int y, Func var l = new Label (" ") { X = dx++, Y = y, - ColorScheme = new ColorScheme () { Normal = new TrueColorAttribute (colorFunc(i > 255 ? 255 : i)) } + ColorScheme = new ColorScheme () { Normal = new Terminal.Gui.Attribute ( + colorFunc (i > 255 ? 255 : i), + colorFunc (i > 255 ? 255 : i) + ) } }; Win.Add (l); } diff --git a/UnitTests/TureColorAttributeTests.cs b/UnitTests/TureColorAttributeTests.cs index 0ac83b2395..b3ae52b878 100644 --- a/UnitTests/TureColorAttributeTests.cs +++ b/UnitTests/TureColorAttributeTests.cs @@ -17,17 +17,17 @@ public void Constuctors_Constuct () var fg = new TrueColor (255, 0, 0); var bg = new TrueColor (0, 255, 0); - var attr = new TrueColorAttribute (fg, bg); + var attr = new Attribute (fg, bg); Assert.Equal (fg, attr.TrueColorForeground); Assert.Equal (bg, attr.TrueColorBackground); // Test unified color - attr = new TrueColorAttribute (fg); + attr = new Attribute (fg,fg); Assert.Equal (fg, attr.TrueColorForeground); Assert.Equal (fg, attr.TrueColorBackground); - attr = new TrueColorAttribute (bg); + attr = new Attribute (bg,bg); Assert.Equal (bg, attr.TrueColorForeground); Assert.Equal (bg, attr.TrueColorBackground); @@ -43,37 +43,37 @@ public void Basic_Colors_Fallback () driver.Init (() => { }); // Test bright basic colors - var attr = new TrueColorAttribute (new TrueColor (128, 0, 0), new TrueColor (0, 128, 0)); + var attr = new Attribute (new TrueColor (128, 0, 0), new TrueColor (0, 128, 0)); Assert.Equal (Color.Red, attr.Foreground); Assert.Equal (Color.Green, attr.Background); - attr = new TrueColorAttribute (new TrueColor (128, 128, 0), new TrueColor (0, 0, 128)); + attr = new Attribute (new TrueColor (128, 128, 0), new TrueColor (0, 0, 128)); Assert.Equal (Color.Brown, attr.Foreground); Assert.Equal (Color.Blue, attr.Background); - attr = new TrueColorAttribute (new TrueColor (128, 0, 128), new TrueColor (0, 128, 128)); + attr = new Attribute (new TrueColor (128, 0, 128), new TrueColor (0, 128, 128)); Assert.Equal (Color.Magenta, attr.Foreground); Assert.Equal (Color.Cyan, attr.Background); // Test basic colors - attr = new TrueColorAttribute (new TrueColor (255, 0, 0), new TrueColor (0, 255, 0)); + attr = new Attribute (new TrueColor (255, 0, 0), new TrueColor (0, 255, 0)); Assert.Equal (Color.BrightRed, attr.Foreground); Assert.Equal (Color.BrightGreen, attr.Background); - attr = new TrueColorAttribute (new TrueColor (255, 255, 0), new TrueColor (0, 0, 255)); + attr = new Attribute (new TrueColor (255, 255, 0), new TrueColor (0, 0, 255)); Assert.Equal (Color.BrightYellow, attr.Foreground); Assert.Equal (Color.BrightBlue, attr.Background); - attr = new TrueColorAttribute (new TrueColor (255, 0, 255), new TrueColor (0, 255, 255)); + attr = new Attribute (new TrueColor (255, 0, 255), new TrueColor (0, 255, 255)); Assert.Equal (Color.BrightMagenta, attr.Foreground); Assert.Equal (Color.BrightCyan, attr.Background); // Test gray basic colors - attr = new TrueColorAttribute (new TrueColor (128, 128, 128), new TrueColor (255, 255, 255)); + attr = new Attribute (new TrueColor (128, 128, 128), new TrueColor (255, 255, 255)); Assert.Equal (Color.DarkGray, attr.Foreground); Assert.Equal (Color.White, attr.Background); - attr = new TrueColorAttribute (new TrueColor (192, 192, 192), new TrueColor (0, 0, 0)); + attr = new Attribute (new TrueColor (192, 192, 192), new TrueColor (0, 0, 0)); Assert.Equal (Color.Gray, attr.Foreground); Assert.Equal (Color.Black, attr.Background); From cef58039e3a4cb77d4597ebd853a5db8bd5d88ca Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 4 Dec 2022 12:02:30 +0000 Subject: [PATCH 2/6] Add HSV based algorithm for fallback color calculation --- Terminal.Gui/Core/ConsoleDriver.cs | 2229 +++++++++++++++------------- 1 file changed, 1173 insertions(+), 1056 deletions(-) diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index da15ad0c8a..a78af8e58e 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -250,7 +250,7 @@ public Attribute (Color color) : this (color, color) { } /// /// Initializes a new instance of the class. Populates - /// and but also + /// and . Also computes /// and (basic console colors) in case /// driver does not support true color rendering. /// @@ -264,6 +264,28 @@ public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground) PopulateConsoleColorsFromTrueColors (); } + + /// + /// + /// Initializes a new instance of the class. Populates + /// and with explicit + /// fallback values for and (in case + /// driver does not support true color rendering). + /// + /// If you do not want to manually specify the fallback colors use + /// instead which auto calculates these. + /// + /// True color RGB values you would like to use. + /// True color RGB values you would like to use. + /// Simple console color replacement if driver does not support true color. + /// Simple console color replacement if driver does not support true color. + public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground, Color foreground, Color background) + : this (foreground, background) + { + TrueColorForeground = trueColorForeground; + TrueColorBackground = trueColorBackground; + } + /// public bool Equals (Attribute other) { @@ -325,24 +347,24 @@ private void PopulateTrueColorsFromConsoleColors () private TrueColor ConsoleColorToTrueColor (Color consoleColor) { - switch(consoleColor) { - case Color.Black : return new TrueColor (0, 0, 0); - case Color.Blue : return new TrueColor (0, 0, 0x80); - case Color.Green : return new TrueColor (0, 0x80, 0); - case Color.Cyan : return new TrueColor (0, 0x80, 0x80); - case Color.Red : return new TrueColor (0x80, 0, 0); - case Color.Magenta : return new TrueColor (0x80, 0, 0x80); - case Color.Brown :return new TrueColor (0xA5, 0x2A, 0x2A); // TODO confirm this - case Color.Gray : return new TrueColor (0xC0, 0xC0, 0xC0); - case Color.DarkGray : return new TrueColor (0x80, 0x80, 0x80); - case Color.BrightBlue : return new TrueColor (0, 0, 0xFF); - case Color.BrightGreen : return new TrueColor (0, 0xFF, 0); - case Color.BrightCyan : return new TrueColor (0, 0xFF, 0xFF); - case Color.BrightRed : return new TrueColor (0xFF, 0, 0); - case Color.BrightMagenta : return new TrueColor (0xFF, 0, 0xFF); - case Color.BrightYellow : return new TrueColor (0xFF, 0xFF, 0); - case Color.White : return new TrueColor (0xFF, 0xFF, 0xFF); - default : throw new ArgumentOutOfRangeException (nameof (consoleColor)); + switch (consoleColor) { + case Color.Black: return new TrueColor (0, 0, 0); + case Color.Blue: return new TrueColor (0, 0, 0x80); + case Color.Green: return new TrueColor (0, 0x80, 0); + case Color.Cyan: return new TrueColor (0, 0x80, 0x80); + case Color.Red: return new TrueColor (0x80, 0, 0); + case Color.Magenta: return new TrueColor (0x80, 0, 0x80); + case Color.Brown: return new TrueColor (0xA5, 0x2A, 0x2A); // TODO confirm this + case Color.Gray: return new TrueColor (0xC0, 0xC0, 0xC0); + case Color.DarkGray: return new TrueColor (0x80, 0x80, 0x80); + case Color.BrightBlue: return new TrueColor (0, 0, 0xFF); + case Color.BrightGreen: return new TrueColor (0, 0xFF, 0); + case Color.BrightCyan: return new TrueColor (0, 0xFF, 0xFF); + case Color.BrightRed: return new TrueColor (0xFF, 0, 0); + case Color.BrightMagenta: return new TrueColor (0xFF, 0, 0xFF); + case Color.BrightYellow: return new TrueColor (0xFF, 0xFF, 0); + case Color.White: return new TrueColor (0xFF, 0xFF, 0xFF); + default: throw new ArgumentOutOfRangeException (nameof (consoleColor)); }; } @@ -350,1227 +372,1322 @@ private void PopulateConsoleColorsFromTrueColors () { Foreground = TrueColorToConsoleColor (TrueColorForeground); Background = TrueColorToConsoleColor (TrueColorBackground); + Value = Make (Foreground, Background).Value; } - private Color TrueColorToConsoleColor (TrueColor trueColorForeground) + private Color TrueColorToConsoleColor (TrueColor trueColor) { - //TODO - throw new NotImplementedException (); + // The points we could return from + var lookup = new Dictionary () { + { new TrueColor(0,0,0),Color.Black}, + { new TrueColor (0, 0, 0x80),Color.Blue}, + { new TrueColor (0, 0x80, 0),Color.Green}, + { new TrueColor (0, 0x80, 0x80),Color.Cyan}, + { new TrueColor (0x80, 0, 0),Color.Red}, + { new TrueColor (0x80, 0, 0x80),Color.Magenta}, + { new TrueColor (0xA5, 0x2A, 0x2A),Color.Brown}, // TODO confirm this + { new TrueColor (0xC0, 0xC0, 0xC0),Color.Gray}, + { new TrueColor (0x80, 0x80, 0x80),Color.DarkGray}, + { new TrueColor (0, 0, 0xFF),Color.BrightBlue}, + { new TrueColor (0, 0xFF, 0),Color.BrightGreen}, + { new TrueColor (0, 0xFF, 0xFF),Color.BrightCyan}, + { new TrueColor (0xFF, 0, 0),Color.BrightRed}, + { new TrueColor (0xFF, 0, 0xFF),Color.BrightMagenta }, + { new TrueColor (0xFF, 0xFF, 0),Color.BrightYellow}, + { new TrueColor (0xFF, 0xFF, 0xFF),Color.White}, + }; + + var distances = lookup.Select ( + k => Tuple.Create ( + // the candidate we are considering matching against (RGB) + k.Key, + + CalculateDistance (k.Key, trueColor) + )); + + // get the closest + var match = distances.OrderBy (t => t.Item2).First (); + + return lookup [match.Item1]; } - } -/// -/// Color scheme definitions, they cover some common scenarios and are used -/// typically in containers such as and to set the scheme that is used by all the -/// views contained inside. -/// -public class ColorScheme : IEquatable { - Attribute _normal = Attribute.Default; - Attribute _focus = Attribute.Default; - Attribute _hotNormal = Attribute.Default; - Attribute _hotFocus = Attribute.Default; - Attribute _disabled = Attribute.Default; - internal string caller = ""; + private float CalculateDistance (TrueColor color1, TrueColor color2) + { + RGBtoHSV (color1, out var h1, out var s1, out var v1); + RGBtoHSV (color2, out var h2, out var s2, out var v2); + - /// - /// The default color for text, when the view is not focused. - /// - public Attribute Normal { get { return _normal; } set { _normal = SetAttribute (value); } } + if(float.IsNaN(h1)) { + h1 = -1f; + } - /// - /// The color for text when the view has the focus. - /// - public Attribute Focus { get { return _focus; } set { _focus = SetAttribute (value); } } + if (float.IsNaN (h2)) { + h2 = -1f; + } + + // the distance we are from this point in HSV space + // with an emphasis on Hue + return Math.Abs(h1 - h2) + + Math.Abs (s1 - s2) + + Math.Abs (v1 - v2); + } - /// - /// The color for the hotkey when a view is not focused - /// - public Attribute HotNormal { get { return _hotNormal; } set { _hotNormal = SetAttribute (value); } } + void RGBtoHSV (TrueColor color, out float h, out float s, out float v) + { + RGBtoHSV (color.Red / 255f, color.Green / 255f, color.Blue / 255f, out h, out s, out v); + } - /// - /// The color for the hotkey when the view is focused. - /// - public Attribute HotFocus { get { return _hotFocus; } set { _hotFocus = SetAttribute (value); } } + + // https://www.cs.rit.edu/~ncs/color/t_convert.html + // r,g,b values are from 0 to 1 + // h = [0,360], s = [0,1], v = [0,1] + // if s == 0, then h = -1 (undefined) + + void RGBtoHSV (float r, float g, float b, out float h, out float s, out float v) + { + float min, max, delta; + + min = Math.Min (Math.Min (r, g), b); + max = Math.Max (Math.Max (r, g), b); + v = max; // v + + delta = max - min; + + if (max != 0) + s = delta / max; // s + else { + // r = g = b = 0 // s = 0, v is undefined + s = 0; + h = -1; + return; + } + + if (r == max) + h = (g - b) / delta; // between yellow & magenta + else if (g == max) + h = 2 + (b - r) / delta; // between cyan & yellow + else + h = 4 + (r - g) / delta; // between magenta & cyan + + h *= 60; // degrees + if (h < 0) + h += 360; + + } + } /// - /// The default color for text, when the view is disabled. + /// Color scheme definitions, they cover some common scenarios and are used + /// typically in containers such as and to set the scheme that is used by all the + /// views contained inside. /// - public Attribute Disabled { get { return _disabled; } set { _disabled = SetAttribute (value); } } + public class ColorScheme : IEquatable { + Attribute _normal = Attribute.Default; + Attribute _focus = Attribute.Default; + Attribute _hotNormal = Attribute.Default; + Attribute _hotFocus = Attribute.Default; + Attribute _disabled = Attribute.Default; + internal string caller = ""; - bool preparingScheme = false; + /// + /// The default color for text, when the view is not focused. + /// + public Attribute Normal { get { return _normal; } set { _normal = SetAttribute (value); } } - Attribute SetAttribute (Attribute attribute, [CallerMemberName] string callerMemberName = null) - { - if (!Application._initialized && !preparingScheme) - return attribute; + /// + /// The color for text when the view has the focus. + /// + public Attribute Focus { get { return _focus; } set { _focus = SetAttribute (value); } } - if (preparingScheme) - return attribute; + /// + /// The color for the hotkey when a view is not focused + /// + public Attribute HotNormal { get { return _hotNormal; } set { _hotNormal = SetAttribute (value); } } - preparingScheme = true; - switch (caller) { - case "TopLevel": - switch (callerMemberName) { - case "Normal": - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - break; - case "Focus": - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); - break; - case "HotNormal": - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - break; - case "HotFocus": - HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); - if (Focus?.Foreground != attribute.Background) - Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); - break; - } - break; + /// + /// The color for the hotkey when the view is focused. + /// + public Attribute HotFocus { get { return _hotFocus; } set { _hotFocus = SetAttribute (value); } } - case "Base": - switch (callerMemberName) { - case "Normal": - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - break; - case "Focus": - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); - break; - case "HotNormal": - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); - break; - case "HotFocus": - HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); - if (Focus?.Foreground != attribute.Background) - Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); - break; - } - break; - - case "Menu": - switch (callerMemberName) { - case "Normal": - if (Focus?.Background != attribute.Background) - Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus?.Background ?? default); - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - Disabled = Application.Driver.MakeAttribute (Disabled?.Foreground ?? default, attribute.Background); - break; - case "Focus": - Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal?.Background ?? default); - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); - break; - case "HotNormal": - if (Focus?.Background != attribute.Background) - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); - Disabled = Application.Driver.MakeAttribute (Disabled?.Foreground ?? default, attribute.Background); - break; - case "HotFocus": - HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); - if (Focus?.Foreground != attribute.Background) - Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); - break; - case "Disabled": - if (Focus?.Background != attribute.Background) + /// + /// The default color for text, when the view is disabled. + /// + public Attribute Disabled { get { return _disabled; } set { _disabled = SetAttribute (value); } } + + bool preparingScheme = false; + + Attribute SetAttribute (Attribute attribute, [CallerMemberName] string callerMemberName = null) + { + if (!Application._initialized && !preparingScheme) + return attribute; + + if (preparingScheme) + return attribute; + + preparingScheme = true; + switch (caller) { + case "TopLevel": + switch (callerMemberName) { + case "Normal": + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + break; + case "Focus": + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - break; - } - break; - - case "Dialog": - switch (callerMemberName) { - case "Normal": - if (Focus?.Background != attribute.Background) - Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus?.Background ?? default); - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - break; - case "Focus": - Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal?.Background ?? default); - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotFocus": + HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); + if (Focus?.Foreground != attribute.Background) + Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); + break; + } break; - case "HotNormal": - if (Focus?.Background != attribute.Background) + + case "Base": + switch (callerMemberName) { + case "Normal": + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + break; + case "Focus": + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); - if (Normal?.Foreground != attribute.Background) Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + break; + case "HotFocus": + HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); + if (Focus?.Foreground != attribute.Background) + Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); + break; + } break; - case "HotFocus": - HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); - if (Focus?.Foreground != attribute.Background) - Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); + + case "Menu": + switch (callerMemberName) { + case "Normal": + if (Focus?.Background != attribute.Background) + Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus?.Background ?? default); + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + Disabled = Application.Driver.MakeAttribute (Disabled?.Foreground ?? default, attribute.Background); + break; + case "Focus": + Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal?.Background ?? default); + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": + if (Focus?.Background != attribute.Background) + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); + Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + Disabled = Application.Driver.MakeAttribute (Disabled?.Foreground ?? default, attribute.Background); + break; + case "HotFocus": + HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); + if (Focus?.Foreground != attribute.Background) + Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); + break; + case "Disabled": + if (Focus?.Background != attribute.Background) + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); + Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + break; + } break; - } - break; - case "Error": - switch (callerMemberName) { - case "Normal": - HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); - HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + case "Dialog": + switch (callerMemberName) { + case "Normal": + if (Focus?.Background != attribute.Background) + Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus?.Background ?? default); + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + break; + case "Focus": + Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal?.Background ?? default); + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": + if (Focus?.Background != attribute.Background) + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus?.Background ?? default); + if (Normal?.Foreground != attribute.Background) + Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + break; + case "HotFocus": + HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal?.Background ?? default); + if (Focus?.Foreground != attribute.Background) + Focus = Application.Driver.MakeAttribute (Focus?.Foreground ?? default, attribute.Background); + break; + } break; - case "HotNormal": - case "HotFocus": - HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, attribute.Background); - Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + + case "Error": + switch (callerMemberName) { + case "Normal": + HotNormal = Application.Driver.MakeAttribute (HotNormal?.Foreground ?? default, attribute.Background); + HotFocus = Application.Driver.MakeAttribute (HotFocus?.Foreground ?? default, attribute.Background); + break; + case "HotNormal": + case "HotFocus": + HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, attribute.Background); + Normal = Application.Driver.MakeAttribute (Normal?.Foreground ?? default, attribute.Background); + break; + } break; } - break; + preparingScheme = false; + return attribute; } - preparingScheme = false; - return attribute; - } - - /// - /// Compares two objects for equality. - /// - /// - /// true if the two objects are equal - public override bool Equals (object obj) - { - return Equals (obj as ColorScheme); - } - /// - /// Compares two objects for equality. - /// - /// - /// true if the two objects are equal - 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); - } + /// + /// Compares two objects for equality. + /// + /// + /// true if the two objects are equal + public override bool Equals (object obj) + { + return Equals (obj as ColorScheme); + } - /// - /// Returns a hashcode for this instance. - /// - /// hashcode for this instance - 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; - } + /// + /// Compares two objects for equality. + /// + /// + /// true if the two objects are equal + 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); + } - /// - /// Compares two objects for equality. - /// - /// - /// - /// true if the two objects are equivalent - public static bool operator == (ColorScheme left, ColorScheme right) - { - return EqualityComparer.Default.Equals (left, right); - } + /// + /// Returns a hashcode for this instance. + /// + /// hashcode for this instance + 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; + } - /// - /// Compares two objects for inequality. - /// - /// - /// - /// true if the two objects are not equivalent - public static bool operator != (ColorScheme left, ColorScheme right) - { - return !(left == right); - } -} + /// + /// Compares two objects for equality. + /// + /// + /// + /// true if the two objects are equivalent + public static bool operator == (ColorScheme left, ColorScheme right) + { + return EqualityComparer.Default.Equals (left, right); + } -/// -/// The default s for the application. -/// -public static class Colors { - static Colors () - { - // Use reflection to dynamically create the default set of ColorSchemes from the list defined - // by the class. - ColorSchemes = typeof (Colors).GetProperties () - .Where (p => p.PropertyType == typeof (ColorScheme)) - .Select (p => new KeyValuePair (p.Name, new ColorScheme ())) // (ColorScheme)p.GetValue (p))) - .ToDictionary (t => t.Key, t => t.Value); + /// + /// Compares two objects for inequality. + /// + /// + /// + /// true if the two objects are not equivalent + public static bool operator != (ColorScheme left, ColorScheme right) + { + return !(left == right); + } } /// - /// The application toplevel color scheme, for the default toplevel views. + /// The default s for the application. /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["TopLevel"]; - /// - /// - public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); } + public static class Colors { + static Colors () + { + // Use reflection to dynamically create the default set of ColorSchemes from the list defined + // by the class. + ColorSchemes = typeof (Colors).GetProperties () + .Where (p => p.PropertyType == typeof (ColorScheme)) + .Select (p => new KeyValuePair (p.Name, new ColorScheme ())) // (ColorScheme)p.GetValue (p))) + .ToDictionary (t => t.Key, t => t.Value); + } - /// - /// The base color scheme, for the default toplevel views. - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Base"]; - /// - /// - public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); } + /// + /// The application toplevel color scheme, for the default toplevel views. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["TopLevel"]; + /// + /// + public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); } - /// - /// The dialog color scheme, for standard popup dialog boxes - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Dialog"]; - /// - /// - public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); } + /// + /// The base color scheme, for the default toplevel views. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Base"]; + /// + /// + public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); } - /// - /// The menu bar color - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Menu"]; - /// - /// - public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); } + /// + /// The dialog color scheme, for standard popup dialog boxes + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Dialog"]; + /// + /// + public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); } - /// - /// The color scheme for showing errors. - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Error"]; - /// - /// - public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); } + /// + /// The menu bar color + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Menu"]; + /// + /// + public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); } - static ColorScheme GetColorScheme ([CallerMemberName] string callerMemberName = null) - { - return ColorSchemes [callerMemberName]; - } + /// + /// The color scheme for showing errors. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Error"]; + /// + /// + public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); } + + static ColorScheme GetColorScheme ([CallerMemberName] string callerMemberName = null) + { + return ColorSchemes [callerMemberName]; + } - static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string callerMemberName = null) - { - ColorSchemes [callerMemberName] = colorScheme; - colorScheme.caller = callerMemberName; - } + static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string callerMemberName = null) + { + ColorSchemes [callerMemberName] = colorScheme; + colorScheme.caller = callerMemberName; + } - /// - /// Provides the defined s. - /// - public static Dictionary ColorSchemes { get; } -} + /// + /// Provides the defined s. + /// + public static Dictionary ColorSchemes { get; } + } -/// -/// Cursors Visibility that are displayed -/// -// -// Hexa value are set as 0xAABBCCDD where : -// -// AA stand for the TERMINFO DECSUSR parameter value to be used under Linux & MacOS -// BB stand for the NCurses curs_set parameter value to be used under Linux & MacOS -// CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows -// DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows -// -public enum CursorVisibility { /// - /// Cursor caret has default + /// Cursors Visibility that are displayed /// - /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking. - Default = 0x00010119, + // + // Hexa value are set as 0xAABBCCDD where : + // + // AA stand for the TERMINFO DECSUSR parameter value to be used under Linux & MacOS + // BB stand for the NCurses curs_set parameter value to be used under Linux & MacOS + // CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows + // DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows + // + public enum CursorVisibility { + /// + /// Cursor caret has default + /// + /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking. + Default = 0x00010119, - /// - /// Cursor caret is hidden - /// - Invisible = 0x03000019, + /// + /// Cursor caret is hidden + /// + Invisible = 0x03000019, - /// - /// Cursor caret is normally shown as a blinking underline bar _ - /// - Underline = 0x03010119, + /// + /// Cursor caret is normally shown as a blinking underline bar _ + /// + Underline = 0x03010119, - /// - /// Cursor caret is normally shown as a underline bar _ - /// - /// Under Windows, this is equivalent to - UnderlineFix = 0x04010119, + /// + /// Cursor caret is normally shown as a underline bar _ + /// + /// Under Windows, this is equivalent to + UnderlineFix = 0x04010119, - /// - /// Cursor caret is displayed a blinking vertical bar | - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - Vertical = 0x05010119, + /// + /// Cursor caret is displayed a blinking vertical bar | + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + Vertical = 0x05010119, - /// - /// Cursor caret is displayed a blinking vertical bar | - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - VerticalFix = 0x06010119, + /// + /// Cursor caret is displayed a blinking vertical bar | + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + VerticalFix = 0x06010119, - /// - /// Cursor caret is displayed as a blinking block ▉ - /// - Box = 0x01020164, + /// + /// Cursor caret is displayed as a blinking block ▉ + /// + Box = 0x01020164, - /// - /// Cursor caret is displayed a block ▉ - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - BoxFix = 0x02020164, -} + /// + /// Cursor caret is displayed a block ▉ + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + BoxFix = 0x02020164, + } -///// -///// Special characters that can be drawn with -///// -//public enum SpecialChar { -// /// -// /// Horizontal line character. -// /// -// HLine, - -// /// -// /// Vertical line character. -// /// -// VLine, - -// /// -// /// Stipple pattern -// /// -// Stipple, - -// /// -// /// Diamond character -// /// -// Diamond, - -// /// -// /// Upper left corner -// /// -// ULCorner, - -// /// -// /// Lower left corner -// /// -// LLCorner, - -// /// -// /// Upper right corner -// /// -// URCorner, - -// /// -// /// Lower right corner -// /// -// LRCorner, - -// /// -// /// Left tee -// /// -// LeftTee, - -// /// -// /// Right tee -// /// -// RightTee, - -// /// -// /// Top tee -// /// -// TopTee, - -// /// -// /// The bottom tee. -// /// -// BottomTee, -//} - -/// -/// ConsoleDriver is an abstract class that defines the requirements for a console driver. -/// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. -/// -public abstract class ConsoleDriver { - /// - /// The handler fired when the terminal is resized. - /// - protected Action TerminalResized; + ///// + ///// Special characters that can be drawn with + ///// + //public enum SpecialChar { + // /// + // /// Horizontal line character. + // /// + // HLine, + + // /// + // /// Vertical line character. + // /// + // VLine, + + // /// + // /// Stipple pattern + // /// + // Stipple, + + // /// + // /// Diamond character + // /// + // Diamond, + + // /// + // /// Upper left corner + // /// + // ULCorner, + + // /// + // /// Lower left corner + // /// + // LLCorner, + + // /// + // /// Upper right corner + // /// + // URCorner, + + // /// + // /// Lower right corner + // /// + // LRCorner, + + // /// + // /// Left tee + // /// + // LeftTee, + + // /// + // /// Right tee + // /// + // RightTee, + + // /// + // /// Top tee + // /// + // TopTee, + + // /// + // /// The bottom tee. + // /// + // BottomTee, + //} + + /// + /// ConsoleDriver is an abstract class that defines the requirements for a console driver. + /// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. + /// + public abstract class ConsoleDriver { + /// + /// The handler fired when the terminal is resized. + /// + protected Action TerminalResized; - /// - /// The current number of columns in the terminal. - /// - public abstract int Cols { get; } + /// + /// The current number of columns in the terminal. + /// + public abstract int Cols { get; } - /// - /// The current number of rows in the terminal. - /// - public abstract int Rows { get; } + /// + /// The current number of rows in the terminal. + /// + public abstract int Rows { get; } - /// - /// The current left in the terminal. - /// - public abstract int Left { get; } + /// + /// The current left in the terminal. + /// + public abstract int Left { get; } - /// - /// The current top in the terminal. - /// - public abstract int Top { get; } + /// + /// The current top in the terminal. + /// + public abstract int Top { get; } - /// - /// Get the operation system clipboard. - /// - public abstract IClipboard Clipboard { get; } + /// + /// Get the operation system clipboard. + /// + public abstract IClipboard Clipboard { get; } - /// - /// If false height is measured by the window height and thus no scrolling. - /// If true then height is measured by the buffer height, enabling scrolling. - /// - public abstract bool HeightAsBuffer { get; set; } + /// + /// If false height is measured by the window height and thus no scrolling. + /// If true then height is measured by the buffer height, enabling scrolling. + /// + public abstract bool HeightAsBuffer { get; set; } - /// - /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - /// - public virtual int [,,] Contents { get; } + /// + /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag + /// + public virtual int [,,] Contents { get; } - /// - /// Determinates if the current console driver supports TrueColor output- - /// - public virtual bool SupportsTrueColorOutput { get => false; } + /// + /// Determinates if the current console driver supports TrueColor output- + /// + public virtual bool SupportsTrueColorOutput { get => false; } - bool useTrueColor; + bool useTrueColor; - /// - /// Controls the TureColor output mode. Can be only enabled if the underlying ConsoleDriver supports it. - /// Note this will be enabled automaticaly if supported. See also - /// - public bool UseTrueColor { - get => useTrueColor; - set => this.useTrueColor = value && SupportsTrueColorOutput; - } + /// + /// Controls the TureColor output mode. Can be only enabled if the underlying ConsoleDriver supports it. + /// Note this will be enabled automaticaly if supported. See also + /// + public bool UseTrueColor { + get => useTrueColor; + set => this.useTrueColor = value && SupportsTrueColorOutput; + } - /// - /// Initializes the driver - /// - /// Method to invoke when the terminal is resized. - public abstract void Init (Action terminalResized); - /// - /// Moves the cursor to the specified column and row. - /// - /// Column to move the cursor to. - /// Row to move the cursor to. - public abstract void Move (int col, int row); + /// + /// Initializes the driver + /// + /// Method to invoke when the terminal is resized. + public abstract void Init (Action terminalResized); + /// + /// Moves the cursor to the specified column and row. + /// + /// Column to move the cursor to. + /// Row to move the cursor to. + public abstract void Move (int col, int row); - /// - /// Adds the specified rune to the display at the current cursor position. - /// - /// Rune to add. - public abstract void AddRune (Rune rune); + /// + /// Adds the specified rune to the display at the current cursor position. + /// + /// Rune to add. + public abstract void AddRune (Rune rune); - /// - /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 - /// to equivalent, printable, Unicode chars. - /// - /// Rune to translate - /// - public static Rune MakePrintable (Rune c) - { - var controlChars = c & 0xFFFF; - if (controlChars <= 0x1F || controlChars >= 0X7F && controlChars <= 0x9F) { - // ASCII (C0) control characters. - // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) - return new Rune (controlChars + 0x2400); - } - - return c; - } - - /// - /// Ensures that the column and line are in a valid range from the size of the driver. - /// - /// The column. - /// The row. - /// The clip. - /// trueif it's a valid range,falseotherwise. - public bool IsValidContent (int col, int row, Rect clip) => - col >= 0 && row >= 0 && col < Cols && row < Rows && clip.Contains (col, row); - - /// - /// Adds the to the display at the cursor position. - /// - /// String. - public abstract void AddStr (ustring str); - - /// - /// Prepare the driver and set the key and mouse events handlers. - /// - /// The main loop. - /// The handler for ProcessKey - /// The handler for key down events - /// The handler for key up events - /// The handler for mouse events - public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler); - - /// - /// Updates the screen to reflect all the changes that have been done to the display buffer - /// - public abstract void Refresh (); - - /// - /// Updates the location of the cursor position - /// - public abstract void UpdateCursor (); + /// + /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 + /// to equivalent, printable, Unicode chars. + /// + /// Rune to translate + /// + public static Rune MakePrintable (Rune c) + { + var controlChars = c & 0xFFFF; + if (controlChars <= 0x1F || controlChars >= 0X7F && controlChars <= 0x9F) { + // ASCII (C0) control characters. + // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) + return new Rune (controlChars + 0x2400); + } - /// - /// Retreive the cursor caret visibility - /// - /// The current - /// true upon success - public abstract bool GetCursorVisibility (out CursorVisibility visibility); + return c; + } - /// - /// Change the cursor caret visibility - /// - /// The wished - /// true upon success - public abstract bool SetCursorVisibility (CursorVisibility visibility); + /// + /// Ensures that the column and line are in a valid range from the size of the driver. + /// + /// The column. + /// The row. + /// The clip. + /// trueif it's a valid range,falseotherwise. + public bool IsValidContent (int col, int row, Rect clip) => + col >= 0 && row >= 0 && col < Cols && row < Rows && clip.Contains (col, row); - /// - /// Ensure the cursor visibility - /// - /// true upon success - public abstract bool EnsureCursorVisibility (); + /// + /// Adds the to the display at the cursor position. + /// + /// String. + public abstract void AddStr (ustring str); - /// - /// Ends the execution of the console driver. - /// - public abstract void End (); + /// + /// Prepare the driver and set the key and mouse events handlers. + /// + /// The main loop. + /// The handler for ProcessKey + /// The handler for key down events + /// The handler for key up events + /// The handler for mouse events + public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler); - /// - /// Resizes the clip area when the screen is resized. - /// - public abstract void ResizeScreen (); + /// + /// Updates the screen to reflect all the changes that have been done to the display buffer + /// + public abstract void Refresh (); - /// - /// Reset and recreate the contents and the driver buffer. - /// - public abstract void UpdateOffScreen (); + /// + /// Updates the location of the cursor position + /// + public abstract void UpdateCursor (); - /// - /// Redraws the physical screen with the contents that have been queued up via any of the printing commands. - /// - public abstract void UpdateScreen (); + /// + /// Retreive the cursor caret visibility + /// + /// The current + /// true upon success + public abstract bool GetCursorVisibility (out CursorVisibility visibility); - /// - /// Selects the specified attribute as the attribute to use for future calls to AddRune, AddString. - /// - /// C. - public abstract void SetAttribute (Attribute c); + /// + /// Change the cursor caret visibility + /// + /// The wished + /// true upon success + public abstract bool SetCursorVisibility (CursorVisibility visibility); - /// - /// Set Colors from limit sets of colors. - /// - /// Foreground. - /// Background. - public abstract void SetColors (ConsoleColor foreground, ConsoleColor background); + /// + /// Ensure the cursor visibility + /// + /// true upon success + public abstract bool EnsureCursorVisibility (); - // Advanced uses - set colors to any pre-set pairs, you would need to init_color - // that independently with the R, G, B values. - /// - /// Advanced uses - set colors to any pre-set pairs, you would need to init_color - /// that independently with the R, G, B values. - /// - /// Foreground color identifier. - /// Background color identifier. - public abstract void SetColors (short foregroundColorId, short backgroundColorId); + /// + /// Ends the execution of the console driver. + /// + public abstract void End (); - /// - /// Gets the foreground and background colors based on the value. - /// - /// The value. - /// The foreground. - /// The background. - /// - public abstract bool GetColors (int value, out Color foreground, out Color background); + /// + /// Resizes the clip area when the screen is resized. + /// + public abstract void ResizeScreen (); - /// - /// Allows sending keys without typing on a keyboard. - /// - /// The character key. - /// The key. - /// If shift key is sending. - /// If alt key is sending. - /// If control key is sending. - public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control); + /// + /// Reset and recreate the contents and the driver buffer. + /// + public abstract void UpdateOffScreen (); - /// - /// Set the handler when the terminal is resized. - /// - /// - public void SetTerminalResized (Action terminalResized) - { - TerminalResized = terminalResized; - } + /// + /// Redraws the physical screen with the contents that have been queued up via any of the printing commands. + /// + public abstract void UpdateScreen (); - /// - /// Draws the title for a Window-style view incorporating padding. - /// - /// Screen relative region where the frame will be drawn. - /// The title for the window. The title will only be drawn if title is not null or empty and paddingTop is greater than 0. - /// Number of columns to pad on the left (if 0 the border will not appear on the left). - /// Number of rows to pad on the top (if 0 the border and title will not appear on the top). - /// Number of columns to pad on the right (if 0 the border will not appear on the right). - /// Number of rows to pad on the bottom (if 0 the border will not appear on the bottom). - /// Not yet implemented. - /// - public virtual void DrawWindowTitle (Rect region, ustring title, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, TextAlignment textAlignment = TextAlignment.Left) - { - var width = region.Width - (paddingLeft + 2) * 2; - if (!ustring.IsNullOrEmpty (title) && width > 4 && region.Y + paddingTop <= region.Y + paddingBottom) { - Move (region.X + 1 + paddingLeft, region.Y + paddingTop); - AddRune (' '); - var str = title.Sum (r => Math.Max (Rune.ColumnWidth (r), 1)) >= width - ? TextFormatter.Format (title, width - 2, false, false) [0] : title; - AddStr (str); - AddRune (' '); - } - } + /// + /// Selects the specified attribute as the attribute to use for future calls to AddRune, AddString. + /// + /// C. + public abstract void SetAttribute (Attribute c); - /// - /// Enables diagnostic functions - /// - [Flags] - public enum DiagnosticFlags : uint { /// - /// All diagnostics off + /// Set Colors from limit sets of colors. /// - Off = 0b_0000_0000, + /// Foreground. + /// Background. + public abstract void SetColors (ConsoleColor foreground, ConsoleColor background); + + // Advanced uses - set colors to any pre-set pairs, you would need to init_color + // that independently with the R, G, B values. /// - /// When enabled, will draw a - /// ruler in the frame for any side with a padding value greater than 0. + /// Advanced uses - set colors to any pre-set pairs, you would need to init_color + /// that independently with the R, G, B values. /// - FrameRuler = 0b_0000_0001, + /// Foreground color identifier. + /// Background color identifier. + public abstract void SetColors (short foregroundColorId, short backgroundColorId); + /// - /// When Enabled, will use - /// 'L', 'R', 'T', and 'B' for padding instead of ' '. + /// Gets the foreground and background colors based on the value. /// - FramePadding = 0b_0000_0010, - } + /// The value. + /// The foreground. + /// The background. + /// + public abstract bool GetColors (int value, out Color foreground, out Color background); - /// - /// Set flags to enable/disable diagnostics. - /// - public static DiagnosticFlags Diagnostics { get; set; } + /// + /// Allows sending keys without typing on a keyboard. + /// + /// The character key. + /// The key. + /// If shift key is sending. + /// If alt key is sending. + /// If control key is sending. + public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control); - /// - /// Draws a frame for a window with padding and an optional visible border inside the padding. - /// - /// Screen relative region where the frame will be drawn. - /// Number of columns to pad on the left (if 0 the border will not appear on the left). - /// Number of rows to pad on the top (if 0 the border and title will not appear on the top). - /// Number of columns to pad on the right (if 0 the border will not appear on the right). - /// Number of rows to pad on the bottom (if 0 the border will not appear on the bottom). - /// If set to true and any padding dimension is > 0 the border will be drawn. - /// If set to true it will clear the content area (the area inside the padding) with the current color, otherwise the content area will be left untouched. - /// The to be used if defined. - public virtual void DrawWindowFrame (Rect region, int paddingLeft = 0, int paddingTop = 0, int paddingRight = 0, - int paddingBottom = 0, bool border = true, bool fill = false, Border borderContent = null) - { - char clearChar = ' '; - char leftChar = clearChar; - char rightChar = clearChar; - char topChar = clearChar; - char bottomChar = clearChar; - - if ((Diagnostics & DiagnosticFlags.FramePadding) == DiagnosticFlags.FramePadding) { - leftChar = 'L'; - rightChar = 'R'; - topChar = 'T'; - bottomChar = 'B'; - clearChar = 'C'; + /// + /// Set the handler when the terminal is resized. + /// + /// + public void SetTerminalResized (Action terminalResized) + { + TerminalResized = terminalResized; } - void AddRuneAt (int col, int row, Rune ch) + /// + /// Draws the title for a Window-style view incorporating padding. + /// + /// Screen relative region where the frame will be drawn. + /// The title for the window. The title will only be drawn if title is not null or empty and paddingTop is greater than 0. + /// Number of columns to pad on the left (if 0 the border will not appear on the left). + /// Number of rows to pad on the top (if 0 the border and title will not appear on the top). + /// Number of columns to pad on the right (if 0 the border will not appear on the right). + /// Number of rows to pad on the bottom (if 0 the border will not appear on the bottom). + /// Not yet implemented. + /// + public virtual void DrawWindowTitle (Rect region, ustring title, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, TextAlignment textAlignment = TextAlignment.Left) { - Move (col, row); - AddRune (ch); + var width = region.Width - (paddingLeft + 2) * 2; + if (!ustring.IsNullOrEmpty (title) && width > 4 && region.Y + paddingTop <= region.Y + paddingBottom) { + Move (region.X + 1 + paddingLeft, region.Y + paddingTop); + AddRune (' '); + var str = title.Sum (r => Math.Max (Rune.ColumnWidth (r), 1)) >= width + ? TextFormatter.Format (title, width - 2, false, false) [0] : title; + AddStr (str); + AddRune (' '); + } } - // fwidth is count of hLine chars - int fwidth = (int)(region.Width - (paddingRight + paddingLeft)); + /// + /// Enables diagnostic functions + /// + [Flags] + public enum DiagnosticFlags : uint { + /// + /// All diagnostics off + /// + Off = 0b_0000_0000, + /// + /// When enabled, will draw a + /// ruler in the frame for any side with a padding value greater than 0. + /// + FrameRuler = 0b_0000_0001, + /// + /// When Enabled, will use + /// 'L', 'R', 'T', and 'B' for padding instead of ' '. + /// + FramePadding = 0b_0000_0010, + } - // fheight is count of vLine chars - int fheight = (int)(region.Height - (paddingBottom + paddingTop)); + /// + /// Set flags to enable/disable diagnostics. + /// + public static DiagnosticFlags Diagnostics { get; set; } - // fleft is location of left frame line - int fleft = region.X + paddingLeft - 1; + /// + /// Draws a frame for a window with padding and an optional visible border inside the padding. + /// + /// Screen relative region where the frame will be drawn. + /// Number of columns to pad on the left (if 0 the border will not appear on the left). + /// Number of rows to pad on the top (if 0 the border and title will not appear on the top). + /// Number of columns to pad on the right (if 0 the border will not appear on the right). + /// Number of rows to pad on the bottom (if 0 the border will not appear on the bottom). + /// If set to true and any padding dimension is > 0 the border will be drawn. + /// If set to true it will clear the content area (the area inside the padding) with the current color, otherwise the content area will be left untouched. + /// The to be used if defined. + public virtual void DrawWindowFrame (Rect region, int paddingLeft = 0, int paddingTop = 0, int paddingRight = 0, + int paddingBottom = 0, bool border = true, bool fill = false, Border borderContent = null) + { + char clearChar = ' '; + char leftChar = clearChar; + char rightChar = clearChar; + char topChar = clearChar; + char bottomChar = clearChar; + + if ((Diagnostics & DiagnosticFlags.FramePadding) == DiagnosticFlags.FramePadding) { + leftChar = 'L'; + rightChar = 'R'; + topChar = 'T'; + bottomChar = 'B'; + clearChar = 'C'; + } - // fright is location of right frame line - int fright = fleft + fwidth + 1; + void AddRuneAt (int col, int row, Rune ch) + { + Move (col, row); + AddRune (ch); + } - // ftop is location of top frame line - int ftop = region.Y + paddingTop - 1; + // fwidth is count of hLine chars + int fwidth = (int)(region.Width - (paddingRight + paddingLeft)); + + // fheight is count of vLine chars + int fheight = (int)(region.Height - (paddingBottom + paddingTop)); + + // fleft is location of left frame line + int fleft = region.X + paddingLeft - 1; + + // fright is location of right frame line + int fright = fleft + fwidth + 1; + + // ftop is location of top frame line + int ftop = region.Y + paddingTop - 1; + + // fbottom is location of bottom frame line + int fbottom = ftop + fheight + 1; + + var borderStyle = borderContent == null ? BorderStyle.Single : borderContent.BorderStyle; + + Rune hLine = default, vLine = default, + uRCorner = default, uLCorner = default, lLCorner = default, lRCorner = default; + + if (border) { + switch (borderStyle) { + case BorderStyle.None: + break; + case BorderStyle.Single: + hLine = HLine; + vLine = VLine; + uRCorner = URCorner; + uLCorner = ULCorner; + lLCorner = LLCorner; + lRCorner = LRCorner; + break; + case BorderStyle.Double: + hLine = HDLine; + vLine = VDLine; + uRCorner = URDCorner; + uLCorner = ULDCorner; + lLCorner = LLDCorner; + lRCorner = LRDCorner; + break; + case BorderStyle.Rounded: + hLine = HRLine; + vLine = VRLine; + uRCorner = URRCorner; + uLCorner = ULRCorner; + lLCorner = LLRCorner; + lRCorner = LRRCorner; + break; + } + } else { + hLine = vLine = uRCorner = uLCorner = lLCorner = lRCorner = clearChar; + } - // fbottom is location of bottom frame line - int fbottom = ftop + fheight + 1; + // Outside top + if (paddingTop > 1) { + for (int r = region.Y; r < ftop; r++) { + for (int c = region.X; c < region.X + region.Width; c++) { + AddRuneAt (c, r, topChar); + } + } + } - var borderStyle = borderContent == null ? BorderStyle.Single : borderContent.BorderStyle; + // Outside top-left + for (int c = region.X; c < fleft; c++) { + AddRuneAt (c, ftop, leftChar); + } - Rune hLine = default, vLine = default, - uRCorner = default, uLCorner = default, lLCorner = default, lRCorner = default; + // Frame top-left corner + AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : leftChar); - if (border) { - switch (borderStyle) { - case BorderStyle.None: - break; - case BorderStyle.Single: - hLine = HLine; - vLine = VLine; - uRCorner = URCorner; - uLCorner = ULCorner; - lLCorner = LLCorner; - lRCorner = LRCorner; - break; - case BorderStyle.Double: - hLine = HDLine; - vLine = VDLine; - uRCorner = URDCorner; - uLCorner = ULDCorner; - lLCorner = LLDCorner; - lRCorner = LRDCorner; - break; - case BorderStyle.Rounded: - hLine = HRLine; - vLine = VRLine; - uRCorner = URRCorner; - uLCorner = ULRCorner; - lLCorner = LLRCorner; - lRCorner = LRRCorner; - break; + // Frame top + for (int c = fleft + 1; c < fleft + 1 + fwidth; c++) { + AddRuneAt (c, ftop, paddingTop > 0 ? hLine : topChar); } - } else { - hLine = vLine = uRCorner = uLCorner = lLCorner = lRCorner = clearChar; - } - // Outside top - if (paddingTop > 1) { - for (int r = region.Y; r < ftop; r++) { - for (int c = region.X; c < region.X + region.Width; c++) { - AddRuneAt (c, r, topChar); - } + // Frame top-right corner + if (fright > fleft) { + AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : rightChar); } - } - // Outside top-left - for (int c = region.X; c < fleft; c++) { - AddRuneAt (c, ftop, leftChar); - } + // Outside top-right corner + for (int c = fright + 1; c < fright + paddingRight; c++) { + AddRuneAt (c, ftop, rightChar); + } - // Frame top-left corner - AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : leftChar); + // Left, Fill, Right + if (fbottom > ftop) { + for (int r = ftop + 1; r < fbottom; r++) { + // Outside left + for (int c = region.X; c < fleft; c++) { + AddRuneAt (c, r, leftChar); + } - // Frame top - for (int c = fleft + 1; c < fleft + 1 + fwidth; c++) { - AddRuneAt (c, ftop, paddingTop > 0 ? hLine : topChar); - } + // Frame left + AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : leftChar); - // Frame top-right corner - if (fright > fleft) { - AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : rightChar); - } + // Fill + if (fill) { + for (int x = fleft + 1; x < fright; x++) { + AddRuneAt (x, r, clearChar); + } + } - // Outside top-right corner - for (int c = fright + 1; c < fright + paddingRight; c++) { - AddRuneAt (c, ftop, rightChar); - } + // Frame right + if (fright > fleft) { + var v = vLine; + if ((Diagnostics & DiagnosticFlags.FrameRuler) == DiagnosticFlags.FrameRuler) { + v = (char)(((int)'0') + ((r - ftop) % 10)); // vLine; + } + AddRuneAt (fright, r, paddingRight > 0 ? v : rightChar); + } - // Left, Fill, Right - if (fbottom > ftop) { - for (int r = ftop + 1; r < fbottom; r++) { - // Outside left - for (int c = region.X; c < fleft; c++) { - AddRuneAt (c, r, leftChar); + // Outside right + for (int c = fright + 1; c < fright + paddingRight; c++) { + AddRuneAt (c, r, rightChar); + } } - // Frame left - AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : leftChar); - - // Fill - if (fill) { - for (int x = fleft + 1; x < fright; x++) { - AddRuneAt (x, r, clearChar); - } + // Outside Bottom + for (int c = region.X; c < region.X + region.Width; c++) { + AddRuneAt (c, fbottom, leftChar); } - // Frame right + // Frame bottom-left + AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : leftChar); + if (fright > fleft) { - var v = vLine; - if ((Diagnostics & DiagnosticFlags.FrameRuler) == DiagnosticFlags.FrameRuler) { - v = (char)(((int)'0') + ((r - ftop) % 10)); // vLine; + // Frame bottom + for (int c = fleft + 1; c < fright; c++) { + var h = hLine; + if ((Diagnostics & DiagnosticFlags.FrameRuler) == DiagnosticFlags.FrameRuler) { + h = (char)(((int)'0') + ((c - fleft) % 10)); // hLine; + } + AddRuneAt (c, fbottom, paddingBottom > 0 ? h : bottomChar); } - AddRuneAt (fright, r, paddingRight > 0 ? v : rightChar); + + // Frame bottom-right + AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : rightChar); } // Outside right for (int c = fright + 1; c < fright + paddingRight; c++) { - AddRuneAt (c, r, rightChar); + AddRuneAt (c, fbottom, rightChar); } } - // Outside Bottom - for (int c = region.X; c < region.X + region.Width; c++) { - AddRuneAt (c, fbottom, leftChar); - } - - // Frame bottom-left - AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : leftChar); - - if (fright > fleft) { - // Frame bottom - for (int c = fleft + 1; c < fright; c++) { - var h = hLine; - if ((Diagnostics & DiagnosticFlags.FrameRuler) == DiagnosticFlags.FrameRuler) { - h = (char)(((int)'0') + ((c - fleft) % 10)); // hLine; + // Out bottom - ensure top is always drawn if we overlap + if (paddingBottom > 0) { + for (int r = fbottom + 1; r < fbottom + paddingBottom; r++) { + for (int c = region.X; c < region.X + region.Width; c++) { + AddRuneAt (c, r, bottomChar); } - AddRuneAt (c, fbottom, paddingBottom > 0 ? h : bottomChar); } - - // Frame bottom-right - AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : rightChar); - } - - // Outside right - for (int c = fright + 1; c < fright + paddingRight; c++) { - AddRuneAt (c, fbottom, rightChar); } } - // Out bottom - ensure top is always drawn if we overlap - if (paddingBottom > 0) { - for (int r = fbottom + 1; r < fbottom + paddingBottom; r++) { - for (int c = region.X; c < region.X + region.Width; c++) { - AddRuneAt (c, r, bottomChar); - } - } + /// + /// Draws a frame on the specified region with the specified padding around the frame. + /// + /// Screen relative region where the frame will be drawn. + /// Padding to add on the sides. + /// If set to true it will clear the contents with the current color, otherwise the contents will be left untouched. + /// This API has been superseded by . + /// This API is equivalent to calling DrawWindowFrame(Rect, p - 1, p - 1, p - 1, p - 1). In other words, + /// A padding value of 0 means there is actually a one cell border. + /// + public virtual void DrawFrame (Rect region, int padding, bool fill) + { + // DrawFrame assumes the border is always at least one row/col thick + // DrawWindowFrame assumes a padding of 0 means NO padding and no frame + DrawWindowFrame (new Rect (region.X, region.Y, region.Width, region.Height), + padding + 1, padding + 1, padding + 1, padding + 1, border: false, fill: fill); } - } - /// - /// Draws a frame on the specified region with the specified padding around the frame. - /// - /// Screen relative region where the frame will be drawn. - /// Padding to add on the sides. - /// If set to true it will clear the contents with the current color, otherwise the contents will be left untouched. - /// This API has been superseded by . - /// This API is equivalent to calling DrawWindowFrame(Rect, p - 1, p - 1, p - 1, p - 1). In other words, - /// A padding value of 0 means there is actually a one cell border. - /// - public virtual void DrawFrame (Rect region, int padding, bool fill) - { - // DrawFrame assumes the border is always at least one row/col thick - // DrawWindowFrame assumes a padding of 0 means NO padding and no frame - DrawWindowFrame (new Rect (region.X, region.Y, region.Width, region.Height), - padding + 1, padding + 1, padding + 1, padding + 1, border: false, fill: fill); - } + /// + /// Suspend the application, typically needs to save the state, suspend the app and upon return, reset the console driver. + /// + public abstract void Suspend (); - /// - /// Suspend the application, typically needs to save the state, suspend the app and upon return, reset the console driver. - /// - public abstract void Suspend (); + Rect clip; - Rect clip; + /// + /// Controls the current clipping region that AddRune/AddStr is subject to. + /// + /// The clip. + public Rect Clip { + get => clip; + set => this.clip = value; + } - /// - /// Controls the current clipping region that AddRune/AddStr is subject to. - /// - /// The clip. - public Rect Clip { - get => clip; - set => this.clip = value; - } + /// + /// Start of mouse moves. + /// + public abstract void StartReportingMouseMoves (); - /// - /// Start of mouse moves. - /// - public abstract void StartReportingMouseMoves (); + /// + /// Stop reporting mouses moves. + /// + public abstract void StopReportingMouseMoves (); - /// - /// Stop reporting mouses moves. - /// - public abstract void StopReportingMouseMoves (); + /// + /// Disables the cooked event processing from the mouse driver. At startup, it is assumed mouse events are cooked. + /// + public abstract void UncookMouse (); - /// - /// Disables the cooked event processing from the mouse driver. At startup, it is assumed mouse events are cooked. - /// - public abstract void UncookMouse (); + /// + /// Enables the cooked event processing from the mouse driver + /// + public abstract void CookMouse (); - /// - /// Enables the cooked event processing from the mouse driver - /// - public abstract void CookMouse (); + /// + /// Horizontal line character. + /// + public Rune HLine = '\u2500'; - /// - /// Horizontal line character. - /// - public Rune HLine = '\u2500'; + /// + /// Vertical line character. + /// + public Rune VLine = '\u2502'; - /// - /// Vertical line character. - /// - public Rune VLine = '\u2502'; + /// + /// Stipple pattern + /// + public Rune Stipple = '\u2591'; - /// - /// Stipple pattern - /// - public Rune Stipple = '\u2591'; + /// + /// Diamond character + /// + public Rune Diamond = '\u25ca'; - /// - /// Diamond character - /// - public Rune Diamond = '\u25ca'; + /// + /// Upper left corner + /// + public Rune ULCorner = '\u250C'; - /// - /// Upper left corner - /// - public Rune ULCorner = '\u250C'; + /// + /// Lower left corner + /// + public Rune LLCorner = '\u2514'; - /// - /// Lower left corner - /// - public Rune LLCorner = '\u2514'; + /// + /// Upper right corner + /// + public Rune URCorner = '\u2510'; - /// - /// Upper right corner - /// - public Rune URCorner = '\u2510'; + /// + /// Lower right corner + /// + public Rune LRCorner = '\u2518'; - /// - /// Lower right corner - /// - public Rune LRCorner = '\u2518'; + /// + /// Left tee + /// + public Rune LeftTee = '\u251c'; - /// - /// Left tee - /// - public Rune LeftTee = '\u251c'; + /// + /// Right tee + /// + public Rune RightTee = '\u2524'; - /// - /// Right tee - /// - public Rune RightTee = '\u2524'; + /// + /// Top tee + /// + public Rune TopTee = '\u252c'; - /// - /// Top tee - /// - public Rune TopTee = '\u252c'; + /// + /// The bottom tee. + /// + public Rune BottomTee = '\u2534'; - /// - /// The bottom tee. - /// - public Rune BottomTee = '\u2534'; + /// + /// Checkmark. + /// + public Rune Checked = '\u221a'; - /// - /// Checkmark. - /// - public Rune Checked = '\u221a'; + /// + /// Un-checked checkmark. + /// + public Rune UnChecked = '\u2574'; - /// - /// Un-checked checkmark. - /// - public Rune UnChecked = '\u2574'; + /// + /// Selected mark. + /// + public Rune Selected = '\u25cf'; - /// - /// Selected mark. - /// - public Rune Selected = '\u25cf'; + /// + /// Un-selected selected mark. + /// + public Rune UnSelected = '\u25cc'; - /// - /// Un-selected selected mark. - /// - public Rune UnSelected = '\u25cc'; + /// + /// Right Arrow. + /// + public Rune RightArrow = '\u25ba'; - /// - /// Right Arrow. - /// - public Rune RightArrow = '\u25ba'; + /// + /// Left Arrow. + /// + public Rune LeftArrow = '\u25c4'; - /// - /// Left Arrow. - /// - public Rune LeftArrow = '\u25c4'; + /// + /// Down Arrow. + /// + public Rune DownArrow = '\u25bc'; - /// - /// Down Arrow. - /// - public Rune DownArrow = '\u25bc'; + /// + /// Up Arrow. + /// + public Rune UpArrow = '\u25b2'; - /// - /// Up Arrow. - /// - public Rune UpArrow = '\u25b2'; + /// + /// Left indicator for default action (e.g. for ). + /// + public Rune LeftDefaultIndicator = '\u25e6'; - /// - /// Left indicator for default action (e.g. for ). - /// - public Rune LeftDefaultIndicator = '\u25e6'; + /// + /// Right indicator for default action (e.g. for ). + /// + public Rune RightDefaultIndicator = '\u25e6'; - /// - /// Right indicator for default action (e.g. for ). - /// - public Rune RightDefaultIndicator = '\u25e6'; + /// + /// Left frame/bracket (e.g. '[' for ). + /// + public Rune LeftBracket = '['; - /// - /// Left frame/bracket (e.g. '[' for ). - /// - public Rune LeftBracket = '['; + /// + /// Right frame/bracket (e.g. ']' for ). + /// + public Rune RightBracket = ']'; - /// - /// Right frame/bracket (e.g. ']' for ). - /// - public Rune RightBracket = ']'; + /// + /// Blocks Segment indicator for meter views (e.g. . + /// + public Rune BlocksMeterSegment = '\u258c'; - /// - /// Blocks Segment indicator for meter views (e.g. . - /// - public Rune BlocksMeterSegment = '\u258c'; + /// + /// Continuous Segment indicator for meter views (e.g. . + /// + public Rune ContinuousMeterSegment = '\u2588'; - /// - /// Continuous Segment indicator for meter views (e.g. . - /// - public Rune ContinuousMeterSegment = '\u2588'; + /// + /// Horizontal double line character. + /// + public Rune HDLine = '\u2550'; - /// - /// Horizontal double line character. - /// - public Rune HDLine = '\u2550'; + /// + /// Vertical double line character. + /// + public Rune VDLine = '\u2551'; - /// - /// Vertical double line character. - /// - public Rune VDLine = '\u2551'; + /// + /// Upper left double corner + /// + public Rune ULDCorner = '\u2554'; - /// - /// Upper left double corner - /// - public Rune ULDCorner = '\u2554'; + /// + /// Lower left double corner + /// + public Rune LLDCorner = '\u255a'; - /// - /// Lower left double corner - /// - public Rune LLDCorner = '\u255a'; + /// + /// Upper right double corner + /// + public Rune URDCorner = '\u2557'; - /// - /// Upper right double corner - /// - public Rune URDCorner = '\u2557'; + /// + /// Lower right double corner + /// + public Rune LRDCorner = '\u255d'; - /// - /// Lower right double corner - /// - public Rune LRDCorner = '\u255d'; + /// + /// Horizontal line character for rounded corners. + /// + public Rune HRLine = '\u2500'; - /// - /// Horizontal line character for rounded corners. - /// - public Rune HRLine = '\u2500'; + /// + /// Vertical line character for rounded corners. + /// + public Rune VRLine = '\u2502'; - /// - /// Vertical line character for rounded corners. - /// - public Rune VRLine = '\u2502'; + /// + /// Upper left rounded corner + /// + public Rune ULRCorner = '\u256d'; - /// - /// Upper left rounded corner - /// - public Rune ULRCorner = '\u256d'; + /// + /// Lower left rounded corner + /// + public Rune LLRCorner = '\u2570'; - /// - /// Lower left rounded corner - /// - public Rune LLRCorner = '\u2570'; + /// + /// Upper right rounded corner + /// + public Rune URRCorner = '\u256e'; - /// - /// Upper right rounded corner - /// - public Rune URRCorner = '\u256e'; + /// + /// Lower right rounded corner + /// + public Rune LRRCorner = '\u256f'; - /// - /// Lower right rounded corner - /// - public Rune LRRCorner = '\u256f'; + /// + /// Make the attribute for the foreground and background colors. + /// + /// Foreground. + /// Background. + /// + public abstract Attribute MakeAttribute (Color fore, Color back); - /// - /// Make the attribute for the foreground and background colors. - /// - /// Foreground. - /// Background. - /// - public abstract Attribute MakeAttribute (Color fore, Color back); + /// + /// Gets the current . + /// + /// The current attribute. + public abstract Attribute GetAttribute (); - /// - /// Gets the current . - /// - /// The current attribute. - public abstract Attribute GetAttribute (); + /// + /// Make the for the . + /// + /// The foreground color. + /// The background color. + /// The attribute for the foreground and background colors. + public abstract Attribute MakeColor (Color foreground, Color background); - /// - /// Make the for the . - /// - /// The foreground color. - /// The background color. - /// The attribute for the foreground and background colors. - public abstract Attribute MakeColor (Color foreground, Color background); + /// + /// Create all with the for the console driver. + /// + /// Flag indicating if colors are supported. + public void CreateColors (bool hasColors = true) + { + Colors.TopLevel = new ColorScheme (); + Colors.Base = new ColorScheme (); + Colors.Dialog = new ColorScheme (); + Colors.Menu = new ColorScheme (); + Colors.Error = new ColorScheme (); + + if (!hasColors) { + return; + } - /// - /// Create all with the for the console driver. - /// - /// Flag indicating if colors are supported. - public void CreateColors (bool hasColors = true) - { - Colors.TopLevel = new ColorScheme (); - Colors.Base = new ColorScheme (); - Colors.Dialog = new ColorScheme (); - Colors.Menu = new ColorScheme (); - Colors.Error = new ColorScheme (); - - if (!hasColors) { - return; + Colors.TopLevel.Normal = MakeColor (Color.BrightGreen, Color.Black); + Colors.TopLevel.Focus = MakeColor (Color.White, Color.Cyan); + Colors.TopLevel.HotNormal = MakeColor (Color.Brown, Color.Black); + Colors.TopLevel.HotFocus = MakeColor (Color.Blue, Color.Cyan); + Colors.TopLevel.Disabled = MakeColor (Color.DarkGray, Color.Black); + + Colors.Base.Normal = MakeColor (Color.White, Color.Blue); + Colors.Base.Focus = MakeColor (Color.Black, Color.Gray); + Colors.Base.HotNormal = MakeColor (Color.BrightCyan, Color.Blue); + Colors.Base.HotFocus = MakeColor (Color.BrightBlue, Color.Gray); + Colors.Base.Disabled = MakeColor (Color.DarkGray, Color.Blue); + + Colors.Dialog.Normal = MakeColor (Color.Black, Color.Gray); + Colors.Dialog.Focus = MakeColor (Color.White, Color.DarkGray); + Colors.Dialog.HotNormal = MakeColor (Color.Blue, Color.Gray); + Colors.Dialog.HotFocus = MakeColor (Color.BrightYellow, Color.DarkGray); + Colors.Dialog.Disabled = MakeColor (Color.Gray, Color.DarkGray); + + Colors.Menu.Normal = MakeColor (Color.White, Color.DarkGray); + Colors.Menu.Focus = MakeColor (Color.White, Color.Black); + Colors.Menu.HotNormal = MakeColor (Color.BrightYellow, Color.DarkGray); + Colors.Menu.HotFocus = MakeColor (Color.BrightYellow, Color.Black); + Colors.Menu.Disabled = MakeColor (Color.Gray, Color.DarkGray); + + Colors.Error.Normal = MakeColor (Color.Red, Color.White); + Colors.Error.Focus = MakeColor (Color.Black, Color.BrightRed); + Colors.Error.HotNormal = MakeColor (Color.Black, Color.White); + Colors.Error.HotFocus = MakeColor (Color.White, Color.BrightRed); + Colors.Error.Disabled = MakeColor (Color.DarkGray, Color.White); } - - Colors.TopLevel.Normal = MakeColor (Color.BrightGreen, Color.Black); - Colors.TopLevel.Focus = MakeColor (Color.White, Color.Cyan); - Colors.TopLevel.HotNormal = MakeColor (Color.Brown, Color.Black); - Colors.TopLevel.HotFocus = MakeColor (Color.Blue, Color.Cyan); - Colors.TopLevel.Disabled = MakeColor (Color.DarkGray, Color.Black); - - Colors.Base.Normal = MakeColor (Color.White, Color.Blue); - Colors.Base.Focus = MakeColor (Color.Black, Color.Gray); - Colors.Base.HotNormal = MakeColor (Color.BrightCyan, Color.Blue); - Colors.Base.HotFocus = MakeColor (Color.BrightBlue, Color.Gray); - Colors.Base.Disabled = MakeColor (Color.DarkGray, Color.Blue); - - Colors.Dialog.Normal = MakeColor (Color.Black, Color.Gray); - Colors.Dialog.Focus = MakeColor (Color.White, Color.DarkGray); - Colors.Dialog.HotNormal = MakeColor (Color.Blue, Color.Gray); - Colors.Dialog.HotFocus = MakeColor (Color.BrightYellow, Color.DarkGray); - Colors.Dialog.Disabled = MakeColor (Color.Gray, Color.DarkGray); - - Colors.Menu.Normal = MakeColor (Color.White, Color.DarkGray); - Colors.Menu.Focus = MakeColor (Color.White, Color.Black); - Colors.Menu.HotNormal = MakeColor (Color.BrightYellow, Color.DarkGray); - Colors.Menu.HotFocus = MakeColor (Color.BrightYellow, Color.Black); - Colors.Menu.Disabled = MakeColor (Color.Gray, Color.DarkGray); - - Colors.Error.Normal = MakeColor (Color.Red, Color.White); - Colors.Error.Focus = MakeColor (Color.Black, Color.BrightRed); - Colors.Error.HotNormal = MakeColor (Color.Black, Color.White); - Colors.Error.HotFocus = MakeColor (Color.White, Color.BrightRed); - Colors.Error.Disabled = MakeColor (Color.DarkGray, Color.White); } } -} From 5133e02fee01e0d325a9d9cb426f423a18c0d1ff Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 4 Dec 2022 12:40:18 +0000 Subject: [PATCH 3/6] Added more comprehensive tests and fixed the brown color value --- Terminal.Gui/Core/ConsoleDriver.cs | 7 ++- UnitTests/TureColorAttributeTests.cs | 82 ++++++++++++++-------------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index a78af8e58e..8a8e846c2c 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -347,6 +347,9 @@ private void PopulateTrueColorsFromConsoleColors () private TrueColor ConsoleColorToTrueColor (Color consoleColor) { + // Brown in cmd is + // C1 9C 00 + switch (consoleColor) { case Color.Black: return new TrueColor (0, 0, 0); case Color.Blue: return new TrueColor (0, 0, 0x80); @@ -354,7 +357,7 @@ private TrueColor ConsoleColorToTrueColor (Color consoleColor) case Color.Cyan: return new TrueColor (0, 0x80, 0x80); case Color.Red: return new TrueColor (0x80, 0, 0); case Color.Magenta: return new TrueColor (0x80, 0, 0x80); - case Color.Brown: return new TrueColor (0xA5, 0x2A, 0x2A); // TODO confirm this + case Color.Brown: return new TrueColor (0xC1, 0x9C, 0x00); // TODO confirm this case Color.Gray: return new TrueColor (0xC0, 0xC0, 0xC0); case Color.DarkGray: return new TrueColor (0x80, 0x80, 0x80); case Color.BrightBlue: return new TrueColor (0, 0, 0xFF); @@ -385,7 +388,7 @@ private Color TrueColorToConsoleColor (TrueColor trueColor) { new TrueColor (0, 0x80, 0x80),Color.Cyan}, { new TrueColor (0x80, 0, 0),Color.Red}, { new TrueColor (0x80, 0, 0x80),Color.Magenta}, - { new TrueColor (0xA5, 0x2A, 0x2A),Color.Brown}, // TODO confirm this + { new TrueColor (0xC1, 0x9C, 0x00),Color.Brown}, // TODO confirm this { new TrueColor (0xC0, 0xC0, 0xC0),Color.Gray}, { new TrueColor (0x80, 0x80, 0x80),Color.DarkGray}, { new TrueColor (0, 0, 0xFF),Color.BrightBlue}, diff --git a/UnitTests/TureColorAttributeTests.cs b/UnitTests/TureColorAttributeTests.cs index b3ae52b878..9d3c9c1ee2 100644 --- a/UnitTests/TureColorAttributeTests.cs +++ b/UnitTests/TureColorAttributeTests.cs @@ -1,3 +1,4 @@ +using System; using Xunit; // Alias Console to MockConsole so we don't accidentally use Console @@ -35,50 +36,49 @@ public void Constuctors_Constuct () Application.Shutdown (); } - [Fact] - public void Basic_Colors_Fallback () + [InlineData (new object [] { 127, 0, 0, Color.Red })] + [InlineData(new object [] { 128, 0, 0, Color.Red})] + [InlineData (new object [] { 130, 0, 0, Color.Red })] + [InlineData (new object [] { 200, 0, 0, Color.BrightRed })] + [InlineData (new object [] { 245, 0, 0, Color.BrightRed })] + [InlineData (new object [] { 255, 0, 0, Color.BrightRed })] + + [InlineData (new object [] { 0, 128, 0, Color.Green })] + //[InlineData (new object [] { 128, 128, 0, Color.Brown })] // TODO : This was an original test in the PR I have kept it but current it does not map to Brown (it maps to BrightYellow) + [InlineData (new object [] { 0, 0, 128, Color.Blue })] + [InlineData (new object [] { 128, 0, 128, Color.Magenta })] + [InlineData (new object [] { 0, 128, 128, Color.Cyan })] + [InlineData (new object [] { 0, 255, 0, Color.BrightGreen })] + [InlineData (new object [] { 255, 255, 0, Color.BrightYellow })] + [InlineData (new object [] { 0, 0, 255, Color.BrightBlue })] + [InlineData (new object [] { 255, 0, 255, Color.BrightMagenta })] + [InlineData (new object [] { 0, 255, 255, Color.BrightCyan })] + [InlineData (new object [] { 128, 128, 128, Color.DarkGray })] + [InlineData (new object [] { 255, 255, 255, Color.White })] + [InlineData (new object [] { 192, 192, 192, Color.Gray })] + [InlineData (new object [] { 0, 0, 0, Color.Black })] + [Theory, AutoInitShutdown] + public void Basic_Colors_Fallback (int r, int g, int b, Color expectedColor) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); - driver.Init (() => { }); - - // Test bright basic colors - var attr = new Attribute (new TrueColor (128, 0, 0), new TrueColor (0, 128, 0)); - Assert.Equal (Color.Red, attr.Foreground); - Assert.Equal (Color.Green, attr.Background); - - attr = new Attribute (new TrueColor (128, 128, 0), new TrueColor (0, 0, 128)); - Assert.Equal (Color.Brown, attr.Foreground); - Assert.Equal (Color.Blue, attr.Background); - - attr = new Attribute (new TrueColor (128, 0, 128), new TrueColor (0, 128, 128)); - Assert.Equal (Color.Magenta, attr.Foreground); - Assert.Equal (Color.Cyan, attr.Background); - - // Test basic colors - attr = new Attribute (new TrueColor (255, 0, 0), new TrueColor (0, 255, 0)); - Assert.Equal (Color.BrightRed, attr.Foreground); - Assert.Equal (Color.BrightGreen, attr.Background); - - attr = new Attribute (new TrueColor (255, 255, 0), new TrueColor (0, 0, 255)); - Assert.Equal (Color.BrightYellow, attr.Foreground); - Assert.Equal (Color.BrightBlue, attr.Background); - - attr = new Attribute (new TrueColor (255, 0, 255), new TrueColor (0, 255, 255)); - Assert.Equal (Color.BrightMagenta, attr.Foreground); - Assert.Equal (Color.BrightCyan, attr.Background); - - // Test gray basic colors - attr = new Attribute (new TrueColor (128, 128, 128), new TrueColor (255, 255, 255)); - Assert.Equal (Color.DarkGray, attr.Foreground); - Assert.Equal (Color.White, attr.Background); - - attr = new Attribute (new TrueColor (192, 192, 192), new TrueColor (0, 0, 0)); - Assert.Equal (Color.Gray, attr.Foreground); + // Test foreground color property + var attr = new Attribute (new TrueColor (r, g, b), new TrueColor (0,0,0)); + Assert.Equal (expectedColor, attr.Foreground); + Assert.Equal (Color.Black, attr.Background); + + // Test background color property + attr = new Attribute (new TrueColor (0, 0, 0), new TrueColor (r, g, b)); + Assert.Equal (Color.Black, attr.Foreground); + Assert.Equal (expectedColor, attr.Background); + + // Test 5 up + attr = new Attribute (new TrueColor (Math.Min (255,r+5), Math.Min(255,g+5), Math.Min (255, b+5)), new TrueColor (0, 0, 0)); + Assert.Equal (expectedColor, attr.Foreground); Assert.Equal (Color.Black, attr.Background); - driver.End (); - Application.Shutdown (); + // Test 5 down + attr = new Attribute (new TrueColor (Math.Max (0, r - 5), Math.Max (0, g - 5), Math.Max (0, b - 5)), new TrueColor (0, 0, 0)); + Assert.Equal (expectedColor, attr.Foreground); + Assert.Equal (Color.Black, attr.Background); } } } \ No newline at end of file From 056137d3ff62463dddc69beb30029b9435b6bdcf Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 4 Dec 2022 13:07:50 +0000 Subject: [PATCH 4/6] Add image scenario --- UICatalog/Scenarios/Images.cs | 123 ++++++++++++++++++++++++++++++++++ UICatalog/UICatalog.csproj | 3 + 2 files changed, 126 insertions(+) create mode 100644 UICatalog/Scenarios/Images.cs diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs new file mode 100644 index 0000000000..d0c671b1b0 --- /dev/null +++ b/UICatalog/Scenarios/Images.cs @@ -0,0 +1,123 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using Terminal.Gui; +using Attribute = Terminal.Gui.Attribute; + +namespace UICatalog.Scenarios { + [ScenarioMetadata (Name: "Images", Description: "Demonstration of how to render an image with/without true color support.")] + [ScenarioCategory ("Colors")] + public class Images : Scenario + { + public override void Setup () + { + base.Setup (); + + var x = 0; + var y = 0; + + var canTrueColor = Application.Driver.SupportsTrueColorOutput; + + var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") { + X = x, + Y = y++ + }; + Win.Add (lblDriverName); + y++; + + var cbSupportsTrueColor = new CheckBox ("Driver supports true color ") { + X = x, + Y = y++, + Checked = canTrueColor, + CanFocus = false + }; + Win.Add (cbSupportsTrueColor); + + var cbUseTrueColor = new CheckBox ("Use true color") { + X = x, + Y = y++, + Checked = Application.Driver.UseTrueColor, + Enabled = canTrueColor, + }; + cbUseTrueColor.Toggled += (_) => Application.Driver.UseTrueColor = cbUseTrueColor.Checked; + Win.Add (cbUseTrueColor); + + var btnOpenImage = new Button ("Open Image") { + X = x, + Y = y++ + }; + Win.Add (btnOpenImage); + + var imageView = new ImageView () { + X = x, + Y = y++, + Width = Dim.Fill(), + Height = Dim.Fill(), + }; + Win.Add (imageView); + + btnOpenImage.Clicked += () => { + var ofd = new OpenDialog ("Open Image", "Image"); + Application.Run (ofd); + + var path = ofd.FilePath.ToString (); + + if (string.IsNullOrWhiteSpace (path)) { + return; + } + + if(!File.Exists(path)) { + return; + } + + imageView.SetImage(Image.Load (File.ReadAllBytes (path))); + }; + } + + class ImageView : View { + + private Image fullResImage; + private Image matchSize; + + ConcurrentDictionary cache = new ConcurrentDictionary(); + + internal void SetImage (Image image) + { + fullResImage = image; + this.SetNeedsDisplay (); + } + + public override void Redraw (Rect bounds) + { + base.Redraw (bounds); + + if (fullResImage == null) { + return; + } + + // if we have not got a cached resized image of this size + if(matchSize == null || bounds.Width != matchSize.Width || bounds.Height != matchSize.Height) { + + // generate one + matchSize = fullResImage.Clone (x => x.Resize (bounds.Width, bounds.Height)); + } + + for (int y = 0; y < bounds.Height; y++) { + for (int x = 0; x < bounds.Width; x++) { + var rgb = matchSize [x, y]; + + var attr = cache.GetOrAdd (rgb, (rgb) => new Attribute (new TrueColor (), new TrueColor (rgb.R, rgb.G, rgb.B))); + + Driver.SetAttribute(attr); + AddRune (x, y, ' '); + } + } + } + } + } +} \ No newline at end of file diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index 234474a265..bdbdcddf00 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -18,6 +18,9 @@ TRACE;DEBUG_IDISPOSABLE + + + From bb81bc905f16db4ab5d0648de35a20787cace329 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 4 Dec 2022 14:01:18 +0000 Subject: [PATCH 5/6] Scrap HSV distance for color comparisons and just use RGB --- Terminal.Gui/Core/ConsoleDriver.cs | 65 +++--------------------------- 1 file changed, 5 insertions(+), 60 deletions(-) diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 8a8e846c2c..ac36c04cf0 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -416,66 +416,11 @@ private Color TrueColorToConsoleColor (TrueColor trueColor) private float CalculateDistance (TrueColor color1, TrueColor color2) { - RGBtoHSV (color1, out var h1, out var s1, out var v1); - RGBtoHSV (color2, out var h2, out var s2, out var v2); - - - if(float.IsNaN(h1)) { - h1 = -1f; - } - - if (float.IsNaN (h2)) { - h2 = -1f; - } - - // the distance we are from this point in HSV space - // with an emphasis on Hue - return Math.Abs(h1 - h2) + - Math.Abs (s1 - s2) + - Math.Abs (v1 - v2); - } - - void RGBtoHSV (TrueColor color, out float h, out float s, out float v) - { - RGBtoHSV (color.Red / 255f, color.Green / 255f, color.Blue / 255f, out h, out s, out v); - } - - - // https://www.cs.rit.edu/~ncs/color/t_convert.html - // r,g,b values are from 0 to 1 - // h = [0,360], s = [0,1], v = [0,1] - // if s == 0, then h = -1 (undefined) - - void RGBtoHSV (float r, float g, float b, out float h, out float s, out float v) - { - float min, max, delta; - - min = Math.Min (Math.Min (r, g), b); - max = Math.Max (Math.Max (r, g), b); - v = max; // v - - delta = max - min; - - if (max != 0) - s = delta / max; // s - else { - // r = g = b = 0 // s = 0, v is undefined - s = 0; - h = -1; - return; - } - - if (r == max) - h = (g - b) / delta; // between yellow & magenta - else if (g == max) - h = 2 + (b - r) / delta; // between cyan & yellow - else - h = 4 + (r - g) / delta; // between magenta & cyan - - h *= 60; // degrees - if (h < 0) - h += 360; - + // use RGB distance + return + Math.Abs (color1.Red - color2.Red) + + Math.Abs (color1.Green - color2.Green) + + Math.Abs (color1.Blue - color2.Blue); } } From 635b534f10855cd84a9551761372c7e6f075dd86 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 4 Dec 2022 14:24:09 +0000 Subject: [PATCH 6/6] Add try/catch for opening image --- UICatalog/Scenarios/Images.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs index d0c671b1b0..7496ffec7e 100644 --- a/UICatalog/Scenarios/Images.cs +++ b/UICatalog/Scenarios/Images.cs @@ -75,7 +75,17 @@ public override void Setup () return; } - imageView.SetImage(Image.Load (File.ReadAllBytes (path))); + Image img; + + try { + img = Image.Load (File.ReadAllBytes (path)); + } catch (Exception ex) { + + MessageBox.ErrorQuery ("Could not open file", ex.Message, "Ok"); + return; + } + + imageView.SetImage(img); }; }