diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 7c4c8ff44..ff5bfe4a9 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -2,9 +2,9 @@ name: Build & Test Terminal.Gui with .NET Core on: push: - branches: [ main, develop ] + branches: [ main, develop, v2_develop ] pull_request: - branches: [ main, develop ] + branches: [ main, develop, v2_develop ] jobs: build: diff --git a/Example/Example.csproj b/Example/Example.csproj index 69d7bd305..845bf6d6a 100644 --- a/Example/Example.csproj +++ b/Example/Example.csproj @@ -5,10 +5,10 @@ - 1.0 - 1.0 - 1.0 - 1.0 + 2.0 + 2.0 + 2.0 + 2.0 diff --git a/README.md b/README.md index 636aa542b..1fb484212 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ [![License](https://img.shields.io/github/license/gui-cs/gui.cs.svg)](LICENSE) ![Bugs](https://img.shields.io/github/issues/gui-cs/gui.cs/bug) -# Terminal.Gui - Cross Platform Terminal UI toolkit for .NET +# Terminal.Gui v2.x - Cross Platform Terminal UI toolkit for .NET A toolkit for building rich console apps for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix. +NOTE: This is the WORK IN PROGRESS `v2.x` branch. The `main` branch is the stable `v1.x` branch. + ![Sample app](docfx/images/sample.gif) ## Quick Start diff --git a/ReactiveExample/ReactiveExample.csproj b/ReactiveExample/ReactiveExample.csproj index 736cd84a0..6d2346f8f 100644 --- a/ReactiveExample/ReactiveExample.csproj +++ b/ReactiveExample/ReactiveExample.csproj @@ -5,14 +5,14 @@ - 1.0 - 1.0 - 1.0 - 1.0 + 2.0 + 2.0 + 2.0 + 2.0 - - + + diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index 85e5c440f..fac2fa85b 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -44,7 +44,7 @@ namespace Terminal.Gui.Configuration { /// 3. Application configuration found in the applications's resources (Resources/config.json). /// /// - /// 4. Global configuration found in the the user's home directory (~/.tui/config.json). + /// 4. Global configuration found in the user's home directory (~/.tui/config.json). /// /// /// 5. Global configuration found in the directory the app was launched from (./.tui/config.json). @@ -63,7 +63,7 @@ namespace Terminal.Gui.Configuration { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, WriteIndented = true, Converters = { - // No need to set converterss - the ConfigRootConverter uses property attributes apply the correct + // No need to set converters - the ConfigRootConverter uses property attributes apply the correct // Converter. }, }; @@ -196,7 +196,7 @@ namespace Terminal.Gui.Configuration { /// /// /// Is until is called. Gets set to a new instance by - /// deserializtion (see ). + /// deserialization (see ). /// private static SettingsScope? _settings; @@ -223,14 +223,14 @@ namespace Terminal.Gui.Configuration { public static ThemeManager? Themes => ThemeManager.Instance; /// - /// Aplication-specific configuration settings scope. + /// Application-specific configuration settings scope. /// [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true), JsonPropertyName ("AppSettings")] public static AppScope? AppSettings { get; set; } /// - /// Initializes the internal state of ConfiguraitonManager. Nominally called once as part of application - /// startup to initilaize global state. Also called from some Unit Tests to ensure correctness (e.g. Reset()). + /// Initializes the internal state of ConfigurationManager. Nominally called once as part of application + /// startup to initialize global state. Also called from some Unit Tests to ensure correctness (e.g. Reset()). /// internal static void Initialize () { @@ -260,7 +260,7 @@ namespace Terminal.Gui.Configuration { select p) { if (p.GetCustomAttribute (typeof (SerializableConfigurationProperty)) is SerializableConfigurationProperty scp) { if (p.GetGetMethod (true)!.IsStatic) { - // If the class name is ommited, JsonPropertyName is allowed. + // If the class name is omitted, JsonPropertyName is allowed. _allConfigProperties!.Add (scp.OmitClassName ? ConfigProperty.GetJsonPropertyName (p) : $"{p.DeclaringType?.Name}.{p.Name}", new ConfigProperty { PropertyInfo = p, PropertyValue = null @@ -357,7 +357,7 @@ namespace Terminal.Gui.Configuration { } /// - /// Event fired when the configuration has been upddated from a configuration source. + /// Event fired when the configuration has been updated from a configuration source. /// application. /// public static event Action? Updated; @@ -378,7 +378,7 @@ namespace Terminal.Gui.Configuration { } ClearJsonErrors (); - + Settings = new SettingsScope (); ThemeManager.Reset (); AppSettings = new AppScope (); @@ -402,7 +402,7 @@ namespace Terminal.Gui.Configuration { /// to generate the JSON doc that is embedded into Terminal.Gui (during development). /// /// - /// WARNING: The Terminal.Gui.Resources.config.json resource has setting defintions (Themes) + /// WARNING: The Terminal.Gui.Resources.config.json resource has setting definitions (Themes) /// that are NOT generated by this function. If you use this function to regenerate Terminal.Gui.Resources.config.json, /// make sure you copy the Theme definitions from the existing Terminal.Gui.Resources.config.json file. /// @@ -455,7 +455,7 @@ namespace Terminal.Gui.Configuration { public static string AppName { get; set; } = Assembly.GetEntryAssembly ()?.FullName?.Split (',') [0]?.Trim ()!; /// - /// Describes the location of the configuration files. The constancts can be + /// Describes the location of the configuration files. The constants can be /// combined (bitwise) to specify multiple locations. /// [Flags] @@ -488,27 +488,26 @@ namespace Terminal.Gui.Configuration { public static ConfigLocations Locations { get; set; } = ConfigLocations.All; /// - /// Loads all settings found in the various configuraiton storage locations to + /// Loads all settings found in the various configuration storage locations to /// the . Optionally, - /// resets all settings attributed with to the defaults - /// defined in . + /// resets all settings attributed with to the defaults. /// /// /// Use to cause the loaded settings to be applied to the running application. /// /// If the state of will - /// be reset to the defaults defined in . + /// be reset to the defaults. public static void Load (bool reset = false) { Debug.WriteLine ($"ConfigurationManager.Load()"); if (reset) Reset (); - // LibraryResoruces is always loaded by Reset + // LibraryResources is always loaded by Reset if (Locations == ConfigLocations.All) { var embeddedStylesResourceName = Assembly.GetEntryAssembly ()? .GetManifestResourceNames ().FirstOrDefault (x => x.EndsWith (_configFilename)); - if (string.IsNullOrEmpty(embeddedStylesResourceName)) { + if (string.IsNullOrEmpty (embeddedStylesResourceName)) { embeddedStylesResourceName = _configFilename; } diff --git a/Terminal.Gui/Configuration/Scope.cs b/Terminal.Gui/Configuration/Scope.cs index 7e8aceb79..da4e5c43f 100644 --- a/Terminal.Gui/Configuration/Scope.cs +++ b/Terminal.Gui/Configuration/Scope.cs @@ -129,9 +129,17 @@ namespace Terminal.Gui.Configuration { } } var readHelper = Activator.CreateInstance ((Type?)typeof (ReadHelper<>).MakeGenericType (typeof (scopeT), propertyType!)!, converter) as ReadHelper; - scope! [propertyName].PropertyValue = readHelper?.Read (ref reader, propertyType!, options); + try { + scope! [propertyName].PropertyValue = readHelper?.Read (ref reader, propertyType!, options); + } catch (NotSupportedException e) { + throw new JsonException ($"Error reading property \"{propertyName}\" of type \"{propertyType?.Name}\".", e); + } } else { - scope! [propertyName].PropertyValue = JsonSerializer.Deserialize (ref reader, propertyType!, options); + try { + scope! [propertyName].PropertyValue = JsonSerializer.Deserialize (ref reader, propertyType!, options); + } catch (Exception ex) { + System.Diagnostics.Debug.WriteLine ($"scopeT Read: {ex}"); + } } } else { // It is not a config property. Maybe it's just a property on the Scope with [JsonInclude] diff --git a/Terminal.Gui/Configuration/SettingsScope.cs b/Terminal.Gui/Configuration/SettingsScope.cs index f66a2bc4b..0c36af658 100644 --- a/Terminal.Gui/Configuration/SettingsScope.cs +++ b/Terminal.Gui/Configuration/SettingsScope.cs @@ -34,6 +34,9 @@ namespace Terminal.Gui.Configuration { [JsonInclude, JsonPropertyName ("$schema")] public string Schema { get; set; } = "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json"; + /// + /// The list of paths to the configuration files. + /// public List Sources = new List (); /// diff --git a/Terminal.Gui/Configuration/ThemeScope.cs b/Terminal.Gui/Configuration/ThemeScope.cs index aab8e0068..22da7e4f2 100644 --- a/Terminal.Gui/Configuration/ThemeScope.cs +++ b/Terminal.Gui/Configuration/ThemeScope.cs @@ -120,21 +120,21 @@ namespace Terminal.Gui.Configuration { /// public static ThemeManager Instance { get { return _instance; } } - private static string theme = string.Empty; + private static string _theme = string.Empty; /// /// The currently selected theme. This is the internal version; see . /// [JsonInclude, SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true), JsonPropertyName ("Theme")] internal static string SelectedTheme { - get => theme; + get => _theme; set { - var oldTheme = theme; - theme = value; - if (oldTheme != theme && + var oldTheme = _theme; + _theme = value; + if (oldTheme != _theme && ConfigurationManager.Settings! ["Themes"]?.PropertyValue is Dictionary themes && - themes.ContainsKey (theme)) { - ConfigurationManager.Settings! ["Theme"].PropertyValue = theme; + themes.ContainsKey (_theme)) { + ConfigurationManager.Settings! ["Theme"].PropertyValue = _theme; Instance.OnThemeChanged (oldTheme); } } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index ec2220519..7a7b09402 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -1,9 +1,6 @@ // // Driver.cs: Curses-based Driver // -// Authors: -// Miguel de Icaza (miguel@gnome.org) -// using System; using System.Collections.Generic; using System.Diagnostics; @@ -23,7 +20,7 @@ namespace Terminal.Gui { public override int Rows => Curses.Lines; public override int Left => 0; public override int Top => 0; - public override bool HeightAsBuffer { get; set; } + public override bool EnableConsoleScrolling { get; set; } public override IClipboard Clipboard { get => clipboard; } CursorVisibility? initialCursorVisibility = null; @@ -116,7 +113,7 @@ namespace Terminal.Gui { if (runeWidth < 0 || runeWidth > 0) { ccol++; } - + if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { contents [crow, ccol, 1] = CurrentAttribute; @@ -158,26 +155,10 @@ namespace Terminal.Gui { public override void End () { - if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) { - StopReportingMouseMoves (); - } - + StopReportingMouseMoves (); SetCursorVisibility (CursorVisibility.Default); Curses.endwin (); - - // I'm commenting this because was used in a trying to fix the Linux hanging and forgot to exclude it. - // Clear and reset entire screen. - //Console.Out.Write ("\x1b[2J"); - //Console.Out.Flush (); - - // Set top and bottom lines of a window. - //Console.Out.Write ("\x1b[1;25r"); - //Console.Out.Flush (); - - //Set cursor key to cursor. - //Console.Out.Write ("\x1b[?1l"); - //Console.Out.Flush (); } public override void UpdateScreen () => window.redrawwin (); @@ -324,305 +305,6 @@ namespace Terminal.Gui { } } - Curses.Event? lastMouseButtonPressed; - bool isButtonPressed; - bool cancelButtonClicked; - bool isReportMousePosition; - Point point; - int buttonPressedCount; - - MouseEvent ToDriverMouse (Curses.MouseEvent cev) - { - MouseFlags mouseFlag = MouseFlags.AllEvents; - - if (lastMouseButtonPressed != null && cev.ButtonState != Curses.Event.ReportMousePosition) { - lastMouseButtonPressed = null; - isButtonPressed = false; - } - - if (cev.ButtonState == Curses.Event.Button1Pressed - || cev.ButtonState == Curses.Event.Button2Pressed - || cev.ButtonState == Curses.Event.Button3Pressed) { - - isButtonPressed = true; - buttonPressedCount++; - } else { - buttonPressedCount = 0; - } - //System.Diagnostics.Debug.WriteLine ($"buttonPressedCount: {buttonPressedCount}"); - - if (buttonPressedCount == 2 - && (cev.ButtonState == Curses.Event.Button1Pressed - || cev.ButtonState == Curses.Event.Button2Pressed - || cev.ButtonState == Curses.Event.Button3Pressed)) { - - switch (cev.ButtonState) { - case Curses.Event.Button1Pressed: - mouseFlag = MouseFlags.Button1DoubleClicked; - break; - - case Curses.Event.Button2Pressed: - mouseFlag = MouseFlags.Button2DoubleClicked; - break; - - case Curses.Event.Button3Pressed: - mouseFlag = MouseFlags.Button3DoubleClicked; - break; - } - cancelButtonClicked = true; - - } else if (buttonPressedCount == 3 - && (cev.ButtonState == Curses.Event.Button1Pressed - || cev.ButtonState == Curses.Event.Button2Pressed - || cev.ButtonState == Curses.Event.Button3Pressed)) { - - switch (cev.ButtonState) { - case Curses.Event.Button1Pressed: - mouseFlag = MouseFlags.Button1TripleClicked; - break; - - case Curses.Event.Button2Pressed: - mouseFlag = MouseFlags.Button2TripleClicked; - break; - - case Curses.Event.Button3Pressed: - mouseFlag = MouseFlags.Button3TripleClicked; - break; - } - buttonPressedCount = 0; - - } else if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked || - cev.ButtonState == Curses.Event.Button3Clicked) && - lastMouseButtonPressed == null) { - - isButtonPressed = false; - mouseFlag = ProcessButtonClickedEvent (cev); - - } else if (((cev.ButtonState == Curses.Event.Button1Pressed || cev.ButtonState == Curses.Event.Button2Pressed || - cev.ButtonState == Curses.Event.Button3Pressed) && lastMouseButtonPressed == null) || - isButtonPressed && lastMouseButtonPressed != null && cev.ButtonState == Curses.Event.ReportMousePosition) { - - mouseFlag = MapCursesButton (cev.ButtonState); - if (cev.ButtonState != Curses.Event.ReportMousePosition) - lastMouseButtonPressed = cev.ButtonState; - isButtonPressed = true; - isReportMousePosition = false; - - if (cev.ButtonState == Curses.Event.ReportMousePosition) { - mouseFlag = MapCursesButton ((Curses.Event)lastMouseButtonPressed) | MouseFlags.ReportMousePosition; - cancelButtonClicked = true; - } - point = new Point () { - X = cev.X, - Y = cev.Y - }; - - if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag)); - return false; - }); - } - - - } else if ((cev.ButtonState == Curses.Event.Button1Released || cev.ButtonState == Curses.Event.Button2Released || - cev.ButtonState == Curses.Event.Button3Released)) { - - mouseFlag = ProcessButtonReleasedEvent (cev); - isButtonPressed = false; - - } else if (cev.ButtonState == Curses.Event.ButtonWheeledUp) { - - mouseFlag = MouseFlags.WheeledUp; - - } else if (cev.ButtonState == Curses.Event.ButtonWheeledDown) { - - mouseFlag = MouseFlags.WheeledDown; - - } else if ((cev.ButtonState & (Curses.Event.ButtonWheeledUp & Curses.Event.ButtonShift)) != 0) { - - mouseFlag = MouseFlags.WheeledLeft; - - } else if ((cev.ButtonState & (Curses.Event.ButtonWheeledDown & Curses.Event.ButtonShift)) != 0) { - - mouseFlag = MouseFlags.WheeledRight; - - } else if (cev.ButtonState == Curses.Event.ReportMousePosition) { - if (cev.X != point.X || cev.Y != point.Y) { - mouseFlag = MouseFlags.ReportMousePosition; - isReportMousePosition = true; - point = new Point (); - } else { - mouseFlag = 0; - } - - } else { - mouseFlag = 0; - var eFlags = cev.ButtonState; - foreach (Enum value in Enum.GetValues (eFlags.GetType ())) { - if (eFlags.HasFlag (value)) { - mouseFlag |= MapCursesButton ((Curses.Event)value); - } - } - } - - mouseFlag = SetControlKeyStates (cev, mouseFlag); - - return new MouseEvent () { - X = cev.X, - Y = cev.Y, - //Flags = MapCursesButton (cev.ButtonState) - Flags = mouseFlag - }; - } - - MouseFlags ProcessButtonClickedEvent (Curses.MouseEvent cev) - { - lastMouseButtonPressed = cev.ButtonState; - var mf = GetButtonState (cev, true); - mouseHandler (ProcessButtonState (cev, mf)); - if (lastMouseButtonPressed != null && lastMouseButtonPressed == cev.ButtonState) { - mf = GetButtonState (cev, false); - mouseHandler (ProcessButtonState (cev, mf)); - if (lastMouseButtonPressed != null && lastMouseButtonPressed == cev.ButtonState) { - mf = MapCursesButton (cev.ButtonState); - } - } - lastMouseButtonPressed = null; - isButtonPressed = false; - return mf; - } - - MouseFlags ProcessButtonReleasedEvent (Curses.MouseEvent cev) - { - var mf = MapCursesButton (cev.ButtonState); - if (!cancelButtonClicked && lastMouseButtonPressed == null && !isReportMousePosition) { - mouseHandler (ProcessButtonState (cev, mf)); - mf = GetButtonState (cev); - } else if (isReportMousePosition) { - mf = MouseFlags.ReportMousePosition; - } - cancelButtonClicked = false; - return mf; - } - - async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) - { - while (isButtonPressed) { - await Task.Delay (100); - var me = new MouseEvent () { - X = point.X, - Y = point.Y, - Flags = mouseFlag - }; - - var view = Application.WantContinuousButtonPressedView; - if (view == null) - break; - if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.Invoke (() => mouseHandler (me)); - } - } - } - - MouseFlags GetButtonState (Curses.MouseEvent cev, bool pressed = false) - { - MouseFlags mf = default; - switch (cev.ButtonState) { - case Curses.Event.Button1Clicked: - if (pressed) - mf = MouseFlags.Button1Pressed; - else - mf = MouseFlags.Button1Released; - break; - - case Curses.Event.Button2Clicked: - if (pressed) - mf = MouseFlags.Button2Pressed; - else - mf = MouseFlags.Button2Released; - break; - - case Curses.Event.Button3Clicked: - if (pressed) - mf = MouseFlags.Button3Pressed; - else - mf = MouseFlags.Button3Released; - break; - - case Curses.Event.Button1Released: - mf = MouseFlags.Button1Clicked; - break; - - case Curses.Event.Button2Released: - mf = MouseFlags.Button2Clicked; - break; - - case Curses.Event.Button3Released: - mf = MouseFlags.Button3Clicked; - break; - - } - return mf; - } - - MouseEvent ProcessButtonState (Curses.MouseEvent cev, MouseFlags mf) - { - return new MouseEvent () { - X = cev.X, - Y = cev.Y, - Flags = mf - }; - } - - MouseFlags MapCursesButton (Curses.Event cursesButton) - { - switch (cursesButton) { - case Curses.Event.Button1Pressed: return MouseFlags.Button1Pressed; - case Curses.Event.Button1Released: return MouseFlags.Button1Released; - case Curses.Event.Button1Clicked: return MouseFlags.Button1Clicked; - case Curses.Event.Button1DoubleClicked: return MouseFlags.Button1DoubleClicked; - case Curses.Event.Button1TripleClicked: return MouseFlags.Button1TripleClicked; - case Curses.Event.Button2Pressed: return MouseFlags.Button2Pressed; - case Curses.Event.Button2Released: return MouseFlags.Button2Released; - case Curses.Event.Button2Clicked: return MouseFlags.Button2Clicked; - case Curses.Event.Button2DoubleClicked: return MouseFlags.Button2DoubleClicked; - case Curses.Event.Button2TrippleClicked: return MouseFlags.Button2TripleClicked; - case Curses.Event.Button3Pressed: return MouseFlags.Button3Pressed; - case Curses.Event.Button3Released: return MouseFlags.Button3Released; - case Curses.Event.Button3Clicked: return MouseFlags.Button3Clicked; - case Curses.Event.Button3DoubleClicked: return MouseFlags.Button3DoubleClicked; - case Curses.Event.Button3TripleClicked: return MouseFlags.Button3TripleClicked; - case Curses.Event.ButtonWheeledUp: return MouseFlags.WheeledUp; - case Curses.Event.ButtonWheeledDown: return MouseFlags.WheeledDown; - case Curses.Event.Button4Pressed: return MouseFlags.Button4Pressed; - case Curses.Event.Button4Released: return MouseFlags.Button4Released; - case Curses.Event.Button4Clicked: return MouseFlags.Button4Clicked; - case Curses.Event.Button4DoubleClicked: return MouseFlags.Button4DoubleClicked; - case Curses.Event.Button4TripleClicked: return MouseFlags.Button4TripleClicked; - case Curses.Event.ButtonShift: return MouseFlags.ButtonShift; - case Curses.Event.ButtonCtrl: return MouseFlags.ButtonCtrl; - case Curses.Event.ButtonAlt: return MouseFlags.ButtonAlt; - case Curses.Event.ReportMousePosition: return MouseFlags.ReportMousePosition; - case Curses.Event.AllEvents: return MouseFlags.AllEvents; - default: return 0; - } - } - - static MouseFlags SetControlKeyStates (Curses.MouseEvent cev, MouseFlags mouseFlag) - { - if ((cev.ButtonState & Curses.Event.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0) - mouseFlag |= MouseFlags.ButtonCtrl; - - if ((cev.ButtonState & Curses.Event.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0) - mouseFlag |= MouseFlags.ButtonShift; - - if ((cev.ButtonState & Curses.Event.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0) - mouseFlag |= MouseFlags.ButtonAlt; - return mouseFlag; - } - - KeyModifiers keyModifiers; KeyModifiers MapKeyModifiers (Key key) @@ -656,9 +338,18 @@ namespace Terminal.Gui { ProcessWinChange (); } if (wch == Curses.KeyMouse) { - Curses.getmouse (out Curses.MouseEvent ev); - //System.Diagnostics.Debug.WriteLine ($"ButtonState: {ev.ButtonState}; ID: {ev.ID}; X: {ev.X}; Y: {ev.Y}; Z: {ev.Z}"); - mouseHandler (ToDriverMouse (ev)); + int wch2 = wch; + + while (wch2 == Curses.KeyMouse) { + KeyEvent key = null; + ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false) + }; + code = 0; + GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); + } return; } k = MapCursesKey (wch); @@ -694,7 +385,7 @@ namespace Terminal.Gui { k = Key.AltMask | MapCursesKey (wch); } if (code == 0) { - KeyEvent key; + KeyEvent key = null; // The ESC-number handling, debatable. // Simulates the AltMask itself by pressing Alt + Space. @@ -706,55 +397,13 @@ namespace Terminal.Gui { k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + (wch2 + 64)); } else if (wch2 >= (uint)Key.D0 && wch2 <= (uint)Key.D9) { k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - (uint)Key.D0)); - } else if (wch2 == 27) { - k = (Key)wch2; - } else if (wch2 == Curses.KEY_CODE_SEQ) { - int [] c = null; - while (code == 0) { - code = Curses.get_wch (out wch2); - if (wch2 > 0) { - Array.Resize (ref c, c == null ? 1 : c.Length + 1); - c [c.Length - 1] = wch2; - } - } - if (c [0] == 49 && c [1] == 59 && c [2] == 55 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Alt+(F1 - F4) - wch2 = c [3] + 185; - k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Alt+(F5 - F8) - wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215; - k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 50 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Ctrl+Alt+(F9 - F12) - wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; - k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Shift+Alt+(F1 - F4) - wch2 = c [3] + 185; - k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Shift+Alt+(F5 - F8) - wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215; - k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 50 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Ctrl+Shift+Alt+(F9 - F12) - wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; - k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [1] == 59 && c [2] == 52 && c [3] == 83) { // Shift+Alt+(F4) - wch2 = 268; - k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Shift+Alt+(F5 - F8) - wch2 = c [1] < 55 ? c [1] + 216 : c [1] + 215; - k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 50 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Shift+Alt+(F9 - F12) - wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; - k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 54 && c [1] == 59 && c [2] == 56 && c [3] == 126) { // Shift+Ctrl+Alt+KeyNPage - k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageDown; - } else if (c [0] == 53 && c [1] == 59 && c [2] == 56 && c [3] == 126) { // Shift+Ctrl+Alt+KeyPPage - k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageUp; - } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 72) { // Shift+Ctrl+Alt+KeyHome - k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.Home; - } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 70) { // Shift+Ctrl+Alt+KeyEnd - k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.End; - } else { - k = MapCursesKey (wch2); - } + } else if (wch2 == Curses.KeyCSI) { + ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false) + }; + GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); + return; } else { // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa. if (((Key)wch2 & Key.CtrlMask) != 0) { @@ -809,6 +458,52 @@ namespace Terminal.Gui { //} } + void GetEscSeq (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref ConsoleKeyInfo [] cki) + { + ConsoleKey ck = 0; + ConsoleModifiers mod = 0; + while (code == 0) { + code = Curses.get_wch (out wch2); + var consoleKeyInfo = new ConsoleKeyInfo ((char)wch2, 0, false, false, false); + if (wch2 == 0 || wch2 == 27 || wch2 == Curses.KeyMouse) { + EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out List mouseFlags, out Point pos, out _, ProcessContinuousButtonPressed); + if (isKeyMouse) { + foreach (var mf in mouseFlags) { + ProcessMouseEvent (mf, pos); + } + cki = null; + if (wch2 == 27) { + cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), cki); + } + } else { + k = ConsoleKeyMapping.MapConsoleKeyToKey (consoleKeyInfo.Key, out _); + k = ConsoleKeyMapping.MapKeyModifiers (consoleKeyInfo, k); + key = new KeyEvent (k, MapKeyModifiers (k)); + keyDownHandler (key); + keyHandler (key); + } + } else { + cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki); + } + } + } + + void ProcessMouseEvent (MouseFlags mouseFlag, Point pos) + { + var me = new MouseEvent () { + Flags = mouseFlag, + X = pos.X, + Y = pos.Y + }; + mouseHandler (me); + } + + void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) + { + ProcessMouseEvent (mouseFlag, pos); + } + Action keyHandler; Action keyDownHandler; Action keyUpHandler; @@ -835,17 +530,12 @@ namespace Terminal.Gui { }; } - Curses.Event oldMouseEvents, reportableMouseEvents; public override void Init (Action terminalResized) { if (window != null) return; try { - //Set cursor key to application. - //Console.Out.Write ("\x1b[?1h"); - //Console.Out.Flush (); - window = Curses.initscr (); Curses.set_escdelay (10); } catch (Exception e) { @@ -892,10 +582,8 @@ namespace Terminal.Gui { Curses.noecho (); Curses.Window.Standard.keypad (true); - reportableMouseEvents = Curses.mousemask (Curses.Event.AllEvents | Curses.Event.ReportMousePosition, out oldMouseEvents); TerminalResized = terminalResized; - if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) - StartReportingMouseMoves (); + StartReportingMouseMoves (); CurrentAttribute = MakeColor (Color.White, Color.Black); @@ -944,8 +632,7 @@ namespace Terminal.Gui { public override void ResizeScreen () { Clip = new Rect (0, 0, Cols, Rows); - Console.Out.Write ("\x1b[3J"); - Console.Out.Flush (); + Curses.refresh (); } public override void UpdateOffScreen () @@ -1065,25 +752,21 @@ namespace Terminal.Gui { public override void Suspend () { - if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) - StopReportingMouseMoves (); + StopReportingMouseMoves (); Platform.Suspend (); Curses.Window.Standard.redrawwin (); Curses.refresh (); - if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) - StartReportingMouseMoves (); + StartReportingMouseMoves (); } public override void StartReportingMouseMoves () { - Console.Out.Write ("\x1b[?1003h"); - Console.Out.Flush (); + Console.Out.Write (EscSeqUtils.EnableMouseEvents); } public override void StopReportingMouseMoves () { - Console.Out.Write ("\x1b[?1003l"); - Console.Out.Flush (); + Console.Out.Write (EscSeqUtils.DisableMouseEvents); } //int lastMouseInterval; @@ -1126,7 +809,6 @@ namespace Terminal.Gui { if (visibility != CursorVisibility.Invisible) { Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF); - Console.Out.Flush (); } currentCursorVisibility = visibility; @@ -1191,8 +873,8 @@ namespace Terminal.Gui { background = default; int back = -1; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains ((value >> 12) & 0xffff)) { hasColor = true; back = (value >> 12) & 0xffff; @@ -1285,6 +967,7 @@ namespace Terminal.Gui { bool CheckSupport () { +#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. try { var (exitCode, result) = ClipboardProcessRunner.Bash ("which xclip", waitForOutput: true); if (exitCode == 0 && result.FileExists ()) { @@ -1294,6 +977,7 @@ namespace Terminal.Gui { } catch (Exception) { // Permissions issue. } +#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. return false; } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs index 977913fa2..df5359542 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs @@ -1,30 +1,6 @@ // // mainloop.cs: Simple managed mainloop implementation. // -// Authors: -// Miguel de Icaza (miguel.de.icaza@gmail.com) -// -// Copyright (C) 2011 Novell (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -52,7 +28,7 @@ namespace Terminal.Gui { } /// - /// Condition on which to wake up from file descriptor activity. These match the Linux/BSD poll definitions. + /// Condition on which to wake up from file descriptor activity. These match the Linux/BSD poll definitions. /// [Flags] public enum Condition : short { @@ -127,10 +103,10 @@ namespace Terminal.Gui { } /// - /// Removes an active watch from the mainloop. + /// Removes an active watch from the mainloop. /// /// - /// The token parameter is the value returned from AddWatch + /// The token parameter is the value returned from AddWatch /// public void RemoveWatch (object token) { diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs index b1b60c3a5..cde433744 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs @@ -6,7 +6,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,8 +21,6 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Threading; - - namespace Unix.Terminal { /// /// Represents a dynamically loaded unmanaged library in a (partially) platform independent manner. @@ -45,7 +43,7 @@ namespace Unix.Terminal { static bool IsNetCore; public static bool IsMacOSPlatform => IsMacOS; - + [DllImport ("libc")] static extern int uname (IntPtr buf); @@ -105,11 +103,11 @@ namespace Unix.Terminal { // public UnmanagedLibrary (string [] libraryPathAlternatives, bool isFullPath) { - if (isFullPath){ + if (isFullPath) { this.libraryPath = FirstValidLibraryPath (libraryPathAlternatives); this.handle = PlatformSpecificLoadLibrary (this.libraryPath); } else { - foreach (var lib in libraryPathAlternatives){ + foreach (var lib in libraryPathAlternatives) { this.handle = PlatformSpecificLoadLibrary (lib); if (this.handle != IntPtr.Zero) break; @@ -164,13 +162,13 @@ namespace Unix.Terminal { } public T GetNativeMethodDelegate (string methodName) - where T : class + where T : class { var ptr = LoadSymbol (methodName); if (ptr == IntPtr.Zero) { throw new MissingMethodException (string.Format ("The native method \"{0}\" does not exist", methodName)); } - return Marshal.GetDelegateForFunctionPointer(ptr); // non-generic version is obsolete + return Marshal.GetDelegateForFunctionPointer (ptr); // non-generic version is obsolete } /// @@ -209,12 +207,11 @@ namespace Unix.Terminal { } } throw new FileNotFoundException ( - String.Format ("Error loading native library. Not found in any of the possible locations: {0}", + String.Format ("Error loading native library. Not found in any of the possible locations: {0}", string.Join (",", libraryPathAlternatives))); } - static class Windows - { + static class Windows { [DllImport ("kernel32.dll")] internal static extern IntPtr LoadLibrary (string filename); @@ -222,8 +219,7 @@ namespace Unix.Terminal { internal static extern IntPtr GetProcAddress (IntPtr hModule, string procName); } - static class Linux - { + static class Linux { [DllImport ("libdl.so")] internal static extern IntPtr dlopen (string filename, int flags); @@ -231,8 +227,7 @@ namespace Unix.Terminal { internal static extern IntPtr dlsym (IntPtr handle, string symbol); } - static class MacOSX - { + static class MacOSX { [DllImport ("libSystem.dylib")] internal static extern IntPtr dlopen (string filename, int flags); @@ -247,8 +242,7 @@ namespace Unix.Terminal { /// dlopen and dlsym from the current process as on Linux /// Mono sure is linked against these symbols. /// - static class Mono - { + static class Mono { [DllImport ("__Internal")] internal static extern IntPtr dlopen (string filename, int flags); @@ -261,13 +255,12 @@ namespace Unix.Terminal { /// dlopen and dlsym from the "libcoreclr.so", /// to avoid the dependency on libc-dev Linux. /// - static class CoreCLR - { + static class CoreCLR { #if NET6_0 // Custom resolver to support true single-file apps // (those which run directly from bundle; in-memory). - // -1 on Unix means self-referencing binary (libcoreclr.so) - // 0 means fallback to CoreCLR's internal resolution + // -1 on Unix means self-referencing binary (libcoreclr.so) + // 0 means fallback to CoreCLR's internal resolution // Note: meaning of -1 stay the same even for non-single-file form factors. static CoreCLR() => NativeLibrary.SetDllImportResolver(typeof(CoreCLR).Assembly, (string libraryName, Assembly assembly, DllImportSearchPath? searchPath) => diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs index 3d6624ecb..1fb3580fb 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs @@ -145,11 +145,6 @@ namespace Unix.Terminal { if (l == 1 || l != lines || c != cols) { lines = l; cols = c; - //if (l <= 0 || c <= 0) { - // Console.Out.Write ($"\x1b[8;50;{c}t"); - // Console.Out.Flush (); - // return false; - //} return true; } return false; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs index b9a63834a..fb9bc326b 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs @@ -53,7 +53,6 @@ namespace Unix.Terminal { public const int COLOR_WHITE = unchecked((int)0x7); public const int COLOR_GRAY = unchecked((int)0x8); public const int KEY_CODE_YES = unchecked((int)0x100); - public const int KEY_CODE_SEQ = unchecked((int)0x5b); public const int ERR = unchecked((int)0xffffffff); public const int TIOCGWINSZ = unchecked((int)0x5413); public const int TIOCGWINSZ_MAC = unchecked((int)0x40087468); @@ -69,7 +68,7 @@ namespace Unix.Terminal { Button2Released = unchecked((int)0x20), Button2Clicked = unchecked((int)0x80), Button2DoubleClicked = unchecked((int)0x100), - Button2TrippleClicked = unchecked((int)0x200), + Button2TripleClicked = unchecked((int)0x200), Button3Pressed = unchecked((int)0x800), Button3Released = unchecked((int)0x400), Button3Clicked = unchecked((int)0x1000), @@ -106,6 +105,7 @@ namespace Unix.Terminal { public const int KeyPPage = unchecked((int)0x153); public const int KeyHome = unchecked((int)0x106); public const int KeyMouse = unchecked((int)0x199); + public const int KeyCSI = unchecked((int)0x5b); public const int KeyEnd = unchecked((int)0x168); public const int KeyDeleteChar = unchecked((int)0x14a); public const int KeyInsertChar = unchecked((int)0x14b); diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index e78baa96a..017050ef0 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -1,15 +1,10 @@ // // FakeConsole.cs: A fake .NET Windows Console API implementation for unit tests. // -// Authors: -// Charlie Kindel (github.com/tig) -// using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; namespace Terminal.Gui { @@ -22,23 +17,23 @@ namespace Terminal.Gui { // // Summary: - // Gets or sets the width of the console window. + // Gets or sets the width of the console window. // // Returns: - // The width of the console window measured in columns. + // The width of the console window measured in columns. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight - // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight - // property plus the value of the System.Console.WindowTop property is greater than - // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth - // property or the value of the System.Console.WindowHeight property is greater - // than the largest possible window width or height for the current screen resolution - // and console font. + // T:System.ArgumentOutOfRangeException: + // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight + // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight + // property plus the value of the System.Console.WindowTop property is greater than + // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth + // property or the value of the System.Console.WindowHeight property is greater + // than the largest possible window width or height for the current screen resolution + // and console font. // - // T:System.IO.IOException: - // Error reading or writing information. + // T:System.IO.IOException: + // Error reading or writing information. #pragma warning disable RCS1138 // Add summary to documentation comment. /// @@ -57,113 +52,113 @@ namespace Terminal.Gui { public static int WindowWidth { get; set; } = WIDTH; // // Summary: - // Gets a value that indicates whether output has been redirected from the standard - // output stream. + // Gets a value that indicates whether output has been redirected from the standard + // output stream. // // Returns: - // true if output is redirected; otherwise, false. + // true if output is redirected; otherwise, false. /// /// /// public static bool IsOutputRedirected { get; } // // Summary: - // Gets a value that indicates whether the error output stream has been redirected - // from the standard error stream. + // Gets a value that indicates whether the error output stream has been redirected + // from the standard error stream. // // Returns: - // true if error output is redirected; otherwise, false. + // true if error output is redirected; otherwise, false. /// /// /// public static bool IsErrorRedirected { get; } // // Summary: - // Gets the standard input stream. + // Gets the standard input stream. // // Returns: - // A System.IO.TextReader that represents the standard input stream. + // A System.IO.TextReader that represents the standard input stream. /// /// /// public static TextReader In { get; } // // Summary: - // Gets the standard output stream. + // Gets the standard output stream. // // Returns: - // A System.IO.TextWriter that represents the standard output stream. + // A System.IO.TextWriter that represents the standard output stream. /// /// /// public static TextWriter Out { get; } // // Summary: - // Gets the standard error output stream. + // Gets the standard error output stream. // // Returns: - // A System.IO.TextWriter that represents the standard error output stream. + // A System.IO.TextWriter that represents the standard error output stream. /// /// /// public static TextWriter Error { get; } // // Summary: - // Gets or sets the encoding the console uses to read input. + // Gets or sets the encoding the console uses to read input. // // Returns: - // The encoding used to read console input. + // The encoding used to read console input. // // Exceptions: - // T:System.ArgumentNullException: - // The property value in a set operation is null. + // T:System.ArgumentNullException: + // The property value in a set operation is null. // - // T:System.IO.IOException: - // An error occurred during the execution of this operation. + // T:System.IO.IOException: + // An error occurred during the execution of this operation. // - // T:System.Security.SecurityException: - // Your application does not have permission to perform this operation. + // T:System.Security.SecurityException: + // Your application does not have permission to perform this operation. /// /// /// public static Encoding InputEncoding { get; set; } // // Summary: - // Gets or sets the encoding the console uses to write output. + // Gets or sets the encoding the console uses to write output. // // Returns: - // The encoding used to write console output. + // The encoding used to write console output. // // Exceptions: - // T:System.ArgumentNullException: - // The property value in a set operation is null. + // T:System.ArgumentNullException: + // The property value in a set operation is null. // - // T:System.IO.IOException: - // An error occurred during the execution of this operation. + // T:System.IO.IOException: + // An error occurred during the execution of this operation. // - // T:System.Security.SecurityException: - // Your application does not have permission to perform this operation. + // T:System.Security.SecurityException: + // Your application does not have permission to perform this operation. /// /// /// public static Encoding OutputEncoding { get; set; } // // Summary: - // Gets or sets the background color of the console. + // Gets or sets the background color of the console. // // Returns: - // A value that specifies the background color of the console; that is, the color - // that appears behind each character. The default is black. + // A value that specifies the background color of the console; that is, the color + // that appears behind each character. The default is black. // // Exceptions: - // T:System.ArgumentException: - // The color specified in a set operation is not a valid member of System.ConsoleColor. + // T:System.ArgumentException: + // The color specified in a set operation is not a valid member of System.ConsoleColor. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black; @@ -174,21 +169,21 @@ namespace Terminal.Gui { // // Summary: - // Gets or sets the foreground color of the console. + // Gets or sets the foreground color of the console. // // Returns: - // A System.ConsoleColor that specifies the foreground color of the console; that - // is, the color of each character that is displayed. The default is gray. + // A System.ConsoleColor that specifies the foreground color of the console; that + // is, the color of each character that is displayed. The default is gray. // // Exceptions: - // T:System.ArgumentException: - // The color specified in a set operation is not a valid member of System.ConsoleColor. + // T:System.ArgumentException: + // The color specified in a set operation is not a valid member of System.ConsoleColor. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray; @@ -198,299 +193,299 @@ namespace Terminal.Gui { public static ConsoleColor ForegroundColor { get; set; } = _defaultForegroundColor; // // Summary: - // Gets or sets the height of the buffer area. + // Gets or sets the height of the buffer area. // // Returns: - // The current height, in rows, of the buffer area. + // The current height, in rows, of the buffer area. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than or equal to zero.-or- The value in - // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value - // in a set operation is less than System.Console.WindowTop + System.Console.WindowHeight. + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than or equal to zero.-or- The value in + // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value + // in a set operation is less than System.Console.WindowTop + System.Console.WindowHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int BufferHeight { get; set; } = HEIGHT; // // Summary: - // Gets or sets the width of the buffer area. + // Gets or sets the width of the buffer area. // // Returns: - // The current width, in columns, of the buffer area. + // The current width, in columns, of the buffer area. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than or equal to zero.-or- The value in - // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value - // in a set operation is less than System.Console.WindowLeft + System.Console.WindowWidth. + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than or equal to zero.-or- The value in + // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value + // in a set operation is less than System.Console.WindowLeft + System.Console.WindowWidth. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int BufferWidth { get; set; } = WIDTH; // // Summary: - // Gets or sets the height of the console window area. + // Gets or sets the height of the console window area. // // Returns: - // The height of the console window measured in rows. + // The height of the console window measured in rows. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight - // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight - // property plus the value of the System.Console.WindowTop property is greater than - // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth - // property or the value of the System.Console.WindowHeight property is greater - // than the largest possible window width or height for the current screen resolution - // and console font. + // T:System.ArgumentOutOfRangeException: + // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight + // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight + // property plus the value of the System.Console.WindowTop property is greater than + // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth + // property or the value of the System.Console.WindowHeight property is greater + // than the largest possible window width or height for the current screen resolution + // and console font. // - // T:System.IO.IOException: - // Error reading or writing information. + // T:System.IO.IOException: + // Error reading or writing information. /// /// /// public static int WindowHeight { get; set; } = HEIGHT; // // Summary: - // Gets or sets a value indicating whether the combination of the System.ConsoleModifiers.Control - // modifier key and System.ConsoleKey.C console key (Ctrl+C) is treated as ordinary - // input or as an interruption that is handled by the operating system. + // Gets or sets a value indicating whether the combination of the System.ConsoleModifiers.Control + // modifier key and System.ConsoleKey.C console key (Ctrl+C) is treated as ordinary + // input or as an interruption that is handled by the operating system. // // Returns: - // true if Ctrl+C is treated as ordinary input; otherwise, false. + // true if Ctrl+C is treated as ordinary input; otherwise, false. // // Exceptions: - // T:System.IO.IOException: - // Unable to get or set the input mode of the console input buffer. + // T:System.IO.IOException: + // Unable to get or set the input mode of the console input buffer. /// /// /// public static bool TreatControlCAsInput { get; set; } // // Summary: - // Gets the largest possible number of console window columns, based on the current - // font and screen resolution. + // Gets the largest possible number of console window columns, based on the current + // font and screen resolution. // // Returns: - // The width of the largest possible console window measured in columns. + // The width of the largest possible console window measured in columns. /// /// /// public static int LargestWindowWidth { get; } // // Summary: - // Gets the largest possible number of console window rows, based on the current - // font and screen resolution. + // Gets the largest possible number of console window rows, based on the current + // font and screen resolution. // // Returns: - // The height of the largest possible console window measured in rows. + // The height of the largest possible console window measured in rows. /// /// /// public static int LargestWindowHeight { get; } // // Summary: - // Gets or sets the leftmost position of the console window area relative to the - // screen buffer. + // Gets or sets the leftmost position of the console window area relative to the + // screen buffer. // // Returns: - // The leftmost console window position measured in columns. + // The leftmost console window position measured in columns. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // In a set operation, the value to be assigned is less than zero.-or-As a result - // of the assignment, System.Console.WindowLeft plus System.Console.WindowWidth - // would exceed System.Console.BufferWidth. + // T:System.ArgumentOutOfRangeException: + // In a set operation, the value to be assigned is less than zero.-or-As a result + // of the assignment, System.Console.WindowLeft plus System.Console.WindowWidth + // would exceed System.Console.BufferWidth. // - // T:System.IO.IOException: - // Error reading or writing information. + // T:System.IO.IOException: + // Error reading or writing information. /// /// /// public static int WindowLeft { get; set; } // // Summary: - // Gets or sets the top position of the console window area relative to the screen - // buffer. + // Gets or sets the top position of the console window area relative to the screen + // buffer. // // Returns: - // The uppermost console window position measured in rows. + // The uppermost console window position measured in rows. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // In a set operation, the value to be assigned is less than zero.-or-As a result - // of the assignment, System.Console.WindowTop plus System.Console.WindowHeight - // would exceed System.Console.BufferHeight. + // T:System.ArgumentOutOfRangeException: + // In a set operation, the value to be assigned is less than zero.-or-As a result + // of the assignment, System.Console.WindowTop plus System.Console.WindowHeight + // would exceed System.Console.BufferHeight. // - // T:System.IO.IOException: - // Error reading or writing information. + // T:System.IO.IOException: + // Error reading or writing information. /// /// /// public static int WindowTop { get; set; } // // Summary: - // Gets or sets the column position of the cursor within the buffer area. + // Gets or sets the column position of the cursor within the buffer area. // // Returns: - // The current position, in columns, of the cursor. + // The current position, in columns, of the cursor. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than zero.-or- The value in a set operation - // is greater than or equal to System.Console.BufferWidth. + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than zero.-or- The value in a set operation + // is greater than or equal to System.Console.BufferWidth. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int CursorLeft { get; set; } // // Summary: - // Gets or sets the row position of the cursor within the buffer area. + // Gets or sets the row position of the cursor within the buffer area. // // Returns: - // The current position, in rows, of the cursor. + // The current position, in rows, of the cursor. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than zero.-or- The value in a set operation - // is greater than or equal to System.Console.BufferHeight. + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than zero.-or- The value in a set operation + // is greater than or equal to System.Console.BufferHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int CursorTop { get; set; } // // Summary: - // Gets or sets the height of the cursor within a character cell. + // Gets or sets the height of the cursor within a character cell. // // Returns: - // The size of the cursor expressed as a percentage of the height of a character - // cell. The property value ranges from 1 to 100. + // The size of the cursor expressed as a percentage of the height of a character + // cell. The property value ranges from 1 to 100. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value specified in a set operation is less than 1 or greater than 100. + // T:System.ArgumentOutOfRangeException: + // The value specified in a set operation is less than 1 or greater than 100. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int CursorSize { get; set; } // // Summary: - // Gets or sets a value indicating whether the cursor is visible. + // Gets or sets a value indicating whether the cursor is visible. // // Returns: - // true if the cursor is visible; otherwise, false. + // true if the cursor is visible; otherwise, false. // // Exceptions: - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static bool CursorVisible { get; set; } // // Summary: - // Gets or sets the title to display in the console title bar. + // Gets or sets the title to display in the console title bar. // // Returns: - // The string to be displayed in the title bar of the console. The maximum length - // of the title string is 24500 characters. + // The string to be displayed in the title bar of the console. The maximum length + // of the title string is 24500 characters. // // Exceptions: - // T:System.InvalidOperationException: - // In a get operation, the retrieved title is longer than 24500 characters. + // T:System.InvalidOperationException: + // In a get operation, the retrieved title is longer than 24500 characters. // - // T:System.ArgumentOutOfRangeException: - // In a set operation, the specified title is longer than 24500 characters. + // T:System.ArgumentOutOfRangeException: + // In a set operation, the specified title is longer than 24500 characters. // - // T:System.ArgumentNullException: - // In a set operation, the specified title is null. + // T:System.ArgumentNullException: + // In a set operation, the specified title is null. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static string Title { get; set; } // // Summary: - // Gets a value indicating whether a key press is available in the input stream. + // Gets a value indicating whether a key press is available in the input stream. // // Returns: - // true if a key press is available; otherwise, false. + // true if a key press is available; otherwise, false. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.InvalidOperationException: - // Standard input is redirected to a file instead of the keyboard. + // T:System.InvalidOperationException: + // Standard input is redirected to a file instead of the keyboard. /// /// /// public static bool KeyAvailable { get; } // // Summary: - // Gets a value indicating whether the NUM LOCK keyboard toggle is turned on or - // turned off. + // Gets a value indicating whether the NUM LOCK keyboard toggle is turned on or + // turned off. // // Returns: - // true if NUM LOCK is turned on; false if NUM LOCK is turned off. + // true if NUM LOCK is turned on; false if NUM LOCK is turned off. /// /// /// public static bool NumberLock { get; } // // Summary: - // Gets a value indicating whether the CAPS LOCK keyboard toggle is turned on or - // turned off. + // Gets a value indicating whether the CAPS LOCK keyboard toggle is turned on or + // turned off. // // Returns: - // true if CAPS LOCK is turned on; false if CAPS LOCK is turned off. + // true if CAPS LOCK is turned on; false if CAPS LOCK is turned off. /// /// /// public static bool CapsLock { get; } // // Summary: - // Gets a value that indicates whether input has been redirected from the standard - // input stream. + // Gets a value that indicates whether input has been redirected from the standard + // input stream. // // Returns: - // true if input is redirected; otherwise, false. + // true if input is redirected; otherwise, false. /// /// /// @@ -498,12 +493,12 @@ namespace Terminal.Gui { // // Summary: - // Plays the sound of a beep through the console speaker. + // Plays the sound of a beep through the console speaker. // // Exceptions: - // T:System.Security.HostProtectionException: - // This method was executed on a server, such as SQL Server, that does not permit - // access to a user interface. + // T:System.Security.HostProtectionException: + // This method was executed on a server, such as SQL Server, that does not permit + // access to a user interface. /// /// /// @@ -513,24 +508,24 @@ namespace Terminal.Gui { } // // Summary: - // Plays the sound of a beep of a specified frequency and duration through the console - // speaker. + // Plays the sound of a beep of a specified frequency and duration through the console + // speaker. // // Parameters: - // frequency: - // The frequency of the beep, ranging from 37 to 32767 hertz. + // frequency: + // The frequency of the beep, ranging from 37 to 32767 hertz. // - // duration: - // The duration of the beep measured in milliseconds. + // duration: + // The duration of the beep measured in milliseconds. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // frequency is less than 37 or more than 32767 hertz.-or- duration is less than - // or equal to zero. + // T:System.ArgumentOutOfRangeException: + // frequency is less than 37 or more than 32767 hertz.-or- duration is less than + // or equal to zero. // - // T:System.Security.HostProtectionException: - // This method was executed on a server, such as SQL Server, that does not permit - // access to the console. + // T:System.Security.HostProtectionException: + // This method was executed on a server, such as SQL Server, that does not permit + // access to the console. /// /// /// @@ -540,11 +535,11 @@ namespace Terminal.Gui { } // // Summary: - // Clears the console buffer and corresponding console window of display information. + // Clears the console buffer and corresponding console window of display information. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. static char [,] _buffer = new char [WindowWidth, WindowHeight]; @@ -559,41 +554,41 @@ namespace Terminal.Gui { // // Summary: - // Copies a specified source area of the screen buffer to a specified destination - // area. + // Copies a specified source area of the screen buffer to a specified destination + // area. // // Parameters: - // sourceLeft: - // The leftmost column of the source area. + // sourceLeft: + // The leftmost column of the source area. // - // sourceTop: - // The topmost row of the source area. + // sourceTop: + // The topmost row of the source area. // - // sourceWidth: - // The number of columns in the source area. + // sourceWidth: + // The number of columns in the source area. // - // sourceHeight: - // The number of rows in the source area. + // sourceHeight: + // The number of rows in the source area. // - // targetLeft: - // The leftmost column of the destination area. + // targetLeft: + // The leftmost column of the destination area. // - // targetTop: - // The topmost row of the destination area. + // targetTop: + // The topmost row of the destination area. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft - // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop - // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight - // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth - // is greater than or equal to System.Console.BufferWidth. + // T:System.ArgumentOutOfRangeException: + // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft + // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop + // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight + // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth + // is greater than or equal to System.Console.BufferWidth. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -604,54 +599,54 @@ namespace Terminal.Gui { // // Summary: - // Copies a specified source area of the screen buffer to a specified destination - // area. + // Copies a specified source area of the screen buffer to a specified destination + // area. // // Parameters: - // sourceLeft: - // The leftmost column of the source area. + // sourceLeft: + // The leftmost column of the source area. // - // sourceTop: - // The topmost row of the source area. + // sourceTop: + // The topmost row of the source area. // - // sourceWidth: - // The number of columns in the source area. + // sourceWidth: + // The number of columns in the source area. // - // sourceHeight: - // The number of rows in the source area. + // sourceHeight: + // The number of rows in the source area. // - // targetLeft: - // The leftmost column of the destination area. + // targetLeft: + // The leftmost column of the destination area. // - // targetTop: - // The topmost row of the destination area. + // targetTop: + // The topmost row of the destination area. // - // sourceChar: - // The character used to fill the source area. + // sourceChar: + // The character used to fill the source area. // - // sourceForeColor: - // The foreground color used to fill the source area. + // sourceForeColor: + // The foreground color used to fill the source area. // - // sourceBackColor: - // The background color used to fill the source area. + // sourceBackColor: + // The background color used to fill the source area. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft - // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop - // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight - // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth - // is greater than or equal to System.Console.BufferWidth. + // T:System.ArgumentOutOfRangeException: + // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft + // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop + // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight + // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth + // is greater than or equal to System.Console.BufferWidth. // - // T:System.ArgumentException: - // One or both of the color parameters is not a member of the System.ConsoleColor - // enumeration. + // T:System.ArgumentException: + // One or both of the color parameters is not a member of the System.ConsoleColor + // enumeration. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -663,10 +658,10 @@ namespace Terminal.Gui { // // Summary: - // Acquires the standard error stream. + // Acquires the standard error stream. // // Returns: - // The standard error stream. + // The standard error stream. /// /// /// @@ -677,18 +672,18 @@ namespace Terminal.Gui { // // Summary: - // Acquires the standard error stream, which is set to a specified buffer size. + // Acquires the standard error stream, which is set to a specified buffer size. // // Parameters: - // bufferSize: - // The internal stream buffer size. + // bufferSize: + // The internal stream buffer size. // // Returns: - // The standard error stream. + // The standard error stream. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. /// /// /// @@ -699,18 +694,18 @@ namespace Terminal.Gui { // // Summary: - // Acquires the standard input stream, which is set to a specified buffer size. + // Acquires the standard input stream, which is set to a specified buffer size. // // Parameters: - // bufferSize: - // The internal stream buffer size. + // bufferSize: + // The internal stream buffer size. // // Returns: - // The standard input stream. + // The standard input stream. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. /// /// /// @@ -721,10 +716,10 @@ namespace Terminal.Gui { // // Summary: - // Acquires the standard input stream. + // Acquires the standard input stream. // // Returns: - // The standard input stream. + // The standard input stream. /// /// /// @@ -735,18 +730,18 @@ namespace Terminal.Gui { // // Summary: - // Acquires the standard output stream, which is set to a specified buffer size. + // Acquires the standard output stream, which is set to a specified buffer size. // // Parameters: - // bufferSize: - // The internal stream buffer size. + // bufferSize: + // The internal stream buffer size. // // Returns: - // The standard output stream. + // The standard output stream. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. /// /// /// @@ -757,10 +752,10 @@ namespace Terminal.Gui { // // Summary: - // Acquires the standard output stream. + // Acquires the standard output stream. // // Returns: - // The standard output stream. + // The standard output stream. /// /// /// @@ -771,15 +766,15 @@ namespace Terminal.Gui { // // Summary: - // Reads the next character from the standard input stream. + // Reads the next character from the standard input stream. // // Returns: - // The next character from the input stream, or negative one (-1) if there are currently - // no more characters to be read. + // The next character from the input stream, or negative one (-1) if there are currently + // no more characters to be read. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -790,25 +785,25 @@ namespace Terminal.Gui { // // Summary: - // Obtains the next character or function key pressed by the user. The pressed key - // is optionally displayed in the console window. + // Obtains the next character or function key pressed by the user. The pressed key + // is optionally displayed in the console window. // // Parameters: - // intercept: - // Determines whether to display the pressed key in the console window. true to - // not display the pressed key; otherwise, false. + // intercept: + // Determines whether to display the pressed key in the console window. true to + // not display the pressed key; otherwise, false. // // Returns: - // An object that describes the System.ConsoleKey constant and Unicode character, - // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo - // object also describes, in a bitwise combination of System.ConsoleModifiers values, - // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously - // with the console key. + // An object that describes the System.ConsoleKey constant and Unicode character, + // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo + // object also describes, in a bitwise combination of System.ConsoleModifiers values, + // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously + // with the console key. // // Exceptions: - // T:System.InvalidOperationException: - // The System.Console.In property is redirected from some stream other than the - // console. + // T:System.InvalidOperationException: + // The System.Console.In property is redirected from some stream other than the + // console. //[SecuritySafeCritical] /// /// @@ -829,20 +824,20 @@ namespace Terminal.Gui { // // Summary: - // Obtains the next character or function key pressed by the user. The pressed key - // is displayed in the console window. + // Obtains the next character or function key pressed by the user. The pressed key + // is displayed in the console window. // // Returns: - // An object that describes the System.ConsoleKey constant and Unicode character, - // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo - // object also describes, in a bitwise combination of System.ConsoleModifiers values, - // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously - // with the console key. + // An object that describes the System.ConsoleKey constant and Unicode character, + // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo + // object also describes, in a bitwise combination of System.ConsoleModifiers values, + // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously + // with the console key. // // Exceptions: - // T:System.InvalidOperationException: - // The System.Console.In property is redirected from some stream other than the - // console. + // T:System.InvalidOperationException: + // The System.Console.In property is redirected from some stream other than the + // console. /// /// /// @@ -853,21 +848,21 @@ namespace Terminal.Gui { // // Summary: - // Reads the next line of characters from the standard input stream. + // Reads the next line of characters from the standard input stream. // // Returns: - // The next line of characters from the input stream, or null if no more lines are - // available. + // The next line of characters from the input stream, or null if no more lines are + // available. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.OutOfMemoryException: - // There is insufficient memory to allocate a buffer for the returned string. + // T:System.OutOfMemoryException: + // There is insufficient memory to allocate a buffer for the returned string. // - // T:System.ArgumentOutOfRangeException: - // The number of characters in the next line of characters is greater than System.Int32.MaxValue. + // T:System.ArgumentOutOfRangeException: + // The number of characters in the next line of characters is greater than System.Int32.MaxValue. /// /// /// @@ -878,14 +873,14 @@ namespace Terminal.Gui { // // Summary: - // Sets the foreground and background console colors to their defaults. + // Sets the foreground and background console colors to their defaults. // // Exceptions: - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -898,27 +893,27 @@ namespace Terminal.Gui { // // Summary: - // Sets the height and width of the screen buffer area to the specified values. + // Sets the height and width of the screen buffer area to the specified values. // // Parameters: - // width: - // The width of the buffer area measured in columns. + // width: + // The width of the buffer area measured in columns. // - // height: - // The height of the buffer area measured in rows. + // height: + // The height of the buffer area measured in rows. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // height or width is less than or equal to zero.-or- height or width is greater - // than or equal to System.Int16.MaxValue.-or- width is less than System.Console.WindowLeft - // + System.Console.WindowWidth.-or- height is less than System.Console.WindowTop - // + System.Console.WindowHeight. + // T:System.ArgumentOutOfRangeException: + // height or width is less than or equal to zero.-or- height or width is greater + // than or equal to System.Int16.MaxValue.-or- width is less than System.Console.WindowLeft + // + System.Console.WindowWidth.-or- height is less than System.Console.WindowTop + // + System.Console.WindowHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -932,27 +927,27 @@ namespace Terminal.Gui { // // Summary: - // Sets the position of the cursor. + // Sets the position of the cursor. // // Parameters: - // left: - // The column position of the cursor. Columns are numbered from left to right starting - // at 0. + // left: + // The column position of the cursor. Columns are numbered from left to right starting + // at 0. // - // top: - // The row position of the cursor. Rows are numbered from top to bottom starting - // at 0. + // top: + // The row position of the cursor. Rows are numbered from top to bottom starting + // at 0. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // left or top is less than zero.-or- left is greater than or equal to System.Console.BufferWidth.-or- - // top is greater than or equal to System.Console.BufferHeight. + // T:System.ArgumentOutOfRangeException: + // left or top is less than zero.-or- left is greater than or equal to System.Console.BufferWidth.-or- + // top is greater than or equal to System.Console.BufferHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -967,19 +962,19 @@ namespace Terminal.Gui { // // Summary: - // Sets the System.Console.Error property to the specified System.IO.TextWriter - // object. + // Sets the System.Console.Error property to the specified System.IO.TextWriter + // object. // // Parameters: - // newError: - // A stream that is the new standard error output. + // newError: + // A stream that is the new standard error output. // // Exceptions: - // T:System.ArgumentNullException: - // newError is null. + // T:System.ArgumentNullException: + // newError is null. // - // T:System.Security.SecurityException: - // The caller does not have the required permission. + // T:System.Security.SecurityException: + // The caller does not have the required permission. //[SecuritySafeCritical] /// /// @@ -991,18 +986,18 @@ namespace Terminal.Gui { // // Summary: - // Sets the System.Console.In property to the specified System.IO.TextReader object. + // Sets the System.Console.In property to the specified System.IO.TextReader object. // // Parameters: - // newIn: - // A stream that is the new standard input. + // newIn: + // A stream that is the new standard input. // // Exceptions: - // T:System.ArgumentNullException: - // newIn is null. + // T:System.ArgumentNullException: + // newIn is null. // - // T:System.Security.SecurityException: - // The caller does not have the required permission. + // T:System.Security.SecurityException: + // The caller does not have the required permission. //[SecuritySafeCritical] /// /// @@ -1014,18 +1009,18 @@ namespace Terminal.Gui { // // Summary: - // Sets the System.Console.Out property to the specified System.IO.TextWriter object. + // Sets the System.Console.Out property to the specified System.IO.TextWriter object. // // Parameters: - // newOut: - // A stream that is the new standard output. + // newOut: + // A stream that is the new standard output. // // Exceptions: - // T:System.ArgumentNullException: - // newOut is null. + // T:System.ArgumentNullException: + // newOut is null. // - // T:System.Security.SecurityException: - // The caller does not have the required permission. + // T:System.Security.SecurityException: + // The caller does not have the required permission. //[SecuritySafeCritical] /// /// @@ -1038,26 +1033,26 @@ namespace Terminal.Gui { // // Summary: - // Sets the position of the console window relative to the screen buffer. + // Sets the position of the console window relative to the screen buffer. // // Parameters: - // left: - // The column position of the upper left corner of the console window. + // left: + // The column position of the upper left corner of the console window. // - // top: - // The row position of the upper left corner of the console window. + // top: + // The row position of the upper left corner of the console window. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // left or top is less than zero.-or- left + System.Console.WindowWidth is greater - // than System.Console.BufferWidth.-or- top + System.Console.WindowHeight is greater - // than System.Console.BufferHeight. + // T:System.ArgumentOutOfRangeException: + // left or top is less than zero.-or- left + System.Console.WindowWidth is greater + // than System.Console.BufferWidth.-or- top + System.Console.WindowHeight is greater + // than System.Console.BufferHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -1072,27 +1067,27 @@ namespace Terminal.Gui { // // Summary: - // Sets the height and width of the console window to the specified values. + // Sets the height and width of the console window to the specified values. // // Parameters: - // width: - // The width of the console window measured in columns. + // width: + // The width of the console window measured in columns. // - // height: - // The height of the console window measured in rows. + // height: + // The height of the console window measured in rows. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // width or height is less than or equal to zero.-or- width plus System.Console.WindowLeft - // or height plus System.Console.WindowTop is greater than or equal to System.Int16.MaxValue. - // -or- width or height is greater than the largest possible window width or height - // for the current screen resolution and console font. + // T:System.ArgumentOutOfRangeException: + // width or height is less than or equal to zero.-or- width plus System.Console.WindowLeft + // or height plus System.Console.WindowTop is greater than or equal to System.Int16.MaxValue. + // -or- width or height is greater than the largest possible window width or height + // for the current screen resolution and console font. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -1107,15 +1102,15 @@ namespace Terminal.Gui { // // Summary: - // Writes the specified string value to the standard output stream. + // Writes the specified string value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1127,16 +1122,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified object to the standard output - // stream. + // Writes the text representation of the specified object to the standard output + // stream. // // Parameters: - // value: - // The value to write, or null. + // value: + // The value to write, or null. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1148,16 +1143,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified 64-bit unsigned integer value - // to the standard output stream. + // Writes the text representation of the specified 64-bit unsigned integer value + // to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[CLSCompliant (false)] /// /// @@ -1170,16 +1165,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified 64-bit signed integer value to - // the standard output stream. + // Writes the text representation of the specified 64-bit signed integer value to + // the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1191,28 +1186,28 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified objects to the standard output - // stream using the specified format information. + // Writes the text representation of the specified objects to the standard output + // stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // The first object to write using format. + // arg0: + // The first object to write using format. // - // arg1: - // The second object to write using format. + // arg1: + // The second object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1226,16 +1221,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified 32-bit signed integer value to - // the standard output stream. + // Writes the text representation of the specified 32-bit signed integer value to + // the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1247,25 +1242,25 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified object to the standard output - // stream using the specified format information. + // Writes the text representation of the specified object to the standard output + // stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // An object to write using format. + // arg0: + // An object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1278,16 +1273,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified 32-bit unsigned integer value - // to the standard output stream. + // Writes the text representation of the specified 32-bit unsigned integer value + // to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[CLSCompliant (false)] /// /// @@ -1314,25 +1309,25 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified array of objects to the standard - // output stream using the specified format information. + // Writes the text representation of the specified array of objects to the standard + // output stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg: - // An array of objects to write using format. + // arg: + // An array of objects to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format or arg is null. + // T:System.ArgumentNullException: + // format or arg is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1345,16 +1340,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified Boolean value to the standard - // output stream. + // Writes the text representation of the specified Boolean value to the standard + // output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1366,15 +1361,15 @@ namespace Terminal.Gui { // // Summary: - // Writes the specified Unicode character value to the standard output stream. + // Writes the specified Unicode character value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1386,15 +1381,15 @@ namespace Terminal.Gui { // // Summary: - // Writes the specified array of Unicode characters to the standard output stream. + // Writes the specified array of Unicode characters to the standard output stream. // // Parameters: - // buffer: - // A Unicode character array. + // buffer: + // A Unicode character array. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1409,30 +1404,30 @@ namespace Terminal.Gui { // // Summary: - // Writes the specified subarray of Unicode characters to the standard output stream. + // Writes the specified subarray of Unicode characters to the standard output stream. // // Parameters: - // buffer: - // An array of Unicode characters. + // buffer: + // An array of Unicode characters. // - // index: - // The starting position in buffer. + // index: + // The starting position in buffer. // - // count: - // The number of characters to write. + // count: + // The number of characters to write. // // Exceptions: - // T:System.ArgumentNullException: - // buffer is null. + // T:System.ArgumentNullException: + // buffer is null. // - // T:System.ArgumentOutOfRangeException: - // index or count is less than zero. + // T:System.ArgumentOutOfRangeException: + // index or count is less than zero. // - // T:System.ArgumentException: - // index plus count specify a position that is not within buffer. + // T:System.ArgumentException: + // index plus count specify a position that is not within buffer. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1446,31 +1441,31 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified objects to the standard output - // stream using the specified format information. + // Writes the text representation of the specified objects to the standard output + // stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // The first object to write using format. + // arg0: + // The first object to write using format. // - // arg1: - // The second object to write using format. + // arg1: + // The second object to write using format. // - // arg2: - // The third object to write using format. + // arg2: + // The third object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1485,16 +1480,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified System.Decimal value to the standard - // output stream. + // Writes the text representation of the specified System.Decimal value to the standard + // output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1506,16 +1501,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified single-precision floating-point - // value to the standard output stream. + // Writes the text representation of the specified single-precision floating-point + // value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1527,16 +1522,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified double-precision floating-point - // value to the standard output stream. + // Writes the text representation of the specified double-precision floating-point + // value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1548,11 +1543,11 @@ namespace Terminal.Gui { // // Summary: - // Writes the current line terminator to the standard output stream. + // Writes the current line terminator to the standard output stream. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1563,16 +1558,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified single-precision floating-point - // value, followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified single-precision floating-point + // value, followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1584,16 +1579,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified 32-bit signed integer value, - // followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified 32-bit signed integer value, + // followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1605,16 +1600,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified 32-bit unsigned integer value, - // followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified 32-bit unsigned integer value, + // followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[CLSCompliant (false)] /// /// @@ -1627,16 +1622,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified 64-bit signed integer value, - // followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified 64-bit signed integer value, + // followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1648,16 +1643,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified 64-bit unsigned integer value, - // followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified 64-bit unsigned integer value, + // followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[CLSCompliant (false)] /// /// @@ -1670,16 +1665,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified object, followed by the current - // line terminator, to the standard output stream. + // Writes the text representation of the specified object, followed by the current + // line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1691,16 +1686,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the specified string value, followed by the current line terminator, to - // the standard output stream. + // Writes the specified string value, followed by the current line terminator, to + // the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1712,25 +1707,25 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified object, followed by the current - // line terminator, to the standard output stream using the specified format information. + // Writes the text representation of the specified object, followed by the current + // line terminator, to the standard output stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // An object to write using format. + // arg0: + // An object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1743,31 +1738,31 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified objects, followed by the current - // line terminator, to the standard output stream using the specified format information. + // Writes the text representation of the specified objects, followed by the current + // line terminator, to the standard output stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // The first object to write using format. + // arg0: + // The first object to write using format. // - // arg1: - // The second object to write using format. + // arg1: + // The second object to write using format. // - // arg2: - // The third object to write using format. + // arg2: + // The third object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1796,26 +1791,26 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified array of objects, followed by - // the current line terminator, to the standard output stream using the specified - // format information. + // Writes the text representation of the specified array of objects, followed by + // the current line terminator, to the standard output stream using the specified + // format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg: - // An array of objects to write using format. + // arg: + // An array of objects to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format or arg is null. + // T:System.ArgumentNullException: + // format or arg is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1828,31 +1823,31 @@ namespace Terminal.Gui { // // Summary: - // Writes the specified subarray of Unicode characters, followed by the current - // line terminator, to the standard output stream. + // Writes the specified subarray of Unicode characters, followed by the current + // line terminator, to the standard output stream. // // Parameters: - // buffer: - // An array of Unicode characters. + // buffer: + // An array of Unicode characters. // - // index: - // The starting position in buffer. + // index: + // The starting position in buffer. // - // count: - // The number of characters to write. + // count: + // The number of characters to write. // // Exceptions: - // T:System.ArgumentNullException: - // buffer is null. + // T:System.ArgumentNullException: + // buffer is null. // - // T:System.ArgumentOutOfRangeException: - // index or count is less than zero. + // T:System.ArgumentOutOfRangeException: + // index or count is less than zero. // - // T:System.ArgumentException: - // index plus count specify a position that is not within buffer. + // T:System.ArgumentException: + // index plus count specify a position that is not within buffer. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1866,16 +1861,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified System.Decimal value, followed - // by the current line terminator, to the standard output stream. + // Writes the text representation of the specified System.Decimal value, followed + // by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1887,16 +1882,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the specified array of Unicode characters, followed by the current line - // terminator, to the standard output stream. + // Writes the specified array of Unicode characters, followed by the current line + // terminator, to the standard output stream. // // Parameters: - // buffer: - // A Unicode character array. + // buffer: + // A Unicode character array. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1908,16 +1903,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the specified Unicode character, followed by the current line terminator, - // value to the standard output stream. + // Writes the specified Unicode character, followed by the current line terminator, + // value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1929,16 +1924,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified Boolean value, followed by the - // current line terminator, to the standard output stream. + // Writes the text representation of the specified Boolean value, followed by the + // current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1950,28 +1945,28 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified objects, followed by the current - // line terminator, to the standard output stream using the specified format information. + // Writes the text representation of the specified objects, followed by the current + // line terminator, to the standard output stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // The first object to write using format. + // arg0: + // The first object to write using format. // - // arg1: - // The second object to write using format. + // arg1: + // The second object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1985,16 +1980,16 @@ namespace Terminal.Gui { // // Summary: - // Writes the text representation of the specified double-precision floating-point - // value, followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified double-precision floating-point + // value, followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 661daaca4..30951dd73 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -1,9 +1,6 @@ // // FakeDriver.cs: A fake ConsoleDriver for unit tests. // -// Authors: -// Charlie Kindel (github.com/tig) -// using System; using System.Collections.Generic; using System.Diagnostics; @@ -48,7 +45,7 @@ namespace Terminal.Gui { // Only handling left here because not all terminals has a horizontal scroll bar. public override int Left => 0; public override int Top => 0; - public override bool HeightAsBuffer { get; set; } + public override bool EnableConsoleScrolling { get; set; } private IClipboard clipboard = null; public override IClipboard Clipboard => clipboard; @@ -245,8 +242,8 @@ namespace Terminal.Gui { { redrawColor = color; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains (color & 0xffff)) { FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff); } @@ -535,7 +532,7 @@ namespace Terminal.Gui { FakeConsole.SetBufferSize (width, height); cols = width; rows = height; - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { SetWindowSize (width, height); } ProcessResize (); @@ -544,7 +541,7 @@ namespace Terminal.Gui { public void SetWindowSize (int width, int height) { FakeConsole.SetWindowSize (width, height); - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { if (width != cols || height != rows) { SetBufferSize (width, height); cols = width; @@ -556,7 +553,7 @@ namespace Terminal.Gui { public void SetWindowPosition (int left, int top) { - if (HeightAsBuffer) { + if (EnableConsoleScrolling) { this.left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); this.top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); } else if (this.left > 0 || this.top > 0) { @@ -575,7 +572,7 @@ namespace Terminal.Gui { public override void ResizeScreen () { - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { if (FakeConsole.WindowHeight > 0) { // Can raise an exception while is still resizing. try { @@ -629,8 +626,8 @@ namespace Terminal.Gui { foreground = default; background = default; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains (value & 0xffff)) { hasColor = true; background = (Color)(ConsoleColor)(value & 0xffff); diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 9ae4eb20e..08e827683 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1,5 +1,4 @@ -//#define PROCESS_REQUEST -// +// // NetDriver.cs: The System.Console-based .NET driver, works on Windows and Unix, but is not particularly efficient. // // Authors: @@ -7,6 +6,7 @@ // using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading; @@ -111,27 +111,37 @@ namespace Terminal.Gui { ManualResetEventSlim winChange = new ManualResetEventSlim (false); Queue inputResultQueue = new Queue (); ConsoleDriver consoleDriver; + volatile ConsoleKeyInfo [] cki = null; + static volatile bool isEscSeq; int lastWindowHeight; - int largestWindowHeight; + bool stopTasks; #if PROCESS_REQUEST - bool neededProcessRequest; + bool neededProcessRequest; #endif - public int NumberOfCSI { get; } + public bool IsTerminalWithOptions { get; set; } + public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc (); - public NetEvents (ConsoleDriver consoleDriver, int numberOfCSI = 1) + public NetEvents (ConsoleDriver consoleDriver) { if (consoleDriver == null) { throw new ArgumentNullException ("Console driver instance must be provided."); } this.consoleDriver = consoleDriver; - NumberOfCSI = numberOfCSI; Task.Run (ProcessInputResultQueue); Task.Run (CheckWinChange); } + internal void StopTasks () + { + stopTasks = true; + } + public InputResult? ReadConsoleInput () { while (true) { + if (stopTasks) { + return null; + } waitForStart.Set (); winChange.Set (); @@ -140,7 +150,7 @@ namespace Terminal.Gui { inputReady.Reset (); } #if PROCESS_REQUEST - neededProcessRequest = false; + neededProcessRequest = false; #endif if (inputResultQueue.Count > 0) { return inputResultQueue.Dequeue (); @@ -155,16 +165,53 @@ namespace Terminal.Gui { waitForStart.Reset (); if (inputResultQueue.Count == 0) { - GetConsoleInputType (Console.ReadKey (true)); + GetConsoleKey (); } inputReady.Set (); } } + void GetConsoleKey () + { + ConsoleKey key = 0; + ConsoleModifiers mod = 0; + ConsoleKeyInfo newConsoleKeyInfo = default; + + while (true) { + ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); + if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !isEscSeq) + || (consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq)) { + if (cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq) { + cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), cki); + } + isEscSeq = true; + newConsoleKeyInfo = consoleKeyInfo; + cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki); + if (!Console.KeyAvailable) { + DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod); + cki = null; + isEscSeq = false; + break; + } + } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && isEscSeq) { + DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod); + cki = null; + break; + } else { + GetConsoleInputType (consoleKeyInfo); + break; + } + } + } + void CheckWinChange () { while (true) { + if (stopTasks) { + return; + } winChange.Wait (); winChange.Reset (); WaitWinChange (); @@ -177,44 +224,58 @@ namespace Terminal.Gui { while (true) { // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. Thread.Sleep (10); - if (!consoleDriver.HeightAsBuffer) { - if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) { - var w = Math.Max (Console.WindowWidth, 0); - var h = Math.Max (Console.WindowHeight, 0); - GetWindowSizeEvent (new Size (w, h)); + if (stopTasks) { + return; + } + switch (IsTerminalWithOptions) { + case false: + int buffHeight, buffWidth; + if (((NetDriver)consoleDriver).IsWinPlatform) { + buffHeight = Math.Max (Console.BufferHeight, 0); + buffWidth = Math.Max (Console.BufferWidth, 0); + } else { + buffHeight = consoleDriver.Rows; + buffWidth = consoleDriver.Cols; + } + if (IsWinChanged ( + Math.Max (Console.WindowHeight, 0), + Math.Max (Console.WindowWidth, 0), + buffHeight, + buffWidth)) { + return; } - } else { - //largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); - largestWindowHeight = Console.BufferHeight; - if (Console.BufferWidth != consoleDriver.Cols || largestWindowHeight != consoleDriver.Rows - || Console.WindowHeight != lastWindowHeight) { - lastWindowHeight = Console.WindowHeight; - GetWindowSizeEvent (new Size (Console.BufferWidth, lastWindowHeight)); - return; - } - if (Console.WindowTop != consoleDriver.Top) { - // Top only working on Windows. - var winPositionEv = new WindowPositionEvent () { - Top = Console.WindowTop, - Left = Console.WindowLeft - }; - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.WindowPosition, - WindowPositionEvent = winPositionEv - }); - return; - } -#if PROCESS_REQUEST - if (!neededProcessRequest) { - Console.Out.Write ("\x1b[6n"); - neededProcessRequest = true; - } -#endif + break; + case true: + //Request the size of the text area in characters. + EscSeqReqProc.Add ("t"); + Console.Out.Write ("\x1b[18t"); + break; } } } + bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth) + { + if (!consoleDriver.EnableConsoleScrolling) { + if (winWidth != consoleDriver.Cols || winHeight != consoleDriver.Rows) { + var w = Math.Max (winWidth, 0); + var h = Math.Max (winHeight, 0); + GetWindowSizeEvent (new Size (w, h)); + return true; + } + } else { + if (winWidth != consoleDriver.Cols || winHeight != lastWindowHeight + || buffWidth != consoleDriver.Cols || buffHeight != consoleDriver.Rows) { + + lastWindowHeight = Math.Max (winHeight, 0); + GetWindowSizeEvent (new Size (winWidth, lastWindowHeight)); + return true; + } + } + return false; + } + void GetWindowSizeEvent (Size size) { WindowSizeEvent windowSizeEvent = new WindowSizeEvent () { @@ -232,876 +293,245 @@ namespace Terminal.Gui { InputResult inputResult = new InputResult { EventType = EventType.Key }; - ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo; - ConsoleKey key = 0; MouseEvent mouseEvent = new MouseEvent (); - var keyChar = consoleKeyInfo.KeyChar; - switch ((uint)keyChar) { - case 0: - if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. - newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + ConsoleKeyInfo newConsoleKeyInfo = EscSeqUtils.GetConsoleInputKey (consoleKeyInfo); + if (inputResult.EventType == EventType.Key) { + inputResult.ConsoleKeyInfo = newConsoleKeyInfo; + } else { + inputResult.MouseEvent = mouseEvent; + } + + inputResultQueue.Enqueue (inputResult); + } + + void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) + { + string c1Control, code, terminating; + string [] values; + // isKeyMouse is true if it's CSI<, false otherwise + bool isKeyMouse; + bool isReq; + List mouseFlags; + Point pos; + EscSeqUtils.DecodeEscSeq (EscSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + + if (isKeyMouse) { + foreach (var mf in mouseFlags) { + GetMouseEvent (MapMouseFlags (mf), pos); } - break; - case uint n when (n >= '\u0001' && n <= '\u001a'): - if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') { - key = ConsoleKey.Enter; - newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); - } else if (consoleKeyInfo.Key == 0) { - key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1); - newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - true); - } - break; - case 27: - //case 91: - ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { consoleKeyInfo }; - ConsoleModifiers mod = consoleKeyInfo.Modifiers; - int delay = 0; - while (delay < 100) { - if (Console.KeyAvailable) { - do { - var result = Console.ReadKey (true); - Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1); - cki [cki.Length - 1] = result; - } while (Console.KeyAvailable); + return; + } else if (isReq) { + GetRequestEvent (c1Control, code, values, terminating); + return; + } + InputResult inputResult = new InputResult { + EventType = EventType.Key, + ConsoleKeyInfo = newConsoleKeyInfo + }; + + inputResultQueue.Enqueue (inputResult); + } + + void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) + { + GetMouseEvent (MapMouseFlags (mouseFlag), pos); + } + + MouseButtonState MapMouseFlags (MouseFlags mouseFlags) + { + MouseButtonState mbs = default; + foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) { + if (mouseFlags.HasFlag ((MouseFlags)flag)) { + switch (flag) { + case MouseFlags.Button1Pressed: + mbs |= MouseButtonState.Button1Pressed; + break; + case MouseFlags.Button1Released: + mbs |= MouseButtonState.Button1Released; + break; + case MouseFlags.Button1Clicked: + mbs |= MouseButtonState.Button1Clicked; + break; + case MouseFlags.Button1DoubleClicked: + mbs |= MouseButtonState.Button1DoubleClicked; + break; + case MouseFlags.Button1TripleClicked: + mbs |= MouseButtonState.Button1TripleClicked; + break; + case MouseFlags.Button2Pressed: + mbs |= MouseButtonState.Button2Pressed; + break; + case MouseFlags.Button2Released: + mbs |= MouseButtonState.Button2Released; + break; + case MouseFlags.Button2Clicked: + mbs |= MouseButtonState.Button2Clicked; + break; + case MouseFlags.Button2DoubleClicked: + mbs |= MouseButtonState.Button2DoubleClicked; + break; + case MouseFlags.Button2TripleClicked: + mbs |= MouseButtonState.Button2TripleClicked; + break; + case MouseFlags.Button3Pressed: + mbs |= MouseButtonState.Button3Pressed; + break; + case MouseFlags.Button3Released: + mbs |= MouseButtonState.Button3Released; + break; + case MouseFlags.Button3Clicked: + mbs |= MouseButtonState.Button3Clicked; + break; + case MouseFlags.Button3DoubleClicked: + mbs |= MouseButtonState.Button3DoubleClicked; + break; + case MouseFlags.Button3TripleClicked: + mbs |= MouseButtonState.Button3TripleClicked; + break; + case MouseFlags.WheeledUp: + mbs |= MouseButtonState.ButtonWheeledUp; + break; + case MouseFlags.WheeledDown: + mbs |= MouseButtonState.ButtonWheeledDown; + break; + case MouseFlags.WheeledLeft: + mbs |= MouseButtonState.ButtonWheeledLeft; + break; + case MouseFlags.WheeledRight: + mbs |= MouseButtonState.ButtonWheeledRight; + break; + case MouseFlags.Button4Pressed: + mbs |= MouseButtonState.Button4Pressed; + break; + case MouseFlags.Button4Released: + mbs |= MouseButtonState.Button4Released; + break; + case MouseFlags.Button4Clicked: + mbs |= MouseButtonState.Button4Clicked; + break; + case MouseFlags.Button4DoubleClicked: + mbs |= MouseButtonState.Button4DoubleClicked; + break; + case MouseFlags.Button4TripleClicked: + mbs |= MouseButtonState.Button4TripleClicked; + break; + case MouseFlags.ButtonShift: + mbs |= MouseButtonState.ButtonShift; + break; + case MouseFlags.ButtonCtrl: + mbs |= MouseButtonState.ButtonCtrl; + break; + case MouseFlags.ButtonAlt: + mbs |= MouseButtonState.ButtonAlt; + break; + case MouseFlags.ReportMousePosition: + mbs |= MouseButtonState.ReportMousePosition; + break; + case MouseFlags.AllEvents: + mbs |= MouseButtonState.AllEvents; break; } - Thread.Sleep (50); - delay += 50; - } - SplitCSI (cki, ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, ref mod); - return; - case 127: - newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); - break; - default: - newConsoleKeyInfo = consoleKeyInfo; - break; - } - if (inputResult.EventType == EventType.Key) { - inputResult.ConsoleKeyInfo = newConsoleKeyInfo; - } else { - inputResult.MouseEvent = mouseEvent; - } - - inputResultQueue.Enqueue (inputResult); - } - - void SplitCSI (ConsoleKeyInfo [] cki, ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ref MouseEvent mouseEvent, ref ConsoleModifiers mod) - { - ConsoleKeyInfo [] splitedCki = new ConsoleKeyInfo [] { }; - int length = 0; - var kChar = GetKeyCharArray (cki); - var nCSI = GetNumberOfCSI (kChar); - int curCSI = 0; - char previousKChar = '\0'; - if (nCSI > 1) { - for (int i = 0; i < cki.Length; i++) { - var ck = cki [i]; - if (NumberOfCSI > 0 && nCSI - curCSI > NumberOfCSI) { - if (i + 1 < cki.Length && cki [i + 1].KeyChar == '\x1b' && previousKChar != '\0') { - curCSI++; - previousKChar = '\0'; - } else { - previousKChar = ck.KeyChar; - } - continue; - } - if (ck.KeyChar == '\x1b') { - if (ck.KeyChar == 'R') { - ResizeArray (ck); - } - if (splitedCki.Length > 1) { - DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, splitedCki, ref mod); - } - splitedCki = new ConsoleKeyInfo [] { }; - length = 0; - } - ResizeArray (ck); - if (i == cki.Length - 1 && splitedCki.Length > 0) { - DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, splitedCki, ref mod); - } - } - } else { - DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, cki, ref mod); - } - - void ResizeArray (ConsoleKeyInfo ck) - { - length++; - Array.Resize (ref splitedCki, length); - splitedCki [length - 1] = ck; - } - } - - char [] GetKeyCharArray (ConsoleKeyInfo [] cki) - { - char [] kChar = new char [] { }; - var length = 0; - foreach (var kc in cki) { - length++; - Array.Resize (ref kChar, length); - kChar [length - 1] = kc.KeyChar; - } - - return kChar; - } - - int GetNumberOfCSI (char [] csi) - { - int nCSI = 0; - for (int i = 0; i < csi.Length; i++) { - if (csi [i] == '\x1b' || (csi [i] == '[' && (i == 0 || (i > 0 && csi [i - 1] != '\x1b')))) { - nCSI++; } } - - return nCSI; - } - - void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ref MouseEvent mouseEvent, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) - { - switch (cki.Length) { - case 2: - if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) { - key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); - newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar, - key, - false, - true, - true); - } else { - if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) { - key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; - } else { - key = (ConsoleKey)cki [1].KeyChar; - } - newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - (ConsoleKey)Math.Min ((uint)key, 255), - false, - true, - false); - } - break; - case 3: - if (cki [1].KeyChar == '[' || cki [1].KeyChar == 79) { - key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); - } - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - break; - case 4: - if (cki [1].KeyChar == '[' && cki [3].KeyChar == 126) { - key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } - break; - case 5: - if (cki [1].KeyChar == '[' && (cki [2].KeyChar == 49 || cki [2].KeyChar == 50) - && cki [4].KeyChar == 126) { - key = GetConsoleKey (cki [3].KeyChar, ref mod, cki.Length); - } else if (cki [1].KeyChar == 49 && cki [2].KeyChar == ';') { // For WSL - mod |= GetConsoleModifiers (cki [3].KeyChar); - key = ConsoleKey.End; - } - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - break; - case 6: - if (cki [1].KeyChar == '[' && cki [2].KeyChar == 49 && cki [3].KeyChar == ';') { - mod |= GetConsoleModifiers (cki [4].KeyChar); - key = GetConsoleKey (cki [5].KeyChar, ref mod, cki.Length); - } else if (cki [1].KeyChar == '[' && cki [3].KeyChar == ';') { - mod |= GetConsoleModifiers (cki [4].KeyChar); - key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); - } - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - break; - case 7: - GetRequestEvent (GetKeyCharArray (cki)); - return; - case int n when n >= 8: - GetMouseEvent (cki); - return; - } - if (inputResult.EventType == EventType.Key) { - inputResult.ConsoleKeyInfo = newConsoleKeyInfo; - } else { - inputResult.MouseEvent = mouseEvent; - } - - inputResultQueue.Enqueue (inputResult); + return mbs; } Point lastCursorPosition; - void GetRequestEvent (char [] kChar) + void GetRequestEvent (string c1Control, string code, string [] values, string terminating) { EventType eventType = new EventType (); - Point point = new Point (); - int foundPoint = 0; - string value = ""; - for (int i = 0; i < kChar.Length; i++) { - var c = kChar [i]; - if (c == '\u001b' || c == '[') { - foundPoint++; - } else if (foundPoint == 1 && c != ';' && c != '?') { - value += c.ToString (); - } else if (c == '?') { - foundPoint++; - } else if (c == ';') { - if (foundPoint >= 1) { - point.Y = int.Parse (value) - 1; - } - value = ""; - foundPoint++; - } else if (foundPoint > 0 && i < kChar.Length - 1) { - value += c.ToString (); - } else if (i == kChar.Length - 1) { - point.X = int.Parse (value) + Console.WindowTop - 1; - - switch (c) { - case 'R': - if (lastCursorPosition.Y != point.Y) { - lastCursorPosition = point; - eventType = EventType.WindowPosition; - var winPositionEv = new WindowPositionEvent () { - CursorPosition = point - }; - inputResultQueue.Enqueue (new InputResult () { - EventType = eventType, - WindowPositionEvent = winPositionEv - }); - } else { - return; - } - break; - case 'c': // CSI?1;0c ("VT101 with No Options") - break; - default: - throw new NotImplementedException (); - } + switch (terminating) { + case "R": // Reports cursor position as CSI r ; c R + Point point = new Point { + X = int.Parse (values [1]) - 1, + Y = int.Parse (values [0]) - 1 + }; + if (lastCursorPosition.Y != point.Y) { + lastCursorPosition = point; + eventType = EventType.WindowPosition; + var winPositionEv = new WindowPositionEvent () { + CursorPosition = point + }; + inputResultQueue.Enqueue (new InputResult () { + EventType = eventType, + WindowPositionEvent = winPositionEv + }); + } else { + return; } + break; + case "c": + try { + var parent = EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ()); + if (parent == null) { Debug.WriteLine ("Not supported!"); } + } catch (Exception ex) { + Debug.WriteLine (ex.Message); + } + + if (c1Control == "CSI" && values.Length == 2 + && values [0] == "1" && values [1] == "0") { + // Reports CSI?1;0c ("VT101 with No Options") + IsTerminalWithOptions = false; + } else { + IsTerminalWithOptions = true; + } + break; + case "t": + switch (values [0]) { + case "8": + IsWinChanged ( + Math.Max (int.Parse (values [1]), 0), + Math.Max (int.Parse (values [2]), 0), + Math.Max (int.Parse (values [1]), 0), + Math.Max (int.Parse (values [2]), 0)); + break; + default: + SetRequestedEvent (c1Control, code, values, terminating); + break; + } + break; + default: + SetRequestedEvent (c1Control, code, values, terminating); + break; } inputReady.Set (); } - MouseEvent lastMouseEvent; - bool isButtonPressed; - bool isButtonClicked; - bool isButtonDoubleClicked; - bool isButtonTripleClicked; - bool isProcContBtnPressedRuning; - int buttonPressedCount; - //bool isButtonReleased; - - void GetMouseEvent (ConsoleKeyInfo [] cki) + void SetRequestedEvent (string c1Control, string code, string [] values, string terminating) { - MouseEvent mouseEvent = new MouseEvent (); - MouseButtonState buttonState = 0; - Point point = new Point (); - int buttonCode = 0; - bool foundButtonCode = false; - int foundPoint = 0; - string value = ""; - var kChar = GetKeyCharArray (cki); - //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}"); - for (int i = 0; i < kChar.Length; i++) { - var c = kChar [i]; - if (c == '<') { - foundButtonCode = true; - } else if (foundButtonCode && c != ';') { - value += c.ToString (); - } else if (c == ';') { - if (foundButtonCode) { - foundButtonCode = false; - buttonCode = int.Parse (value); - } - if (foundPoint == 1) { - point.X = int.Parse (value) - 1; - } - value = ""; - foundPoint++; - } else if (foundPoint > 0 && c != 'm' && c != 'M') { - value += c.ToString (); - } else if (c == 'm' || c == 'M') { - point.Y = int.Parse (value) + Console.WindowTop - 1; + EventType eventType = EventType.RequestResponse; + var requestRespEv = new RequestResponseEvent () { + ResultTuple = (c1Control, code, values, terminating) + }; + inputResultQueue.Enqueue (new InputResult () { + EventType = eventType, + RequestResponseEvent = requestRespEv + }); + } - //if (c == 'M') { - // isButtonPressed = true; - //} else if (c == 'm') { - // isButtonPressed = false; - //} - - //System.Diagnostics.Debug.WriteLine ($"buttonCode: {buttonCode}"); - - switch (buttonCode) { - case 0: - case 8: - case 16: - case 24: - case 32: - case 36: - case 40: - case 48: - case 56: - buttonState = c == 'M' ? MouseButtonState.Button1Pressed - : MouseButtonState.Button1Released; - break; - case 1: - case 9: - case 17: - case 25: - case 33: - case 37: - case 41: - case 45: - case 49: - case 53: - case 57: - case 61: - buttonState = c == 'M' ? MouseButtonState.Button2Pressed - : MouseButtonState.Button2Released; - break; - case 2: - case 10: - case 14: - case 18: - case 22: - case 26: - case 30: - case 34: - case 42: - case 46: - case 50: - case 54: - case 58: - case 62: - buttonState = c == 'M' ? MouseButtonState.Button3Pressed - : MouseButtonState.Button3Released; - break; - case 35: - case 39: - case 43: - case 47: - case 55: - case 59: - case 63: - buttonState = MouseButtonState.ReportMousePosition; - break; - case 64: - buttonState = MouseButtonState.ButtonWheeledUp; - break; - case 65: - buttonState = MouseButtonState.ButtonWheeledDown; - break; - case 68: - case 72: - case 80: - buttonState = MouseButtonState.ButtonWheeledLeft; // Shift/Ctrl+ButtonWheeledUp - break; - case 69: - case 73: - case 81: - buttonState = MouseButtonState.ButtonWheeledRight; // Shift/Ctrl+ButtonWheeledDown - break; - } - // Modifiers. - switch (buttonCode) { - case 8: - case 9: - case 10: - case 43: - buttonState |= MouseButtonState.ButtonAlt; - break; - case 14: - case 47: - buttonState |= MouseButtonState.ButtonAlt | MouseButtonState.ButtonShift; - break; - case 16: - case 17: - case 18: - case 51: - buttonState |= MouseButtonState.ButtonCtrl; - break; - case 22: - case 55: - buttonState |= MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift; - break; - case 24: - case 25: - case 26: - case 59: - buttonState |= MouseButtonState.ButtonAlt | MouseButtonState.ButtonCtrl; - break; - case 30: - case 63: - buttonState |= MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift | MouseButtonState.ButtonAlt; - break; - case 32: - case 33: - case 34: - buttonState |= MouseButtonState.ReportMousePosition; - break; - case 36: - case 37: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonShift; - break; - case 39: - case 68: - case 69: - buttonState |= MouseButtonState.ButtonShift; - break; - case 40: - case 41: - case 42: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonAlt; - break; - case 45: - case 46: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonAlt | MouseButtonState.ButtonShift; - break; - case 48: - case 49: - case 50: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl; - break; - case 53: - case 54: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift; - break; - case 56: - case 57: - case 58: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonAlt; - break; - case 61: - case 62: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift | MouseButtonState.ButtonAlt; - break; - } - } - } - mouseEvent.Position.X = point.X; - mouseEvent.Position.Y = point.Y; - mouseEvent.ButtonState = buttonState; - //System.Diagnostics.Debug.WriteLine ($"ButtonState: {mouseEvent.ButtonState} X: {mouseEvent.Position.X} Y: {mouseEvent.Position.Y}"); - - if (isButtonDoubleClicked) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); - return false; - }); - } - - if ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0) { - - if ((buttonState & MouseButtonState.ReportMousePosition) == 0) { - buttonPressedCount++; - } else { - buttonPressedCount = 0; - } - //System.Diagnostics.Debug.WriteLine ($"buttonPressedCount: {buttonPressedCount}"); - isButtonPressed = true; - } else { - isButtonPressed = false; - buttonPressedCount = 0; - } - - if (buttonPressedCount == 2 && !isButtonDoubleClicked - && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { - - isButtonDoubleClicked = true; - ProcessButtonDoubleClicked (mouseEvent); - inputReady.Set (); - return; - } else if (buttonPressedCount == 3 && isButtonDoubleClicked - && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { - - isButtonDoubleClicked = false; - isButtonTripleClicked = true; - buttonPressedCount = 0; - ProcessButtonTripleClicked (mouseEvent); - lastMouseEvent = mouseEvent; - inputReady.Set (); - return; - } - - //System.Diagnostics.Debug.WriteLine ($"isButtonClicked: {isButtonClicked} isButtonDoubleClicked: {isButtonDoubleClicked} isButtonTripleClicked: {isButtonTripleClicked}"); - if ((isButtonClicked || isButtonDoubleClicked || isButtonTripleClicked) - && ((buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { - - //isButtonClicked = false; - //isButtonDoubleClicked = false; - isButtonTripleClicked = false; - buttonPressedCount = 0; - return; - } - - if (isButtonClicked && !isButtonDoubleClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0 - || (buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { - - isButtonClicked = false; - isButtonDoubleClicked = true; - ProcessButtonDoubleClicked (mouseEvent); - Application.MainLoop.AddIdle (() => { - Task.Run (async () => { - await Task.Delay (600); - isButtonDoubleClicked = false; - }); - return false; - }); - inputReady.Set (); - return; - } - if (isButtonDoubleClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0 - || (buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { - - isButtonDoubleClicked = false; - isButtonTripleClicked = true; - ProcessButtonTripleClicked (mouseEvent); - inputReady.Set (); - return; - } - - //if (!isButtonPressed && !isButtonClicked && !isButtonDoubleClicked && !isButtonTripleClicked - // && !isButtonReleased && lastMouseEvent.ButtonState != 0 - // && ((buttonState & MouseButtonState.Button1Released) == 0 - // && (buttonState & MouseButtonState.Button2Released) == 0 - // && (buttonState & MouseButtonState.Button3Released) == 0)) { - // ProcessButtonReleased (lastMouseEvent); - // inputReady.Set (); - // return; - //} + void GetMouseEvent (MouseButtonState buttonState, Point pos) + { + MouseEvent mouseEvent = new MouseEvent () { + Position = pos, + ButtonState = buttonState, + }; inputResultQueue.Enqueue (new InputResult () { EventType = EventType.Mouse, MouseEvent = mouseEvent }); - if (!isButtonClicked && !lastMouseEvent.ButtonState.HasFlag (MouseButtonState.ReportMousePosition) - && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { - isButtonClicked = true; - ProcessButtonClicked (mouseEvent); - Application.MainLoop.AddIdle (() => { - Task.Run (async () => { - await Task.Delay (300); - isButtonClicked = false; - }); - return false; - }); - inputReady.Set (); - return; - } - - lastMouseEvent = mouseEvent; - if (isButtonPressed && !isButtonClicked && !isButtonDoubleClicked && !isButtonTripleClicked && !isProcContBtnPressedRuning) { - //isButtonReleased = false; - if ((buttonState & MouseButtonState.ReportMousePosition) != 0) { - point = new Point (); - } else { - point = new Point () { - X = mouseEvent.Position.X, - Y = mouseEvent.Position.Y - }; - } - if ((buttonState & MouseButtonState.ReportMousePosition) == 0) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessContinuousButtonPressedAsync ()); - return false; - }); - } - } - inputReady.Set (); } - void ProcessButtonClicked (MouseEvent mouseEvent) - { - var me = new MouseEvent () { - Position = mouseEvent.Position, - ButtonState = mouseEvent.ButtonState - }; - if ((mouseEvent.ButtonState & MouseButtonState.Button1Released) != 0) { - me.ButtonState &= ~MouseButtonState.Button1Released; - me.ButtonState |= MouseButtonState.Button1Clicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Released) != 0) { - me.ButtonState &= ~MouseButtonState.Button2Released; - me.ButtonState |= MouseButtonState.Button2Clicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Released) != 0) { - me.ButtonState &= ~MouseButtonState.Button3Released; - me.ButtonState |= MouseButtonState.Button3Clicked; - } - //isButtonReleased = true; - - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = me - }); - } - - async Task ProcessButtonDoubleClickedAsync () - { - await Task.Delay (300); - isButtonDoubleClicked = false; - buttonPressedCount = 0; - } - - void ProcessButtonDoubleClicked (MouseEvent mouseEvent) - { - var me = new MouseEvent () { - Position = mouseEvent.Position, - ButtonState = mouseEvent.ButtonState - }; - if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button1Pressed; - me.ButtonState |= MouseButtonState.Button1DoubleClicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button2Pressed; - me.ButtonState |= MouseButtonState.Button2DoubleClicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button3Pressed; - me.ButtonState |= MouseButtonState.Button3DoubleClicked; - } - //isButtonReleased = true; - - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = me - }); - } - - void ProcessButtonTripleClicked (MouseEvent mouseEvent) - { - var me = new MouseEvent () { - Position = mouseEvent.Position, - ButtonState = mouseEvent.ButtonState - }; - if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button1Pressed; - me.ButtonState |= MouseButtonState.Button1TripleClicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button2Pressed; - me.ButtonState |= MouseButtonState.Button2TrippleClicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button3Pressed; - me.ButtonState |= MouseButtonState.Button3TripleClicked; - } - //isButtonReleased = true; - - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = me - }); - } - - async Task ProcessContinuousButtonPressedAsync () - { - isProcContBtnPressedRuning = true; - await Task.Delay (200); - while (isButtonPressed) { - await Task.Delay (100); - var view = Application.WantContinuousButtonPressedView; - if (view == null) { - break; - } - if (isButtonPressed && (lastMouseEvent.ButtonState & MouseButtonState.ReportMousePosition) == 0) { - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = lastMouseEvent - }); - inputReady.Set (); - } - } - isProcContBtnPressedRuning = false; - //isButtonPressed = false; - } - - //void ProcessButtonReleased (MouseEvent mouseEvent) - //{ - // var me = new MouseEvent () { - // Position = mouseEvent.Position, - // ButtonState = mouseEvent.ButtonState - // }; - // if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) { - // me.ButtonState &= ~(MouseButtonState.Button1Pressed | MouseButtonState.ReportMousePosition); - // me.ButtonState |= MouseButtonState.Button1Released; - // } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) { - // me.ButtonState &= ~(MouseButtonState.Button2Pressed | MouseButtonState.ReportMousePosition); - // me.ButtonState |= MouseButtonState.Button2Released; - // } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) { - // me.ButtonState &= ~(MouseButtonState.Button3Pressed | MouseButtonState.ReportMousePosition); - // me.ButtonState |= MouseButtonState.Button3Released; - // } - // isButtonReleased = true; - // lastMouseEvent = me; - - // inputResultQueue.Enqueue (new InputResult () { - // EventType = EventType.Mouse, - // MouseEvent = me - // }); - //} - - ConsoleModifiers GetConsoleModifiers (uint keyChar) - { - switch (keyChar) { - case 50: - return ConsoleModifiers.Shift; - case 51: - return ConsoleModifiers.Alt; - case 52: - return ConsoleModifiers.Shift | ConsoleModifiers.Alt; - case 53: - return ConsoleModifiers.Control; - case 54: - return ConsoleModifiers.Shift | ConsoleModifiers.Control; - case 55: - return ConsoleModifiers.Alt | ConsoleModifiers.Control; - case 56: - return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control; - default: - return 0; - } - } - - ConsoleKey GetConsoleKey (char keyChar, ref ConsoleModifiers mod, int length) - { - ConsoleKey key; - switch (keyChar) { - case 'A': - key = ConsoleKey.UpArrow; - break; - case 'B': - key = ConsoleKey.DownArrow; - break; - case 'C': - key = ConsoleKey.RightArrow; - break; - case 'D': - key = ConsoleKey.LeftArrow; - break; - case 'F': - key = ConsoleKey.End; - break; - case 'H': - key = ConsoleKey.Home; - break; - case 'P': - key = ConsoleKey.F1; - break; - case 'Q': - key = ConsoleKey.F2; - break; - case 'R': - key = ConsoleKey.F3; - break; - case 'S': - key = ConsoleKey.F4; - break; - case 'Z': - key = ConsoleKey.Tab; - mod |= ConsoleModifiers.Shift; - break; - case '0': - key = ConsoleKey.F9; - break; - case '1': - key = ConsoleKey.F10; - break; - case '2': - key = ConsoleKey.Insert; - break; - case '3': - if (length == 5) { - key = ConsoleKey.F11; - } else { - key = ConsoleKey.Delete; - } - break; - case '4': - key = ConsoleKey.F12; - break; - case '5': - if (length == 5) { - key = ConsoleKey.F5; - } else { - key = ConsoleKey.PageUp; - } - break; - case '6': - key = ConsoleKey.PageDown; - break; - case '7': - key = ConsoleKey.F6; - break; - case '8': - key = ConsoleKey.F7; - break; - case '9': - key = ConsoleKey.F8; - break; - default: - key = 0; - break; - } - - return key; - } - public enum EventType { Key = 1, Mouse = 2, WindowSize = 3, - WindowPosition = 4 + WindowPosition = 4, + RequestResponse = 5 } [Flags] @@ -1115,7 +545,7 @@ namespace Terminal.Gui { Button2Released = 0x40, Button2Clicked = 0x80, Button2DoubleClicked = 0x100, - Button2TrippleClicked = 0x200, + Button2TripleClicked = 0x200, Button3Pressed = 0x400, Button3Released = 0x800, Button3Clicked = 0x1000, @@ -1134,7 +564,7 @@ namespace Terminal.Gui { ButtonCtrl = 0x2000000, ButtonAlt = 0x4000000, ReportMousePosition = 0x8000000, - AllEvents = Button1Pressed | Button1Released | Button1Clicked | Button1DoubleClicked | Button1TripleClicked | Button2Pressed | Button2Released | Button2Clicked | Button2DoubleClicked | Button2TrippleClicked | Button3Pressed | Button3Released | Button3Clicked | Button3DoubleClicked | Button3TripleClicked | ButtonWheeledUp | ButtonWheeledDown | ButtonWheeledLeft | ButtonWheeledRight | Button4Pressed | Button4Released | Button4Clicked | Button4DoubleClicked | Button4TripleClicked | ReportMousePosition + AllEvents = -1 } public struct MouseEvent { @@ -1152,12 +582,17 @@ namespace Terminal.Gui { public Point CursorPosition; } + public struct RequestResponseEvent { + public (string c1Control, string code, string [] values, string terminating) ResultTuple; + } + public struct InputResult { public EventType EventType; public ConsoleKeyInfo ConsoleKeyInfo; public MouseEvent MouseEvent; public WindowSizeEvent WindowSizeEvent; public WindowPositionEvent WindowPositionEvent; + public RequestResponseEvent RequestResponseEvent; } } @@ -1185,14 +620,13 @@ namespace Terminal.Gui { public override int Rows => rows; public override int Left => left; public override int Top => top; - public override bool HeightAsBuffer { get; set; } - + public override bool EnableConsoleScrolling { get; set; } public NetWinVTConsole NetWinConsole { get; } public bool IsWinPlatform { get; } public override IClipboard Clipboard { get; } public override int [,,] Contents => contents; - int largestWindowHeight; + int largestBufferHeight; public NetDriver () { @@ -1201,8 +635,6 @@ namespace Terminal.Gui { IsWinPlatform = true; NetWinConsole = new NetWinVTConsole (); } - //largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); - largestWindowHeight = Console.BufferHeight; if (IsWinPlatform) { Clipboard = new WindowsClipboard (); } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { @@ -1283,7 +715,7 @@ namespace Terminal.Gui { if (runeWidth < 0 || runeWidth > 0) { ccol++; } - + if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { contents [crow, ccol, 1] = CurrentAttribute; @@ -1292,11 +724,6 @@ namespace Terminal.Gui { ccol++; } - //if (ccol == Cols) { - // ccol = 0; - // if (crow + 1 < Rows) - // crow++; - //} if (sync) { UpdateScreen (); } @@ -1310,25 +737,22 @@ namespace Terminal.Gui { public override void End () { + mainLoop.netEvents.StopTasks (); + if (IsWinPlatform) { NetWinConsole.Cleanup (); } StopReportingMouseMoves (); Console.ResetColor (); - Clear (); + + //Disable alternative screen buffer. + Console.Out.Write ("\x1b[?1049l"); + //Set cursor key to cursor. Console.Out.Write ("\x1b[?25h"); - Console.Out.Flush (); - } - void Clear () - { - if (Rows > 0) { - Console.Clear (); - Console.Out.Write ("\x1b[3J"); - //Console.Out.Write ("\x1b[?25l"); - } + Console.Out.Close (); } public override Attribute MakeColor (Color foreground, Color background) @@ -1350,14 +774,25 @@ namespace Terminal.Gui { { TerminalResized = terminalResized; + //Enable alternative screen buffer. + Console.Out.Write ("\x1b[?1049h"); + //Set cursor key to application. Console.Out.Write ("\x1b[?25l"); - Console.Out.Flush (); Console.TreatControlCAsInput = true; + if (EnableConsoleScrolling) { + largestBufferHeight = Console.BufferHeight; + } else { + largestBufferHeight = Console.WindowHeight; + } + cols = Console.WindowWidth; - rows = Console.WindowHeight; + rows = largestBufferHeight; + + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitalizeColorSchemes (); CurrentAttribute = MakeColor (Color.White, Color.Black); InitalizeColorSchemes (); @@ -1366,55 +801,64 @@ namespace Terminal.Gui { UpdateOffScreen (); StartReportingMouseMoves (); - - - Clear (); } public override void ResizeScreen () { - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { if (Console.WindowHeight > 0) { - // Can raise an exception while is still resizing. - try { - // Not supported on Unix. - if (IsWinPlatform) { + // Not supported on Unix. + if (IsWinPlatform) { + // Can raise an exception while is still resizing. + try { #pragma warning disable CA1416 Console.CursorTop = 0; Console.CursorLeft = 0; Console.WindowTop = 0; Console.WindowLeft = 0; + if (Console.WindowHeight > Rows) { + Console.SetWindowSize (Cols, Rows); + } Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 - } else { - //Console.Out.Write ($"\x1b[8;{Console.WindowHeight};{Console.WindowWidth}t"); - Console.Out.Write ($"\x1b[0;0" + - $";{Rows};{Cols}w"); + } catch (System.IO.IOException) { + setClip (); + } catch (ArgumentOutOfRangeException) { + setClip (); } - } catch (System.IO.IOException) { - return; - } catch (ArgumentOutOfRangeException) { - return; + } else { + Console.Out.Write ($"\x1b[8;{Rows};{Cols}t"); } } } else { - if (IsWinPlatform && Console.WindowHeight > 0) { - // Can raise an exception while is still resizing. - try { + if (IsWinPlatform) { + if (Console.WindowHeight > 0) { + // Can raise an exception while is still resizing. + try { #pragma warning disable CA1416 - Console.WindowTop = Math.Max (Math.Min (top, Rows - Console.WindowHeight), 0); + Console.CursorTop = 0; + Console.CursorLeft = 0; + if (Console.WindowHeight > Rows) { + Console.SetWindowSize (Cols, Rows); + } + Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 - } catch (Exception) { - return; + } catch (System.IO.IOException) { + setClip (); + } catch (ArgumentOutOfRangeException) { + setClip (); + } } } else { - Console.Out.Write ($"\x1b[{top};{Console.WindowLeft}" + - $";{Rows};{Cols}w"); + Console.Out.Write ($"\x1b[30;{Rows};{Cols}t"); } } - Clip = new Rect (0, 0, Cols, Rows); - Console.Out.Write ("\x1b[3J"); - Console.Out.Flush (); + setClip (); + + void setClip () + { + Clip = new Rect (0, 0, Cols, Rows); + } } public override void UpdateOffScreen () @@ -1448,42 +892,43 @@ namespace Terminal.Gui { UpdateCursor (); } - int redrawAttr = -1; - public override void UpdateScreen () { - if (Console.WindowHeight == 0 || contents.Length != Rows * Cols * 3 - || (!HeightAsBuffer && Rows != Console.WindowHeight) - || (HeightAsBuffer && Rows != largestWindowHeight)) { + if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3 + || (!EnableConsoleScrolling && Rows != Console.WindowHeight) + || (EnableConsoleScrolling && Rows != largestBufferHeight)) { return; } - int top = Top; - int left = Left; - int rows = Math.Min (Console.WindowHeight + top, Rows); + int top = 0; + int left = 0; + int rows = Rows; int cols = Cols; System.Text.StringBuilder output = new System.Text.StringBuilder (); + int redrawAttr = -1; var lastCol = -1; Console.CursorVisible = false; + for (int row = top; row < rows; row++) { + if (Console.WindowHeight < 1) { + return; + } if (!dirtyLine [row]) { continue; } + if (!SetCursorPosition (0, row)) { + return; + } dirtyLine [row] = false; output.Clear (); for (int col = left; col < cols; col++) { - if (Console.WindowHeight > 0 && !SetCursorPosition (col, row)) { - return; - } lastCol = -1; var outputWidth = 0; for (; col < cols; col++) { if (contents [row, col, 2] == 0) { if (output.Length > 0) { - //Console.CursorLeft = lastCol; - //Console.CursorTop = row; - SetVirtualCursorPosition (lastCol, row); + SetCursorPosition (lastCol, row); Console.Write (output); output.Clear (); lastCol += outputWidth; @@ -1501,6 +946,7 @@ namespace Terminal.Gui { var attr = contents [row, col, 1]; if (attr != redrawAttr) { + redrawAttr = attr; output.Append (WriteAttributes (attr)); } outputWidth++; @@ -1515,18 +961,16 @@ namespace Terminal.Gui { } } if (output.Length > 0) { - //Console.CursorLeft = lastCol; - //Console.CursorTop = row; - SetVirtualCursorPosition (lastCol, row); + SetCursorPosition (lastCol, row); Console.Write (output); } } + SetCursorPosition (0, 0); } - void SetVirtualCursorPosition (int lastCol, int row) + void SetVirtualCursorPosition (int col, int row) { - Console.Out.Write ($"\x1b[{row + 1};{lastCol + 1}H"); - Console.Out.Flush (); + Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); } System.Text.StringBuilder WriteAttributes (int attr) @@ -1536,10 +980,9 @@ namespace Terminal.Gui { int fg = 0; System.Text.StringBuilder sb = new System.Text.StringBuilder (); - redrawAttr = attr; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains (attr & 0xffff)) { bg = MapColors ((ConsoleColor)(attr & 0xffff), false); } @@ -1592,42 +1035,80 @@ namespace Terminal.Gui { bool SetCursorPosition (int col, int row) { - // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. - try { - Console.SetCursorPosition (col, row); + if (IsWinPlatform) { + // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. + try { + Console.SetCursorPosition (col, row); + return true; + } catch (Exception) { + return false; + } + } else { + SetVirtualCursorPosition (col, row); return true; - } catch (Exception) { - return false; } } + private void SetWindowPosition (int col, int row) + { + if (IsWinPlatform && EnableConsoleScrolling) { + var winTop = Math.Max (Rows - Console.WindowHeight - row, 0); + winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1); + winTop = Math.Max (winTop, 0); + if (winTop != Console.WindowTop) { + try { + if (!EnsureBufferSize ()) { + return; + } +#pragma warning disable CA1416 + Console.SetWindowPosition (col, winTop); +#pragma warning restore CA1416 + } catch (System.IO.IOException) { + + } catch (System.ArgumentOutOfRangeException) { } + } + } + top = Console.WindowTop; + left = Console.WindowLeft; + } + + private bool EnsureBufferSize () + { +#pragma warning disable CA1416 + if (IsWinPlatform && Console.BufferHeight < Rows) { + try { + Console.SetBufferSize (Console.WindowWidth, Rows); + } catch (Exception) { + return false; + } + } +#pragma warning restore CA1416 + return true; + } + private CursorVisibility? savedCursorVisibility; public override void UpdateCursor () { - if (!EnsureCursorVisibility ()) - return; + EnsureCursorVisibility (); + //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); - // Prevents the exception of size changing during resizing. - try { - if (ccol >= 0 && ccol < Console.BufferWidth && crow >= 0 && crow < Console.BufferHeight) { - Console.SetCursorPosition (ccol, crow); - } - } catch (System.IO.IOException) { - } catch (ArgumentOutOfRangeException) { + if (ccol >= 0 && ccol < Cols && crow >= 0 && crow < Rows) { + SetCursorPosition (ccol, crow); + SetWindowPosition (0, crow); } + //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}"); + //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); } public override void StartReportingMouseMoves () { - Console.Out.Write ("\x1b[?1003h\x1b[?1015h\x1b[?1006h"); - Console.Out.Flush (); + Console.Out.Write (EscSeqUtils.EnableMouseEvents); } public override void StopReportingMouseMoves () { - Console.Out.Write ("\x1b[?1003l\x1b[?1015l\x1b[?1006l"); - Console.Out.Flush (); + Console.Out.Write (EscSeqUtils.DisableMouseEvents); } public override void Suspend () @@ -1782,6 +1263,7 @@ namespace Terminal.Gui { Action keyDownHandler; Action keyUpHandler; Action mouseHandler; + NetMainLoop mainLoop; public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { @@ -1790,10 +1272,14 @@ namespace Terminal.Gui { this.keyUpHandler = keyUpHandler; this.mouseHandler = mouseHandler; - var mLoop = mainLoop.Driver as NetMainLoop; + var mLoop = this.mainLoop = mainLoop.Driver as NetMainLoop; // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. mLoop.ProcessInput = (e) => ProcessInput (e); + + // Check if terminal supports requests + this.mainLoop.netEvents.EscSeqReqProc.Add ("c"); + Console.Out.Write ("\x1b[0c"); } void ProcessInput (NetEvents.InputResult inputEvent) @@ -1822,38 +1308,31 @@ namespace Terminal.Gui { mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); break; case NetEvents.EventType.WindowSize: - ChangeWin (); + ChangeWin (inputEvent.WindowSizeEvent.Size); break; - case NetEvents.EventType.WindowPosition: - var newTop = inputEvent.WindowPositionEvent.Top; - var newLeft = inputEvent.WindowPositionEvent.Left; - if (HeightAsBuffer && (top != newTop || left != newLeft)) { - top = newTop; - left = newLeft; - Refresh (); - } + case NetEvents.EventType.RequestResponse: + Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; break; } } - void ChangeWin () + volatile bool winChanging; + + void ChangeWin (Size size) { - const int Min_WindowWidth = 14; - Size size = new Size (); - if (!HeightAsBuffer) { - size = new Size (Math.Max (Min_WindowWidth, Console.WindowWidth), - Console.WindowHeight); - top = 0; - left = 0; + winChanging = true; + if (!EnableConsoleScrolling) { + largestBufferHeight = Math.Max (size.Height, 0); } else { - //largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); - largestWindowHeight = Console.BufferHeight; - size = new Size (Console.BufferWidth, largestWindowHeight); + largestBufferHeight = Math.Max (size.Height, largestBufferHeight); } + top = 0; + left = 0; cols = size.Width; - rows = size.Height; + rows = largestBufferHeight; ResizeScreen (); UpdateOffScreen (); + winChanging = false; TerminalResized?.Invoke (); } @@ -1890,7 +1369,7 @@ namespace Terminal.Gui { if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) { mouseFlag |= MouseFlags.Button2DoubleClicked; } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2TrippleClicked) != 0) { + if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) { mouseFlag |= MouseFlags.Button2TripleClicked; } if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) { @@ -2001,8 +1480,8 @@ namespace Terminal.Gui { foreground = default; background = default; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains (value & 0xffff)) { hasColor = true; background = (Color)(ConsoleColor)(value & 0xffff); @@ -2053,7 +1532,7 @@ namespace Terminal.Gui { Queue inputResult = new Queue (); MainLoop mainLoop; CancellationTokenSource tokenSource = new CancellationTokenSource (); - NetEvents netEvents; + internal NetEvents netEvents; /// /// Invoked when a Key is pressed. diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index d7b72fe37..5de19fa85 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1,30 +1,6 @@ // // WindowsDriver.cs: Windows specific driver // -// Authors: -// Miguel de Icaza (miguel@gnome.org) -// Nick Van Dyck (vandyck.nick@outlook.com) -// -// Copyright (c) 2018 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// using NStack; using System; using System.Collections.Generic; @@ -646,7 +622,7 @@ namespace Terminal.Gui { } } -#if false // Not needed on the constructor. Perhaps could be used on resizing. To study. +#if false // Not needed on the constructor. Perhaps could be used on resizing. To study. [DllImport ("kernel32.dll", ExactSpelling = true)] static extern IntPtr GetConsoleWindow (); @@ -739,7 +715,7 @@ namespace Terminal.Gui { public override int Rows => rows; public override int Left => left; public override int Top => top; - public override bool HeightAsBuffer { get; set; } + public override bool EnableConsoleScrolling { get; set; } public override IClipboard Clipboard => clipboard; public override int [,,] Contents => contents; @@ -774,7 +750,7 @@ namespace Terminal.Gui { private void ChangeWin (Size e) { - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { var w = e.Width; if (w == cols - 3 && e.Height < rows) { w += 3; @@ -915,8 +891,12 @@ namespace Terminal.Gui { left = pos.X; top = pos.Y; cols = inputEvent.WindowBufferSizeEvent.size.X; - rows = inputEvent.WindowBufferSizeEvent.size.Y; - //System.Diagnostics.Debug.WriteLine ($"{HeightAsBuffer},{cols},{rows}"); + if (EnableConsoleScrolling) { + rows = Math.Max (inputEvent.WindowBufferSizeEvent.size.Y, rows); + } else { + rows = inputEvent.WindowBufferSizeEvent.size.Y; + } + //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}"); ResizeScreen (); UpdateOffScreen (); TerminalResized?.Invoke (); @@ -1459,6 +1439,19 @@ namespace Terminal.Gui { TerminalResized = terminalResized; try { + // Needed for Windows Terminal + // ESC [ ? 1047 h Activate xterm alternative buffer (no backscroll) + // ESC [ ? 1047 l Restore xterm working buffer (with backscroll) + // ESC [ ? 1048 h Save cursor position + // ESC [ ? 1048 l Restore cursor position + // ESC [ ? 1049 h Save cursor position and activate xterm alternative buffer (no backscroll) + // ESC [ ? 1049 l Restore cursor position and restore xterm working buffer (with backscroll) + // Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not + // wipe out the backscroll buffer when the application exits. + Console.Out.Write ("\x1b[?1047h"); + + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 + var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); cols = winSize.Width; rows = winSize.Height; @@ -1467,6 +1460,9 @@ namespace Terminal.Gui { CurrentAttribute = MakeColor (Color.White, Color.Black); InitalizeColorSchemes (); + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitalizeColorSchemes (); + ResizeScreen (); UpdateOffScreen (); } catch (Win32Exception e) { @@ -1485,8 +1481,15 @@ namespace Terminal.Gui { Right = (short)Cols }; WinConsole.ForceRefreshCursorVisibility (); - Console.Out.Write ("\x1b[3J"); - Console.Out.Flush (); + if (!EnableConsoleScrolling) { + // ANSI ESC "[xJ" Clears part of the screen. + // If n is 0 (or missing), clear from cursor to end of screen. + // If n is 1, clear from cursor to beginning of the screen. + // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer + Console.Out.Write ("\x1b[3J"); + } } public override void UpdateOffScreen () @@ -1653,7 +1656,7 @@ namespace Terminal.Gui { if (damageRegion.Left == -1) return; - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { var windowSize = WinConsole.GetConsoleBufferWindow (out _); if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) return; @@ -1700,6 +1703,18 @@ namespace Terminal.Gui { { WinConsole.Cleanup (); WinConsole = null; + + // Needed for Windows Terminal + // Clear the alternative screen buffer from the cursor to the + // end of the screen. + // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE + // backbuffer! So we need to use [0J instead. + Console.Out.Write ("\x1b[0J"); + + // Disable alternative screen buffer. + Console.Out.Write ("\x1b[?1047l"); + + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 } /// @@ -1780,8 +1795,8 @@ namespace Terminal.Gui { foreground = default; background = default; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains ((value >> 4) & 0xffff)) { hasColor = true; background = (Color)(ConsoleColor)((value >> 4) & 0xffff); @@ -1897,9 +1912,9 @@ namespace Terminal.Gui { { while (true) { Thread.Sleep (100); - if (!consoleDriver.HeightAsBuffer) { + if (!consoleDriver.EnableConsoleScrolling) { windowSize = winConsole.GetConsoleBufferWindow (out _); - //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.HeightAsBuffer},{windowSize.Width},{windowSize.Height}"); + //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.EnableConsoleScrolling},{windowSize.Width},{windowSize.Height}"); if (windowSize != Size.Empty && windowSize.Width != consoleDriver.Cols || windowSize.Height != consoleDriver.Rows) { return; diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 9fb6ecbc9..a646cbddb 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -114,27 +114,39 @@ namespace Terminal.Gui { /// public static View WantContinuousButtonPressedView { get; private set; } - private static bool? _heightAsBuffer; + private static bool? _enableConsoleScrolling; /// - /// The current used in the terminal. + /// The current used in the terminal. /// - /// - [SerializableConfigurationProperty (Scope = typeof(SettingsScope))] - public static bool HeightAsBuffer { + /// + /// + /// If (the default) the height of the Terminal.Gui application () + /// tracks to the height of the visible console view when the console is resized. In this case + /// scrolling in the console will be disabled and all will remain visible. + /// + /// + /// If then height of the Terminal.Gui application only tracks + /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). + /// In this case console scrolling is enabled and the contents ( high) will scroll + /// as the console scrolls. + /// + /// This API was previously named 'HeightAsBuffer` but was renamed to make its purpose more clear. + /// + [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] + public static bool EnableConsoleScrolling { get { if (Driver == null) { - return _heightAsBuffer.HasValue && _heightAsBuffer.Value; + return _enableConsoleScrolling.HasValue && _enableConsoleScrolling.Value; } - return Driver.HeightAsBuffer; + return Driver.EnableConsoleScrolling; } set { - _heightAsBuffer = value; + _enableConsoleScrolling = value; if (Driver == null) { return; } - - Driver.HeightAsBuffer = _heightAsBuffer.Value; + Driver.EnableConsoleScrolling = value; } } @@ -143,7 +155,7 @@ namespace Terminal.Gui { /// /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key. /// - [SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter(typeof(KeyJsonConverter))] + [SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))] public static Key AlternateForwardKey { get => alternateForwardKey; set { @@ -157,7 +169,7 @@ namespace Terminal.Gui { static void OnAlternateForwardKeyChanged (Key oldKey) { - foreach (var top in toplevels.ToArray()) { + foreach (var top in toplevels.ToArray ()) { top.OnAlternateForwardKeyChanged (oldKey); } } @@ -167,7 +179,7 @@ namespace Terminal.Gui { /// /// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key. /// - [SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter (typeof (KeyJsonConverter))] + [SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))] public static Key AlternateBackwardKey { get => alternateBackwardKey; set { @@ -181,7 +193,7 @@ namespace Terminal.Gui { static void OnAlternateBackwardKeyChanged (Key oldKey) { - foreach (var top in toplevels.ToArray()) { + foreach (var top in toplevels.ToArray ()) { top.OnAlternateBackwardKeyChanged (oldKey); } } @@ -191,7 +203,7 @@ namespace Terminal.Gui { /// /// Gets or sets the key to quit the application. /// - [SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter (typeof (KeyJsonConverter))] + [SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))] public static Key QuitKey { get => quitKey; set { @@ -213,7 +225,7 @@ namespace Terminal.Gui { static void OnQuitKeyChanged (Key oldKey) { // Duplicate the list so if it changes during enumeration we're safe - foreach (var top in toplevels.ToArray()) { + foreach (var top in toplevels.ToArray ()) { top.OnQuitKeyChanged (oldKey); } } @@ -445,7 +457,7 @@ namespace Terminal.Gui { MainLoop = new MainLoop (mainLoopDriver); try { - Driver.HeightAsBuffer = HeightAsBuffer; + Driver.EnableConsoleScrolling = EnableConsoleScrolling; Driver.Init (TerminalResized); } catch (InvalidOperationException ex) { // This is a case where the driver is unable to initialize the console. @@ -1121,6 +1133,7 @@ namespace Terminal.Gui { NotifyStopRunState = null; _initialized = false; mouseGrabView = null; + _enableConsoleScrolling = false; // Reset synchronization context to allow the user to run async/await, // as the main loop has been ended, the synchronization context from diff --git a/Terminal.Gui/Core/Autocomplete/Autocomplete.cs b/Terminal.Gui/Core/Autocomplete/Autocomplete.cs index 61ccc01ea..728332773 100644 --- a/Terminal.Gui/Core/Autocomplete/Autocomplete.cs +++ b/Terminal.Gui/Core/Autocomplete/Autocomplete.cs @@ -106,7 +106,7 @@ namespace Terminal.Gui { } if (!Visible && popup != null) { - top.Remove (popup); + top?.Remove (popup); popup.Dispose (); popup = null; } @@ -323,6 +323,7 @@ namespace Terminal.Gui { { if (IsWordChar ((char)kb.Key)) { Visible = true; + ManipulatePopup (); closed = false; return false; } diff --git a/Terminal.Gui/Core/Border.cs b/Terminal.Gui/Core/Border.cs index aa25bd41d..20e3aceed 100644 --- a/Terminal.Gui/Core/Border.cs +++ b/Terminal.Gui/Core/Border.cs @@ -513,6 +513,7 @@ namespace Terminal.Gui { private Point effect3DOffset = new Point (1, 1); private Attribute? effect3DBrush; private ustring title = ustring.Empty; + //private View child; /// /// Specifies the for a view. @@ -628,7 +629,47 @@ namespace Terminal.Gui { ///// Gets or sets the single child element of a . ///// //[JsonIgnore] - //public View Child { get; set; } + //public View Child { + // get => child; + // set { + // child = value; + // if (child != null && Parent != null) { + // Parent.Initialized += Parent_Initialized; + // Parent.Removed += Parent_Removed; + // } + // } + //} + + //private void Parent_Removed (View obj) + //{ + // BorderBrush = default; + // Background = default; + // child.Removed -= Parent_Removed; + //} + + //private void Parent_Initialized (object s, EventArgs e) + //{ + // SetMarginFrameTitleBrush (); + // child.Initialized -= Parent_Initialized; + //} + + //private void SetMarginFrameTitleBrush () + //{ + // if (child != null) { + // var view = Parent?.Border != null ? Parent : child; + // if (view.ColorScheme != null) { + // if (borderBrush == default) { + // BorderBrush = view.GetNormalColor ().Foreground; + // } + // if (background == default) { + // Background = view.GetNormalColor ().Background; + // } + // return; + // } + // } + // BorderBrush = default; + // Background = default; + //} ///// ///// Gets the parent parent if any. @@ -791,7 +832,7 @@ namespace Terminal.Gui { // Child.Clear (borderRect); // } - // driver.SetAttribute (savedAttribute); + // driver.SetAttribute (new Attribute (BorderBrush, Background)); // // Draw margin frame // if (DrawMarginFrame) { @@ -815,6 +856,7 @@ namespace Terminal.Gui { // driver.DrawWindowFrame (borderRect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill: true, this); // } // } + // driver.SetAttribute (savedAttribute); //} //private void DrawChildBorder (Rect frame, bool fill = true) @@ -829,35 +871,47 @@ namespace Terminal.Gui { // driver.SetAttribute (new Attribute (BorderBrush)); - // // Draw the upper BorderThickness + // Draw the upper BorderThickness // for (int r = frame.Y - drawMarginFrame - sumThickness.Top; // r < frame.Y - drawMarginFrame - padding.Top; r++) { + + // if (r < 0) { + // continue; + // } // for (int c = frame.X - drawMarginFrame - sumThickness.Left; // c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { - // AddRuneAt (driver, c, r, ' '); - // } - // } + //// AddRuneAt (driver, c, r, ' '); + //// } + //// } // // Draw the left BorderThickness // for (int r = frame.Y - drawMarginFrame - padding.Top; // r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) { + + // if (r < 0) { + // continue; + // } // for (int c = frame.X - drawMarginFrame - sumThickness.Left; // c < frame.X - drawMarginFrame - padding.Left; c++) { - // AddRuneAt (driver, c, r, ' '); - // } - // } + //// AddRuneAt (driver, c, r, ' '); + //// } + //// } // // Draw the right BorderThickness // for (int r = frame.Y - drawMarginFrame - padding.Top; // r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) { + + // if (r < 0) { + // continue; + // } // for (int c = frame.Right + drawMarginFrame + padding.Right; // c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { - // AddRuneAt (driver, c, r, ' '); - // } - // } + //// AddRuneAt (driver, c, r, ' '); + //// } + //// } // // Draw the lower BorderThickness // for (int r = frame.Bottom + drawMarginFrame + padding.Bottom; @@ -865,21 +919,25 @@ namespace Terminal.Gui { // for (int c = frame.X - drawMarginFrame - sumThickness.Left; // c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { - // AddRuneAt (driver, c, r, ' '); - // } - // } + //// AddRuneAt (driver, c, r, ' '); + //// } + //// } - // driver.SetAttribute (new Attribute (Background)); + //// driver.SetAttribute (new Attribute (Background)); // // Draw the upper Padding // for (int r = frame.Y - drawMarginFrame - padding.Top; // r < frame.Y - drawMarginFrame; r++) { + + // if (r < 0) { + // continue; + // } // for (int c = frame.X - drawMarginFrame - padding.Left; // c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { - // AddRuneAt (driver, c, r, ' '); - // } - // } + //// AddRuneAt (driver, c, r, ' '); + //// } + //// } // // Draw the left Padding // for (int r = frame.Y - drawMarginFrame; @@ -887,9 +945,9 @@ namespace Terminal.Gui { // for (int c = frame.X - drawMarginFrame - padding.Left; // c < frame.X - drawMarginFrame; c++) { - // AddRuneAt (driver, c, r, ' '); - // } - // } + //// AddRuneAt (driver, c, r, ' '); + //// } + //// } // // Draw the right Padding // for (int r = frame.Y - drawMarginFrame; @@ -897,9 +955,9 @@ namespace Terminal.Gui { // for (int c = frame.Right + drawMarginFrame; // c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { - // AddRuneAt (driver, c, r, ' '); - // } - // } + //// AddRuneAt (driver, c, r, ' '); + //// } + //// } // // Draw the lower Padding // for (int r = frame.Bottom + drawMarginFrame; @@ -907,11 +965,11 @@ namespace Terminal.Gui { // for (int c = frame.X - drawMarginFrame - padding.Left; // c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { - // AddRuneAt (driver, c, r, ' '); - // } - // } + //// AddRuneAt (driver, c, r, ' '); + //// } + //// } - // driver.SetAttribute (savedAttribute); + // driver.SetAttribute (new Attribute (BorderBrush, Background)); // // Draw the MarginFrame // if (DrawMarginFrame) { @@ -1003,148 +1061,7 @@ namespace Terminal.Gui { // driver.SetAttribute (new Attribute (BorderBrush)); - // // Draw the upper BorderThickness - // for (int r = frame.Y; - // r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) { - // for (int c = frame.X; - // c < Math.Min (frame.Right, driver.Cols); c++) { - - // AddRuneAt (driver, c, r, ' '); - // } - // } - - // // Draw the left BorderThickness - // for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom); - // r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) { - // for (int c = frame.X; - // c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) { - - // AddRuneAt (driver, c, r, ' '); - // } - // } - - // // Draw the right BorderThickness - // for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom); - // r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) { - // for (int c = Math.Max (frame.Right - borderThickness.Right, frame.X); - // c < Math.Min (frame.Right, driver.Cols); c++) { - - // AddRuneAt (driver, c, r, ' '); - // } - // } - - // // Draw the lower BorderThickness - // for (int r = Math.Max (frame.Bottom - borderThickness.Bottom, frame.Y); - // r < Math.Min (frame.Bottom, driver.Rows); r++) { - // for (int c = frame.X; - // c < Math.Min (frame.Right, driver.Cols); c++) { - - // AddRuneAt (driver, c, r, ' '); - // } - // } - - // driver.SetAttribute (new Attribute (Background)); - - // // Draw the upper Padding - // for (int r = frame.Y + borderThickness.Top; - // r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) { - // for (int c = frame.X + borderThickness.Left; - // c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) { - - // AddRuneAt (driver, c, r, ' '); - // } - // } - - // // Draw the left Padding - // for (int r = frame.Y + sumThickness.Top; - // r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) { - // for (int c = frame.X + borderThickness.Left; - // c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) { - - // AddRuneAt (driver, c, r, ' '); - // } - // } - - // // Draw the right Padding - // for (int r = frame.Y + sumThickness.Top; - // r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) { - // for (int c = Math.Max (frame.Right - sumThickness.Right, frame.X + sumThickness.Left); - // c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) { - - // AddRuneAt (driver, c, r, ' '); - // } - // } - - // // Draw the lower Padding - // for (int r = Math.Max (frame.Bottom - sumThickness.Bottom, frame.Y + borderThickness.Top); - // r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) { - // for (int c = frame.X + borderThickness.Left; - // c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) { - - // AddRuneAt (driver, c, r, ' '); - // } - // } - - // driver.SetAttribute (savedAttribute); - - // // Draw the MarginFrame - // if (DrawMarginFrame) { - // var rect = new Rect () { - // X = frame.X + sumThickness.Left, - // Y = frame.Y + sumThickness.Top, - // Width = Math.Max (frame.Width - sumThickness.Right - sumThickness.Left, 0), - // Height = Math.Max (frame.Height - sumThickness.Bottom - sumThickness.Top, 0) - // }; - // if (rect.Width > 0 && rect.Height > 0) { - // driver.DrawWindowFrame (rect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill, this); - // DrawTitle (Parent); - // } - // } - - // if (Effect3D) { - // driver.SetAttribute ((Attribute)Effect3DBrush); - - // // Draw the upper Effect3D - // for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); - // r < frame.Y; r++) { - // for (int c = Math.Max (frame.X + effect3DOffset.X, 0); - // c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { - - // AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); - // } - // } - - // // Draw the left Effect3D - // for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); - // r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { - // for (int c = Math.Max (frame.X + effect3DOffset.X, 0); - // c < frame.X; c++) { - - // AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); - // } - // } - - // // Draw the right Effect3D - // for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); - // r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { - // for (int c = frame.Right; - // c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { - - // AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); - // } - // } - - // // Draw the lower Effect3D - // for (int r = frame.Bottom; - // r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { - // for (int c = Math.Max (frame.X + effect3DOffset.X, 0); - // c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { - - // AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); - // } - // } - // } - // driver.SetAttribute (savedAttribute); + // Draw the upper BorderThickness //} //private void AddRuneAt (ConsoleDriver driver, int col, int row, Rune ch) @@ -1167,9 +1084,10 @@ namespace Terminal.Gui { //{ // var driver = Application.Driver; // if (DrawMarginFrame) { - // driver.SetAttribute (Child.GetNormalColor ()); - // if (Child.HasFocus) - // driver.SetAttribute (Child.ColorScheme.HotNormal); + // driver.SetAttribute (new Attribute (BorderBrush, Background)); + // if (view.HasFocus) { + // driver.SetAttribute (new Attribute (Child.ColorScheme.HotNormal.Foreground, Background)); + // } // var padding = view.Border.GetSumThickness (); // Rect scrRect; // if (view == Child) { @@ -1178,7 +1096,7 @@ namespace Terminal.Gui { // driver.DrawWindowTitle (scrRect, Title, 0, 0, 0, 0); // } else { // scrRect = view.ViewToScreen (new Rect (0, 0, view.Frame.Width, view.Frame.Height)); - // driver.DrawWindowTitle (scrRect, Title, + // driver.DrawWindowTitle (scrRect, Parent.Border.Title, // padding.Left, padding.Top, padding.Right, padding.Bottom); // } // } @@ -1194,9 +1112,9 @@ namespace Terminal.Gui { //{ // var driver = Application.Driver; // if (DrawMarginFrame) { - // driver.SetAttribute (view.GetNormalColor ()); + // driver.SetAttribute (new Attribute (BorderBrush, Background)); // if (view.HasFocus) { - // driver.SetAttribute (view.ColorScheme.HotNormal); + // driver.SetAttribute (new Attribute (view.ColorScheme.HotNormal.Foreground, Background)); // } // var padding = Parent.Border.GetSumThickness (); // var scrRect = Parent.ViewToScreen (new Rect (0, 0, rect.Width, rect.Height)); @@ -1214,4 +1132,4 @@ namespace Terminal.Gui { BorderChanged?.Invoke (this); } } -} +} \ No newline at end of file diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 46e1ce018..674c19477 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -89,7 +89,7 @@ namespace Terminal.Gui { } /// - /// + /// Indicates the RGB for true colors. /// public class TrueColor { /// @@ -119,7 +119,7 @@ namespace Terminal.Gui { } /// - /// + /// Converts true color to console color. /// /// public Color ToConsoleColor () @@ -504,7 +504,7 @@ namespace Terminal.Gui { public bool Equals (string x, string y) { if (x != null && y != null) { - return x.ToLowerInvariant () == y.ToLowerInvariant (); + return string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase); } return false; } @@ -659,72 +659,7 @@ namespace Terminal.Gui { /// 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. @@ -761,10 +696,22 @@ namespace Terminal.Gui { 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. + /// + /// If (the default) the height of the Terminal.Gui application () + /// tracks to the height of the visible console view when the console is resized. In this case + /// scrolling in the console will be disabled and all will remain visible. + /// + /// + /// If then height of the Terminal.Gui application only tracks + /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). + /// In this case console scrolling is enabled and the contents ( high) will scroll + /// as the console scrolls. + /// /// - public abstract bool HeightAsBuffer { get; set; } + /// + /// NOTE: This functionaliy is currently broken on Windows Terminal. + /// + public abstract bool EnableConsoleScrolling { get; set; } /// /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag diff --git a/Terminal.Gui/Core/ConsoleKeyMapping.cs b/Terminal.Gui/Core/ConsoleKeyMapping.cs index d7bc3d584..441adac48 100644 --- a/Terminal.Gui/Core/ConsoleKeyMapping.cs +++ b/Terminal.Gui/Core/ConsoleKeyMapping.cs @@ -334,6 +334,25 @@ namespace Terminal.Gui { return (Key)consoleKey; } + /// + /// Maps a to a . + /// + /// The console key info. + /// The key. + /// The with or the + public static Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) + { + Key keyMod = new Key (); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) + keyMod = Key.ShiftMask; + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) + keyMod |= Key.CtrlMask; + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) + keyMod |= Key.AltMask; + + return keyMod != Key.Null ? keyMod | key : key; + } + private static HashSet scanCodes = new HashSet { new ScanCodeMapping (1,27,0,27), // Escape new ScanCodeMapping (1,27,ConsoleModifiers.Shift,27), diff --git a/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs b/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs new file mode 100644 index 000000000..eaa3084ad --- /dev/null +++ b/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; + +namespace Terminal.Gui { + /// + /// Represents the state of an ANSI escape sequence request. + /// + /// + /// This is needed because there are some escape sequence requests responses that are equal + /// with some normal escape sequences and thus, will be only considered the responses to the + /// requests that were registered with this object. + /// + public class EscSeqReqStatus { + /// + /// Gets the terminating. + /// + public string Terminating { get; } + /// + /// Gets the number of requests. + /// + public int NumRequests { get; } + /// + /// Gets information about unfinished requests. + /// + public int NumOutstanding { get; set; } + + /// + /// Creates a new state of escape sequence request. + /// + /// The terminating. + /// The number of requests. + public EscSeqReqStatus (string terminating, int numOfReq) + { + Terminating = terminating; + NumRequests = NumOutstanding = numOfReq; + } + } + + /// + /// Manages a list of . + /// + public class EscSeqReqProc { + /// + /// Gets the list. + /// + public List EscSeqReqStats { get; } = new List (); + + /// + /// Adds a new instance to the list. + /// + /// The terminating. + /// The number of requests. + public void Add (string terminating, int numOfReq = 1) + { + lock (EscSeqReqStats) { + var found = EscSeqReqStats.Find (x => x.Terminating == terminating); + if (found == null) { + EscSeqReqStats.Add (new EscSeqReqStatus (terminating, numOfReq)); + } else if (found != null && found.NumOutstanding < found.NumRequests) { + found.NumOutstanding = Math.Min (found.NumOutstanding + numOfReq, found.NumRequests); + } + } + } + + /// + /// Removes a instance from the list. + /// + /// The terminating string. + public void Remove (string terminating) + { + lock (EscSeqReqStats) { + var found = EscSeqReqStats.Find (x => x.Terminating == terminating); + if (found == null) { + return; + } + if (found != null && found.NumOutstanding == 0) { + EscSeqReqStats.Remove (found); + } else if (found != null && found.NumOutstanding > 0) { + found.NumOutstanding--; + if (found.NumOutstanding == 0) { + EscSeqReqStats.Remove (found); + } + } + } + } + + /// + /// Indicates if a with the exist + /// in the list. + /// + /// + /// if exist, otherwise. + public bool Requested (string terminating) + { + lock (EscSeqReqStats) { + var found = EscSeqReqStats.Find (x => x.Terminating == terminating); + if (found == null) { + return false; + } + if (found != null && found.NumOutstanding > 0) { + return true; + } else { + EscSeqReqStats.Remove (found); + } + return false; + } + } + } +} diff --git a/Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs new file mode 100644 index 000000000..21a9aac59 --- /dev/null +++ b/Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs @@ -0,0 +1,907 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Management; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Terminal.Gui { + /// + /// Provides a platform-independent API for managing ANSI escape sequence codes. + /// + public static class EscSeqUtils { + /// + /// Represents the escape key. + /// + public static readonly char KeyEsc = (char)Key.Esc; + /// + /// Represents the CSI (Control Sequence Introducer). + /// + public static readonly string KeyCSI = $"{KeyEsc}["; + /// + /// Represents the CSI for enable any mouse event tracking. + /// + public static readonly string CSI_EnableAnyEventMouse = KeyCSI + "?1003h"; + /// + /// Represents the CSI for enable SGR (Select Graphic Rendition). + /// + public static readonly string CSI_EnableSgrExtModeMouse = KeyCSI + "?1006h"; + /// + /// Represents the CSI for enable URXVT (Unicode Extended Virtual Terminal). + /// + public static readonly string CSI_EnableUrxvtExtModeMouse = KeyCSI + "?1015h"; + /// + /// Represents the CSI for disable any mouse event tracking. + /// + public static readonly string CSI_DisableAnyEventMouse = KeyCSI + "?1003l"; + /// + /// Represents the CSI for disable SGR (Select Graphic Rendition). + /// + public static readonly string CSI_DisableSgrExtModeMouse = KeyCSI + "?1006l"; + /// + /// Represents the CSI for disable URXVT (Unicode Extended Virtual Terminal). + /// + public static readonly string CSI_DisableUrxvtExtModeMouse = KeyCSI + "?1015l"; + + /// + /// Control sequence for enable mouse events. + /// + public static string EnableMouseEvents { get; set; } = + CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse; + /// + /// Control sequence for disable mouse events. + /// + public static string DisableMouseEvents { get; set; } = + CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse; + + /// + /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences. + /// + /// The . + /// The modified. + public static ConsoleKeyInfo GetConsoleInputKey (ConsoleKeyInfo consoleKeyInfo) + { + ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo; + ConsoleKey key; + var keyChar = consoleKeyInfo.KeyChar; + switch ((uint)keyChar) { + case 0: + if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. + newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + } + break; + case uint n when (n >= '\u0001' && n <= '\u001a'): + if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') { + key = ConsoleKey.Enter; + newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + } else if (consoleKeyInfo.Key == 0) { + key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1); + newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + true); + } + break; + case 127: + newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + break; + default: + newConsoleKeyInfo = consoleKeyInfo; + break; + } + + return newConsoleKeyInfo; + } + + /// + /// A helper to resize the as needed. + /// + /// The . + /// The array to resize. + /// The resized. + public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, ConsoleKeyInfo [] cki) + { + Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1); + cki [cki.Length - 1] = consoleKeyInfo; + return cki; + } + + /// + /// Decodes a escape sequence to been processed in the appropriate manner. + /// + /// The which may contain a request. + /// The which may changes. + /// The which may changes. + /// The array. + /// The which may changes. + /// The control returned by the method. + /// The code returned by the method. + /// The values returned by the method. + /// The terminating returned by the method. + /// Indicates if the escape sequence is a mouse key. + /// The button state. + /// The position. + /// Indicates if the escape sequence is a response to a request. + /// The handler that will process the event. + public static void DecodeEscSeq (EscSeqReqProc escSeqReqProc, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminating, out bool isKeyMouse, out List buttonState, out Point pos, out bool isReq, Action continuousButtonPressedHandler) + { + char [] kChars = GetKeyCharArray (cki); + (c1Control, code, values, terminating) = GetEscapeResult (kChars); + isKeyMouse = false; + buttonState = new List () { 0 }; + pos = default; + isReq = false; + switch (c1Control) { + case "ESC": + if (values == null && string.IsNullOrEmpty (terminating)) { + key = ConsoleKey.Escape; + newConsoleKeyInfo = new ConsoleKeyInfo (cki [0].KeyChar, key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + } else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) { + key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); + newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar, + key, + false, + true, + true); + } else { + if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) { + key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; + } else { + key = (ConsoleKey)cki [1].KeyChar; + } + newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, + (ConsoleKey)Math.Min ((uint)key, 255), + false, + true, + false); + } + break; + case "SS3": + key = GetConsoleKey (terminating [0], values [0], ref mod); + newConsoleKeyInfo = new ConsoleKeyInfo ('\0', + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + break; + case "CSI": + if (!string.IsNullOrEmpty (code) && code == "<") { + GetMouse (cki, out buttonState, out pos, continuousButtonPressedHandler); + isKeyMouse = true; + return; + } else if (escSeqReqProc != null && escSeqReqProc.Requested (terminating)) { + isReq = true; + escSeqReqProc.Remove (terminating); + return; + } + key = GetConsoleKey (terminating [0], values [0], ref mod); + if (key != 0 && values.Length > 1) { + mod |= GetConsoleModifiers (values [1]); + } + newConsoleKeyInfo = new ConsoleKeyInfo ('\0', + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + break; + } + } + + /// + /// Gets all the needed information about a escape sequence. + /// + /// The array with all chars. + /// + /// The c1Control returned by , code, values and terminating. + /// + public static (string c1Control, string code, string [] values, string terminating) GetEscapeResult (char [] kChar) + { + if (kChar == null || kChar.Length == 0) { + return (null, null, null, null); + } + if (kChar [0] != '\x1b') { + throw new InvalidOperationException ("Invalid escape character!"); + } + if (kChar.Length == 1) { + return ("ESC", null, null, null); + } + if (kChar.Length == 2) { + return ("ESC", null, null, kChar [1].ToString ()); + } + string c1Control = GetC1ControlChar (kChar [1]); + string code = null; + int nSep = kChar.Count (x => x == ';') + 1; + string [] values = new string [nSep]; + int valueIdx = 0; + string terminating = ""; + for (int i = 2; i < kChar.Length; i++) { + var c = kChar [i]; + if (char.IsDigit (c)) { + values [valueIdx] += c.ToString (); + } else if (c == ';') { + valueIdx++; + } else if (valueIdx == nSep - 1 || i == kChar.Length - 1) { + terminating += c.ToString (); + } else { + code += c.ToString (); + } + } + + return (c1Control, code, values, terminating); + } + + /// + /// Gets the c1Control used in the called escape sequence. + /// + /// The char used. + /// The c1Control. + public static string GetC1ControlChar (char c) + { + // These control characters are used in the vtXXX emulation. + switch (c) { + case 'D': + return "IND"; // Index + case 'E': + return "NEL"; // Next Line + case 'H': + return "HTS"; // Tab Set + case 'M': + return "RI"; // Reverse Index + case 'N': + return "SS2"; // Single Shift Select of G2 Character Set: affects next character only + case 'O': + return "SS3"; // Single Shift Select of G3 Character Set: affects next character only + case 'P': + return "DCS"; // Device Control String + case 'V': + return "SPA"; // Start of Guarded Area + case 'W': + return "EPA"; // End of Guarded Area + case 'X': + return "SOS"; // Start of String + case 'Z': + return "DECID"; // Return Terminal ID Obsolete form of CSI c (DA) + case '[': + return "CSI"; // Control Sequence Introducer + case '\\': + return "ST"; // String Terminator + case ']': + return "OSC"; // Operating System Command + case '^': + return "PM"; // Privacy Message + case '_': + return "APC"; // Application Program Command + default: + return ""; // Not supported + } + } + + /// + /// Gets the from the value. + /// + /// The value. + /// The or zero. + public static ConsoleModifiers GetConsoleModifiers (string value) + { + switch (value) { + case "2": + return ConsoleModifiers.Shift; + case "3": + return ConsoleModifiers.Alt; + case "4": + return ConsoleModifiers.Shift | ConsoleModifiers.Alt; + case "5": + return ConsoleModifiers.Control; + case "6": + return ConsoleModifiers.Shift | ConsoleModifiers.Control; + case "7": + return ConsoleModifiers.Alt | ConsoleModifiers.Control; + case "8": + return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control; + default: + return 0; + } + } + + /// + /// Gets the depending on terminating and value. + /// + /// The terminating. + /// The value. + /// The which may changes. + /// The and probably the . + public static ConsoleKey GetConsoleKey (char terminating, string value, ref ConsoleModifiers mod) + { + ConsoleKey key; + switch (terminating) { + case 'A': + key = ConsoleKey.UpArrow; + break; + case 'B': + key = ConsoleKey.DownArrow; + break; + case 'C': + key = ConsoleKey.RightArrow; + break; + case 'D': + key = ConsoleKey.LeftArrow; + break; + case 'F': + key = ConsoleKey.End; + break; + case 'H': + key = ConsoleKey.Home; + break; + case 'P': + key = ConsoleKey.F1; + break; + case 'Q': + key = ConsoleKey.F2; + break; + case 'R': + key = ConsoleKey.F3; + break; + case 'S': + key = ConsoleKey.F4; + break; + case 'Z': + key = ConsoleKey.Tab; + mod |= ConsoleModifiers.Shift; + break; + case '~': + switch (value) { + case "2": + key = ConsoleKey.Insert; + break; + case "3": + key = ConsoleKey.Delete; + break; + case "5": + key = ConsoleKey.PageUp; + break; + case "6": + key = ConsoleKey.PageDown; + break; + case "15": + key = ConsoleKey.F5; + break; + case "17": + key = ConsoleKey.F6; + break; + case "18": + key = ConsoleKey.F7; + break; + case "19": + key = ConsoleKey.F8; + break; + case "20": + key = ConsoleKey.F9; + break; + case "21": + key = ConsoleKey.F10; + break; + case "23": + key = ConsoleKey.F11; + break; + case "24": + key = ConsoleKey.F12; + break; + default: + key = 0; + break; + } + break; + default: + key = 0; + break; + } + + return key; + } + + /// + /// A helper to get only the from the array. + /// + /// + /// The char array of the escape sequence. + public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki) + { + char [] kChar = new char [] { }; + var length = 0; + foreach (var kc in cki) { + length++; + Array.Resize (ref kChar, length); + kChar [length - 1] = kc.KeyChar; + } + + return kChar; + } + + private static MouseFlags? lastMouseButtonPressed; + //private static MouseFlags? lastMouseButtonReleased; + private static bool isButtonPressed; + //private static bool isButtonReleased; + private static bool isButtonClicked; + private static bool isButtonDoubleClicked; + private static bool isButtonTripleClicked; + private static Point point; + + /// + /// Gets the mouse button flags and the position. + /// + /// The array. + /// The mouse button flags. + /// The mouse position. + /// The handler that will process the event. + public static void GetMouse (ConsoleKeyInfo [] cki, out List mouseFlags, out Point pos, Action continuousButtonPressedHandler) + { + MouseFlags buttonState = 0; + pos = new Point (); + int buttonCode = 0; + bool foundButtonCode = false; + int foundPoint = 0; + string value = ""; + var kChar = GetKeyCharArray (cki); + //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}"); + for (int i = 0; i < kChar.Length; i++) { + var c = kChar [i]; + if (c == '<') { + foundButtonCode = true; + } else if (foundButtonCode && c != ';') { + value += c.ToString (); + } else if (c == ';') { + if (foundButtonCode) { + foundButtonCode = false; + buttonCode = int.Parse (value); + } + if (foundPoint == 1) { + pos.X = int.Parse (value) - 1; + } + value = ""; + foundPoint++; + } else if (foundPoint > 0 && c != 'm' && c != 'M') { + value += c.ToString (); + } else if (c == 'm' || c == 'M') { + //pos.Y = int.Parse (value) + Console.WindowTop - 1; + pos.Y = int.Parse (value) - 1; + + switch (buttonCode) { + case 0: + case 8: + case 16: + case 24: + case 32: + case 36: + case 40: + case 48: + case 56: + buttonState = c == 'M' ? MouseFlags.Button1Pressed + : MouseFlags.Button1Released; + break; + case 1: + case 9: + case 17: + case 25: + case 33: + case 37: + case 41: + case 45: + case 49: + case 53: + case 57: + case 61: + buttonState = c == 'M' ? MouseFlags.Button2Pressed + : MouseFlags.Button2Released; + break; + case 2: + case 10: + case 14: + case 18: + case 22: + case 26: + case 30: + case 34: + case 42: + case 46: + case 50: + case 54: + case 58: + case 62: + buttonState = c == 'M' ? MouseFlags.Button3Pressed + : MouseFlags.Button3Released; + break; + case 35: + //// Needed for Windows OS + //if (isButtonPressed && c == 'm' + // && (lastMouseEvent.ButtonState == MouseFlags.Button1Pressed + // || lastMouseEvent.ButtonState == MouseFlags.Button2Pressed + // || lastMouseEvent.ButtonState == MouseFlags.Button3Pressed)) { + + // switch (lastMouseEvent.ButtonState) { + // case MouseFlags.Button1Pressed: + // buttonState = MouseFlags.Button1Released; + // break; + // case MouseFlags.Button2Pressed: + // buttonState = MouseFlags.Button2Released; + // break; + // case MouseFlags.Button3Pressed: + // buttonState = MouseFlags.Button3Released; + // break; + // } + //} else { + // buttonState = MouseFlags.ReportMousePosition; + //} + //break; + case 39: + case 43: + case 47: + case 51: + case 55: + case 59: + case 63: + buttonState = MouseFlags.ReportMousePosition; + break; + case 64: + buttonState = MouseFlags.WheeledUp; + break; + case 65: + buttonState = MouseFlags.WheeledDown; + break; + case 68: + case 72: + case 80: + buttonState = MouseFlags.WheeledLeft; // Shift/Ctrl+WheeledUp + break; + case 69: + case 73: + case 81: + buttonState = MouseFlags.WheeledRight; // Shift/Ctrl+WheeledDown + break; + } + // Modifiers. + switch (buttonCode) { + case 8: + case 9: + case 10: + case 43: + buttonState |= MouseFlags.ButtonAlt; + break; + case 14: + case 47: + buttonState |= MouseFlags.ButtonAlt | MouseFlags.ButtonShift; + break; + case 16: + case 17: + case 18: + case 51: + buttonState |= MouseFlags.ButtonCtrl; + break; + case 22: + case 55: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift; + break; + case 24: + case 25: + case 26: + case 59: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt; + break; + case 30: + case 63: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt; + break; + case 32: + case 33: + case 34: + buttonState |= MouseFlags.ReportMousePosition; + break; + case 36: + case 37: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonShift; + break; + case 39: + case 68: + case 69: + buttonState |= MouseFlags.ButtonShift; + break; + case 40: + case 41: + case 42: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt; + break; + case 45: + case 46: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt | MouseFlags.ButtonShift; + break; + case 48: + case 49: + case 50: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl; + break; + case 53: + case 54: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift; + break; + case 56: + case 57: + case 58: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt; + break; + case 61: + case 62: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt; + break; + } + } + } + + mouseFlags = new List () { MouseFlags.AllEvents }; + + if (lastMouseButtonPressed != null && !isButtonPressed && !buttonState.HasFlag (MouseFlags.ReportMousePosition) + && !buttonState.HasFlag (MouseFlags.Button1Released) + && !buttonState.HasFlag (MouseFlags.Button2Released) + && !buttonState.HasFlag (MouseFlags.Button3Released) + && !buttonState.HasFlag (MouseFlags.Button4Released)) { + + lastMouseButtonPressed = null; + isButtonPressed = false; + } + + if (!isButtonClicked && !isButtonDoubleClicked && ((buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed) && lastMouseButtonPressed == null) || + isButtonPressed && lastMouseButtonPressed != null && buttonState.HasFlag (MouseFlags.ReportMousePosition)) { + + mouseFlags [0] = buttonState; + lastMouseButtonPressed = buttonState; + isButtonPressed = true; + + if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0) { + point = new Point () { + X = pos.X, + Y = pos.Y + }; + + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessContinuousButtonPressedAsync (buttonState, continuousButtonPressedHandler)); + return false; + }); + } else if (mouseFlags [0] == MouseFlags.ReportMousePosition) { + isButtonPressed = false; + } + + } else if (isButtonDoubleClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) { + + mouseFlags [0] = GetButtonTripleClicked (buttonState); + isButtonDoubleClicked = false; + isButtonTripleClicked = true; + + } else if (isButtonClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) { + + mouseFlags [0] = GetButtonDoubleClicked (buttonState); + isButtonClicked = false; + isButtonDoubleClicked = true; + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); + return false; + }); + + } + //else if (isButtonReleased && !isButtonClicked && buttonState == MouseFlags.ReportMousePosition) { + // mouseFlag [0] = GetButtonClicked ((MouseFlags)lastMouseButtonReleased); + // lastMouseButtonReleased = null; + // isButtonReleased = false; + // isButtonClicked = true; + // Application.MainLoop.AddIdle (() => { + // Task.Run (async () => await ProcessButtonClickedAsync ()); + // return false; + // }); + + //} + else if (!isButtonClicked && !isButtonDoubleClicked && (buttonState == MouseFlags.Button1Released || buttonState == MouseFlags.Button2Released || + buttonState == MouseFlags.Button3Released || buttonState == MouseFlags.Button4Released)) { + + mouseFlags [0] = buttonState; + isButtonPressed = false; + + if (isButtonTripleClicked) { + isButtonTripleClicked = false; + } else if (pos.X == point.X && pos.Y == point.Y) { + mouseFlags.Add (GetButtonClicked (buttonState)); + isButtonClicked = true; + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessButtonClickedAsync ()); + return false; + }); + } + + point = pos; + + //if ((lastMouseButtonPressed & MouseFlags.ReportMousePosition) == 0) { + // lastMouseButtonReleased = buttonState; + // isButtonPressed = false; + // isButtonReleased = true; + //} else { + // lastMouseButtonPressed = null; + // isButtonPressed = false; + //} + + } else if (buttonState == MouseFlags.WheeledUp) { + + mouseFlags [0] = MouseFlags.WheeledUp; + + } else if (buttonState == MouseFlags.WheeledDown) { + + mouseFlags [0] = MouseFlags.WheeledDown; + + } else if (buttonState == MouseFlags.WheeledLeft) { + + mouseFlags [0] = MouseFlags.WheeledLeft; + + } else if (buttonState == MouseFlags.WheeledRight) { + + mouseFlags [0] = MouseFlags.WheeledRight; + + } else if (buttonState == MouseFlags.ReportMousePosition) { + mouseFlags [0] = MouseFlags.ReportMousePosition; + + } else { + mouseFlags [0] = buttonState; + //foreach (var flag in buttonState.GetUniqueFlags()) { + // mouseFlag [0] |= flag; + //} + } + + mouseFlags [0] = SetControlKeyStates (buttonState, mouseFlags [0]); + //buttonState = mouseFlags; + + //System.Diagnostics.Debug.WriteLine ($"buttonState: {buttonState} X: {pos.X} Y: {pos.Y}"); + //foreach (var mf in mouseFlags) { + // System.Diagnostics.Debug.WriteLine ($"mouseFlags: {mf} X: {pos.X} Y: {pos.Y}"); + //} + } + + private static async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag, Action continuousButtonPressedHandler) + { + while (isButtonPressed) { + await Task.Delay (100); + //var me = new MouseEvent () { + // X = point.X, + // Y = point.Y, + // Flags = mouseFlag + //}; + + var view = Application.WantContinuousButtonPressedView; + if (view == null) + break; + if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Application.MainLoop.Invoke (() => continuousButtonPressedHandler (mouseFlag, point)); + } + } + } + + private static async Task ProcessButtonClickedAsync () + { + await Task.Delay (300); + isButtonClicked = false; + } + + private static async Task ProcessButtonDoubleClickedAsync () + { + await Task.Delay (300); + isButtonDoubleClicked = false; + } + + private static MouseFlags GetButtonClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Released: + mf = MouseFlags.Button1Clicked; + break; + + case MouseFlags.Button2Released: + mf = MouseFlags.Button2Clicked; + break; + + case MouseFlags.Button3Released: + mf = MouseFlags.Button3Clicked; + break; + } + return mf; + } + + private static MouseFlags GetButtonDoubleClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Pressed: + mf = MouseFlags.Button1DoubleClicked; + break; + + case MouseFlags.Button2Pressed: + mf = MouseFlags.Button2DoubleClicked; + break; + + case MouseFlags.Button3Pressed: + mf = MouseFlags.Button3DoubleClicked; + break; + } + return mf; + } + + private static MouseFlags GetButtonTripleClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Pressed: + mf = MouseFlags.Button1TripleClicked; + break; + + case MouseFlags.Button2Pressed: + mf = MouseFlags.Button2TripleClicked; + break; + + case MouseFlags.Button3Pressed: + mf = MouseFlags.Button3TripleClicked; + break; + } + return mf; + } + + private static MouseFlags SetControlKeyStates (MouseFlags buttonState, MouseFlags mouseFlag) + { + if ((buttonState & MouseFlags.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0) + mouseFlag |= MouseFlags.ButtonCtrl; + + if ((buttonState & MouseFlags.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0) + mouseFlag |= MouseFlags.ButtonShift; + + if ((buttonState & MouseFlags.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0) + mouseFlag |= MouseFlags.ButtonAlt; + return mouseFlag; + } + + /// + /// Get the terminal that holds the console driver. + /// + /// The process. + /// If supported the executable console process, null otherwise. + public static Process GetParentProcess (Process process) + { + if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { + return null; + } + + string query = "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = " + process.Id; + using (ManagementObjectSearcher mos = new ManagementObjectSearcher (query)) { + foreach (ManagementObject mo in mos.Get ()) { + if (mo ["ParentProcessId"] != null) { + try { + var id = Convert.ToInt32 (mo ["ParentProcessId"]); + return Process.GetProcessById (id); + } catch { + } + } + } + } + return null; + } + } +} \ No newline at end of file diff --git a/Terminal.Gui/Core/Event.cs b/Terminal.Gui/Core/Event.cs index 4203faa86..a46bbf7e5 100644 --- a/Terminal.Gui/Core/Event.cs +++ b/Terminal.Gui/Core/Event.cs @@ -749,7 +749,7 @@ namespace Terminal.Gui { /// WheeledUp = unchecked((int)0x10000000), /// - /// Vertical button wheeled up. + /// Vertical button wheeled down. /// WheeledDown = unchecked((int)0x20000000), /// diff --git a/Terminal.Gui/Core/PosDim.cs b/Terminal.Gui/Core/PosDim.cs index 696607696..91787b434 100644 --- a/Terminal.Gui/Core/PosDim.cs +++ b/Terminal.Gui/Core/PosDim.cs @@ -345,7 +345,7 @@ namespace Terminal.Gui { case 3: tside = "bottom"; break; default: tside = "unknown"; break; } - return $"View({tside},{Target.ToString()})"; + return $"View({tside},{Target.ToString ()})"; } public override int GetHashCode () => Target.GetHashCode (); @@ -691,7 +691,7 @@ namespace Terminal.Gui { case 1: tside = "Width"; break; default: tside = "unknown"; break; } - return $"DimView({tside},{Target.ToString ()})"; + return $"View({tside},{Target.ToString ()})"; } public override int GetHashCode () => Target.GetHashCode (); diff --git a/Terminal.Gui/Core/TextFormatter.cs b/Terminal.Gui/Core/TextFormatter.cs index e6e1592e3..5d71066e4 100644 --- a/Terminal.Gui/Core/TextFormatter.cs +++ b/Terminal.Gui/Core/TextFormatter.cs @@ -1176,22 +1176,29 @@ namespace Terminal.Gui { } var isVertical = IsVerticalDirection (textDirection); - var savedClip = Application.Driver?.Clip; var maxBounds = bounds; if (Application.Driver != null) { - Application.Driver.Clip = maxBounds = containerBounds == default + maxBounds = containerBounds == default ? bounds : new Rect (Math.Max (containerBounds.X, bounds.X), Math.Max (containerBounds.Y, bounds.Y), Math.Max (Math.Min (containerBounds.Width, containerBounds.Right - bounds.Left), 0), Math.Max (Math.Min (containerBounds.Height, containerBounds.Bottom - bounds.Top), 0)); } + if (maxBounds.Width == 0 || maxBounds.Height == 0) { + return; + } + var savedClip = Application.Driver?.Clip; + if (Application.Driver != null) { + Application.Driver.Clip = maxBounds; + } + var lineOffset = !isVertical && bounds.Y < 0 ? Math.Abs (bounds.Y) : 0; - for (int line = 0; line < linesFormated.Count; line++) { + for (int line = lineOffset; line < linesFormated.Count; line++) { if ((isVertical && line > bounds.Width) || (!isVertical && line > bounds.Height)) continue; if ((isVertical && line >= maxBounds.Left + maxBounds.Width) - || (!isVertical && line >= maxBounds.Top + maxBounds.Height)) + || (!isVertical && line >= maxBounds.Top + maxBounds.Height + lineOffset)) break; @@ -1267,18 +1274,21 @@ namespace Terminal.Gui { throw new ArgumentOutOfRangeException (); } + var colOffset = bounds.X < 0 ? Math.Abs (bounds.X) : 0; var start = isVertical ? bounds.Top : bounds.Left; var size = isVertical ? bounds.Height : bounds.Width; - var current = start; + var current = start + colOffset; - for (var idx = (isVertical ? start - y : start - x); current < start + size; idx++) { - if (!fillRemaining && idx < 0) { + for (var idx = (isVertical ? start - y : start - x) + colOffset; current < start + size; idx++) { + if (idx < 0 || x + current + colOffset < 0) { current++; continue; } else if (!fillRemaining && idx > runes.Length - 1) { break; } - if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X) || (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y)) + if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X + colOffset) + || (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y)) + break; var rune = (Rune)' '; @@ -1316,8 +1326,9 @@ namespace Terminal.Gui { } } } - if (Application.Driver != null) + if (Application.Driver != null) { Application.Driver.Clip = (Rect)savedClip; + } } } } diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index 02691b84f..08952f48b 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -167,6 +167,7 @@ namespace Terminal.Gui { /// virtual public void OnLoaded () { + IsLoaded = true; foreach (Toplevel tl in Subviews.Where (v => v is Toplevel)) { tl.OnLoaded (); } @@ -367,6 +368,13 @@ namespace Terminal.Gui { } } + /// + /// if was already loaded by the + /// , otherwise. This is used to avoid the + /// having wrong values while this was not yet loaded. + /// + public bool IsLoaded { get; private set; } + /// public override bool OnKeyDown (KeyEvent keyEvent) { @@ -741,12 +749,8 @@ namespace Terminal.Gui { if (view.Frame.IntersectsWith (bounds) && !OutsideTopFrame (this)) { view.SetNeedsLayout (); view.SetNeedsDisplay (view.Bounds); - //view.Redraw (view.Bounds); } } - - ClearLayoutNeeded (); - ClearNeedsDisplay (); } base.Redraw (Bounds); diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index cb647fc61..35dcf6f31 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -447,16 +447,34 @@ namespace Terminal.Gui { public virtual Rect Frame { get => frame; set { - var rect = GetMaxNeedDisplay (frame, value); frame = new Rect (value.X, value.Y, Math.Max (value.Width, 0), Math.Max (value.Height, 0)); TextFormatter.Size = GetBoundsTextFormatterSize (); SetNeedsLayout (); - SetNeedsDisplay (rect); + SetNeedsDisplay (); } } + /// + /// The Thickness that separates a View from other SubViews of the same SuperView. + /// The Margin is not part of the View's content and is not clipped by the View's Clip Area. + /// public Frame Margin { get; set; } + + /// + /// Thickness where a visual border (drawn using line-drawing glyphs) and the Title are drawn. + /// The Border expands inward; in other words if `Border.Thickness.Top == 2` the border and + /// title will take up the first row and the second row will be filled with spaces. + /// The Border is not part of the View's content and is not clipped by the View's `ClipArea`. + /// public Frame BorderFrame { get; set; } + + /// + /// Means the Thickness inside of an element that offsets the `Content` from the Border. + /// Padding is `{0, 0, 0, 0}` by default. Padding is not part of the View's content and is not clipped by the View's `ClipArea`. + /// + /// + /// (NOTE: in v1 `Padding` is OUTSIDE of the `Border`). + /// public Frame Padding { get; set; } /// @@ -501,7 +519,7 @@ namespace Terminal.Gui { ustring title; /// - /// The title to be displayed for this . + /// The title to be displayed for this . /// /// The title. public ustring Title { @@ -860,7 +878,6 @@ namespace Terminal.Gui { { var actX = x is Pos.PosAbsolute ? x.Anchor (0) : frame.X; var actY = y is Pos.PosAbsolute ? y.Anchor (0) : frame.Y; - Rect oldFrame = frame; if (AutoSize) { var s = GetAutoSize (); @@ -875,21 +892,7 @@ namespace Terminal.Gui { } TextFormatter.Size = GetBoundsTextFormatterSize (); SetNeedsLayout (); - SetNeedsDisplay (GetMaxNeedDisplay (oldFrame, frame)); - } - - Rect GetMaxNeedDisplay (Rect oldFrame, Rect newFrame) - { - var rect = new Rect () { - X = Math.Min (oldFrame.X, newFrame.X), - Y = Math.Min (oldFrame.Y, newFrame.Y), - Width = Math.Max (oldFrame.Width, newFrame.Width), - Height = Math.Max (oldFrame.Height, newFrame.Height) - }; - rect.Width += Math.Max (oldFrame.X - newFrame.X, 0); - rect.Height += Math.Max (oldFrame.Y - newFrame.Y, 0); - - return rect; + SetNeedsDisplay (); } void TextFormatter_HotKeyChanged (Key obj) @@ -1161,15 +1164,8 @@ namespace Terminal.Gui { /// public void Clear () { - Rect containerBounds = GetContainerBounds (); - Rect viewBounds = Bounds; - if (!containerBounds.IsEmpty) { - viewBounds.Width = Math.Min (viewBounds.Width, containerBounds.Width); - viewBounds.Height = Math.Min (viewBounds.Height, containerBounds.Height); - } - - var h = viewBounds.Height; - var w = viewBounds.Width; + var h = Frame.Height; + var w = Frame.Width; for (var line = 0; line < h; line++) { Move (0, line); for (var col = 0; col < w; col++) @@ -1432,10 +1428,10 @@ namespace Terminal.Gui { public virtual void OnAdded (View view) { view.IsAdded = true; - view.x = view.x ?? view.frame.X; - view.y = view.y ?? view.frame.Y; - view.width = view.width ?? view.frame.Width; - view.height = view.height ?? view.frame.Height; + view.x ??= view.frame.X; + view.y ??= view.frame.Y; + view.width ??= view.frame.Width; + view.height ??= view.frame.Height; view.Added?.Invoke (this); } @@ -1573,9 +1569,10 @@ namespace Terminal.Gui { } var boundsAdjustedForBorder = Bounds; - if (!IgnoreBorderPropertyOnRedraw && Border != null) { - throw new InvalidOperationException("Don't use border!"); - } else if (ustring.IsNullOrEmpty (TextFormatter.Text) && + //if (!IgnoreBorderPropertyOnRedraw && Border != null) { + // throw new InvalidOperationException("Don't use border!"); + //} else + if (ustring.IsNullOrEmpty (TextFormatter.Text) && (GetType ().IsNestedPublic && !IsOverridden (this, "Redraw") || GetType ().Name == "View") && (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded)) { @@ -1585,15 +1582,17 @@ namespace Terminal.Gui { if (!ustring.IsNullOrEmpty (TextFormatter.Text)) { Rect containerBounds = GetContainerBounds (); - Clear (ViewToScreen (GetNeedDisplay (containerBounds))); - SetChildNeedsDisplay (); - // Draw any Text - if (TextFormatter != null) { - TextFormatter.NeedsFormat = true; + if (!containerBounds.IsEmpty) { + Clear (GetNeedDisplay (containerBounds)); + SetChildNeedsDisplay (); + // Draw any Text + if (TextFormatter != null) { + TextFormatter.NeedsFormat = true; + } + TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (), + HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (), + containerBounds); } - TextFormatter?.Draw (ViewToScreen (boundsAdjustedForBorder), HasFocus ? ColorScheme.Focus : GetNormalColor (), - HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled, - containerBounds); } // Invoke DrawContentEvent @@ -1602,7 +1601,7 @@ namespace Terminal.Gui { if (subviews != null) { foreach (var view in subviews) { if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay || view.LayoutNeeded) { - if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (boundsAdjustedForBorder) || boundsAdjustedForBorder.X < 0 || bounds.Y < 0)) { + if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) { if (view.LayoutNeeded) { view.LayoutSubviews (); } @@ -1631,7 +1630,7 @@ namespace Terminal.Gui { Rect GetNeedDisplay (Rect containerBounds) { - Rect rect = NeedDisplay; + Rect rect = ViewToScreen (NeedDisplay); if (!containerBounds.IsEmpty) { rect.Width = Math.Min (NeedDisplay.Width, containerBounds.Width); rect.Height = Math.Min (NeedDisplay.Height, containerBounds.Height); @@ -2272,8 +2271,7 @@ namespace Terminal.Gui { newLocation = pos.Anchor (superviewDimension - newDimension); break; - case Pos.PosCombine: - var combine = pos as Pos.PosCombine; + case Pos.PosCombine combine: int left, right; (left, newDimension) = GetNewLocationAndDimension (superviewLocation, superviewDimension, combine.left, dim, autosizeDimension); (right, newDimension) = GetNewLocationAndDimension (superviewLocation, superviewDimension, combine.right, dim, autosizeDimension); @@ -2300,7 +2298,7 @@ namespace Terminal.Gui { // Recursively calculates the new dimension (width or height) of the given Dim given: // the current location (x or y) - // the current dimennsion (width or height) + // the current dimension (width or height) int CalculateNewDimension (Dim d, int location, int dimension, int autosize) { int newDimension; @@ -2318,12 +2316,12 @@ namespace Terminal.Gui { } newDimension = AutoSize && autosize > newDimension ? autosize : newDimension; break; - + case Dim.DimFactor factor when !factor.IsFromRemaining (): newDimension = d.Anchor (dimension); newDimension = AutoSize && autosize > newDimension ? autosize : newDimension; break; - + case Dim.DimFill: default: newDimension = Math.Max (d.Anchor (dimension - location), 0); @@ -2335,11 +2333,11 @@ namespace Terminal.Gui { } - // horiztonal - (newX, newW) = GetNewLocationAndDimension (superviewFrame.X, superviewFrame.Width, x, Width, autosize.Width); + // horizontal + (newX, newW) = GetNewLocationAndDimension (superviewFrame.X, superviewFrame.Width, x, width, autosize.Width); // vertical - (newY, newH) = GetNewLocationAndDimension (superviewFrame.Y, superviewFrame.Height, y, Height, autosize.Height); + (newY, newH) = GetNewLocationAndDimension (superviewFrame.Y, superviewFrame.Height, y, height, autosize.Height); var r = new Rect (newX, newY, newW, newH); if (Frame != r) { @@ -2488,7 +2486,7 @@ namespace Terminal.Gui { if (edges.Any ()) { (var from, var to) = edges.First (); - if (from != Application.Top) { + if (from != superView?.GetTopSuperView (to, from)) { if (!ReferenceEquals (from, to)) { throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {from} linked with {to}. Did you forget to add it to {superView}?"); } else { @@ -2499,8 +2497,8 @@ namespace Terminal.Gui { // return L (a topologically sorted order) return result; } // TopologicalSort - - + + /// /// Invoked when a view starts executing or when the dimensions of the view have changed, for example in /// response to the container view or terminal resizing. @@ -2525,20 +2523,16 @@ namespace Terminal.Gui { CollectAll (this, ref nodes, ref edges); var ordered = View.TopologicalSort (SuperView, nodes, edges); foreach (var v in ordered) { - if (v.LayoutStyle == LayoutStyle.Computed) { - v.SetRelativeLayout (Frame); - } - - v.LayoutSubviews (); - v.LayoutNeeded = false; + LayoutSubview (v, Frame); } - // If our SuperView is Application.Top and the layoutstyle is Computed it's a special-cass. - // Use SetRelativeaLayout with the Frame of the Application.Top - if (SuperView != null && SuperView == Application.Top && LayoutNeeded - && ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) { - Debug.Assert (Application.Top.Frame.Location == Point.Empty); - SetRelativeLayout (Application.Top.Frame); + // If the 'to' is rooted to 'from' and the layoutstyle is Computed it's a special-case. + // Use LayoutSubview with the Frame of the 'from' + if (SuperView != null && GetTopSuperView () != null && LayoutNeeded + && ordered.Count == 0 && edges.Count > 0 && LayoutStyle == LayoutStyle.Computed) { + + (var from, var to) = edges.First (); + LayoutSubview (to, from.Frame); } LayoutNeeded = false; @@ -2546,6 +2540,16 @@ namespace Terminal.Gui { OnLayoutComplete (new LayoutEventArgs () { OldBounds = oldBounds }); } + private void LayoutSubview (View v, Rect hostFrame) + { + if (v.LayoutStyle == LayoutStyle.Computed) { + v.SetRelativeLayout (hostFrame); + } + + v.LayoutSubviews (); + v.LayoutNeeded = false; + } + ustring text; /// @@ -3211,11 +3215,14 @@ namespace Terminal.Gui { /// Get the top superview of a given . /// /// The superview view. - public View GetTopSuperView () + public View GetTopSuperView (View view = null, View superview = null) { - View top = Application.Top; - for (var v = this?.SuperView; v != null; v = v.SuperView) { + View top = superview ?? Application.Top; + for (var v = view?.SuperView ?? (this?.SuperView); v != null; v = v.SuperView) { top = v; + if (top == superview) { + break; + } } return top; diff --git a/Terminal.Gui/Core/Window.cs b/Terminal.Gui/Core/Window.cs index 46755114a..cba895495 100644 --- a/Terminal.Gui/Core/Window.cs +++ b/Terminal.Gui/Core/Window.cs @@ -7,7 +7,7 @@ // - FrameView Does not support padding (but should) // - FrameView Does not support mouse dragging // - FrameView Does not support IEnumerable -// Any udpates done here should probably be done in FrameView as well; TODO: Merge these classes +// Any updates done here should probably be done in FrameView as well; TODO: Merge these classes using System; using System.Collections; @@ -108,7 +108,7 @@ namespace Terminal.Gui { Border = new Border () { BorderStyle = DefaultBorderStyle, Padding = new Thickness (padding), - BorderBrush = ColorScheme.Normal.Background + //Title = title }; } else { Border = border; @@ -136,10 +136,82 @@ namespace Terminal.Gui { SetNeedsDisplay (); base.Remove (view); RemoveMenuStatusBar (view); + } + ///// + //public override void RemoveAll () + //{ + // contentView.RemoveAll (); + //} + + ///// + //public override void Redraw (Rect bounds) + //{ + // var padding = Border.GetSumThickness (); + // var scrRect = ViewToScreen (new Rect (0, 0, Frame.Width, Frame.Height)); + + // if (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded) { + // Driver.SetAttribute (GetNormalColor ()); + // Clear (); + // contentView.SetNeedsDisplay (); + // } + // var savedClip = contentView.ClipToBounds (); + + // // Redraw our contentView + // // DONE: smartly constrict contentView.Bounds to just be what intersects with the 'bounds' we were passed + // contentView.Redraw (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded ? contentView.Bounds : bounds); + // Driver.Clip = savedClip; + + // ClearLayoutNeeded (); + // ClearNeedsDisplay (); + + // Driver.SetAttribute (GetNormalColor ()); + // //Driver.DrawWindowFrame (scrRect, padding.Left + borderLength, padding.Top + borderLength, padding.Right + borderLength, padding.Bottom + borderLength, + // // Border.BorderStyle != BorderStyle.None, fill: true, Border.BorderStyle); + // Border.DrawContent (this, false); + // if (HasFocus) + // Driver.SetAttribute (ColorScheme.HotNormal); + // if (Border.DrawMarginFrame) + // Driver.DrawWindowTitle (scrRect, Title, padding.Left, padding.Top, padding.Right, padding.Bottom); + // Driver.SetAttribute (GetNormalColor ()); + //} + + ///// + //public override void OnCanFocusChanged () + //{ + // if (contentView != null) { + // contentView.CanFocus = CanFocus; + // } + // base.OnCanFocusChanged (); + //} + + ///// + ///// The text displayed by the . + ///// + //public override ustring Text { + // get => contentView?.Text; + // set { + // base.Text = value; + // if (contentView != null) { + // contentView.Text = value; + // } + // } + //} + + ///// + ///// Controls the text-alignment property of the label, changing it will redisplay the . + ///// + ///// The text alignment. + //public override TextAlignment TextAlignment { + // get => contentView.TextAlignment; + // set { + // base.TextAlignment = contentView.TextAlignment = value; + // } + //} + /// - /// Event arguments for chane events. + /// Event arguments for change events. /// public class TitleEventArgs : EventArgs { /// @@ -153,15 +225,15 @@ namespace Terminal.Gui { public ustring OldTitle { get; set; } /// - /// Flag which allows cancelling the Title change. + /// Flag which allows canceling the Title change. /// public bool Cancel { get; set; } /// /// Initializes a new instance of /// - /// The that is/has been replaced. - /// The new to be replaced. + /// The that is/has been replaced. + /// The new to be replaced. public TitleEventArgs (ustring oldTitle, ustring newTitle) { OldTitle = oldTitle; @@ -169,11 +241,11 @@ namespace Terminal.Gui { } } /// - /// Called before the changes. Invokes the event, which can be cancelled. + /// Called before the changes. Invokes the event, which can be cancelled. /// - /// The that is/has been replaced. - /// The new to be replaced. - /// `true` if an event handler cancelled the Title change. + /// The that is/has been replaced. + /// The new to be replaced. + /// `true` if an event handler canceled the Title change. public virtual bool OnTitleChanging (ustring oldTitle, ustring newTitle) { var args = new TitleEventArgs (oldTitle, newTitle); @@ -182,16 +254,16 @@ namespace Terminal.Gui { } /// - /// Event fired when the is changing. Set to + /// Event fired when the is changing. Set to /// `true` to cancel the Title change. /// public event Action TitleChanging; /// - /// Called when the has been changed. Invokes the event. + /// Called when the has been changed. Invokes the event. /// - /// The that is/has been replaced. - /// The new to be replaced. + /// The that is/has been replaced. + /// The new to be replaced. public virtual void OnTitleChanged (ustring oldTitle, ustring newTitle) { var args = new TitleEventArgs (oldTitle, newTitle); @@ -199,7 +271,7 @@ namespace Terminal.Gui { } /// - /// Event fired after the has been changed. + /// Event fired after the has been changed. /// public event Action TitleChanged; } diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json index 7bb465f68..b59236606 100644 --- a/Terminal.Gui/Resources/config.json +++ b/Terminal.Gui/Resources/config.json @@ -23,7 +23,7 @@ "Ctrl" ] }, - "Application.HeightAsBuffer": false, + "Application.EnableConsoleScrolling": false, "Application.QuitKey": { "Key": "Q", "Modifiers": [ diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 87ed2bcc7..a54bc1cac 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -10,10 +10,10 @@ - 1.0 - 1.0 - 1.0 - 1.0 + 2.0 + 2.0 + 2.0 + 2.0 @@ -22,11 +22,15 @@ + + + + - + @@ -56,11 +60,6 @@ Strings.Designer.cs - - - - - net7.0 9.0 diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs index 01bd8617e..2b0271f6e 100644 --- a/Terminal.Gui/Views/FrameView.cs +++ b/Terminal.Gui/Views/FrameView.cs @@ -11,6 +11,42 @@ namespace Terminal.Gui { /// public class FrameView : View { + //internal class FrameViewConfig : Configuration.Config { + + // /// + // /// + // /// + // /// + // [JsonConverter (typeof (JsonStringEnumConverter))] + // public BorderStyle? DefaultBorderStyle { get; set; } + + // public override void Apply () + // { + // if (DefaultBorderStyle.HasValue) { + // FrameView.DefaultBorderStyle = DefaultBorderStyle.Value; + // } + // } + + // public override void CopyUpdatedProperitesFrom (FrameViewConfig changedConfig) + // { + // if (changedConfig.DefaultBorderStyle.HasValue) { + // DefaultBorderStyle = changedConfig.DefaultBorderStyle; + // } + // } + + // public override void GetHardCodedDefaults () + // { + // DefaultBorderStyle = FrameView.DefaultBorderStyle; + // } + //} + + //[Configuration.ConfigProperty] + //internal static FrameViewConfig Config { get; set; } = new FrameViewConfig (); + + + + + /// /// Initializes a new instance of the class using layout. /// @@ -49,17 +85,52 @@ namespace Terminal.Gui { void Initialize (Rect frame, ustring title, View [] views = null, Border border = null) { - if (title == null) title = ustring.Empty; this.Title = title; if (border == null) { Border = new Border () { - BorderStyle = DefaultBorderStyle + BorderStyle = DefaultBorderStyle, + //Title = title }; } else { Border = border; + //if (ustring.IsNullOrEmpty (border.Title)) { + // border.Title = title; + //} } } + + + void DrawFrame () + { + DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), 0, fill: true); + } + + /// + /// Add the specified to this container. + /// + /// to add to this container + public override void Add (View view) + { + if (view.CanFocus) + CanFocus = true; + } + + + /// + /// Removes a from this container. + /// + /// + /// + public override void Remove (View view) + { + if (view == null) + return; + + SetNeedsDisplay (); + } + + /// public override bool OnEnter (View view) { diff --git a/Terminal.Gui/Views/TableView.cs b/Terminal.Gui/Views/TableView.cs index 1a69dec07..c1647437b 100644 --- a/Terminal.Gui/Views/TableView.cs +++ b/Terminal.Gui/Views/TableView.cs @@ -99,7 +99,7 @@ namespace Terminal.Gui { /// When is enabled this property contain all rectangles of selected cells. Rectangles describe column/rows selected in (not screen coordinates) /// /// - public Stack MultiSelectedRegions { get; } = new Stack (); + public Stack MultiSelectedRegions { get; private set; } = new Stack (); /// /// Horizontal scroll offset. The index of the first column in to display when when rendering the view. @@ -109,7 +109,7 @@ namespace Terminal.Gui { get => columnOffset; //try to prevent this being set to an out of bounds column - set => columnOffset = TableIsNullOrInvisible() ? 0 : Math.Max (0, Math.Min (Table.Columns.Count - 1, value)); + set => columnOffset = TableIsNullOrInvisible () ? 0 : Math.Max (0, Math.Min (Table.Columns.Count - 1, value)); } /// @@ -186,7 +186,7 @@ namespace Terminal.Gui { set { if (cellActivationKey != value) { ReplaceKeyBinding (cellActivationKey, value); - + // of API user is mixing and matching old and new methods of keybinding then they may have lost // the old binding (e.g. with ClearKeybindings) so ReplaceKeyBinding alone will fail AddKeyBinding (value, Command.Accept); @@ -218,9 +218,9 @@ namespace Terminal.Gui { AddCommand (Command.LineDown, () => { ChangeSelectionByOffset (0, 1, false); return true; }); AddCommand (Command.PageUp, () => { PageUp (false); return true; }); AddCommand (Command.PageDown, () => { PageDown (false); return true; }); - AddCommand (Command.LeftHome, () => { ChangeSelectionToStartOfRow (false); return true; }); + AddCommand (Command.LeftHome, () => { ChangeSelectionToStartOfRow (false); return true; }); AddCommand (Command.RightEnd, () => { ChangeSelectionToEndOfRow (false); return true; }); - AddCommand (Command.TopHome, () => { ChangeSelectionToStartOfTable(false); return true; }); + AddCommand (Command.TopHome, () => { ChangeSelectionToStartOfTable (false); return true; }); AddCommand (Command.BottomEnd, () => { ChangeSelectionToEndOfTable (false); return true; }); AddCommand (Command.RightExtend, () => { ChangeSelectionByOffset (1, 0, true); return true; }); @@ -234,8 +234,10 @@ namespace Terminal.Gui { AddCommand (Command.TopHomeExtend, () => { ChangeSelectionToStartOfTable (true); return true; }); AddCommand (Command.BottomEndExtend, () => { ChangeSelectionToEndOfTable (true); return true; }); - AddCommand (Command.SelectAll, () => { SelectAll(); return true; }); - AddCommand (Command.Accept, () => { OnCellActivated(new CellActivatedEventArgs (Table, SelectedColumn, SelectedRow)); return true; }); + AddCommand (Command.SelectAll, () => { SelectAll (); return true; }); + AddCommand (Command.Accept, () => { OnCellActivated (new CellActivatedEventArgs (Table, SelectedColumn, SelectedRow)); return true; }); + + AddCommand (Command.ToggleChecked, () => { ToggleCurrentCellSelection (); return true; }); // Default keybindings for this view AddKeyBinding (Key.CursorLeft, Command.Left); @@ -252,7 +254,7 @@ namespace Terminal.Gui { AddKeyBinding (Key.CursorLeft | Key.ShiftMask, Command.LeftExtend); AddKeyBinding (Key.CursorRight | Key.ShiftMask, Command.RightExtend); AddKeyBinding (Key.CursorUp | Key.ShiftMask, Command.LineUpExtend); - AddKeyBinding (Key.CursorDown| Key.ShiftMask, Command.LineDownExtend); + AddKeyBinding (Key.CursorDown | Key.ShiftMask, Command.LineDownExtend); AddKeyBinding (Key.PageUp | Key.ShiftMask, Command.PageUpExtend); AddKeyBinding (Key.PageDown | Key.ShiftMask, Command.PageDownExtend); AddKeyBinding (Key.Home | Key.ShiftMask, Command.LeftHomeExtend); @@ -264,33 +266,34 @@ namespace Terminal.Gui { AddKeyBinding (CellActivationKey, Command.Accept); } + /// public override void Redraw (Rect bounds) - { - Move (0, 0); - var frame = Frame; + { + Move (0, 0); + var frame = Frame; - scrollRightPoint = null; - scrollLeftPoint = null; + scrollRightPoint = null; + scrollLeftPoint = null; - // What columns to render at what X offset in viewport - var columnsToRender = CalculateViewport (bounds).ToArray (); + // What columns to render at what X offset in viewport + var columnsToRender = CalculateViewport (bounds).ToArray (); - Driver.SetAttribute (GetNormalColor ()); + Driver.SetAttribute (GetNormalColor ()); - //invalidate current row (prevents scrolling around leaving old characters in the frame - Driver.AddStr (new string (' ', bounds.Width)); + //invalidate current row (prevents scrolling around leaving old characters in the frame + Driver.AddStr (new string (' ', bounds.Width)); - int line = 0; + int line = 0; - if (ShouldRenderHeaders ()) { - // Render something like: - /* - ┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐ - │ArithmeticComparator│chi │Healthboard│Interpretation│Labnumber│ - └────────────────────┴──────────┴───────────┴──────────────┴─────────┘ - */ - if (Style.ShowHorizontalHeaderOverline) { + if (ShouldRenderHeaders ()) { + // Render something like: + /* + ┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐ + │ArithmeticComparator│chi │Healthboard│Interpretation│Labnumber│ + └────────────────────┴──────────┴───────────┴──────────────┴─────────┘ + */ + if (Style.ShowHorizontalHeaderOverline) { RenderHeaderOverline (line, bounds.Width, columnsToRender); line++; } @@ -436,19 +439,19 @@ namespace Terminal.Gui { bool moreColumnsToLeft = ColumnOffset > 0; // if we moved left would we find a new column (or are they all invisible?) - if(!TryGetNearestVisibleColumn (ColumnOffset-1, false, false, out _)) { + if (!TryGetNearestVisibleColumn (ColumnOffset - 1, false, false, out _)) { moreColumnsToLeft = false; } // are there visible columns to the right that have not yet been reached? // lets find out, what is the column index of the last column we are rendering int lastColumnIdxRendered = ColumnOffset + columnsToRender.Length - 1; - + // are there more valid indexes? bool moreColumnsToRight = lastColumnIdxRendered < Table.Columns.Count; // if we went right from the last column would we find a new visible column? - if(!TryGetNearestVisibleColumn (lastColumnIdxRendered + 1, true, false, out _)) { + if (!TryGetNearestVisibleColumn (lastColumnIdxRendered + 1, true, false, out _)) { // no we would not moreColumnsToRight = false; } @@ -466,7 +469,7 @@ namespace Terminal.Gui { // whole way but update to instead draw a header indicator // or scroll arrow etc var rune = Driver.HLine; - + if (Style.ShowVerticalHeaderLines) { if (c == 0) { // for first character render line @@ -475,12 +478,11 @@ namespace Terminal.Gui { // unless we have horizontally scrolled along // in which case render an arrow, to indicate user // can scroll left - if(Style.ShowHorizontalScrollIndicators && moreColumnsToLeft) - { + if (Style.ShowHorizontalScrollIndicators && moreColumnsToLeft) { rune = Driver.LeftArrow; - scrollLeftPoint = new Point(c,row); + scrollLeftPoint = new Point (c, row); } - + } // if the next column is the start of a header else if (columnsToRender.Any (r => r.X == c + 1)) { @@ -495,10 +497,9 @@ namespace Terminal.Gui { // unless there is more of the table we could horizontally // scroll along to see. In which case render an arrow, // to indicate user can scroll right - if(Style.ShowHorizontalScrollIndicators && moreColumnsToRight) - { + if (Style.ShowHorizontalScrollIndicators && moreColumnsToRight) { rune = Driver.RightArrow; - scrollRightPoint = new Point(c,row); + scrollRightPoint = new Point (c, row); } } @@ -518,7 +519,7 @@ namespace Terminal.Gui { var focused = HasFocus; var rowScheme = (Style.RowColorGetter?.Invoke ( - new RowColorGetterArgs(Table,rowToRender))) ?? ColorScheme; + new RowColorGetterArgs (Table, rowToRender))) ?? ColorScheme; //render start of line if (style.ShowVerticalCellLines) @@ -529,11 +530,9 @@ namespace Terminal.Gui { Attribute color; - if(FullRowSelect && IsSelected (0, rowToRender)) { + if (FullRowSelect && IsSelected (0, rowToRender)) { color = focused ? rowScheme.HotFocus : rowScheme.HotNormal; - } - else - { + } else { color = Enabled ? rowScheme.Normal : rowScheme.Disabled; } @@ -562,17 +561,16 @@ namespace Terminal.Gui { var colorSchemeGetter = colStyle?.ColorGetter; ColorScheme scheme; - if(colorSchemeGetter != null) { + if (colorSchemeGetter != null) { // user has a delegate for defining row color per cell, call it - scheme = colorSchemeGetter( - new CellColorGetterArgs (Table, rowToRender, current.Column.Ordinal, val, representation,rowScheme)); + scheme = colorSchemeGetter ( + new CellColorGetterArgs (Table, rowToRender, current.Column.Ordinal, val, representation, rowScheme)); // if users custom color getter returned null, use the row scheme - if(scheme == null) { + if (scheme == null) { scheme = rowScheme; } - } - else { + } else { // There is no custom cell coloring delegate so use the scheme for the row scheme = rowScheme; } @@ -588,16 +586,15 @@ namespace Terminal.Gui { // While many cells can be selected (see MultiSelectedRegions) only one cell is the primary (drives navigation etc) bool isPrimaryCell = current.Column.Ordinal == selectedColumn && rowToRender == selectedRow; - - RenderCell (cellColor,render,isPrimaryCell); - + + RenderCell (cellColor, render, isPrimaryCell); + // Reset color scheme to normal for drawing separators if we drew text with custom scheme if (scheme != rowScheme) { - if(isSelectedCell) { + if (isSelectedCell) { color = focused ? rowScheme.HotFocus : rowScheme.HotNormal; - } - else { + } else { color = Enabled ? rowScheme.Normal : rowScheme.Disabled; } Driver.SetAttribute (color); @@ -629,7 +626,7 @@ namespace Terminal.Gui { /// /// /// - protected virtual void RenderCell (Attribute cellColor, string render,bool isPrimaryCell) + protected virtual void RenderCell (Attribute cellColor, string render, bool isPrimaryCell) { // If the cell is the selected col/row then draw the first rune in inverted colors // this allows the user to track which cell is the active one during a multi cell @@ -740,12 +737,15 @@ namespace Terminal.Gui { col = GetNearestVisibleColumn (col, lookRight, true); - if (!MultiSelect || !extendExistingSelection) - MultiSelectedRegions.Clear (); + if (!MultiSelect || !extendExistingSelection) { + ClearMultiSelectedRegions (true); + } + if (extendExistingSelection) { + // If we are extending current selection but there isn't one - if (MultiSelectedRegions.Count == 0) { + if (MultiSelectedRegions.Count == 0 || MultiSelectedRegions.All(m=>m.IsToggled)) { // Create a new region between the old active cell and the new cell var rect = CreateTableSelection (SelectedColumn, SelectedRow, col, row); MultiSelectedRegions.Push (rect); @@ -761,6 +761,24 @@ namespace Terminal.Gui { SelectedRow = row; } + private void ClearMultiSelectedRegions (bool keepToggledSelections) + { + if (!keepToggledSelections) { + MultiSelectedRegions.Clear (); + return; + } + + var oldRegions = MultiSelectedRegions.ToArray ().Reverse (); + + MultiSelectedRegions.Clear (); + + foreach (var region in oldRegions) { + if (region.IsToggled) { + MultiSelectedRegions.Push (region); + } + } + } + /// /// Unions the current selected cell (and/or regions) with the provided cell and makes /// it the active one. @@ -769,10 +787,10 @@ namespace Terminal.Gui { /// private void UnionSelection (int col, int row) { - if (!MultiSelect || TableIsNullOrInvisible()) { + if (!MultiSelect || TableIsNullOrInvisible ()) { return; } - + EnsureValidSelection (); var oldColumn = SelectedColumn; @@ -812,7 +830,7 @@ namespace Terminal.Gui { /// Moves the selection up by one page /// /// true to extend the current selection (if any) instead of replacing - public void PageUp(bool extend) + public void PageUp (bool extend) { ChangeSelectionByOffset (0, -(Bounds.Height - GetHeaderHeightIfAny ()), extend); Update (); @@ -822,7 +840,7 @@ namespace Terminal.Gui { /// Moves the selection down by one page /// /// true to extend the current selection (if any) instead of replacing - public void PageDown(bool extend) + public void PageDown (bool extend) { ChangeSelectionByOffset (0, Bounds.Height - GetHeaderHeightIfAny (), extend); Update (); @@ -846,7 +864,7 @@ namespace Terminal.Gui { /// to (,nY) i.e. no horizontal scrolling. /// /// true to extend the current selection (if any) instead of replacing - public void ChangeSelectionToEndOfTable(bool extend) + public void ChangeSelectionToEndOfTable (bool extend) { var finalColumn = Table.Columns.Count - 1; @@ -880,10 +898,10 @@ namespace Terminal.Gui { /// public void SelectAll () { - if (TableIsNullOrInvisible() || !MultiSelect || Table.Rows.Count == 0) + if (TableIsNullOrInvisible () || !MultiSelect || Table.Rows.Count == 0) return; - MultiSelectedRegions.Clear (); + ClearMultiSelectedRegions (true); // Create a single region over entire table, set the origin of the selection to the active cell so that a followup spread selection e.g. shift-right behaves properly MultiSelectedRegions.Push (new TableSelection (new Point (SelectedColumn, SelectedRow), new Rect (0, 0, Table.Columns.Count, table.Rows.Count))); @@ -893,16 +911,18 @@ namespace Terminal.Gui { /// /// Returns all cells in any (if is enabled) and the selected cell /// - /// Return value is not affected by (i.e. returned s are not expanded to - /// include all points on row). /// public IEnumerable GetAllSelectedCells () { if (TableIsNullOrInvisible () || Table.Rows.Count == 0) - yield break; + { + return Enumerable.Empty(); + } EnsureValidSelection (); + var toReturn = new HashSet(); + // If there are one or more rectangular selections if (MultiSelect && MultiSelectedRegions.Any ()) { @@ -916,25 +936,27 @@ namespace Terminal.Gui { for (int y = yMin; y < yMax; y++) { for (int x = xMin; x < xMax; x++) { if (IsSelected (x, y)) { - yield return new Point (x, y); + toReturn.Add(new Point (x, y)); } } } - } else { + } - // if there are no region selections then it is just the active cell - // if we are selecting the full row - if (FullRowSelect) { - // all cells in active row are selected - for (int x = 0; x < Table.Columns.Count; x++) { - yield return new Point (x, SelectedRow); - } - } else { - // Not full row select and no multi selections - yield return new Point (SelectedColumn, SelectedRow); + // if there are no region selections then it is just the active cell + + // if we are selecting the full row + if (FullRowSelect) { + // all cells in active row are selected + for (int x = 0; x < Table.Columns.Count; x++) { + toReturn.Add(new Point (x, SelectedRow)); } + } else { + // Not full row select and no multi selections + toReturn.Add(new Point (SelectedColumn, SelectedRow)); } + + return toReturn; } /// @@ -944,17 +966,60 @@ namespace Terminal.Gui { /// Origin point for the selection in Y /// End point for the selection in X /// End point for the selection in Y + /// True if selection is result of /// - private TableSelection CreateTableSelection (int pt1X, int pt1Y, int pt2X, int pt2Y) + private TableSelection CreateTableSelection (int pt1X, int pt1Y, int pt2X, int pt2Y, bool toggle = false) { - var top = Math.Max(Math.Min (pt1Y, pt2Y), 0); - var bot = Math.Max(Math.Max (pt1Y, pt2Y), 0); + var top = Math.Max (Math.Min (pt1Y, pt2Y), 0); + var bot = Math.Max (Math.Max (pt1Y, pt2Y), 0); - var left = Math.Max(Math.Min (pt1X, pt2X), 0); - var right = Math.Max(Math.Max (pt1X, pt2X), 0); + var left = Math.Max (Math.Min (pt1X, pt2X), 0); + var right = Math.Max (Math.Max (pt1X, pt2X), 0); // Rect class is inclusive of Top Left but exclusive of Bottom Right so extend by 1 - return new TableSelection (new Point (pt1X, pt1Y), new Rect (left, top, right - left + 1, bot - top + 1)); + return new TableSelection (new Point (pt1X, pt1Y), new Rect (left, top, right - left + 1, bot - top + 1)) { + IsToggled = toggle + }; + } + + private void ToggleCurrentCellSelection () + { + if (!MultiSelect) { + return; + } + + var regions = GetMultiSelectedRegionsContaining(selectedColumn, selectedRow).ToArray(); + var toggles = regions.Where(s=>s.IsToggled).ToArray (); + + // Toggle it off + if (toggles.Any ()) { + + var oldRegions = MultiSelectedRegions.ToArray ().Reverse (); + MultiSelectedRegions.Clear (); + + foreach (var region in oldRegions) { + if (!toggles.Contains (region)) + MultiSelectedRegions.Push (region); + } + } else { + + // user is toggling selection within a rectangular + // select. So toggle the full region + if(regions.Any()) + { + foreach(var r in regions) + { + r.IsToggled = true; + } + } + else{ + // Toggle on a single cell selection + MultiSelectedRegions.Push ( + CreateTableSelection (selectedColumn, SelectedRow, selectedColumn, selectedRow, true) + ); + } + + } } /// @@ -978,22 +1043,36 @@ namespace Terminal.Gui { /// public bool IsSelected (int col, int row) { - if(!IsColumnVisible(col)) { + if (!IsColumnVisible (col)) { return false; - } + } - // Cell is also selected if in any multi selection region - if (MultiSelect && MultiSelectedRegions.Any (r => r.Rect.Contains (col, row))) - return true; - - // Cell is also selected if Y axis appears in any region (when FullRowSelect is enabled) - if (FullRowSelect && MultiSelect && MultiSelectedRegions.Any (r => r.Rect.Bottom > row && r.Rect.Top <= row)) + if(GetMultiSelectedRegionsContaining(col,row).Any()) + { return true; + } return row == SelectedRow && (col == SelectedColumn || FullRowSelect); } + private IEnumerable GetMultiSelectedRegionsContaining(int col, int row) + { + if(!MultiSelect) + { + return Enumerable.Empty(); + } + + if(FullRowSelect) + { + return MultiSelectedRegions.Where (r => r.Rect.Bottom > row && r.Rect.Top <= row); + } + else + { + return MultiSelectedRegions.Where (r => r.Rect.Contains (col, row)); + } + } + /// /// Returns true if the given indexes a visible /// column otherwise false. Returns false for indexes that are out of bounds. @@ -1071,19 +1150,17 @@ namespace Terminal.Gui { if (me.Flags.HasFlag (MouseFlags.Button1Clicked)) { - if (scrollLeftPoint != null + if (scrollLeftPoint != null && scrollLeftPoint.Value.X == me.X - && scrollLeftPoint.Value.Y == me.Y) - { + && scrollLeftPoint.Value.Y == me.Y) { ColumnOffset--; EnsureValidScrollOffsets (); SetNeedsDisplay (); } - if (scrollRightPoint != null + if (scrollRightPoint != null && scrollRightPoint.Value.X == me.X - && scrollRightPoint.Value.Y == me.Y) - { + && scrollRightPoint.Value.Y == me.Y) { ColumnOffset++; EnsureValidScrollOffsets (); SetNeedsDisplay (); @@ -1092,8 +1169,8 @@ namespace Terminal.Gui { var hit = ScreenToCell (me.X, me.Y); if (hit != null) { - if(MultiSelect && HasControlOrAlt(me)) { - UnionSelection(hit.Value.X, hit.Value.Y); + if (MultiSelect && HasControlOrAlt (me)) { + UnionSelection (hit.Value.X, hit.Value.Y); } else { SetSelection (hit.Value.X, hit.Value.Y, me.Flags.HasFlag (MouseFlags.ButtonShift)); } @@ -1128,7 +1205,7 @@ namespace Terminal.Gui { /// Cell clicked or null. public Point? ScreenToCell (int clientX, int clientY) { - return ScreenToCell(clientX, clientY, out _); + return ScreenToCell (clientX, clientY, out _); } /// @@ -1153,7 +1230,7 @@ namespace Terminal.Gui { headerIfAny = col?.Column; return null; } - + var rowIdx = RowOffset - headerHeight + clientY; @@ -1161,7 +1238,7 @@ namespace Terminal.Gui { // invalid index back to user! if (rowIdx >= Table.Rows.Count) { return null; - } + } if (col != null && rowIdx >= 0) { @@ -1242,10 +1319,10 @@ namespace Terminal.Gui { /// Changes will not be immediately visible in the display until you call public void EnsureValidSelection () { - if (TableIsNullOrInvisible()) { + if (TableIsNullOrInvisible ()) { // Table doesn't exist, we should probably clear those selections - MultiSelectedRegions.Clear (); + ClearMultiSelectedRegions (false); return; } @@ -1315,8 +1392,7 @@ namespace Terminal.Gui { /// Use false if you are primarily interested in learning about directional column visibility. private int GetNearestVisibleColumn (int columnIndex, bool lookRight, bool allowBumpingInOppositeDirection) { - if(TryGetNearestVisibleColumn(columnIndex,lookRight,allowBumpingInOppositeDirection, out var answer)) - { + if (TryGetNearestVisibleColumn (columnIndex, lookRight, allowBumpingInOppositeDirection, out var answer)) { return answer; } @@ -1335,7 +1411,7 @@ namespace Terminal.Gui { // get the column visibility by index (if no style visible is true) bool [] columnVisibility = Table.Columns.Cast () .Select (c => this.Style.GetColumnStyleIfAny (c)?.Visible ?? true) - .ToArray(); + .ToArray (); // column is visible if (columnVisibility [columnIndex]) { @@ -1346,10 +1422,9 @@ namespace Terminal.Gui { int increment = lookRight ? 1 : -1; // move in that direction - for (int i = columnIndex; i >=0 && i < columnVisibility.Length; i += increment) { + for (int i = columnIndex; i >= 0 && i < columnVisibility.Length; i += increment) { // if we find a visible column - if(columnVisibility [i]) - { + if (columnVisibility [i]) { idx = i; return true; } @@ -1357,7 +1432,7 @@ namespace Terminal.Gui { // Caller only wants to look in one direction and we did not find any // visible columns in that direction - if(!allowBumpingInOppositeDirection) { + if (!allowBumpingInOppositeDirection) { idx = columnIndex; return false; } @@ -1400,10 +1475,10 @@ namespace Terminal.Gui { //if we have scrolled too far to the right if (SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) { - if(Style.SmoothHorizontalScrolling) { + if (Style.SmoothHorizontalScrolling) { // Scroll right 1 column at a time until the users selected column is visible - while(SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) { + while (SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) { ColumnOffset++; columnsToRender = CalculateViewport (Bounds).ToArray (); @@ -1414,11 +1489,10 @@ namespace Terminal.Gui { break; } - } - else { + } else { ColumnOffset = SelectedColumn; } - + } //if we have scrolled too far down @@ -1482,7 +1556,7 @@ namespace Terminal.Gui { int colWidth; // if column is not being rendered - if(colStyle?.Visible == false) { + if (colStyle?.Visible == false) { // do not add it to the returned columns continue; } @@ -1492,16 +1566,14 @@ namespace Terminal.Gui { // there is not enough space for this columns // visible content - if (usedSpace + colWidth > availableHorizontalSpace) - { + if (usedSpace + colWidth > availableHorizontalSpace) { bool showColumn = false; // if this column accepts flexible width rendering and // is therefore happy rendering into less space - if ( colStyle != null && colStyle.MinAcceptableWidth > 0 && + if (colStyle != null && colStyle.MinAcceptableWidth > 0 && // is there enough space to meet the MinAcceptableWidth - (availableHorizontalSpace - usedSpace) >= colStyle.MinAcceptableWidth) - { + (availableHorizontalSpace - usedSpace) >= colStyle.MinAcceptableWidth) { // show column and use use whatever space is // left for rendering it showColumn = true; @@ -1510,14 +1582,13 @@ namespace Terminal.Gui { // If its the only column we are able to render then // accept it anyway (that must be one massively wide column!) - if (first) - { + if (first) { showColumn = true; } // no special exceptions and we are out of space // so stop accepting new columns for the render area - if(!showColumn) + if (!showColumn) break; } @@ -1771,7 +1842,7 @@ namespace Terminal.Gui { /// Delegate for coloring specific rows in a different color. For cell color /// /// - public RowColorGetterDelegate RowColorGetter {get;set;} + public RowColorGetterDelegate RowColorGetter { get; set; } /// /// Determines rendering when the last column in the table is visible but it's @@ -1781,7 +1852,7 @@ namespace Terminal.Gui { /// and leave a blank column that cannot be selected in the remaining space. /// /// - public bool ExpandLastColumn {get;set;} = true; + public bool ExpandLastColumn { get; set; } = true; /// /// @@ -1798,7 +1869,7 @@ namespace Terminal.Gui { /// /// public bool SmoothHorizontalScrolling { get; set; } = true; - + /// /// Returns the entry from for the given or null if no custom styling is defined for it /// @@ -2003,6 +2074,12 @@ namespace Terminal.Gui { /// public Rect Rect { get; set; } + /// + /// True if the selection was made through + /// and therefore should persist even through keyboard navigation. + /// + public bool IsToggled { get; set; } + /// /// Creates a new selected area starting at the origin corner and covering the provided rectangular area /// diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index c45401abe..d5a431721 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -13,6 +13,12 @@ namespace Terminal.Gui { public class TileView : View { TileView parentTileView; + /// + /// The keyboard key that the user can press to toggle resizing + /// of splitter lines. Mouse drag splitting is always enabled. + /// + public Key ToggleResizable { get; set; } = Key.CtrlMask | Key.F10; + /// /// A single presented in a . To create /// new instances use @@ -167,7 +173,6 @@ namespace Terminal.Gui { /// public TileView (int tiles) { - CanFocus = true; RebuildForTileCount (tiles); IgnoreBorderPropertyOnRedraw = true; Border = new Border () { @@ -403,15 +408,6 @@ namespace Terminal.Gui { return true; } - /// - public override bool OnEnter (View view) - { - Driver.SetCursorVisibility (CursorVisibility.Invisible); - if (!Tiles.Where (t => t.ContentView.HasFocus).Any ()) { - Tiles.FirstOrDefault ()?.ContentView.SetFocus (); - } - return base.OnEnter (view); - } /// public override void Redraw (Rect bounds) @@ -550,6 +546,30 @@ namespace Terminal.Gui { return true; } + /// + public override bool ProcessHotKey (KeyEvent keyEvent) + { + bool focusMoved = false; + + if(keyEvent.Key == ToggleResizable) { + foreach(var l in splitterLines) { + + var iniBefore = l.IsInitialized; + l.IsInitialized = false; + l.CanFocus = !l.CanFocus; + l.IsInitialized = iniBefore; + + if (l.CanFocus && !focusMoved) { + l.SetFocus (); + focusMoved = true; + } + } + return true; + } + + return base.ProcessHotKey (keyEvent); + } + private bool IsValidNewSplitterPos (int idx, Pos value, int fullSpace) { int newSize = value.Anchor (fullSpace); @@ -750,7 +770,7 @@ namespace Terminal.Gui { tile.ContentView.Width = GetTileWidthOrHeight (i, Bounds.Width, visibleTiles, visibleSplitterLines); } else { tile.ContentView.X = bounds.X; - tile.ContentView.Y = i == 0 ? 0 : Pos.Bottom (visibleSplitterLines [i - 1]); + tile.ContentView.Y = i == 0 ? bounds.Y : Pos.Bottom (visibleSplitterLines [i - 1]); tile.ContentView.Width = bounds.Width; tile.ContentView.Height = GetTileWidthOrHeight (i, Bounds.Height, visibleTiles, visibleSplitterLines); } @@ -864,7 +884,7 @@ namespace Terminal.Gui { public TileViewLineView (TileView parent, int idx) { - CanFocus = true; + CanFocus = false; TabStop = true; this.Parent = parent; @@ -929,7 +949,7 @@ namespace Terminal.Gui { public void DrawSplitterSymbol () { - if (CanFocus && HasFocus) { + if (dragPosition != null || CanFocus) { var location = moveRuneRenderLocation ?? new Point (Bounds.Width / 2, Bounds.Height / 2); @@ -939,10 +959,6 @@ namespace Terminal.Gui { public override bool MouseEvent (MouseEvent mouseEvent) { - if (!CanFocus) { - return true; - } - if (!dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed)) { // Start a Drag diff --git a/Terminal.Gui/Windows/Dialog.cs b/Terminal.Gui/Windows/Dialog.cs index 66327ac5a..74d1c3fd8 100644 --- a/Terminal.Gui/Windows/Dialog.cs +++ b/Terminal.Gui/Windows/Dialog.cs @@ -79,6 +79,7 @@ namespace Terminal.Gui { Modal = true; ButtonAlignment = DefaultButtonAlignment; Border = DefaultBorder; + //Border.Title = title; if (buttons != null) { foreach (var b in buttons) { diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json index 80329d78d..4fe02b923 100644 --- a/UICatalog/Properties/launchSettings.json +++ b/UICatalog/Properties/launchSettings.json @@ -3,6 +3,12 @@ "UICatalog": { "commandName": "Project" }, + "WSL : UICatalog": { + "commandName": "Executable", + "executablePath": "wsl", + "commandLineArgs": "dotnet UICatalog.dll", + "distributionName": "" + }, "UICatalog -usc": { "commandName": "Project", "commandLineArgs": "-usc" @@ -29,10 +35,6 @@ "commandName": "Project", "commandLineArgs": "WizardAsView" }, - "VkeyPacketSimulator": { - "commandName": "Project", - "commandLineArgs": "VkeyPacketSimulator" - }, "CollectionNavigatorTester": { "commandName": "Project", "commandLineArgs": "\"Search Collection Nav\"" @@ -48,16 +50,6 @@ "Windows & FrameViews": { "commandName": "Project", "commandLineArgs": "\"Windows & FrameViews\"" - }, - "WSL : UICatalog": { - "commandName": "Executable", - "executablePath": "wsl", - "commandLineArgs": "dotnet UICatalog.dll", - "distributionName": "" - }, - "Tile View Experiments": { - "commandName": "Project", - "commandLineArgs": "\"Tile View Experiments\"" } } } \ No newline at end of file diff --git a/UICatalog/Scenarios/ASCIICustomButton.cs b/UICatalog/Scenarios/ASCIICustomButton.cs new file mode 100644 index 000000000..a58b2200c --- /dev/null +++ b/UICatalog/Scenarios/ASCIICustomButton.cs @@ -0,0 +1,313 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Terminal.Gui; + +namespace UICatalog.Scenarios { + [ScenarioMetadata (Name: "ASCIICustomButtonTest", Description: "ASCIICustomButton sample")] + [ScenarioCategory ("Controls")] + public class ASCIICustomButtonTest : Scenario { + private static bool smallerWindow; + private ScrollViewTestWindow scrollViewTestWindow; + private MenuItem miSmallerWindow; + + public override void Init (ColorScheme colorScheme) + { + Application.Init (); + scrollViewTestWindow = new ScrollViewTestWindow (); + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem("Window Size", new MenuItem [] { + miSmallerWindow = new MenuItem ("Smaller Window", "", ChangeWindowSize) { + CheckType = MenuItemCheckStyle.Checked + }, + null, + new MenuItem("Quit", "",() => Application.RequestStop(),null,null, Key.Q | Key.CtrlMask) + }) + }); + Application.Top.Add (menu, scrollViewTestWindow); + Application.Run (); + } + + private void ChangeWindowSize () + { + smallerWindow = (bool)(miSmallerWindow.Checked = !miSmallerWindow.Checked); + scrollViewTestWindow.Dispose (); + Application.Top.Remove (scrollViewTestWindow); + scrollViewTestWindow = new ScrollViewTestWindow (); + Application.Top.Add (scrollViewTestWindow); + } + + public override void Run () + { + } + + public class ASCIICustomButton : Button { + public string Description => $"Description of: {id}"; + + public event Action PointerEnter; + + private Label fill; + private FrameView border; + private string id; + + public ASCIICustomButton (string text, Pos x, Pos y, int width, int height) : base (text) + { + CustomInitialize ("", text, x, y, width, height); + } + + public ASCIICustomButton (string id, string text, Pos x, Pos y, int width, int height) : base (text) + { + CustomInitialize (id, text, x, y, width, height); + } + + private void CustomInitialize (string id, string text, Pos x, Pos y, int width, int height) + { + this.id = id; + X = x; + Y = y; + + Frame = new Rect { + Width = width, + Height = height + }; + + border = new FrameView () { + Width = width, + Height = height + }; + + AutoSize = false; + + var fillText = new System.Text.StringBuilder (); + for (int i = 0; i < Bounds.Height; i++) { + if (i > 0) { + fillText.AppendLine (""); + } + for (int j = 0; j < Bounds.Width; j++) { + fillText.Append ("█"); + } + } + + fill = new Label (fillText.ToString ()) { + Visible = false, + CanFocus = false + }; + + var title = new Label (text) { + X = Pos.Center (), + Y = Pos.Center (), + }; + + border.MouseClick += This_MouseClick; + border.Subviews [0].MouseClick += This_MouseClick; + fill.MouseClick += This_MouseClick; + title.MouseClick += This_MouseClick; + + Add (border, fill, title); + } + + private void This_MouseClick (MouseEventArgs obj) + { + OnMouseEvent (obj.MouseEvent); + } + + public override bool OnMouseEvent (MouseEvent mouseEvent) + { + Debug.WriteLine ($"{mouseEvent.Flags}"); + if (mouseEvent.Flags == MouseFlags.Button1Clicked) { + if (!HasFocus && SuperView != null) { + if (!SuperView.HasFocus) { + SuperView.SetFocus (); + } + SetFocus (); + SetNeedsDisplay (); + } + + OnClicked (); + return true; + } + return base.OnMouseEvent (mouseEvent); + } + + public override bool OnEnter (View view) + { + border.Visible = false; + fill.Visible = true; + PointerEnter.Invoke (this); + view = this; + return base.OnEnter (view); + } + + public override bool OnLeave (View view) + { + border.Visible = true; + fill.Visible = false; + if (view == null) + view = this; + return base.OnLeave (view); + } + } + + public class ScrollViewTestWindow : Window { + private List public class UICatalogTopLevel : Toplevel { public MenuItem miIsMouseDisabled; - public MenuItem miHeightAsBuffer; + public MenuItem miEnableConsoleScrolling; public TileView ContentPane; public ListView CategoryListView; @@ -349,6 +356,8 @@ namespace UICatalog { { ConfigChanged (); + miIsMouseDisabled.Checked = Application.IsMouseDisabled; + miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling; DriverName.Title = $"Driver: {Driver.GetType ().Name}"; OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}"; @@ -403,13 +412,12 @@ namespace UICatalog { List CreateDiagnosticMenuItems () { - List menuItems = new List { - CreateDiagnosticFlagsMenuItems (), - new MenuItem [] { null }, - CreateHeightAsBufferMenuItems (), - CreateDisabledEnabledMouseItems (), - CreateKeybindingsMenuItems () - }; + List menuItems = new List (); + menuItems.Add (CreateDiagnosticFlagsMenuItems ()); + menuItems.Add (new MenuItem [] { null }); + menuItems.Add (CreateEnableConsoleScrollingMenuItems ()); + menuItems.Add (CreateDisabledEnabledMouseItems ()); + menuItems.Add (CreateKeybindingsMenuItems ()); return menuItems; } @@ -447,19 +455,18 @@ namespace UICatalog { return menuItems.ToArray (); } - MenuItem [] CreateHeightAsBufferMenuItems () + MenuItem [] CreateEnableConsoleScrollingMenuItems () { List menuItems = new List (); - miHeightAsBuffer = new MenuItem { - Title = "_Height As Buffer" + miEnableConsoleScrolling = new MenuItem (); + miEnableConsoleScrolling.Title = "_Enable Console Scrolling"; + miEnableConsoleScrolling.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miEnableConsoleScrolling.Title.ToString ().Substring (1, 1) [0]; + miEnableConsoleScrolling.CheckType |= MenuItemCheckStyle.Checked; + miEnableConsoleScrolling.Action += () => { + miEnableConsoleScrolling.Checked = !miEnableConsoleScrolling.Checked; + Application.EnableConsoleScrolling = (bool)miEnableConsoleScrolling.Checked; }; - miHeightAsBuffer.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miHeightAsBuffer.Title.ToString ().Substring (1, 1) [0]; - miHeightAsBuffer.CheckType |= MenuItemCheckStyle.Checked; - miHeightAsBuffer.Action += () => { - miHeightAsBuffer.Checked = !miHeightAsBuffer.Checked; - Application.HeightAsBuffer = (bool)miHeightAsBuffer.Checked; - }; - menuItems.Add (miHeightAsBuffer); + menuItems.Add (miEnableConsoleScrolling); return menuItems.ToArray (); } @@ -634,7 +641,7 @@ namespace UICatalog { StatusBar.Items [0].Title = $"~{Application.QuitKey} to quit"; miIsMouseDisabled.Checked = Application.IsMouseDisabled; - miHeightAsBuffer.Checked = Application.HeightAsBuffer; + miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling; var height = (UICatalogApp.ShowStatusBar ? 1 : 0);// + (MenuBar.Visible ? 1 : 0); ContentPane.Height = Dim.Fill (height); diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index ef5c6e2e0..9b27b05df 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -7,10 +7,10 @@ - 1.0 - 1.0 - 1.0 - 1.0 + 2.0 + 2.0 + 2.0 + 2.0 TRACE @@ -28,7 +28,7 @@ - + diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs index eaa15848f..637174d17 100644 --- a/UnitTests/Application/ApplicationTests.cs +++ b/UnitTests/Application/ApplicationTests.cs @@ -24,8 +24,7 @@ namespace Terminal.Gui.ApplicationTests { Assert.Null (Application.Driver); Assert.Null (Application.Top); Assert.Null (Application.Current); - // removed below as HeightAsBuffer now works without a driver loaded - //Assert.Throws (() => Application.HeightAsBuffer == true); + Assert.False (Application.EnableConsoleScrolling); Assert.Null (Application.MainLoop); Assert.Null (Application.Iteration); Assert.Null (Application.RootMouseEvent); @@ -37,7 +36,7 @@ namespace Terminal.Gui.ApplicationTests { Assert.NotNull (Application.Driver); Assert.NotNull (Application.Top); Assert.NotNull (Application.Current); - Assert.False (Application.HeightAsBuffer); + Assert.False (Application.EnableConsoleScrolling); Assert.NotNull (Application.MainLoop); Assert.Null (Application.Iteration); Assert.Null (Application.RootMouseEvent); @@ -314,7 +313,7 @@ namespace Terminal.Gui.ApplicationTests { public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws () { Init (); - + Application.Driver = null; Application.Iteration = () => { @@ -334,8 +333,8 @@ namespace Terminal.Gui.ApplicationTests { [Fact] public void Run_T_NoInit_DoesNotThrow () { - Application.ForceFakeConsole = true; - + Application.ForceFakeConsole = true; + Application.Iteration = () => { Application.RequestStop (); }; @@ -431,7 +430,7 @@ namespace Terminal.Gui.ApplicationTests { } // TODO: Add tests for Run that test errorHandler - + #endregion #region ShutdownTests diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index be039028f..5e5c34b3d 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -182,12 +182,13 @@ namespace Terminal.Gui.ConfigurationTests { /// Save the `config.json` file; this can be used to update the file in `Terminal.Gui.Resources.config.json'. /// /// - /// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Conifg Properties - /// are all satic and thus can be overwritten by other tests. + /// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Config Properties + /// are all static and thus can be overwritten by other tests. [Fact] public void SaveDefaults () { ConfigurationManager.Initialize (); + ConfigurationManager.Reset (); // Get the hard coded settings ConfigurationManager.GetHardCodedDefaults (); @@ -226,7 +227,7 @@ namespace Terminal.Gui.ConfigurationTests { ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true; + ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Apply (); // assert apply worked @@ -235,7 +236,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.Equal (Key.B, Application.AlternateBackwardKey); Assert.True (Application.UseSystemConsole); Assert.True (Application.IsMouseDisabled); - Assert.True (Application.HeightAsBuffer); + Assert.True (Application.EnableConsoleScrolling); //act ConfigurationManager.Reset (); @@ -248,7 +249,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey); Assert.False (Application.UseSystemConsole); Assert.False (Application.IsMouseDisabled); - Assert.False (Application.HeightAsBuffer); + Assert.False (Application.EnableConsoleScrolling); // arrange ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; @@ -256,7 +257,7 @@ namespace Terminal.Gui.ConfigurationTests { ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true; + ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Apply (); @@ -274,7 +275,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey); Assert.False (Application.UseSystemConsole); Assert.False (Application.IsMouseDisabled); - Assert.False (Application.HeightAsBuffer); + Assert.False (Application.EnableConsoleScrolling); } @@ -316,12 +317,11 @@ namespace Terminal.Gui.ConfigurationTests { [Fact, AutoInitShutdown] public void TestConfigurationManagerToJson () { + ConfigurationManager.Reset (); ConfigurationManager.GetHardCodedDefaults (); var stream = ConfigurationManager.ToStream (); - ConfigurationManager.Settings.Update (stream, "TestConfigurationManagerToJson"); - } [Fact, AutoInitShutdown (configLocation: ConfigLocations.None)] @@ -771,7 +771,7 @@ namespace Terminal.Gui.ConfigurationTests { ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true; + ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Updated += ConfigurationManager_Updated; bool fired = false; @@ -784,7 +784,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.Equal (Key.PageUp | Key.CtrlMask, ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue); - Assert.False ((bool)ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue); + Assert.False ((bool)ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue); } ConfigurationManager.Load (true); @@ -810,7 +810,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.Equal (Key.B, Application.AlternateBackwardKey); Assert.True (Application.UseSystemConsole); Assert.True (Application.IsMouseDisabled); - Assert.True (Application.HeightAsBuffer); + Assert.True (Application.EnableConsoleScrolling); } // act @@ -819,7 +819,7 @@ namespace Terminal.Gui.ConfigurationTests { ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true; + ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Apply (); diff --git a/UnitTests/Configuration/SettingsScopeTests.cs b/UnitTests/Configuration/SettingsScopeTests.cs index 09b048491..040c564bd 100644 --- a/UnitTests/Configuration/SettingsScopeTests.cs +++ b/UnitTests/Configuration/SettingsScopeTests.cs @@ -26,7 +26,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.True (ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue is Key); Assert.True (ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue is bool); Assert.True (ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue is bool); - Assert.True (ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue is bool); + Assert.True (ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue is bool); Assert.True (ConfigurationManager.Settings ["Theme"].PropertyValue is string); Assert.Equal ("Default", ConfigurationManager.Settings ["Theme"].PropertyValue as string); @@ -45,7 +45,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.Equal (Key.PageUp | Key.CtrlMask, (Key)ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue); - Assert.False ((bool)ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue); + Assert.False ((bool)ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue); // act ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; @@ -53,7 +53,7 @@ namespace Terminal.Gui.ConfigurationTests { ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true; + ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Apply (); @@ -63,7 +63,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.Equal (Key.B, Application.AlternateBackwardKey); Assert.True (Application.UseSystemConsole); Assert.True (Application.IsMouseDisabled); - Assert.True (Application.HeightAsBuffer); + Assert.True (Application.EnableConsoleScrolling); } [Fact, AutoInitShutdown] @@ -78,7 +78,7 @@ namespace Terminal.Gui.ConfigurationTests { updatedSettings["Application.AlternateBackwardKey"].PropertyValue = Key.B; updatedSettings["Application.UseSystemConsole"].PropertyValue = true; updatedSettings["Application.IsMouseDisabled"].PropertyValue = true; - updatedSettings["Application.HeightAsBuffer"].PropertyValue = true; + updatedSettings["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Update (updatedSettings); Assert.Equal (Key.End, ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue); @@ -86,7 +86,7 @@ namespace Terminal.Gui.ConfigurationTests { Assert.Equal (Key.B, updatedSettings ["Application.AlternateBackwardKey"].PropertyValue); Assert.True ((bool)updatedSettings ["Application.UseSystemConsole"].PropertyValue); Assert.True ((bool)updatedSettings ["Application.IsMouseDisabled"].PropertyValue); - Assert.True ((bool)updatedSettings ["Application.HeightAsBuffer"].PropertyValue); + Assert.True ((bool)updatedSettings ["Application.EnableConsoleScrolling"].PropertyValue); } } } \ No newline at end of file diff --git a/UnitTests/Core/BorderTests.cs b/UnitTests/Core/BorderTests.cs index 1b98e579c..db3504aa4 100644 --- a/UnitTests/Core/BorderTests.cs +++ b/UnitTests/Core/BorderTests.cs @@ -1,15 +1,19 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Reflection.Emit; using Xunit; +using Xunit.Abstractions; using Rune = System.Rune; namespace Terminal.Gui.CoreTests { public class BorderTests { - [Fact] - [AutoInitShutdown] + readonly ITestOutputHelper output; + + public BorderTests (ITestOutputHelper output) + { + this.output = output; + } + + [Fact, AutoInitShutdown] public void Constructor_Defaults () { var b = new Border (); @@ -40,15 +44,10 @@ namespace Terminal.Gui.CoreTests { Assert.False (b.DrawMarginFrame); } - //[Fact] - //[AutoInitShutdown] - //public void ActualWidth_ActualHeight () - //{ - // var v = new View (new Rect (5, 10, 60, 20), "", new Border ()); - - // Assert.Equal (60, v.Border.ActualWidth); - // Assert.Equal (20, v.Border.ActualHeight); - //} + //[Fact, AutoInitShutdown] + // public void ActualWidth_ActualHeight () + // { + // var v = new View (new Rect (5, 10, 60, 20), "", new Border ()); //[Fact] //public void ToplevelContainer_LayoutStyle_Computed_Constuctor_ () @@ -83,23 +82,12 @@ namespace Terminal.Gui.CoreTests { // var top = Application.Top; // var driver = (FakeDriver)Application.Driver; - // var label = new Label () { - // X = Pos.Center (), - // Y = Pos.Center (), - // Border = new Border () { - // BorderStyle = BorderStyle.Single, - // Padding = new Thickness (2), - // BorderThickness = new Thickness (2), - // BorderBrush = Color.Red, - // Background = Color.BrightGreen, - // Effect3D = true, - // Effect3DOffset = new Point (2, -3) - // }, - // ColorScheme = Colors.TopLevel, - // Text = "This is a test" - // }; - // label.Border.Child = label; - // top.Add (label); + // [Fact] + // [AutoInitShutdown] + // public void DrawContent_With_Child_Border () + // { + // var top = Application.Top; + // var driver = (FakeDriver)Application.Driver; // top.LayoutSubviews (); // label.Redraw (label.Bounds); @@ -243,11 +231,34 @@ namespace Terminal.Gui.CoreTests { // } // Assert.Equal ("This is a test", text.Trim ()); - // // Check the upper Effect3D - // for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y; - // r < frame.Y - drawMarginFrame - sumThickness.Top; r++) { - // for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X; - // c < frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X; c++) { + // var color = (Attribute)driver.Contents [r, c, 1]; + // var rune = (Rune)driver.Contents [r, c, 0]; + // if (r == frame.Y - drawMarginFrame || r == frame.Bottom + drawMarginFrame - 1 + // || c == frame.X - drawMarginFrame || c == frame.Right + drawMarginFrame - 1) { + // Assert.Equal (Color.BrightGreen, color.Background); + // } else { + // Assert.Equal (Color.Black, color.Background); + // } + // if (c == frame.X - drawMarginFrame && r == frame.Y - drawMarginFrame) { + // Assert.Equal (uLCorner, rune); + // } else if (c == frame.Right && r == frame.Y - drawMarginFrame) { + // Assert.Equal (uRCorner, rune); + // } else if (c == frame.X - drawMarginFrame && r == frame.Bottom) { + // Assert.Equal (lLCorner, rune); + // } else if (c == frame.Right && r == frame.Bottom) { + // Assert.Equal (lRCorner, rune); + // } else if (c >= frame.X && (r == frame.Y - drawMarginFrame + // || r == frame.Bottom)) { + // Assert.Equal (hLine, rune); + // } else if ((c == frame.X - drawMarginFrame || c == frame.Right) + // && r >= frame.Y && r <= frame.Bottom - drawMarginFrame) { + // Assert.Equal (vLine, rune); + // } else { + // text += rune.ToString (); + // } + // } + // } + // Assert.Equal ("This is a test", text.Trim ()); // var color = (Attribute)driver.Contents [r, c, 1]; // Assert.Equal (Color.DarkGray, color.Background); @@ -305,27 +316,12 @@ namespace Terminal.Gui.CoreTests { // var top = Application.Top; // var driver = (FakeDriver)Application.Driver; - // var frameView = new FrameView () { - // X = Pos.Center (), - // Y = Pos.Center (), - // Width = 24, - // Height = 13, - // Border = new Border () { - // BorderStyle = BorderStyle.Single, - // Padding = new Thickness (2), - // BorderThickness = new Thickness (2), - // BorderBrush = Color.Red, - // Background = Color.BrightGreen, - // Effect3D = true, - // Effect3DOffset = new Point (2, -3) - // } - // }; - // frameView.Add (new Label () { - // ColorScheme = Colors.TopLevel, - // Text = "This is a test" - // }); - // //frameView.Border.Child = frameView; - // top.Add (frameView); + // [Fact] + // [AutoInitShutdown] + // public void DrawContent_With_Parent_Border () + // { + // var top = Application.Top; + // var driver = (FakeDriver)Application.Driver; // top.LayoutSubviews (); // frameView.Redraw (frameView.Bounds); @@ -481,13 +477,40 @@ namespace Terminal.Gui.CoreTests { // //} // //Assert.Equal ("This is a test", text.Trim ()); - // // TODO: Re-enable 3deffect - - // //// Check the upper Effect3D - // //for (int r = frame.Y + effect3DOffset.Y; - // // r < frame.Y; r++) { - // // for (int c = frame.X + effect3DOffset.X; - // // c < frame.Right + effect3DOffset.X; c++) { + // var color = (Attribute)driver.Contents [r, c, 1]; + // var rune = (Rune)driver.Contents [r, c, 0]; + // Assert.Equal (Color.Black, color.Background); + // if (c == frame.X + sumThickness.Left && r == frame.Y + sumThickness.Top) { + // Assert.Equal (uLCorner, rune); + // } else if (c == frame.Right - drawMarginFrame - sumThickness.Right + // && r == frame.Y + sumThickness.Top) { + // Assert.Equal (uRCorner, rune); + // } else if (c == frame.X + sumThickness.Left + // && r == frame.Bottom - drawMarginFrame - sumThickness.Bottom) { + // Assert.Equal (lLCorner, rune); + // } else if (c == frame.Right - drawMarginFrame - sumThickness.Right + // && r == frame.Bottom - drawMarginFrame - sumThickness.Bottom) { + // Assert.Equal (lRCorner, rune); + // } else if (c > frame.X + sumThickness.Left + // && (r == frame.Y + sumThickness.Top + // || r == frame.Bottom - drawMarginFrame - sumThickness.Bottom)) { + // Assert.Equal (hLine, rune); + // } else if ((c == frame.X + sumThickness.Left + // || c == frame.Right - drawMarginFrame - sumThickness.Right) + // && r >= frame.Y + drawMarginFrame + sumThickness.Top) { + // Assert.Equal (vLine, rune); + // } else { + // text += rune.ToString (); + // } + // } + // } + // Assert.Equal ("This is a test", text.Trim ()); + + // // Check the upper Effect3D + // for (int r = frame.Y + effect3DOffset.Y; + // r < frame.Y; r++) { + // for (int c = frame.X + effect3DOffset.X; + // c < frame.Right + effect3DOffset.X; c++) { // // var color = (Attribute)driver.Contents [r, c, 1]; // // Assert.Equal (Color.DarkGray, color.Background); @@ -540,8 +563,7 @@ namespace Terminal.Gui.CoreTests { // //} //} - [Fact] - [AutoInitShutdown] + [Fact, AutoInitShutdown] public void BorderOnControlWithNoChildren () { var label = new TextField ("Loading...") { @@ -557,5 +579,57 @@ namespace Terminal.Gui.CoreTests { Assert.Null (Record.Exception (() => label.Redraw (label.Bounds))); } + + [Fact, AutoInitShutdown] + public void BorderStyle_And_DrawMarginFrame_Gets_Sets () + { + var lblTop = new Label ("At 0,0"); + var lblFrame = new Label ("Centered") { X = Pos.Center (), Y = Pos.Center () }; + var frame = new FrameView () { Y = 1, Width = 20, Height = 3 }; + var lblFill = new Label () { Width = Dim.Fill(),Height = Dim.Fill(), Visible = false }; + var fillText = new System.Text.StringBuilder (); + for (int i = 0; i < frame.Bounds.Height; i++) { + if (i > 0) { + fillText.AppendLine (""); + } + for (int j = 0; j < frame.Bounds.Width; j++) { + fillText.Append ("█"); + } + } + lblFill.Text = fillText.ToString (); + frame.Add (lblFill, lblFrame); + var lblBottom = new Label ("At 0,4") { Y = 4 }; + Application.Top.Add (lblTop, frame, lblBottom); + Application.Begin (Application.Top); + + Assert.Equal (BorderStyle.Single, frame.Border.BorderStyle); + Assert.True (frame.Border.DrawMarginFrame); + TestHelpers.AssertDriverContentsWithFrameAre (@" +At 0,0 +┌──────────────────┐ +│ Centered │ +└──────────────────┘ +At 0,4 ", output); + + frame.Border.BorderStyle = BorderStyle.None; + Application.Refresh (); + Assert.True (frame.Border.DrawMarginFrame); + TestHelpers.AssertDriverContentsWithFrameAre (@" +At 0,0 + + Centered + +At 0,4 ", output); + + frame.Border.DrawMarginFrame = false; + lblFill.Visible = true; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +At 0,0 +████████████████████ +██████Centered██████ +████████████████████ +At 0,4 ", output); + } } } diff --git a/UnitTests/Core/EscSeqReqTests.cs b/UnitTests/Core/EscSeqReqTests.cs new file mode 100644 index 000000000..33f955d49 --- /dev/null +++ b/UnitTests/Core/EscSeqReqTests.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Terminal.Gui.CoreTests { + public class EscSeqReqTests { + [Fact] + public void Constructor_Defaults () + { + var escSeqReq = new EscSeqReqProc (); + Assert.NotNull (escSeqReq.EscSeqReqStats); + Assert.Empty (escSeqReq.EscSeqReqStats); + } + + [Fact] + public void Add_Tests () + { + var escSeqReq = new EscSeqReqProc (); + escSeqReq.Add ("t"); + Assert.Single (escSeqReq.EscSeqReqStats); + Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + + escSeqReq.Add ("t", 2); + Assert.Single (escSeqReq.EscSeqReqStats); + Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + + escSeqReq = new EscSeqReqProc (); + escSeqReq.Add ("t", 2); + Assert.Single (escSeqReq.EscSeqReqStats); + Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + + escSeqReq.Add ("t", 3); + Assert.Single (escSeqReq.EscSeqReqStats); + Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + } + + [Fact] + public void Remove_Tests () + { + var escSeqReq = new EscSeqReqProc (); + escSeqReq.Add ("t"); + escSeqReq.Remove ("t"); + Assert.Empty (escSeqReq.EscSeqReqStats); + + escSeqReq.Add ("t", 2); + escSeqReq.Remove ("t"); + Assert.Single (escSeqReq.EscSeqReqStats); + Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + + escSeqReq.Remove ("t"); + Assert.Empty (escSeqReq.EscSeqReqStats); + } + + [Fact] + public void Requested_Tests () + { + var escSeqReq = new EscSeqReqProc (); + Assert.False (escSeqReq.Requested ("t")); + + escSeqReq.Add ("t"); + Assert.False (escSeqReq.Requested ("r")); + Assert.True (escSeqReq.Requested ("t")); + } + } +} diff --git a/UnitTests/Core/EscSeqUtilsTests.cs b/UnitTests/Core/EscSeqUtilsTests.cs new file mode 100644 index 000000000..6db843ffe --- /dev/null +++ b/UnitTests/Core/EscSeqUtilsTests.cs @@ -0,0 +1,870 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Xunit; + +namespace Terminal.Gui.CoreTests { + public class EscSeqUtilsTests { + [Fact] + public void Defaults_Values () + { + Assert.Equal ('\x1b', EscSeqUtils.KeyEsc); + Assert.Equal ("\x1b[", EscSeqUtils.KeyCSI); + Assert.Equal ("\x1b[?1003h", EscSeqUtils.CSI_EnableAnyEventMouse); + Assert.Equal ("\x1b[?1006h", EscSeqUtils.CSI_EnableSgrExtModeMouse); + Assert.Equal ("\x1b[?1015h", EscSeqUtils.CSI_EnableUrxvtExtModeMouse); + Assert.Equal ("\x1b[?1003l", EscSeqUtils.CSI_DisableAnyEventMouse); + Assert.Equal ("\x1b[?1006l", EscSeqUtils.CSI_DisableSgrExtModeMouse); + Assert.Equal ("\x1b[?1015l", EscSeqUtils.CSI_DisableUrxvtExtModeMouse); + Assert.Equal ("\x1b[?1003h\x1b[?1015h\u001b[?1006h", EscSeqUtils.EnableMouseEvents); + Assert.Equal ("\x1b[?1003l\x1b[?1015l\u001b[?1006l", EscSeqUtils.DisableMouseEvents); + } + + [Fact] + public void GetConsoleInputKey_ConsoleKeyInfo () + { + var cki = new ConsoleKeyInfo ('r', 0, false, false, false); + var expectedCki = new ConsoleKeyInfo ('r', 0, false, false, false); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('r', 0, true, false, false); + expectedCki = new ConsoleKeyInfo ('r', 0, true, false, false); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('r', 0, false, true, false); + expectedCki = new ConsoleKeyInfo ('r', 0, false, true, false); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('r', 0, false, false, true); + expectedCki = new ConsoleKeyInfo ('r', 0, false, false, true); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('r', 0, true, true, false); + expectedCki = new ConsoleKeyInfo ('r', 0, true, true, false); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('r', 0, false, true, true); + expectedCki = new ConsoleKeyInfo ('r', 0, false, true, true); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('r', 0, true, true, true); + expectedCki = new ConsoleKeyInfo ('r', 0, true, true, true); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('\u0012', 0, false, false, false); + expectedCki = new ConsoleKeyInfo ('R', ConsoleKey.R, false, false, true); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('\0', (ConsoleKey)64, false, false, true); + expectedCki = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, false, false, true); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('\r', 0, false, false, false); + expectedCki = new ConsoleKeyInfo ('\r', ConsoleKey.Enter, false, false, false); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('\u007f', 0, false, false, false); + expectedCki = new ConsoleKeyInfo ('\u007f', ConsoleKey.Backspace, false, false, false); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + + cki = new ConsoleKeyInfo ('R', 0, false, false, false); + expectedCki = new ConsoleKeyInfo ('R', 0, false, false, false); + Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + } + + [Fact] + public void ResizeArray_ConsoleKeyInfo () + { + ConsoleKeyInfo [] expectedCkInfos = null; + var cki = new ConsoleKeyInfo ('\u001b', ConsoleKey.Escape, false, false, false); + expectedCkInfos = EscSeqUtils.ResizeArray (cki, expectedCkInfos); + Assert.Single (expectedCkInfos); + Assert.Equal (cki, expectedCkInfos [0]); + } + + private EscSeqReqProc escSeqReqProc; + private ConsoleKeyInfo newConsoleKeyInfo; + private ConsoleKey key; + private ConsoleKeyInfo [] cki; + private ConsoleModifiers mod; + private string c1Control, code, terminating; + private string [] values; + private bool isKeyMouse; + private bool isReq; + private List mouseFlags; + Point pos; + private MouseFlags arg1; + private Point arg2; + private bool actionStarted; + + [Fact, AutoInitShutdown] + public void DecodeEscSeq_Tests () + { + // ESC + cki = new ConsoleKeyInfo [] { new ConsoleKeyInfo ('\u001b', 0, false, false, false) }; + var expectedCki = new ConsoleKeyInfo ('\u001b', ConsoleKey.Escape, false, false, false); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.Escape, key); + Assert.Equal (0, (int)mod); + Assert.Equal ("ESC", c1Control); + Assert.Null (code); + Assert.Null (values); + Assert.Null (terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('\u0012', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\u0012', ConsoleKey.R, false, true, true); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.R, key); + Assert.Equal (0, (int)mod); + Assert.Equal ("ESC", c1Control); + Assert.Null (code); + Assert.Null (values); + Assert.Equal ("\u0012", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('r', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('R', ConsoleKey.R, false, true, false); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.R, key); + Assert.Equal (0, (int)mod); + Assert.Equal ("ESC", c1Control); + Assert.Null (code); + Assert.Null (values); + Assert.Equal ("r", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + // SS3 + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('O', 0, false, false, false), + new ConsoleKeyInfo ('R', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, false, false); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.F3, key); + Assert.Equal (0, (int)mod); + Assert.Equal ("SS3", c1Control); + Assert.Null (code); + Assert.Single (values); + Assert.Null (values [0]); + Assert.Equal ("R", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + // CSI + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('1', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo ('R', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, true, false, false); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.F3, key); + Assert.Equal (ConsoleModifiers.Shift, mod); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (2, values.Length); + Assert.Equal ("1", values [0]); + Assert.Equal ("2", values [^1]); + Assert.Equal ("R", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('1', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('R', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, true, false); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.F3, key); + Assert.Equal (ConsoleModifiers.Alt, mod); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (2, values.Length); + Assert.Equal ("1", values [0]); + Assert.Equal ("3", values [^1]); + Assert.Equal ("R", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('1', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('4', 0, false, false, false), + new ConsoleKeyInfo ('R', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, true, true, false); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.F3, key); + Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Alt, mod); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (2, values.Length); + Assert.Equal ("1", values [0]); + Assert.Equal ("4", values [^1]); + Assert.Equal ("R", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('1', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('5', 0, false, false, false), + new ConsoleKeyInfo ('R', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, false, true); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.F3, key); + Assert.Equal (ConsoleModifiers.Control, mod); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (2, values.Length); + Assert.Equal ("1", values [0]); + Assert.Equal ("5", values [^1]); + Assert.Equal ("R", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('1', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('6', 0, false, false, false), + new ConsoleKeyInfo ('R', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, true, false, true); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.F3, key); + Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Control, mod); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (2, values.Length); + Assert.Equal ("1", values [0]); + Assert.Equal ("6", values [^1]); + Assert.Equal ("R", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('1', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('7', 0, false, false, false), + new ConsoleKeyInfo ('R', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, true, true); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.F3, key); + Assert.Equal (ConsoleModifiers.Alt | ConsoleModifiers.Control, mod); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (2, values.Length); + Assert.Equal ("1", values [0]); + Assert.Equal ("7", values [^1]); + Assert.Equal ("R", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('1', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('8', 0, false, false, false), + new ConsoleKeyInfo ('R', 0, false, false, false) + }; + expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, true, true, true); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (ConsoleKey.F3, key); + Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control, mod); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (2, values.Length); + Assert.Equal ("1", values [0]); + Assert.Equal ("8", values [^1]); + Assert.Equal ("R", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('M', 0, false, false, false) + }; + expectedCki = default; + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (0, (int)key); + Assert.Equal (0, (int)mod); + Assert.Equal ("CSI", c1Control); + Assert.Equal ("<", code); + Assert.Equal (3, values.Length); + Assert.Equal ("0", values [0]); + Assert.Equal ("2", values [1]); + Assert.Equal ("3", values [^1]); + Assert.Equal ("M", terminating); + Assert.True (isKeyMouse); + Assert.Equal (new List () { MouseFlags.Button1Pressed }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('m', 0, false, false, false) + }; + expectedCki = default; + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (0, (int)key); + Assert.Equal (0, (int)mod); + Assert.Equal ("CSI", c1Control); + Assert.Equal ("<", code); + Assert.Equal (3, values.Length); + Assert.Equal ("0", values [0]); + Assert.Equal ("2", values [1]); + Assert.Equal ("3", values [^1]); + Assert.Equal ("m", terminating); + Assert.True (isKeyMouse); + Assert.Equal (2, mouseFlags.Count); + Assert.Equal (new List () { MouseFlags.Button1Released, MouseFlags.Button1Clicked }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('M', 0, false, false, false) + }; + expectedCki = default; + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (0, (int)key); + Assert.Equal (0, (int)mod); + Assert.Equal ("CSI", c1Control); + Assert.Equal ("<", code); + Assert.Equal (3, values.Length); + Assert.Equal ("0", values [0]); + Assert.Equal ("2", values [1]); + Assert.Equal ("3", values [^1]); + Assert.Equal ("M", terminating); + Assert.True (isKeyMouse); + Assert.Equal (new List () { MouseFlags.Button1DoubleClicked }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + Assert.False (isReq); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('M', 0, false, false, false) + }; + expectedCki = default; + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (0, (int)key); + Assert.Equal (0, (int)mod); + Assert.Equal ("CSI", c1Control); + Assert.Equal ("<", code); + Assert.Equal (3, values.Length); + Assert.Equal ("0", values [0]); + Assert.Equal ("2", values [1]); + Assert.Equal ("3", values [^1]); + Assert.Equal ("M", terminating); + Assert.True (isKeyMouse); + Assert.Equal (new List () { MouseFlags.Button1TripleClicked }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + Assert.False (isReq); + + var view = new View () { + Width = Dim.Fill (), + Height = Dim.Fill (), + WantContinuousButtonPressed = true + }; + Application.Top.Add (view); + Application.Begin (Application.Top); + + ReflectionTools.InvokePrivate ( + typeof (Application), + "ProcessMouseEvent", + new MouseEvent () { + X = 0, + Y = 0, + Flags = 0 + }); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('M', 0, false, false, false) + }; + expectedCki = default; + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (0, (int)key); + Assert.Equal (0, (int)mod); + Assert.Equal ("CSI", c1Control); + Assert.Equal ("<", code); + Assert.Equal (3, values.Length); + Assert.Equal ("0", values [0]); + Assert.Equal ("2", values [1]); + Assert.Equal ("3", values [^1]); + Assert.Equal ("M", terminating); + Assert.True (isKeyMouse); + Assert.Equal (new List () { MouseFlags.Button1Pressed }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + Assert.False (isReq); + + Application.Iteration += () => { + if (actionStarted) { + // set Application.WantContinuousButtonPressedView to null + view.WantContinuousButtonPressed = false; + ReflectionTools.InvokePrivate ( + typeof (Application), + "ProcessMouseEvent", + new MouseEvent () { + X = 0, + Y = 0, + Flags = 0 + }); + + Application.RequestStop (); + } + }; + + Application.Run (); + + Assert.Null (Application.WantContinuousButtonPressedView); + + Assert.Equal (MouseFlags.Button1Pressed, arg1); + Assert.Equal (new Point (1, 2), arg2); + + ClearAll (); + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('m', 0, false, false, false) + }; + expectedCki = default; + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Null (escSeqReqProc); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (0, (int)key); + Assert.Equal (0, (int)mod); + Assert.Equal ("CSI", c1Control); + Assert.Equal ("<", code); + Assert.Equal (3, values.Length); + Assert.Equal ("0", values [0]); + Assert.Equal ("2", values [1]); + Assert.Equal ("3", values [^1]); + Assert.Equal ("m", terminating); + Assert.True (isKeyMouse); + Assert.Equal (new List () { MouseFlags.Button1Released }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + Assert.False (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + + ClearAll (); + + Assert.Null (escSeqReqProc); + escSeqReqProc = new EscSeqReqProc (); + escSeqReqProc.Add ("t"); + + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('8', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('1', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo ('t', 0, false, false, false) + }; + expectedCki = default; + Assert.Single (escSeqReqProc.EscSeqReqStats); + Assert.Equal ("t", escSeqReqProc.EscSeqReqStats [^1].Terminating); + EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + Assert.Empty (escSeqReqProc.EscSeqReqStats); + Assert.Equal (expectedCki, newConsoleKeyInfo); + Assert.Equal (0, (int)key); + Assert.Equal (0, (int)mod); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (3, values.Length); + Assert.Equal ("8", values [0]); + Assert.Equal ("10", values [1]); + Assert.Equal ("20", values [^1]); + Assert.Equal ("t", terminating); + Assert.False (isKeyMouse); + Assert.Equal (new List () { 0 }, mouseFlags); + Assert.Equal (Point.Empty, pos); + Assert.True (isReq); + Assert.Equal (0, (int)arg1); + Assert.Equal (Point.Empty, arg2); + } + + private void ClearAll () + { + escSeqReqProc = default; + newConsoleKeyInfo = default; + key = default; + cki = default; + mod = default; + c1Control = default; + code = default; + terminating = default; + values = default; + isKeyMouse = default; + isReq = default; + mouseFlags = default; + pos = default; + arg1 = default; + arg2 = default; + } + + private void ProcessContinuousButtonPressed (MouseFlags arg1, Point arg2) + { + this.arg1 = arg1; + this.arg2 = arg2; + actionStarted = true; + } + + [Fact] + public void GetEscapeResult_Tests () + { + char [] kChars = new char [] { '\u001b', '[', '5', ';', '1', '0', 'r' }; + (c1Control, code, values, terminating) = EscSeqUtils.GetEscapeResult (kChars); + Assert.Equal ("CSI", c1Control); + Assert.Null (code); + Assert.Equal (2, values.Length); + Assert.Equal ("5", values [0]); + Assert.Equal ("10", values [^1]); + Assert.Equal ("r", terminating); + } + + [Fact] + public void GetC1ControlChar_Tests () + { + Assert.Equal ("IND", EscSeqUtils.GetC1ControlChar ('D')); + Assert.Equal ("NEL", EscSeqUtils.GetC1ControlChar ('E')); + Assert.Equal ("HTS", EscSeqUtils.GetC1ControlChar ('H')); + Assert.Equal ("RI", EscSeqUtils.GetC1ControlChar ('M')); + Assert.Equal ("SS2", EscSeqUtils.GetC1ControlChar ('N')); + Assert.Equal ("SS3", EscSeqUtils.GetC1ControlChar ('O')); + Assert.Equal ("DCS", EscSeqUtils.GetC1ControlChar ('P')); + Assert.Equal ("SPA", EscSeqUtils.GetC1ControlChar ('V')); + Assert.Equal ("EPA", EscSeqUtils.GetC1ControlChar ('W')); + Assert.Equal ("SOS", EscSeqUtils.GetC1ControlChar ('X')); + Assert.Equal ("DECID", EscSeqUtils.GetC1ControlChar ('Z')); + Assert.Equal ("CSI", EscSeqUtils.GetC1ControlChar ('[')); + Assert.Equal ("ST", EscSeqUtils.GetC1ControlChar ('\\')); + Assert.Equal ("OSC", EscSeqUtils.GetC1ControlChar (']')); + Assert.Equal ("PM", EscSeqUtils.GetC1ControlChar ('^')); + Assert.Equal ("APC", EscSeqUtils.GetC1ControlChar ('_')); + Assert.Equal ("", EscSeqUtils.GetC1ControlChar ('\0')); + } + + [Fact] + public void GetConsoleModifiers_Tests () + { + Assert.Equal (ConsoleModifiers.Shift, EscSeqUtils.GetConsoleModifiers ("2")); + Assert.Equal (ConsoleModifiers.Alt, EscSeqUtils.GetConsoleModifiers ("3")); + Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Alt, EscSeqUtils.GetConsoleModifiers ("4")); + Assert.Equal (ConsoleModifiers.Control, EscSeqUtils.GetConsoleModifiers ("5")); + Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Control, EscSeqUtils.GetConsoleModifiers ("6")); + Assert.Equal (ConsoleModifiers.Alt | ConsoleModifiers.Control, EscSeqUtils.GetConsoleModifiers ("7")); + Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control, EscSeqUtils.GetConsoleModifiers ("8")); + Assert.Equal (0, (int)EscSeqUtils.GetConsoleModifiers ("")); + } + + [Fact] + public void GetConsoleKey_Tests () + { + ConsoleModifiers mod = 0; + Assert.Equal (ConsoleKey.UpArrow, EscSeqUtils.GetConsoleKey ('A', "", ref mod)); + Assert.Equal (ConsoleKey.DownArrow, EscSeqUtils.GetConsoleKey ('B', "", ref mod)); + Assert.Equal (key = ConsoleKey.RightArrow, EscSeqUtils.GetConsoleKey ('C', "", ref mod)); + Assert.Equal (ConsoleKey.LeftArrow, EscSeqUtils.GetConsoleKey ('D', "", ref mod)); + Assert.Equal (ConsoleKey.End, EscSeqUtils.GetConsoleKey ('F', "", ref mod)); + Assert.Equal (ConsoleKey.Home, EscSeqUtils.GetConsoleKey ('H', "", ref mod)); + Assert.Equal (ConsoleKey.F1, EscSeqUtils.GetConsoleKey ('P', "", ref mod)); + Assert.Equal (ConsoleKey.F2, EscSeqUtils.GetConsoleKey ('Q', "", ref mod)); + Assert.Equal (ConsoleKey.F3, EscSeqUtils.GetConsoleKey ('R', "", ref mod)); + Assert.Equal (ConsoleKey.F4, EscSeqUtils.GetConsoleKey ('S', "", ref mod)); + Assert.Equal (ConsoleKey.Tab, EscSeqUtils.GetConsoleKey ('Z', "", ref mod)); + Assert.Equal (ConsoleModifiers.Shift, mod); + Assert.Equal (0, (int)EscSeqUtils.GetConsoleKey ('\0', "", ref mod)); + Assert.Equal (ConsoleKey.Insert, EscSeqUtils.GetConsoleKey ('~', "2", ref mod)); + Assert.Equal (ConsoleKey.Delete, EscSeqUtils.GetConsoleKey ('~', "3", ref mod)); + Assert.Equal (ConsoleKey.PageUp, EscSeqUtils.GetConsoleKey ('~', "5", ref mod)); + Assert.Equal (ConsoleKey.PageDown, EscSeqUtils.GetConsoleKey ('~', "6", ref mod)); + Assert.Equal (ConsoleKey.F5, EscSeqUtils.GetConsoleKey ('~', "15", ref mod)); + Assert.Equal (ConsoleKey.F6, EscSeqUtils.GetConsoleKey ('~', "17", ref mod)); + Assert.Equal (ConsoleKey.F7, EscSeqUtils.GetConsoleKey ('~', "18", ref mod)); + Assert.Equal (ConsoleKey.F8, EscSeqUtils.GetConsoleKey ('~', "19", ref mod)); + Assert.Equal (ConsoleKey.F9, EscSeqUtils.GetConsoleKey ('~', "20", ref mod)); + Assert.Equal (ConsoleKey.F10, EscSeqUtils.GetConsoleKey ('~', "21", ref mod)); + Assert.Equal (ConsoleKey.F11, EscSeqUtils.GetConsoleKey ('~', "23", ref mod)); + Assert.Equal (ConsoleKey.F12, EscSeqUtils.GetConsoleKey ('~', "24", ref mod)); + Assert.Equal (0, (int)EscSeqUtils.GetConsoleKey ('~', "", ref mod)); + } + + [Fact] + public void GetKeyCharArray_Tests () + { + var cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo('\u001b', 0, false, false, false), + new ConsoleKeyInfo('[', 0, false, false, false), + new ConsoleKeyInfo('5', 0, false, false, false), + new ConsoleKeyInfo(';', 0, false, false, false), + new ConsoleKeyInfo('1', 0, false, false, false), + new ConsoleKeyInfo('0', 0, false, false, false), + new ConsoleKeyInfo('r', 0, false, false, false), + }; + + Assert.Equal (new char [] { '\u001b', '[', '5', ';', '1', '0', 'r' }, EscSeqUtils.GetKeyCharArray (cki)); + } + + [Fact, AutoInitShutdown] + public void GetMouse_Tests () + { + var cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('M', 0, false, false, false) + }; + EscSeqUtils.GetMouse (cki, out List mouseFlags, out Point pos, ProcessContinuousButtonPressed); + Assert.Equal (new List () { MouseFlags.Button1Pressed }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('m', 0, false, false, false) + }; + EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed); + Assert.Equal (2, mouseFlags.Count); + Assert.Equal (new List () { MouseFlags.Button1Released, MouseFlags.Button1Clicked }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('M', 0, false, false, false) + }; + EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed); + Assert.Equal (new List () { MouseFlags.Button1DoubleClicked }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('M', 0, false, false, false) + }; + EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed); + Assert.Equal (new List () { MouseFlags.Button1TripleClicked }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + + cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ('\u001b', 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false), + new ConsoleKeyInfo ('0', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('2', 0, false, false, false), + new ConsoleKeyInfo (';', 0, false, false, false), + new ConsoleKeyInfo ('3', 0, false, false, false), + new ConsoleKeyInfo ('m', 0, false, false, false) + }; + EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed); + Assert.Equal (new List () { MouseFlags.Button1Released }, mouseFlags); + Assert.Equal (new Point (1, 2), pos); + } + + [Fact] + public void GetParentProcess_Tests () + { + if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { + Assert.NotNull (EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ())); + } else { + Assert.Null (EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ())); + } + } + } +} diff --git a/UnitTests/Core/LayoutTests.cs b/UnitTests/Core/LayoutTests.cs index cf6cffe6c..68ea9577f 100644 --- a/UnitTests/Core/LayoutTests.cs +++ b/UnitTests/Core/LayoutTests.cs @@ -328,12 +328,15 @@ namespace Terminal.Gui.CoreTests { Application.Begin (Application.Top); Assert.True (label.AutoSize); + // Here the AutoSize ensuring the right size with width 28 (Dim.Fill) + // and height 0 because wasn't set and the text is empty Assert.Equal ("{X=0,Y=0,Width=28,Height=0}", label.Bounds.ToString ()); label.Text = "First line\nSecond line"; Application.Refresh (); - // Here the AutoSize ensuring the right size + // Here the AutoSize ensuring the right size with width 28 (Dim.Fill) + // and height 2 because wasn't set and the text has 2 lines Assert.True (label.AutoSize); Assert.Equal ("{X=0,Y=0,Width=28,Height=2}", label.Bounds.ToString ()); @@ -347,12 +350,16 @@ namespace Terminal.Gui.CoreTests { label.Text = "First changed line\nSecond changed line\nNew line"; Application.Refresh (); + // Here the AutoSize is false and the width 28 (Dim.Fill) and + // height 1 because wasn't set and SetMinWidthHeight ensuring the minimum height Assert.False (label.AutoSize); Assert.Equal ("{X=0,Y=0,Width=28,Height=1}", label.Bounds.ToString ()); label.AutoSize = true; Application.Refresh (); + // Here the AutoSize ensuring the right size with width 28 (Dim.Fill) + // and height 3 because wasn't set and the text has 3 lines Assert.True (label.AutoSize); Assert.Equal ("{X=0,Y=0,Width=28,Height=3}", label.Bounds.ToString ()); } @@ -461,9 +468,14 @@ Y Assert.Equal ("123 ", GetContents ()); lbl.Text = "12"; - - lbl.SuperView.Redraw (lbl.SuperView.NeedDisplay); - + // Here the AutoSize ensuring the right size with width 3 (Dim.Absolute) + // that was set on the OnAdded method with the text length of 3 + // and height 1 because wasn't set and the text has 1 line + Assert.Equal (new Rect (0, 0, 3, 1), lbl.Frame); + Assert.Equal (new Rect (0, 0, 3, 1), lbl.NeedDisplay); + Assert.Equal (new Rect (0, 0, 0, 0), lbl.SuperView.NeedDisplay); + Assert.True (lbl.SuperView.LayoutNeeded); + lbl.SuperView.Redraw (lbl.SuperView.Bounds); Assert.Equal ("12 ", GetContents ()); string GetContents () diff --git a/UnitTests/Core/ViewTests.cs b/UnitTests/Core/ViewTests.cs index 13fd93e88..db4d54299 100644 --- a/UnitTests/Core/ViewTests.cs +++ b/UnitTests/Core/ViewTests.cs @@ -2353,21 +2353,7 @@ This is a tes return true; } - public void CorrectRedraw (Rect bounds) - { - // Clear the old and new frame area - Clear (NeedDisplay); - DrawText (); - } - - public void IncorrectRedraw (Rect bounds) - { - // Clear only the new frame area - Clear (); - DrawText (); - } - - private void DrawText () + public override void Redraw (Rect bounds) { var idx = 0; for (int r = 0; r < Frame.Height; r++) { @@ -2501,7 +2487,7 @@ This is a tes [InlineData (false)] public void Clear_Does_Not_Spillover_Its_Parent (bool label) { - var root = new View () { Width = 20, Height = 10 }; + var root = new View () { Width = 20, Height = 10, ColorScheme = Colors.Base }; var v = label == true ? new Label (new string ('c', 100)) { @@ -2533,15 +2519,17 @@ cccccccccccccccccccc", output); var attributes = new Attribute [] { Colors.TopLevel.Normal, - Colors.TopLevel.Focus, - + Colors.Base.Normal, + Colors.Base.Focus }; if (label) { TestHelpers.AssertDriverColorsAre (@" -000000000000000000000", attributes); +111111111111111111110 +111111111111111111110", attributes); } else { TestHelpers.AssertDriverColorsAre (@" -111111111111111111110", attributes); +222222222222222222220 +222222222222222222220", attributes); } if (label) { @@ -2552,7 +2540,8 @@ cccccccccccccccccccc", output); Assert.True (v.HasFocus); Application.Refresh (); TestHelpers.AssertDriverColorsAre (@" -111111111111111111110", attributes); +222222222222222222220 +222222222222222222220", attributes); } } @@ -2571,7 +2560,7 @@ cccccccccccccccccccc", output); top.Add (label, view); Application.Begin (top); - view.CorrectRedraw (view.Bounds); + top.Redraw (top.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2579,9 +2568,12 @@ At 0,0 and also with two lines. ", output); view.Frame = new Rect (1, 1, 10, 1); + Assert.Equal (new Rect (1, 1, 10, 1), view.Frame); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); + view.LayoutStyle = LayoutStyle.Absolute; Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (1, 1, 31, 3), view.NeedDisplay); - view.CorrectRedraw (view.Bounds); + Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay); + top.Redraw (top.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 A text wit", output); @@ -2602,7 +2594,7 @@ At 0,0 top.Add (label, view); Application.Begin (top); - view.CorrectRedraw (view.Bounds); + top.Redraw (top.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2615,8 +2607,8 @@ At 0,0 view.Height = 1; Assert.Equal (new Rect (1, 1, 10, 1), view.Frame); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (1, 1, 31, 3), view.NeedDisplay); - view.CorrectRedraw (view.Bounds); + Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay); + top.Redraw (top.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 A text wit", output); @@ -2637,7 +2629,7 @@ At 0,0 top.Add (label, view); Application.Begin (top); - view.IncorrectRedraw (view.Bounds); + view.Redraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2645,9 +2637,12 @@ At 0,0 and also with two lines. ", output); view.Frame = new Rect (1, 1, 10, 1); + Assert.Equal (new Rect (1, 1, 10, 1), view.Frame); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); + view.LayoutStyle = LayoutStyle.Absolute; Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (1, 1, 31, 3), view.NeedDisplay); - view.IncorrectRedraw (view.Bounds); + Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay); + view.Redraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 A text wit @@ -2670,7 +2665,7 @@ At 0,0 top.Add (label, view); Application.Begin (top); - view.IncorrectRedraw (view.Bounds); + view.Redraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2683,8 +2678,8 @@ At 0,0 view.Height = 1; Assert.Equal (new Rect (1, 1, 10, 1), view.Frame); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (1, 1, 31, 3), view.NeedDisplay); - view.IncorrectRedraw (view.Bounds); + Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay); + view.Redraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 A text wit @@ -2707,7 +2702,6 @@ At 0,0 top.Add (label, view); Application.Begin (top); - view.CorrectRedraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2715,9 +2709,12 @@ At 0,0 and also with two lines. ", output); view.Frame = new Rect (3, 3, 10, 1); + Assert.Equal (new Rect (3, 3, 10, 1), view.Frame); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); + view.LayoutStyle = LayoutStyle.Absolute; Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (2, 2, 30, 2), view.NeedDisplay); - view.CorrectRedraw (view.Bounds); + Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay); + top.Redraw (top.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2740,7 +2737,7 @@ At 0,0 top.Add (label, view); Application.Begin (top); - view.CorrectRedraw (view.Bounds); + top.Redraw (top.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2753,8 +2750,8 @@ At 0,0 view.Height = 1; Assert.Equal (new Rect (3, 3, 10, 1), view.Frame); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (2, 2, 30, 2), view.NeedDisplay); - view.CorrectRedraw (view.Bounds); + Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay); + top.Redraw (top.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2777,7 +2774,7 @@ At 0,0 top.Add (label, view); Application.Begin (top); - view.IncorrectRedraw (view.Bounds); + view.Redraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2786,8 +2783,8 @@ At 0,0 view.Frame = new Rect (3, 3, 10, 1); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (2, 2, 30, 2), view.NeedDisplay); - view.IncorrectRedraw (view.Bounds); + Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay); + view.Redraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2810,7 +2807,7 @@ At 0,0 top.Add (label, view); Application.Begin (top); - view.IncorrectRedraw (view.Bounds); + view.Redraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2823,8 +2820,8 @@ At 0,0 view.Height = 1; Assert.Equal (new Rect (3, 3, 10, 1), view.Frame); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (2, 2, 30, 2), view.NeedDisplay); - view.IncorrectRedraw (view.Bounds); + Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay); + view.Redraw (view.Bounds); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -2857,5 +2854,104 @@ At 0,0 222"; TestHelpers.AssertDriverContentsAre (looksLike, output); } + + [Fact] + [AutoInitShutdown] + public void Frame_Set_After_Initialze_Update_NeededDisplay () + { + var frame = new FrameView (); + + var label = new Label ("This should be the first line.") { + TextAlignment = Terminal.Gui.TextAlignment.Centered, + ColorScheme = Colors.Menu, + Width = Dim.Fill (), + X = Pos.Center (), + Y = Pos.Center () - 2 // center minus 2 minus two lines top and bottom borders equal to zero (4-2-2=0) + }; + + var button = new Button ("Press me!") { + X = Pos.Center (), + Y = Pos.Center () + }; + + frame.Add (label, button); + + frame.X = Pos.Center (); + frame.Y = Pos.Center (); + frame.Width = 40; + frame.Height = 8; + + var top = Application.Top; + + top.Add (frame); + + Assert.Equal (new Rect (0, 0, 80, 25), top.Frame); + Assert.Equal (new Rect (0, 0, 40, 8), frame.Frame); + Assert.Equal (new Rect (1, 1, 0, 0), frame.Subviews [0].Frame); + Assert.Equal ("ContentView()({X=1,Y=1,Width=0,Height=0})", frame.Subviews [0].ToString ()); + Assert.Equal (new Rect (0, 0, 40, 8), new Rect ( + frame.Frame.Left, frame.Frame.Top, + frame.Frame.Right, frame.Frame.Bottom)); + Assert.Equal (new Rect (0, 0, 30, 1), label.Frame); + Assert.Equal (new Rect (0, 0, 13, 1), button.Frame); + + Assert.Equal (new Rect (0, 0, 80, 25), top.NeedDisplay); + Assert.Equal (new Rect (0, 0, 40, 8), frame.NeedDisplay); + Assert.Equal (Rect.Empty, frame.Subviews [0].NeedDisplay); + Assert.Equal (new Rect (0, 0, 40, 8), new Rect ( + frame.NeedDisplay.Left, frame.NeedDisplay.Top, + frame.NeedDisplay.Right, frame.NeedDisplay.Bottom)); + Assert.Equal (new Rect (0, 0, 30, 1), label.NeedDisplay); + Assert.Equal (new Rect (0, 0, 13, 1), button.NeedDisplay); + + top.LayoutComplete += e => { + Assert.Equal (new Rect (0, 0, 80, 25), top.NeedDisplay); + }; + + frame.LayoutComplete += e => { + Assert.Equal (new Rect (0, 0, 40, 8), frame.NeedDisplay); + }; + + frame.Subviews [0].LayoutComplete += e => { + if (top.IsLoaded) { + Assert.Equal (new Rect (0, 0, 38, 6), frame.Subviews [0].NeedDisplay); + } else { + Assert.Equal (new Rect (0, 0, 38, 6), frame.Subviews [0].NeedDisplay); + } + }; + + label.LayoutComplete += e => { + Assert.Equal (new Rect (0, 0, 38, 1), label.NeedDisplay); + }; + + button.LayoutComplete += e => { + Assert.Equal (new Rect (0, 0, 13, 1), button.NeedDisplay); + }; + + Application.Begin (top); + + Assert.True (label.AutoSize); + Assert.Equal (new Rect (0, 0, 80, 25), top.Frame); + Assert.Equal (new Rect (20, 8, 40, 8), frame.Frame); + Assert.Equal (new Rect (1, 1, 38, 6), frame.Subviews [0].Frame); + Assert.Equal ("ContentView()({X=1,Y=1,Width=38,Height=6})", frame.Subviews [0].ToString ()); + Assert.Equal (new Rect (20, 8, 60, 16), new Rect ( + frame.Frame.Left, frame.Frame.Top, + frame.Frame.Right, frame.Frame.Bottom)); + Assert.Equal (new Rect (0, 0, 38, 1), label.Frame); + Assert.Equal (new Rect (12, 2, 13, 1), button.Frame); + var expected = @" + ┌──────────────────────────────────────┐ + │ This should be the first line. │ + │ │ + │ [ Press me! ] │ + │ │ + │ │ + │ │ + └──────────────────────────────────────┘ +"; + + TestHelpers.AssertDriverContentsWithFrameAre (expected, output); + } } } diff --git a/UnitTests/Drivers/ClipboardTests.cs b/UnitTests/Drivers/ClipboardTests.cs index 03e023ea1..90f30bdbd 100644 --- a/UnitTests/Drivers/ClipboardTests.cs +++ b/UnitTests/Drivers/ClipboardTests.cs @@ -102,8 +102,8 @@ namespace Terminal.Gui.DriverTests { [Fact, AutoInitShutdown (useFakeClipboard: false)] public void IsSupported_Get () { - if (Clipboard.IsSupported) Assert.True (Clipboard.IsSupported); -else Assert.False (Clipboard.IsSupported); + if (Clipboard.IsSupported) Assert.True (Clipboard.IsSupported); + else Assert.False (Clipboard.IsSupported); } [Fact, AutoInitShutdown (useFakeClipboard: false)] @@ -129,18 +129,19 @@ else Assert.False (Clipboard.IsSupported); public void TrySetClipboardData_Sets_The_OS_Clipboard () { var clipText = "The TrySetClipboardData_Sets_The_OS_Clipboard unit test pasted this to the OS clipboard."; - if (Clipboard.IsSupported) Assert.True (Clipboard.TrySetClipboardData (clipText)); -else Assert.False (Clipboard.TrySetClipboardData (clipText)); + if (Clipboard.IsSupported) Assert.True (Clipboard.TrySetClipboardData (clipText)); + else Assert.False (Clipboard.TrySetClipboardData (clipText)); Application.Iteration += () => Application.RequestStop (); Application.Run (); - if (Clipboard.IsSupported) Assert.Equal (clipText, Clipboard.Contents); -else Assert.NotEqual (clipText, Clipboard.Contents); + if (Clipboard.IsSupported) Assert.Equal (clipText, Clipboard.Contents); + else Assert.NotEqual (clipText, Clipboard.Contents); } - + // Disabling this test for now because it is not reliable +#if false [Fact, AutoInitShutdown (useFakeClipboard: false)] public void Contents_Copies_From_OS_Clipboard () { @@ -262,12 +263,13 @@ else Assert.NotEqual (clipText, Clipboard.Contents); Application.RequestStop (); }; - + Application.Run (); if (!failed) Assert.Equal (clipText, clipReadText.TrimEnd ()); } +#endif bool Is_WSL_Platform () { @@ -284,5 +286,6 @@ else Assert.NotEqual (clipText, Clipboard.Contents); return false; } } + } } diff --git a/UnitTests/Drivers/ConsoleDriverTests.cs b/UnitTests/Drivers/ConsoleDriverTests.cs index 8289e9737..6444b8f1c 100644 --- a/UnitTests/Drivers/ConsoleDriverTests.cs +++ b/UnitTests/Drivers/ConsoleDriverTests.cs @@ -171,7 +171,7 @@ namespace Terminal.Gui.DriverTests { // MockDriver will still be 120x40 wasTerminalResized = false; - Application.HeightAsBuffer = true; + Application.EnableConsoleScrolling = true; driver.SetWindowSize (40, 20); Assert.Equal (120, Application.Driver.Cols); Assert.Equal (40, Application.Driver.Rows); @@ -186,12 +186,12 @@ namespace Terminal.Gui.DriverTests { [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_False_Left_And_Top_Is_Always_Zero (Type driverType) + public void EnableConsoleScrolling_Is_False_Left_And_Top_Is_Always_Zero (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Assert.False (Application.HeightAsBuffer); + Assert.False (Application.EnableConsoleScrolling); Assert.Equal (0, Console.WindowLeft); Assert.Equal (0, Console.WindowTop); @@ -204,13 +204,13 @@ namespace Terminal.Gui.DriverTests { [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType) + public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Application.HeightAsBuffer = true; - Assert.True (Application.HeightAsBuffer); + Application.EnableConsoleScrolling = true; + Assert.True (Application.EnableConsoleScrolling); driver.SetWindowPosition (81, 25); Assert.Equal (0, Console.WindowLeft); @@ -221,13 +221,13 @@ namespace Terminal.Gui.DriverTests { [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType) + public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Application.HeightAsBuffer = true; - Assert.True (Application.HeightAsBuffer); + Application.EnableConsoleScrolling = true; + Assert.True (Application.EnableConsoleScrolling); driver.SetWindowPosition (81, 25); Assert.Equal (0, Console.WindowLeft); @@ -261,13 +261,13 @@ namespace Terminal.Gui.DriverTests { [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType) + public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Application.HeightAsBuffer = true; - Assert.True (Application.HeightAsBuffer); + Application.EnableConsoleScrolling = true; + Assert.True (Application.EnableConsoleScrolling); driver.SetWindowPosition (80, 26); Assert.Equal (0, Console.WindowLeft); @@ -278,13 +278,13 @@ namespace Terminal.Gui.DriverTests { [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType) + public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Application.HeightAsBuffer = true; - Assert.True (Application.HeightAsBuffer); + Application.EnableConsoleScrolling = true; + Assert.True (Application.EnableConsoleScrolling); driver.SetWindowPosition (80, 26); Assert.Equal (0, Console.WindowLeft); diff --git a/UnitTests/Text/TextFormatterTests.cs b/UnitTests/Text/TextFormatterTests.cs index a791a6ffb..142035b21 100644 --- a/UnitTests/Text/TextFormatterTests.cs +++ b/UnitTests/Text/TextFormatterTests.cs @@ -2162,7 +2162,7 @@ namespace Terminal.Gui.TextTests { var height = 8; var wrappedLines = TextFormatter.WordWrap (text, width, true); var breakLines = ""; - foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}"; + foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}"; var label = new Label (breakLines) { Width = Dim.Fill (), Height = Dim.Fill () }; var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () }; @@ -2200,7 +2200,7 @@ namespace Terminal.Gui.TextTests { var height = 3; var wrappedLines = TextFormatter.WordWrap (text, height, true); var breakLines = ""; - for (int i = 0; i < wrappedLines.Count; i++) breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}"; + for (int i = 0; i < wrappedLines.Count; i++) breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}"; var label = new Label (breakLines) { TextDirection = TextDirection.TopBottom_LeftRight, Width = Dim.Fill (), @@ -2237,7 +2237,7 @@ namespace Terminal.Gui.TextTests { var height = 8; var wrappedLines = TextFormatter.WordWrap (text, width, true); var breakLines = ""; - foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}"; + foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}"; var label = new Label (breakLines) { Width = Dim.Fill (), Height = Dim.Fill () }; var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () }; @@ -2276,7 +2276,7 @@ namespace Terminal.Gui.TextTests { var height = 4; var wrappedLines = TextFormatter.WordWrap (text, width, true); var breakLines = ""; - for (int i = 0; i < wrappedLines.Count; i++) breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}"; + for (int i = 0; i < wrappedLines.Count; i++) breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}"; var label = new Label (breakLines) { TextDirection = TextDirection.TopBottom_LeftRight, Width = Dim.Fill (), @@ -2888,7 +2888,7 @@ namespace Terminal.Gui.TextTests { Assert.Equal ("nd", list1 [10].ToString ()); Assert.Equal ("Line", list1 [11].ToString ()); Assert.Equal ("- 2.", list1 [^1].ToString ()); - foreach (var txt in list1) wrappedText1 += txt; + foreach (var txt in list1) wrappedText1 += txt; Assert.Equal (" Asentencehaswords. This isthesecondLine- 2.", wrappedText1); // With preserveTrailingSpaces = true. @@ -2910,7 +2910,7 @@ namespace Terminal.Gui.TextTests { Assert.Equal ("Line", list2 [13].ToString ()); Assert.Equal (" - ", list2 [14].ToString ()); Assert.Equal ("2. ", list2 [^1].ToString ()); - foreach (var txt in list2) wrappedText2 += txt; + foreach (var txt in list2) wrappedText2 += txt; Assert.Equal (" A sentence has words. This is the second Line - 2. ", wrappedText2); } @@ -4288,5 +4288,183 @@ t ", output); 0 0", new Attribute [] { Colors.Base.Normal }); } + + [Fact, AutoInitShutdown] + public void Draw_Negative_Bounds_Horizontal_Without_New_Lines () + { + var subView = new View () { Id = "subView", Y = 1, Width = 7, Text = "subView" }; + var view = new View () { Id = "view", Width = 20, Height = 2, Text = "01234567890123456789" }; + view.Add (subView); + var content = new View () { Id = "content", Width = 20, Height = 20 }; + content.Add (view); + var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 }; + container.Add (content); + var top = Application.Top; + top.Add (container); + Application.Driver.Clip = container.Frame; + Application.Begin (top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" + 01234 + subVi", output); + + content.X = -1; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + 12345 + ubVie", output); + + content.Y = -1; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + ubVie", output); + + content.Y = -2; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre ("", output); + + content.X = -20; + content.Y = 0; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre ("", output); + } + + [Fact, AutoInitShutdown] + public void Draw_Negative_Bounds_Horizontal_With_New_Lines () + { + var subView = new View () { Id = "subView", X = 1, Width = 1, Height = 7, Text = "s\nu\nb\nV\ni\ne\nw" }; + var view = new View () { Id = "view", Width = 2, Height = 20, Text = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" }; + view.Add (subView); + var content = new View () { Id = "content", Width = 20, Height = 20 }; + content.Add (view); + var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 }; + container.Add (content); + var top = Application.Top; + top.Add (container); + Application.Driver.Clip = container.Frame; + Application.Begin (top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" + 0s + 1u + 2b + 3V + 4i", output); + + content.X = -1; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + s + u + b + V + i", output); + + content.X = -2; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@"", output); + + content.X = 0; + content.Y = -1; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + 1u + 2b + 3V + 4i + 5e", output); + + content.Y = -6; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + 6w + 7 + 8 + 9 + 0 ", output); + + content.Y = -19; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + 9", output); + + content.Y = -20; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre ("", output); + + content.X = -2; + content.Y = 0; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre ("", output); + } + + [Fact, AutoInitShutdown] + public void Draw_Negative_Bounds_Vertical () + { + var subView = new View () { Id = "subView", X = 1, Width = 1, Height = 7, Text = "subView", TextDirection = TextDirection.TopBottom_LeftRight }; + var view = new View () { Id = "view", Width = 2, Height = 20, Text = "01234567890123456789", TextDirection = TextDirection.TopBottom_LeftRight }; + view.Add (subView); + var content = new View () { Id = "content", Width = 20, Height = 20 }; + content.Add (view); + var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 }; + container.Add (content); + var top = Application.Top; + top.Add (container); + Application.Driver.Clip = container.Frame; + Application.Begin (top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" + 0s + 1u + 2b + 3V + 4i", output); + + content.X = -1; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + s + u + b + V + i", output); + + content.X = -2; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@"", output); + + content.X = 0; + content.Y = -1; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + 1u + 2b + 3V + 4i + 5e", output); + + content.Y = -6; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + 6w + 7 + 8 + 9 + 0 ", output); + + content.Y = -19; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + 9", output); + + content.Y = -20; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre ("", output); + + content.X = -2; + content.Y = 0; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre ("", output); + } } } \ No newline at end of file diff --git a/UnitTests/TopLevels/MessageBoxTests.cs b/UnitTests/TopLevels/MessageBoxTests.cs index 3847922ed..d6b7e1cc2 100644 --- a/UnitTests/TopLevels/MessageBoxTests.cs +++ b/UnitTests/TopLevels/MessageBoxTests.cs @@ -29,7 +29,7 @@ namespace Terminal.Gui.TopLevelTests { Application.RequestStop (); } else if (iterations == 1) { - Application.Top.Redraw (Application.Top.Bounds); + Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre (@" ┌ Title ───────────────────────────────────────┐ │ Message │ @@ -71,7 +71,7 @@ namespace Terminal.Gui.TopLevelTests { Application.RequestStop (); } else if (iterations == 1) { - Application.Top.Redraw (Application.Top.Bounds); + Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre (@" ┌ About UI Catalog ──────────────────────────────────────────┐ │ A comprehensive sample library for │ @@ -110,7 +110,7 @@ namespace Terminal.Gui.TopLevelTests { Application.RequestStop (); } else if (iterations == 1) { - Application.Top.Redraw (Application.Top.Bounds); + Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre (@" ┌─────┐ │Messa│ @@ -140,7 +140,7 @@ namespace Terminal.Gui.TopLevelTests { Application.RequestStop (); } else if (iterations == 1) { - Application.Top.Redraw (Application.Top.Bounds); + Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre (@" ┌ Title ──┐ │ Message │ @@ -170,7 +170,7 @@ namespace Terminal.Gui.TopLevelTests { Application.RequestStop (); } else if (iterations == 1) { - Application.Top.Redraw (Application.Top.Bounds); + Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre (@" ┌ mywindow ────────────────────────────────────────────────────────────────────┐ │ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff│ @@ -224,7 +224,7 @@ namespace Terminal.Gui.TopLevelTests { Application.RequestStop (); } else if (iterations == 1) { - Application.Top.Redraw (Application.Top.Bounds); + Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre (@" ┌ mywindow ────────────────────────────────────────────────────────────────────┐ │ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff │ diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index 1be120dea..a6f803af0 100644 --- a/UnitTests/TopLevels/ToplevelTests.cs +++ b/UnitTests/TopLevels/ToplevelTests.cs @@ -149,7 +149,9 @@ namespace Terminal.Gui.TopLevelTests { [AutoInitShutdown] public void Internal_Tests () { + Toplevel.dragPosition = null; // dragPosition is `static` and must be reset for each instance or unit tests will fail? var top = new Toplevel (); + var eventInvoked = ""; top.ChildUnloaded += (e) => eventInvoked = "ChildUnloaded"; @@ -1031,5 +1033,53 @@ namespace Terminal.Gui.TopLevelTests { Application.Driver.GetCursorVisibility (out cursor); Assert.Equal (CursorVisibility.Invisible, cursor); } + + [Fact, AutoInitShutdown] + public void IsLoaded_Application_Begin () + { + var top = Application.Top; + Assert.False (top.IsLoaded); + + Application.Begin (top); + Assert.True (top.IsLoaded); + } + + [Fact, AutoInitShutdown] + public void IsLoaded_With_Sub_Toplevel_Application_Begin_NeedDisplay () + { + var top = Application.Top; + var subTop = new Toplevel (); + var view = new View (new Rect (0, 0, 20, 10)); + subTop.Add (view); + top.Add (subTop); + + Assert.False (top.IsLoaded); + Assert.False (subTop.IsLoaded); + Assert.Equal (new Rect (0, 0, 20, 10), view.Frame); + Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay); + + view.LayoutStarted += view_LayoutStarted; + + void view_LayoutStarted (View.LayoutEventArgs e) + { + Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay); + view.LayoutStarted -= view_LayoutStarted; + } + + Application.Begin (top); + + Assert.True (top.IsLoaded); + Assert.True (subTop.IsLoaded); + Assert.Equal (new Rect (0, 0, 20, 10), view.Frame); + + view.Frame = new Rect (1, 3, 10, 5); + Assert.Equal (new Rect (1, 3, 10, 5), view.Frame); + Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay); + + view.Redraw (view.Bounds); + view.Frame = new Rect (1, 3, 10, 5); + Assert.Equal (new Rect (1, 3, 10, 5), view.Frame); + Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay); + } } } \ No newline at end of file diff --git a/UnitTests/Types/DimTests.cs b/UnitTests/Types/DimTests.cs index 5a98d5db9..e04a4b818 100644 --- a/UnitTests/Types/DimTests.cs +++ b/UnitTests/Types/DimTests.cs @@ -83,11 +83,11 @@ namespace Terminal.Gui.TypeTests { var testVal = Rect.Empty; testVal = Rect.Empty; dim = Dim.Width (new View (testVal)); - Assert.Equal ($"DimView(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); + Assert.Equal ($"View(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); testVal = new Rect (1, 2, 3, 4); dim = Dim.Width (new View (testVal)); - Assert.Equal ($"DimView(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); + Assert.Equal ($"View(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); } [Fact] @@ -141,11 +141,11 @@ namespace Terminal.Gui.TypeTests { var testVal = Rect.Empty; testVal = Rect.Empty; dim = Dim.Height (new View (testVal)); - Assert.Equal ($"DimView(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); + Assert.Equal ($"View(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); testVal = new Rect (1, 2, 3, 4); dim = Dim.Height (new View (testVal)); - Assert.Equal ($"DimView(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); + Assert.Equal ($"View(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); } // TODO: Other Dim.Height tests (e.g. Equal?) @@ -435,12 +435,12 @@ namespace Terminal.Gui.TypeTests { Assert.Equal (49, f2.Frame.Width); // 50-1=49 Assert.Equal (5, f2.Frame.Height); - Assert.Equal ("Combine(DimView(Width,FrameView()({X=0,Y=0,Width=49,Height=5}))-Absolute(2))", v1.Width.ToString ()); + Assert.Equal ("Combine(View(Width,FrameView()({X=0,Y=0,Width=49,Height=5}))-Absolute(2))", v1.Width.ToString ()); Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ()); Assert.Equal (47, v1.Frame.Width); // 49-2=47 Assert.Equal (89, v1.Frame.Height); // 98-5-2-2=89 - Assert.Equal ("Combine(DimView(Width,FrameView()({X=49,Y=0,Width=49,Height=5}))-Absolute(2))", v2.Width.ToString ()); + Assert.Equal ("Combine(View(Width,FrameView()({X=49,Y=0,Width=49,Height=5}))-Absolute(2))", v2.Width.ToString ()); Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ()); Assert.Equal (47, v2.Frame.Width); // 49-2=47 Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89 @@ -455,8 +455,8 @@ namespace Terminal.Gui.TypeTests { Assert.Equal (50, v4.Frame.Width); Assert.Equal (50, v4.Frame.Height); - Assert.Equal ("Combine(DimView(Width,Button()({X=2,Y=7,Width=47,Height=89}))-DimView(Width,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Width.ToString ()); - Assert.Equal ("Combine(DimView(Height,Button()({X=2,Y=7,Width=47,Height=89}))-DimView(Height,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Height.ToString ()); + Assert.Equal ("Combine(View(Width,Button()({X=2,Y=7,Width=47,Height=89}))-View(Width,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Width.ToString ()); + Assert.Equal ("Combine(View(Height,Button()({X=2,Y=7,Width=47,Height=89}))-View(Height,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Height.ToString ()); Assert.Equal (38, v5.Frame.Width); // 47-9=38 Assert.Equal (80, v5.Frame.Height); // 89-9=80 @@ -466,6 +466,7 @@ namespace Terminal.Gui.TypeTests { Assert.Equal (18, v6.Frame.Height); // 89*20%=18 w.Width = 200; + Assert.True (t.LayoutNeeded); w.Height = 200; t.LayoutSubviews (); @@ -487,13 +488,13 @@ namespace Terminal.Gui.TypeTests { Assert.Equal (5, f2.Frame.Height); v1.Text = "Button1"; - Assert.Equal ("Combine(DimView(Width,FrameView()({X=0,Y=0,Width=99,Height=5}))-Absolute(2))", v1.Width.ToString ()); + Assert.Equal ("Combine(View(Width,FrameView()({X=0,Y=0,Width=99,Height=5}))-Absolute(2))", v1.Width.ToString ()); Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ()); Assert.Equal (97, v1.Frame.Width); // 99-2=97 Assert.Equal (189, v1.Frame.Height); // 198-2-7=189 v2.Text = "Button2"; - Assert.Equal ("Combine(DimView(Width,FrameView()({X=99,Y=0,Width=99,Height=5}))-Absolute(2))", v2.Width.ToString ()); + Assert.Equal ("Combine(View(Width,FrameView()({X=99,Y=0,Width=99,Height=5}))-Absolute(2))", v2.Width.ToString ()); Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ()); Assert.Equal (97, v2.Frame.Width); // 99-2=97 Assert.Equal (189, v2.Frame.Height); // 198-2-7=189 @@ -517,16 +518,16 @@ namespace Terminal.Gui.TypeTests { Assert.Equal (1, v4.Frame.Height); // 1 because is Dim.DimAbsolute v5.Text = "Button5"; - Assert.Equal ("Combine(DimView(Width,Button()({X=2,Y=7,Width=97,Height=189}))-DimView(Width,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Width.ToString ()); - Assert.Equal ("Combine(DimView(Height,Button()({X=2,Y=7,Width=97,Height=189}))-DimView(Height,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Height.ToString ()); - Assert.Equal (78, v5.Frame.Width); // 97-19=78 + Assert.Equal ("Combine(View(Width,Button()({X=2,Y=7,Width=97,Height=189}))-View(Width,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Width.ToString ()); + Assert.Equal ("Combine(View(Height,Button()({X=2,Y=7,Width=97,Height=189}))-View(Height,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Height.ToString ()); + Assert.Equal (78, v5.Frame.Width); // 97-9=78 Assert.Equal (170, v5.Frame.Height); // 189-19=170 v6.Text = "Button6"; Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ()); Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ()); Assert.Equal (19, v6.Frame.Width); // 99*20%=19 - Assert.Equal (38, v6.Frame.Height); // 198-7*20=38 + Assert.Equal (38, v6.Frame.Height); // 198-7*20=18 }; Application.Iteration += () => Application.RequestStop (); diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index ae3766244..1d5885e17 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -21,8 +21,8 @@ TRACE;DEBUG_IDISPOSABLE - - + + diff --git a/UnitTests/Views/ScrollBarViewTests.cs b/UnitTests/Views/ScrollBarViewTests.cs index 8a22ca13c..78b1c568b 100644 --- a/UnitTests/Views/ScrollBarViewTests.cs +++ b/UnitTests/Views/ScrollBarViewTests.cs @@ -364,12 +364,12 @@ namespace Terminal.Gui.ViewTests { Assert.True (_scrollBar.Visible); Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ()); Assert.Equal (1, _scrollBar.Bounds.Width); - Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", + Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", _scrollBar.Height.ToString ()); Assert.Equal (24, _scrollBar.Bounds.Height); Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator); Assert.True (_scrollBar.OtherScrollBarView.Visible); - Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", + Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", _scrollBar.OtherScrollBarView.Width.ToString ()); Assert.Equal (79, _scrollBar.OtherScrollBarView.Bounds.Width); Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ()); @@ -381,12 +381,12 @@ namespace Terminal.Gui.ViewTests { Assert.False (_scrollBar.Visible); Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ()); Assert.Equal (1, _scrollBar.Bounds.Width); - Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", + Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", _scrollBar.Height.ToString ()); Assert.Equal (24, _scrollBar.Bounds.Height); Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator); Assert.True (_scrollBar.OtherScrollBarView.Visible); - Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))", + Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))", _scrollBar.OtherScrollBarView.Width.ToString ()); Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width); Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ()); @@ -398,12 +398,12 @@ namespace Terminal.Gui.ViewTests { Assert.False (_scrollBar.Visible); Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ()); Assert.Equal (1, _scrollBar.Bounds.Width); - Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", + Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", _scrollBar.Height.ToString ()); Assert.Equal (24, _scrollBar.Bounds.Height); Assert.False (_scrollBar.OtherScrollBarView.ShowScrollIndicator); Assert.False (_scrollBar.OtherScrollBarView.Visible); - Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))", + Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))", _scrollBar.OtherScrollBarView.Width.ToString ()); Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width); Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ()); @@ -415,12 +415,12 @@ namespace Terminal.Gui.ViewTests { Assert.True (_scrollBar.Visible); Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ()); Assert.Equal (1, _scrollBar.Bounds.Width); - Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))", + Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))", _scrollBar.Height.ToString ()); Assert.Equal (25, _scrollBar.Bounds.Height); Assert.False (_scrollBar.OtherScrollBarView.ShowScrollIndicator); Assert.False (_scrollBar.OtherScrollBarView.Visible); - Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))", + Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))", _scrollBar.OtherScrollBarView.Width.ToString ()); Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width); Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ()); @@ -432,12 +432,12 @@ namespace Terminal.Gui.ViewTests { Assert.True (_scrollBar.Visible); Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ()); Assert.Equal (1, _scrollBar.Bounds.Width); - Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", + Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", _scrollBar.Height.ToString ()); Assert.Equal (24, _scrollBar.Bounds.Height); Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator); Assert.True (_scrollBar.OtherScrollBarView.Visible); - Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", + Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))", _scrollBar.OtherScrollBarView.Width.ToString ()); Assert.Equal (79, _scrollBar.OtherScrollBarView.Bounds.Width); Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ()); diff --git a/UnitTests/Views/ScrollViewTests.cs b/UnitTests/Views/ScrollViewTests.cs index eb14fe339..f441e3c86 100644 --- a/UnitTests/Views/ScrollViewTests.cs +++ b/UnitTests/Views/ScrollViewTests.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using NStack; using Xunit; using Xunit.Abstractions; @@ -280,5 +276,227 @@ namespace Terminal.Gui.ViewTests { ◄░░░├─┤░► ", output); } + + [Fact, AutoInitShutdown] + public void Frame_And_Labels_Does_Not_Overspill_ScrollView () + { + var sv = new ScrollView { + X = 3, + Y = 3, + Width = 10, + Height = 10, + ContentSize = new Size (50, 50) + }; + for (int i = 0; i < 8; i++) { + sv.Add (new CustomButton ("█", $"Button {i}", 20, 3) { Y = i * 3 }); + } + Application.Top.Add (sv); + Application.Begin (Application.Top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" + █████████▲ + ██████But┬ + █████████┴ + ┌────────░ + │ But░ + └────────░ + ┌────────░ + │ But░ + └────────▼ + ◄├┤░░░░░► ", output); + + sv.ContentOffset = new Point (5, 5); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + ─────────▲ + ─────────┬ + Button 2│ + ─────────┴ + ─────────░ + Button 3░ + ─────────░ + ─────────░ + Button 4▼ + ◄├─┤░░░░► ", output); + } + + private class CustomButton : FrameView { + private Label labelFill; + private Label labelText; + + public CustomButton (string fill, ustring text, int width, int height) + { + Width = width; + Height = height; + labelFill = new Label () { AutoSize = false, Width = Dim.Fill (), Height = Dim.Fill (), Visible = false }; + var fillText = new System.Text.StringBuilder (); + for (int i = 0; i < Bounds.Height; i++) { + if (i > 0) { + fillText.AppendLine (""); + } + for (int j = 0; j < Bounds.Width; j++) { + fillText.Append (fill); + } + } + labelFill.Text = fillText.ToString (); + labelText = new Label (text) { X = Pos.Center (), Y = Pos.Center () }; + Add (labelFill, labelText); + CanFocus = true; + } + + public override bool OnEnter (View view) + { + Border.BorderStyle = BorderStyle.None; + Border.DrawMarginFrame = false; + labelFill.Visible = true; + view = this; + return base.OnEnter (view); + } + + public override bool OnLeave (View view) + { + Border.BorderStyle = BorderStyle.Single; + Border.DrawMarginFrame = true; + labelFill.Visible = false; + if (view == null) + view = this; + return base.OnLeave (view); + } + } + + [Fact, AutoInitShutdown] + public void Clear_Window_Inside_ScrollView () + { + var topLabel = new Label ("At 15,0") { X = 15 }; + var sv = new ScrollView { + X = 3, + Y = 3, + Width = 10, + Height = 10, + ContentSize = new Size (23, 23), + KeepContentAlwaysInViewport = false + }; + var bottomLabel = new Label ("At 15,15") { X = 15, Y = 15 }; + Application.Top.Add (topLabel, sv, bottomLabel); + Application.Begin (Application.Top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" + At 15,0 + + + ▲ + ┬ + ┴ + ░ + ░ + ░ + ░ + ░ + ▼ + ◄├┤░░░░░► + + + At 15,15", output); + + var attributes = new Attribute [] { + Colors.TopLevel.Normal, + Colors.TopLevel.Focus, + Colors.Base.Normal + }; + + TestHelpers.AssertDriverColorsAre (@" +00000000000000000000000 +00000000000000000000000 +00000000000000000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00011111111110000000000 +00000000000000000000000 +00000000000000000000000 +00000000000000000000000", attributes); + + sv.Add (new Window ("1") { X = 3, Y = 3, Width = 20, Height = 20 }); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + At 15,0 + + + ▲ + ┬ + ┴ + ┌ 1 ──░ + │ ░ + │ ░ + │ ░ + │ ░ + │ ▼ + ◄├┤░░░░░► + + + At 15,15", output); + + TestHelpers.AssertDriverColorsAre (@" +00000000000000000000000 +00000000000000000000000 +00000000000000000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000022222210000000000 +00000022222210000000000 +00000022222210000000000 +00000022222210000000000 +00000022222210000000000 +00000022222210000000000 +00011111111110000000000 +00000000000000000000000 +00000000000000000000000 +00000000000000000000000", attributes); + + sv.ContentOffset = new Point (20, 20); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + At 15,0 + + + │ ▲ + │ ░ + ──┘ ░ + ░ + ░ + ┬ + │ + ┴ + ▼ + ◄░░░░├─┤► + + + At 15,15", output); + + TestHelpers.AssertDriverColorsAre (@" +00000000000000000000000 +00000000000000000000000 +00000000000000000000000 +00022200000010000000000 +00022200000010000000000 +00022200000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00011111111110000000000 +00000000000000000000000 +00000000000000000000000 +00000000000000000000000", attributes); + } } } diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs index bb08a22ff..2bcc46d95 100644 --- a/UnitTests/Views/TableViewTests.cs +++ b/UnitTests/Views/TableViewTests.cs @@ -321,6 +321,8 @@ namespace Terminal.Gui.ViewTests { Bounds = new Rect (0, 0, 10, 5) }; + tableView.ChangeSelectionToEndOfTable(false); + // select the last row tableView.MultiSelectedRegions.Clear (); tableView.MultiSelectedRegions.Push (new TableView.TableSelection (new Point (0, 3), new Rect (0, 3, 4, 1))); @@ -1506,6 +1508,185 @@ namespace Terminal.Gui.ViewTests { Assert.DoesNotContain (new Point (1, 0), tableView.GetAllSelectedCells ()); } + + [Fact, AutoInitShutdown] + public void TestToggleCells_MultiSelectOn () + { + // 2 row table + var tableView = GetABCDEFTableView (out var dt); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + + tableView.MultiSelect = true; + tableView.AddKeyBinding(Key.Space,Command.ToggleChecked); + + var selectedCell = tableView.GetAllSelectedCells().Single(); + Assert.Equal(0,selectedCell.X); + Assert.Equal(0,selectedCell.Y); + + // Go Right + tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight }); + + selectedCell = tableView.GetAllSelectedCells().Single(); + Assert.Equal(1,selectedCell.X); + Assert.Equal(0,selectedCell.Y); + + // Toggle Select + tableView.ProcessKey (new KeyEvent { Key = Key.Space}); + var m = tableView.MultiSelectedRegions.Single(); + Assert.True(m.IsToggled); + Assert.Equal(1,m.Origin.X); + Assert.Equal(0,m.Origin.Y); + selectedCell = tableView.GetAllSelectedCells().Single(); + Assert.Equal(1,selectedCell.X); + Assert.Equal(0,selectedCell.Y); + + // Go Left + tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft }); + + // Both Toggled and Moved to should be selected + Assert.Equal(2,tableView.GetAllSelectedCells().Count()); + var s1 = tableView.GetAllSelectedCells().ElementAt(0); + var s2 = tableView.GetAllSelectedCells().ElementAt(1); + Assert.Equal(1,s1.X); + Assert.Equal(0,s1.Y); + Assert.Equal(0,s2.X); + Assert.Equal(0,s2.Y); + + // Go Down + tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown }); + + // Both Toggled and Moved to should be selected but not 0,0 + // which we moved down from + Assert.Equal(2,tableView.GetAllSelectedCells().Count()); + s1 = tableView.GetAllSelectedCells().ElementAt(0); + s2 = tableView.GetAllSelectedCells().ElementAt(1); + Assert.Equal(1,s1.X); + Assert.Equal(0,s1.Y); + Assert.Equal(0,s2.X); + Assert.Equal(1,s2.Y); + + + // Go back to the toggled cell + tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight}); + tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp}); + + // Toggle off + tableView.ProcessKey (new KeyEvent { Key = Key.Space}); + + // Go Left + tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft}); + + selectedCell = tableView.GetAllSelectedCells().Single(); + Assert.Equal(0,selectedCell.X); + Assert.Equal(0,selectedCell.Y); + } + + [Fact, AutoInitShutdown] + public void TestToggleCells_MultiSelectOn_FullRowSelect () + { + // 2 row table + var tableView = GetABCDEFTableView (out var dt); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + tableView.FullRowSelect = true; + tableView.MultiSelect = true; + tableView.AddKeyBinding(Key.Space,Command.ToggleChecked); + + // Toggle Select Cell 0,0 + tableView.ProcessKey (new KeyEvent { Key = Key.Space}); + + // Go Down + tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown }); + + var m = tableView.MultiSelectedRegions.Single(); + Assert.True(m.IsToggled); + Assert.Equal(0,m.Origin.X); + Assert.Equal(0,m.Origin.Y); + + //First row toggled and Second row active = 12 selected cells + Assert.Equal(12,tableView.GetAllSelectedCells().Count()); + + tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight }); + tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp }); + + Assert.Single(tableView.MultiSelectedRegions.Where(r=>r.IsToggled)); + + // Can untoggle at 1,0 even though 0,0 was initial toggle because FullRowSelect is on + tableView.ProcessKey (new KeyEvent { Key = Key.Space}); + + Assert.Empty(tableView.MultiSelectedRegions.Where(r=>r.IsToggled)); + + } + + + [Fact, AutoInitShutdown] + public void TestToggleCells_MultiSelectOn_SquareSelectToggled () + { + // 3 row table + var tableView = GetABCDEFTableView (out var dt); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + tableView.MultiSelect = true; + tableView.AddKeyBinding(Key.Space,Command.ToggleChecked); + + // Make a square selection + tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown}); + tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight}); + + Assert.Equal(4,tableView.GetAllSelectedCells().Count()); + + // Toggle the square selected region on + tableView.ProcessKey (new KeyEvent { Key = Key.Space}); + + // Go Right + tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight }); + + //Toggled on square + the active cell (x=2,y=1) + Assert.Equal(5,tableView.GetAllSelectedCells().Count()); + Assert.Equal(2,tableView.SelectedColumn); + Assert.Equal(1,tableView.SelectedRow); + + // Untoggle the rectangular region by hitting toggle in + // any cell in that rect + tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp }); + tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft }); + + Assert.Equal(4,tableView.GetAllSelectedCells().Count()); + tableView.ProcessKey (new KeyEvent { Key = Key.Space }); + Assert.Equal(1,tableView.GetAllSelectedCells().Count()); + } + + + + [Fact, AutoInitShutdown] + public void TestToggleCells_MultiSelectOn_Two_SquareSelects_BothToggled () + { + // 6 row table + var tableView = GetABCDEFTableView (out var dt); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + dt.Rows.Add (1, 2, 3, 4, 5, 6); + tableView.MultiSelect = true; + tableView.AddKeyBinding(Key.Space,Command.ToggleChecked); + + // Make first square selection (0,0 to 1,1) + tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown}); + tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight}); + tableView.ProcessKey (new KeyEvent { Key = Key.Space}); + Assert.Equal(4,tableView.GetAllSelectedCells().Count()); + + // Make second square selection leaving 1 unselected line between them + tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft }); + tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown }); + tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown }); + tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown}); + tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight}); + + // 2 square selections + Assert.Equal(8,tableView.GetAllSelectedCells().Count()); + } + [Theory, AutoInitShutdown] [InlineData(new object[] { true,true })] diff --git a/UnitTests/Views/TileViewTests.cs b/UnitTests/Views/TileViewTests.cs index 0155d3aba..d3e97acec 100644 --- a/UnitTests/Views/TileViewTests.cs +++ b/UnitTests/Views/TileViewTests.cs @@ -1,6 +1,6 @@ using System; +using System.ComponentModel; using System.Linq; -using Terminal.Gui; using Terminal.Gui.Graphs; using Xunit; using Xunit.Abstractions; @@ -60,7 +60,7 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_Vertical_Focused () { var tileView = Get11By3TileView (out var line); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Redraw (tileView.Bounds); @@ -100,7 +100,7 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_Vertical_Focused_WithBorder () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Redraw (tileView.Bounds); @@ -141,9 +141,10 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_Vertical_Focused_50PercentSplit () { var tileView = Get11By3TileView (out var line); - SetInputFocusLine (tileView); tileView.SetSplitterPos (0, Pos.Percent (50)); Assert.IsType (tileView.SplitterDistances.ElementAt (0)); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); + tileView.Redraw (tileView.Bounds); string looksLike = @@ -209,7 +210,7 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_Vertical_View1MinSize_Absolute () { var tileView = Get11By3TileView (out var line); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Tiles.ElementAt (0).MinSize = 6; // distance is too small (below 6) @@ -254,7 +255,7 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_Vertical_View1MinSize_Absolute_WithBorder () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Tiles.ElementAt (0).MinSize = 5; // distance is too small (below 5) @@ -298,7 +299,7 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_Vertical_View2MinSize_Absolute () { var tileView = Get11By3TileView (out var line); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Tiles.ElementAt (1).MinSize = 6; // distance leaves too little space for view2 (less than 6 would remain) @@ -342,7 +343,7 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_Vertical_View2MinSize_Absolute_WithBorder () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Tiles.ElementAt (1).MinSize = 5; // distance leaves too little space for view2 (less than 5 would remain) @@ -386,8 +387,6 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_InsertPanelAtStart () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); - tileView.InsertTile (0); tileView.Redraw (tileView.Bounds); @@ -405,8 +404,6 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_InsertPanelMiddle () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); - tileView.InsertTile (1); tileView.Redraw (tileView.Bounds); @@ -424,8 +421,6 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_InsertPanelAtEnd () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); - tileView.InsertTile (2); tileView.Redraw (tileView.Bounds); @@ -445,7 +440,9 @@ namespace Terminal.Gui.ViewTests { var tileView = Get11By3TileView (out var line); tileView.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal; - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); + + Assert.True (line.HasFocus); tileView.Redraw (tileView.Bounds); @@ -485,9 +482,9 @@ namespace Terminal.Gui.ViewTests { public void TestTileView_Horizontal_View1MinSize_Absolute () { var tileView = Get11By3TileView (out var line); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal; - SetInputFocusLine (tileView); tileView.Tiles.ElementAt (0).MinSize = 1; // 0 should not be allowed because it brings us below minimum size of View1 @@ -2045,6 +2042,45 @@ namespace Terminal.Gui.ViewTests { } + [Fact, AutoInitShutdown] + public void Test_SplitTop_WholeBottom() + { + var tileView = new TileView (2) { + Width = 20, + Height = 10, + Orientation = Orientation.Horizontal, + }; + tileView.Border.BorderStyle = BorderStyle.Single; + + Assert.True (tileView.TrySplitTile (0,2,out TileView top)); + + top.Tiles.ElementAt (0).ContentView.Add (new Label ("bleh")); + top.Tiles.ElementAt (1).ContentView.Add (new Label ("blah")); + + tileView.Tiles.ElementAt (1).ContentView.Add (new Label ("Hello")); + tileView.ColorScheme = new ColorScheme (); + top.ColorScheme = new ColorScheme (); + tileView.LayoutSubviews (); + + tileView.Redraw (tileView.Bounds); + + string looksLike = +@" +┌─────────┬────────┐ +│bleh │blah │ +│ │ │ +│ │ │ +│ │ │ +├─────────┴────────┤ +│Hello │ +│ │ +│ │ +└──────────────────┘"; + + TestHelpers.AssertDriverContentsAre (looksLike, output); + + } + [Fact, AutoInitShutdown] public void TestNestedContainer3RightAnd1Down_TitleDoesNotOverspill() { @@ -2094,12 +2130,10 @@ namespace Terminal.Gui.ViewTests { TestHelpers.AssertDriverContentsAre (looksLike, output); } - - - [Fact,AutoInitShutdown] + [Fact, AutoInitShutdown] public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRebuildForTileCount () { - var tv = GetTileView (20,10); + var tv = GetTileView (20, 10); var myReusableView = new DisposeCounter (); @@ -2113,10 +2147,10 @@ namespace Terminal.Gui.ViewTests { // but I still want my view in the first tile tv.Tiles.ElementAt (0).ContentView.Add (myReusableView); Assert.Multiple ( - ()=>Assert.Equal (0, myReusableView.DisposalCount) - ,()=> { + () => Assert.Equal (0, myReusableView.DisposalCount) + , () => { tv.Dispose (); - Assert.Equal (1, myReusableView.DisposalCount); + Assert.Equal (1, myReusableView.DisposalCount); }); } [Fact, AutoInitShutdown] @@ -2140,15 +2174,13 @@ namespace Terminal.Gui.ViewTests { () => Assert.Equal (0, myReusableView.DisposalCount) , () => { tv.Dispose (); - - // TODO seems to be double disposed ?! - Assert.True (myReusableView.DisposalCount >= 1); + Assert.True (myReusableView.DisposalCount>=1); }); } [Theory, AutoInitShutdown] - [InlineData(0)] + [InlineData (0)] [InlineData (1)] - public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile(int idx) + public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile (int idx) { var tv = GetTileView (20, 10); @@ -2166,14 +2198,10 @@ namespace Terminal.Gui.ViewTests { () => Assert.Equal (0, myReusableView.DisposalCount) , () => { tv.Dispose (); - - // TODO seems to be double disposed ?! Assert.True (myReusableView.DisposalCount >= 1); }); } - - private class DisposeCounter : View - { + private class DisposeCounter : View { public int DisposalCount; protected override void Dispose (bool disposing) { @@ -2255,13 +2283,6 @@ namespace Terminal.Gui.ViewTests { return tileView.Subviews.OfType ().Single (); } - private void SetInputFocusLine (TileView tileView) - { - var line = GetLine (tileView); - line.SetFocus (); - Assert.True (line.HasFocus); - } - private TileView Get5x1TilesView (bool border = true) {