From e6a0ec64ca28443f856ef0bcfbb35b566cc579d1 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:34:14 -0600 Subject: [PATCH] Fixes #4361 - Consolidate `FakeDriver` into library and refactor driver architecture (#4362) * Initial plan * Add ScreenChanged event, SetScreenSize method, and fix FakeDriver buffer initialization Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add comprehensive tests for ScreenChanged event and buffer integrity Co-authored-by: tig <585482+tig@users.noreply.github.com> * Replace obsolete SizeChanged usage with ScreenChanged in core and tests Co-authored-by: tig <585482+tig@users.noreply.github.com> * Refactor terminal size event handling Replaced `ScreenChanged` with `SizeChanged` across the codebase to standardize naming and improve clarity. Updated event handling logic, including subscriptions, unsubscriptions, and raising methods. Removed deprecated `ScreenChanged` event and backward compatibility code. Refactored driver initialization to handle legacy `IConsoleDriver` types separately. Updated tests and mock implementations to align with the new `SizeChanged` event. Improved documentation and comments to reflect these changes. These updates enhance maintainability, consistency, and modernize the architecture. * Refactor & Code Cleanup: Replace IWindowSizeMonitor with IConsoleSizeMonitor Renamed `IWindowSizeMonitor` to `IConsoleSizeMonitor` across the codebase, updating all references, method signatures, and event handlers. Replaced the `WindowSizeMonitor` class with the new `ConsoleSizeMonitor` implementation, which includes improved terminal size change handling via the `Poll` method. Enabled nullable reference types in several files to enhance code safety. Updated test cases to reflect the new `IConsoleSizeMonitor` interface. Removed redundant code, simplified null checks, and corrected minor typos in comments. Streamlined the codebase by removing the obsolete `WindowSizeMonitor` class and its interface. * Code cleanup - Refactor and enhance ShadowView and FakeDriverTests Updated ShadowView.cs to use null-conditional operators and added null checks for safer access to `ScreenContents`. Refined XML documentation in View.Layout.cs for clarity and consistency. Refactored FakeDriverTests.cs to leverage modern C# features, including shorthand object instantiation, inline lambdas, and tuple-like syntax for `Size` and `Rectangle`. Removed redundant tests and improved test readability and reliability. Enhanced error handling with null checks and ensured backward compatibility for deprecated events. Improved test coverage for resizing, clipboard operations, and invalid coordinates. Verified buffer integrity and screen updates after resizing. General improvements include replacing explicit type declarations with `var`, removing unused imports, and aligning code formatting for better readability. Refactor and improve code quality and test coverage Updated `ShadowView` for null safety using null-conditional operators. Simplified object initializations and modernized syntax across the codebase, including shorthand initializations and inline lambdas. Enhanced event handling logic and ensured compatibility with obsolete members. Refactored `FakeDriverTests` by removing redundant code, standardizing formatting, and improving test setup. Suppressed obsolete warnings where necessary. Improved XML documentation in `View.Layout.cs` for clarity and removed outdated references. Performed general cleanup, including removing unused namespaces, redundant comments, and ensuring consistent formatting. These changes enhance readability, maintainability, and runtime safety. * Code cleanup Refactor TimedEventsTests for readability and consistency Improved code readability and maintainability: - Enabled nullable reference types with `#nullable enable`. - Removed unused `using System.Diagnostics;`. - Updated namespace to `UnitTests.ApplicationTests`. - Replaced `Terminal.Gui.App.TimedEvents` with `TimedEvents`. - Reformatted XML documentation comments for alignment. - Used `var` and target-typed new expressions for consistency. - Reformatted `Parallel.For` loops and lambdas for clarity. - Added `Thread.Sleep(10)` to prevent excessive CPU usage in tests. - Improved assertions and event handler formatting in tests. Aligned with modern C# coding practices. * Code Cleanup - No more driver warnings. Refactor codebase and introduce FakeClipboard - Adjusted `.editorconfig` to change severity levels for CS0612, CS0618, and CS0672 diagnostics. - Replaced `FakeDriver.FakeClipboard` with a new `FakeClipboard` class for testing purposes, supporting exception handling and clipboard data manipulation. - Removed redundant methods (`MakeColor`, `MapKey`) and unused classes (`MockConsoleDriver`) to streamline the codebase. - Refactored `ConsoleDriverFacade` and `FakeDriver` to simplify logic and improve maintainability. - Updated tests to use `CreateFakeDriver` and removed or commented out obsolete tests. - Reformatted and cleaned up code for readability across multiple files. * Refactor FakeDriver - Code Cleanup Standardized console size management by replacing `WindowSizeMonitor` with `ConsoleSizeMonitor` across the codebase. Updated methods `GetWindowSize` and `SetWindowSize` to `GetSize` and `SetSize` for consistency. Refactored `FakeDriver` to use `SetScreenSize` and removed redundant methods. Simplified driver initialization by removing legacy `InternalInit` logic. Standardized ANSI escape sequences by replacing `CSI_ReportTerminalSizeInChars` with `CSI_ReportWindowSizeInChars`. Updated test cases to align with the new `ConsoleSizeMonitor` and `SetScreenSize` methods. Removed obsolete test utilities like `FakeSizeMonitor` and `FakeWindowSizeMonitor`. Performed general code cleanup, including removing unused classes, redundant code, and improving formatting. Fixed resizing logic issues and improved exception handling in driver methods. * Update Terminal.Gui/Drivers/OutputBuffer.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Terminal.Gui/Drivers/MouseButtonStateEx.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Tests/UnitTests/Views/ToplevelTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Terminal.Gui/ViewBase/View.Layout.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Moved all Drawing tests to Paralleizable - proving Fakedriver works Enhanced `Ruler` and `Thickness` classes by adding an optional `IConsoleDriver? driver` parameter to decouple rendering from the default `Application.Driver`, improving flexibility and testability. Updated `DriverAssert` to support nullable drivers and added defensive checks. Refactored and expanded test cases for `Ruler`, `Thickness`, `LineCanvas`, and `StraightLineExtensions` to ensure comprehensive coverage, including zero-length intersections, line rendering, and exclusion logic. Migrated rendering-dependent tests to a parallelizable namespace. Removed redundant tests, improved naming conventions, and updated documentation for better maintainability and clarity. * Fixed Run startup hang. Refactor: Simplify driver logic and update SetSize methods Removed FakeDriver safeguard in unit tests to simplify CreateDriver logic. Updated SetSize methods in NetOutput, UnixOutput, and WindowsOutput to do nothing instead of throwing NotImplementedException. Modified SizeChanged event in ConsoleDriverFacade to call SetScreenSize directly. Commented out unnecessary debug validation in DimAuto. These changes improve maintainability and reduce complexity. * Fixed intermittent unit test bug. Refactored `_cachedRunStateToplevel` to `CachedRunStateToplevel` as an internal static property, delegating its management to `ApplicationImpl` for improved encapsulation. Updated all references to use the new property and centralized its handling in `ApplicationImpl`. Removed the `MouseGrabHandler` property from `ApplicationImpl` and simplified driver-related assignments by replacing `Application.ForceDriver` and `Application.Screen` with direct references. Reset `CachedRunStateToplevel` during cleanup to ensure proper state management. Updated the `Invoke` method to use `Top` directly, aligning with the refactored design. Improved debug assertions and performed general cleanup to enhance code readability and maintainability. * Fixed intermittent bug an massive code cleanup of warnings. Refactor and enhance codebase for maintainability - Applied null-conditional operator (`!`) to improve null-safety. - Refactored tests for clarity, added new cases, and removed redundancies. - Introduced `FakeApplicationFactory` and `FakeSizeMonitor` for testing. - Removed unused code, including legacy `DecodeEscSeq` logic. - Reformatted code for readability and consistency. - Updated assertions for more accurate validation in tests. - Fixed potential null reference issues across multiple files. - Improved event handling with proper null checks. - Enhanced documentation for new classes and methods. - Modernized code with C# features like `record struct` and `nullable enable`. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tig <585482+tig@users.noreply.github.com> Co-authored-by: Tig Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .editorconfig | 6 +- Examples/UICatalog/Scenarios/Adornments.cs | 24 +- .../UICatalog/Scenarios/AllViewsTester.cs | 2 +- .../Scenarios/AnsiRequestsScenario.cs | 2 +- Examples/UICatalog/Scenarios/Bars.cs | 2 +- Examples/UICatalog/Scenarios/Clipping.cs | 2 +- Examples/UICatalog/Scenarios/DimAutoDemo.cs | 2 +- .../EditorsAndHelpers/AdornmentsEditor.cs | 6 +- .../UICatalog/Scenarios/GraphViewExample.cs | 10 +- .../Scenarios/LineCanvasExperiment.cs | 6 +- Examples/UICatalog/Scenarios/PosAlignDemo.cs | 8 +- Examples/UICatalog/Scenarios/Shortcuts.cs | 2 +- .../UICatalog/Scenarios/ViewportSettings.cs | 4 +- .../Scenarios/WindowsAndFrameViews.cs | 2 +- Examples/UICatalog/UICatalog.cs | 4 +- .../HandledEventArgsAnalyzerTests.cs | 5 +- Terminal.Gui/App/Application.Lifecycle.cs | 115 +- Terminal.Gui/App/Application.Run.cs | 14 +- Terminal.Gui/App/Application.Screen.cs | 28 +- Terminal.Gui/App/Application.Toplevel.cs | 8 + Terminal.Gui/App/Application.cs | 10 +- Terminal.Gui/App/ApplicationImpl.cs | 55 +- Terminal.Gui/App/IApplication.cs | 5 + .../App/MainLoop/ApplicationMainLoop.cs | 12 +- .../App/MainLoop/IApplicationMainLoop.cs | 2 +- .../App/MainLoop/MainLoopCoordinator.cs | 2 +- Terminal.Gui/App/Timeout/ITimedEvents.cs | 12 - Terminal.Gui/Drawing/Ruler.cs | 13 +- .../Drawing/Sixel/SixelSupportDetector.cs | 12 +- Terminal.Gui/Drawing/Thickness.cs | 61 +- .../AnsiHandling/EscSeqUtils/EscSeqUtils.cs | 1430 ++------------- .../Drivers/AnsiHandling/Osc8UrlLinker.cs | 2 +- Terminal.Gui/Drivers/ComponentFactory.cs | 4 +- Terminal.Gui/Drivers/ConsoleDriver.cs | 50 +- Terminal.Gui/Drivers/ConsoleDriverFacade.cs | 62 +- Terminal.Gui/Drivers/ConsoleKeyMapping.cs | 2 +- Terminal.Gui/Drivers/ConsoleSizeMonitor.cs | 35 + .../Drivers/DotNetDriver/NetOutput.cs | 8 +- .../Drivers/FakeDriver/FakeClipboard.cs | 59 + .../FakeDriver/FakeComponentFactory.cs | 5 +- .../Drivers/FakeDriver/FakeConsole.cs | 6 +- .../Drivers/FakeDriver/FakeConsoleOutput.cs | 46 +- Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs | 210 +-- .../FakeDriver/FakeWindowSizeMonitor.cs | 41 - Terminal.Gui/Drivers/IComponentFactory.cs | 6 +- Terminal.Gui/Drivers/IConsoleDriver.cs | 22 +- Terminal.Gui/Drivers/IConsoleDriverFacade.cs | 2 +- Terminal.Gui/Drivers/IConsoleOutput.cs | 11 +- ...wSizeMonitor.cs => IConsoleSizeMonitor.cs} | 6 +- Terminal.Gui/Drivers/IInputProcessor.cs | 2 +- Terminal.Gui/Drivers/IOutputBuffer.cs | 2 +- Terminal.Gui/Drivers/InputProcessor.cs | 4 +- Terminal.Gui/Drivers/MouseButtonStateEx.cs | 2 +- Terminal.Gui/Drivers/OutputBase.cs | 2 +- Terminal.Gui/Drivers/OutputBuffer.cs | 6 +- Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs | 8 +- Terminal.Gui/Drivers/WindowSizeMonitor.cs | 42 - .../Drivers/WindowsDriver/WindowsConsole.cs | 939 +--------- .../Drivers/WindowsDriver/WindowsInput.cs | 17 +- .../Drivers/WindowsDriver/WindowsOutput.cs | 8 +- .../ViewBase/Adornment/Border.Arrangment.cs | 12 +- Terminal.Gui/ViewBase/Adornment/Border.cs | 2 +- Terminal.Gui/ViewBase/Adornment/Margin.cs | 14 +- Terminal.Gui/ViewBase/Adornment/ShadowView.cs | 4 +- Terminal.Gui/ViewBase/Layout/DimAuto.cs | 2 +- Terminal.Gui/ViewBase/View.Layout.cs | 16 +- Terminal.Gui/Views/Menuv1/MenuBar.cs | 4 +- Terminal.Gui/Views/Shortcut.cs | 18 +- Terminal.Gui/Views/StatusBar.cs | 2 +- .../Views/TextInput/NetMaskedTextProvider.cs | 2 +- Terminal.Gui/Views/TreeView/Branch.cs | 1 - .../FakeDriver/FakeApplicationFactory.cs | 8 +- .../FakeDriver/FakeConsoleDriver.cs | 58 - .../FakeDriver/FakeDriverFactory.cs | 20 - .../{ => FakeDriver}/FakeInput.cs | 0 .../{ => FakeDriver}/FakeOutput.cs | 8 +- .../FakeDriver/FakeSizeMonitor.cs | 12 +- .../{ => FakeDriver}/FakeWindowsInput.cs | 0 .../FakeDriver/IFakeConsoleDriver.cs | 8 - .../GuiTestContext.cs | 63 +- .../Application.NavigationTests.cs | 2 +- .../Application/ApplicationImplTests.cs | 4 +- .../Application/ApplicationPopoverTests.cs | 5 +- .../Application/ApplicationScreenTests.cs | 4 +- .../UnitTests/Application/ApplicationTests.cs | 75 +- .../UnitTests/Application/TimedEventsTests.cs | 108 +- Tests/UnitTests/AutoInitShutdownAttribute.cs | 37 +- .../ConsoleDrivers/ClipRegionTests.cs | 137 +- .../ConsoleDrivers/ConsoleDriverTests.cs | 2 +- .../ConsoleDrivers/EscSeqUtilsTests.cs | 106 ++ .../ConsoleDrivers/FakeDriverTests.cs | 309 +++- .../ConsoleDrivers/WindowSizeMonitorTests.cs | 50 +- Tests/UnitTests/Dialogs/DialogTests.cs | 46 +- Tests/UnitTests/Dialogs/MessageBoxTests.cs | 14 +- Tests/UnitTests/Dialogs/WizardTests.cs | 148 +- Tests/UnitTests/Drawing/LineCanvasTests.cs | 1263 ------------- Tests/UnitTests/Drawing/RulerTests.cs | 142 -- Tests/UnitTests/Drawing/ThicknessTests.cs | 255 --- Tests/UnitTests/DriverAssert.cs | 37 +- Tests/UnitTests/Input/EscSeqUtilsTests.cs | 1582 ----------------- Tests/UnitTests/SetupFakeDriverAttribute.cs | 5 +- Tests/UnitTests/TestsAllViews.cs | 11 +- Tests/UnitTests/Text/AutocompleteTests.cs | 6 +- Tests/UnitTests/Text/TextFormatterTests.cs | 4 +- .../View/Adornment/AdornmentSubViewTests.cs | 10 +- Tests/UnitTests/View/Adornment/BorderTests.cs | 119 +- Tests/UnitTests/View/Adornment/MarginTests.cs | 4 +- .../UnitTests/View/Adornment/PaddingTests.cs | 2 +- .../View/Adornment/ShadowStyleTests.cs | 4 +- .../UnitTests/View/Draw/ClearViewportTests.cs | 4 +- Tests/UnitTests/View/Draw/ClipTests.cs | 4 +- Tests/UnitTests/View/Draw/DrawTests.cs | 6 +- Tests/UnitTests/View/Draw/NeedsDrawTests.cs | 2 +- Tests/UnitTests/View/Layout/Dim.Tests.cs | 2 +- .../View/Layout/GetViewsUnderLocationTests.cs | 18 +- .../View/Layout/Pos.AnchorEndTests.cs | 2 +- .../UnitTests/View/Layout/Pos.CenterTests.cs | 6 +- Tests/UnitTests/View/Layout/SetLayoutTests.cs | 4 +- Tests/UnitTests/View/Mouse/MouseTests.cs | 19 +- Tests/UnitTests/View/TextTests.cs | 18 +- Tests/UnitTests/View/ViewTests.cs | 2 +- Tests/UnitTests/Views/ButtonTests.cs | 3 +- Tests/UnitTests/Views/CheckBoxTests.cs | 8 +- Tests/UnitTests/Views/FrameViewTests.cs | 6 +- Tests/UnitTests/Views/LabelTests.cs | 28 +- Tests/UnitTests/Views/ListViewTests.cs | 4 +- .../UnitTests/Views/Menuv1/MenuBarv1Tests.cs | 26 +- Tests/UnitTests/Views/RadioGroupTests.cs | 2 +- Tests/UnitTests/Views/TableViewTests.cs | 2 +- Tests/UnitTests/Views/TextFieldTests.cs | 67 - Tests/UnitTests/Views/TextViewTests.cs | 6 +- Tests/UnitTests/Views/ToplevelTests.cs | 21 +- Tests/UnitTests/Views/TreeTableSourceTests.cs | 4 +- Tests/UnitTests/Views/WindowTests.cs | 10 +- .../Application/MouseInterfaceTests.cs | 5 +- .../EscSeqRequestsTests.cs | 69 +- .../ConsoleDrivers/UrlHyperlinkerTests.cs | 2 +- .../Drawing/AttributeTests.cs | 18 - .../Drawing/LineCanvasTests.cs | 1277 ++++++++++++- .../Drawing/RulerTests.cs | 146 +- .../Drawing/StraightLineExtensionsTests.cs | 21 +- .../Drawing/ThicknessTests.cs | 268 ++- .../Input/Keyboard/KeyTests.cs | 18 + .../Input/ResponderTests.cs | 27 - .../MockConsoleDriver.cs | 203 --- .../ParallelizableBase.cs | 7 +- .../UnitTests.Parallelizable.csproj | 1 + .../View/Adornment/AdornmentTests.cs | 90 +- .../View/Adornment/MarginTests.cs | 6 +- .../View/Layout/GetViewsUnderLocationTests.cs | 8 +- .../View/Layout/LayoutTests.cs | 10 +- .../View/Layout/ToScreenTests.cs | 2 +- .../View/Layout/ViewportTests.cs | 12 +- .../View/Navigation/SetFocusTests.cs | 26 +- .../View/SchemeTests.cs | 33 +- .../Views/AllViewsTests.cs | 56 +- .../Views/CheckBoxTests.cs | 2 +- .../Views/TextFieldTests.cs | 81 +- 158 files changed, 3359 insertions(+), 7523 deletions(-) create mode 100644 Terminal.Gui/Drivers/ConsoleSizeMonitor.cs create mode 100644 Terminal.Gui/Drivers/FakeDriver/FakeClipboard.cs delete mode 100644 Terminal.Gui/Drivers/FakeDriver/FakeWindowSizeMonitor.cs rename Terminal.Gui/Drivers/{IWindowSizeMonitor.cs => IConsoleSizeMonitor.cs} (77%) delete mode 100644 Terminal.Gui/Drivers/WindowSizeMonitor.cs delete mode 100644 Tests/TerminalGuiFluentTesting/FakeDriver/FakeConsoleDriver.cs delete mode 100644 Tests/TerminalGuiFluentTesting/FakeDriver/FakeDriverFactory.cs rename Tests/TerminalGuiFluentTesting/{ => FakeDriver}/FakeInput.cs (100%) rename Tests/TerminalGuiFluentTesting/{ => FakeDriver}/FakeOutput.cs (83%) rename Tests/TerminalGuiFluentTesting/{ => FakeDriver}/FakeWindowsInput.cs (100%) delete mode 100644 Tests/TerminalGuiFluentTesting/FakeDriver/IFakeConsoleDriver.cs create mode 100644 Tests/UnitTests/ConsoleDrivers/EscSeqUtilsTests.cs delete mode 100644 Tests/UnitTests/Drawing/LineCanvasTests.cs delete mode 100644 Tests/UnitTests/Drawing/RulerTests.cs delete mode 100644 Tests/UnitTests/Drawing/ThicknessTests.cs delete mode 100644 Tests/UnitTests/Input/EscSeqUtilsTests.cs rename Tests/UnitTestsParallelizable/{Input => ConsoleDrivers}/EscSeqRequestsTests.cs (59%) rename Tests/{UnitTests => UnitTestsParallelizable}/Drawing/StraightLineExtensionsTests.cs (97%) delete mode 100644 Tests/UnitTestsParallelizable/Input/ResponderTests.cs delete mode 100644 Tests/UnitTestsParallelizable/MockConsoleDriver.cs diff --git a/.editorconfig b/.editorconfig index 95013c249..fa758e3bb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -82,8 +82,8 @@ dotnet_diagnostic.cs0464.severity = warning dotnet_diagnostic.cs0465.severity = warning dotnet_diagnostic.cs0469.severity = warning dotnet_diagnostic.cs0472.severity = warning -dotnet_diagnostic.cs0612.severity = warning -dotnet_diagnostic.cs0618.severity = warning +dotnet_diagnostic.cs0612.severity = none +dotnet_diagnostic.cs0618.severity = none dotnet_diagnostic.cs0628.severity = warning dotnet_diagnostic.cs0642.severity = warning dotnet_diagnostic.cs0649.severity = warning @@ -94,7 +94,7 @@ dotnet_diagnostic.cs0659.severity = warning dotnet_diagnostic.cs0660.severity = warning dotnet_diagnostic.cs0661.severity = warning dotnet_diagnostic.cs0665.severity = warning -dotnet_diagnostic.cs0672.severity = warning +dotnet_diagnostic.cs0672.severity = none dotnet_diagnostic.cs0675.severity = warning dotnet_diagnostic.cs0693.severity = warning dotnet_diagnostic.cs0728.severity = warning diff --git a/Examples/UICatalog/Scenarios/Adornments.cs b/Examples/UICatalog/Scenarios/Adornments.cs index 5e0efc628..938d23a53 100644 --- a/Examples/UICatalog/Scenarios/Adornments.cs +++ b/Examples/UICatalog/Scenarios/Adornments.cs @@ -26,7 +26,7 @@ public class Adornments : Scenario X = Pos.AnchorEnd () }; - editor.Border.Thickness = new (1, 2, 1, 1); + editor.Border!.Thickness = new (1, 2, 1, 1); app.Add (editor); @@ -71,7 +71,7 @@ public class Adornments : Scenario Width = 40, Height = 6 // TODO: Use Dim.Auto }; - label.Border.Thickness = new (1, 3, 1, 1); + label.Border!.Thickness = new (1, 3, 1, 1); var btnButtonInWindow = new Button { X = Pos.AnchorEnd (), Y = Pos.AnchorEnd (), Text = "Button" }; @@ -84,13 +84,13 @@ public class Adornments : Scenario SchemeName = "Dialog" }; - window.Margin.Data = "Margin"; - window.Margin.Text = "Margin Text"; - window.Margin.Thickness = new (0); + window.Margin!.Data = "Margin"; + window.Margin!.Text = "Margin Text"; + window.Margin!.Thickness = new (0); - window.Border.Data = "Border"; - window.Border.Text = "Border Text"; - window.Border.Thickness = new (0); + window.Border!.Data = "Border"; + window.Border!.Text = "Border Text"; + window.Border!.Thickness = new (0); window.Padding.Data = "Padding"; window.Padding.Text = "Padding Text line 1\nPadding Text line 3\nPadding Text line 3\nPadding Text line 4\nPadding Text line 5"; @@ -134,14 +134,14 @@ public class Adornments : Scenario }; btnButtonInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "Hi", "Button in Padding Pressed!", "Ok"); btnButtonInPadding.BorderStyle = LineStyle.Dashed; - btnButtonInPadding.Border.Thickness = new (1, 1, 1, 1); + btnButtonInPadding.Border!.Thickness = new (1, 1, 1, 1); window.Padding.Add (btnButtonInPadding); #if SUBVIEW_BASED_BORDER - btnButtonInPadding.Border.CloseButton.Visible = true; + btnButtonInPadding.Border!.CloseButton.Visible = true; - view.Border.CloseButton.Visible = true; - view.Border.CloseButton.Accept += (s, e) => + view.Border!.CloseButton.Visible = true; + view.Border!.CloseButton.Accept += (s, e) => { MessageBox.Query (20, 7, "Hi", "Window Close Button Pressed!", "Ok"); e.Handled = true; diff --git a/Examples/UICatalog/Scenarios/AllViewsTester.cs b/Examples/UICatalog/Scenarios/AllViewsTester.cs index f967742bc..99fbbdb50 100644 --- a/Examples/UICatalog/Scenarios/AllViewsTester.cs +++ b/Examples/UICatalog/Scenarios/AllViewsTester.cs @@ -38,7 +38,7 @@ public class AllViewsTester : Scenario // Set the BorderStyle we use for all subviews, but disable the app border thickness app.Border!.LineStyle = LineStyle.Heavy; - app.Border.Thickness = new (0); + app.Border!.Thickness = new (0); _viewClasses = GetAllViewClassesCollection () diff --git a/Examples/UICatalog/Scenarios/AnsiRequestsScenario.cs b/Examples/UICatalog/Scenarios/AnsiRequestsScenario.cs index 9a68b2567..ad4f881ba 100644 --- a/Examples/UICatalog/Scenarios/AnsiRequestsScenario.cs +++ b/Examples/UICatalog/Scenarios/AnsiRequestsScenario.cs @@ -112,7 +112,7 @@ public sealed class AnsiEscapeSequenceRequests : Scenario break; case "CSI_ReportTerminalSizeInChars": - selAnsiEscapeSequenceRequest = EscSeqUtils.CSI_ReportTerminalSizeInChars; + selAnsiEscapeSequenceRequest = EscSeqUtils.CSI_ReportWindowSizeInChars; break; case "CSI_RequestCursorPositionReport": diff --git a/Examples/UICatalog/Scenarios/Bars.cs b/Examples/UICatalog/Scenarios/Bars.cs index 226e18e26..af79c0cbd 100644 --- a/Examples/UICatalog/Scenarios/Bars.cs +++ b/Examples/UICatalog/Scenarios/Bars.cs @@ -40,7 +40,7 @@ public class Bars : Scenario SchemeName = "Toplevel", Source = new ListWrapper (eventSource) }; - eventLog.Border.Thickness = new (0, 1, 0, 0); + eventLog.Border!.Thickness = new (0, 1, 0, 0); Application.Top.Add (eventLog); FrameView menuBarLikeExamples = new () diff --git a/Examples/UICatalog/Scenarios/Clipping.cs b/Examples/UICatalog/Scenarios/Clipping.cs index 2cebee301..b9bb47528 100644 --- a/Examples/UICatalog/Scenarios/Clipping.cs +++ b/Examples/UICatalog/Scenarios/Clipping.cs @@ -151,7 +151,7 @@ public class Clipping : Scenario //tiled.Padding.Thickness = new (1); //tiled.Padding.Diagnostics = ViewDiagnosticFlags.Thickness; - //tiled.Margin.Thickness = new (1); + //tiled.Margin!.Thickness = new (1); FrameView fv = new () { diff --git a/Examples/UICatalog/Scenarios/DimAutoDemo.cs b/Examples/UICatalog/Scenarios/DimAutoDemo.cs index 7c99647e4..efad9eeb7 100644 --- a/Examples/UICatalog/Scenarios/DimAutoDemo.cs +++ b/Examples/UICatalog/Scenarios/DimAutoDemo.cs @@ -56,7 +56,7 @@ public class DimAutoDemo : Scenario Width = Dim.Auto (DimAutoStyle.Content, minimumContentDim: Dim.Percent (25)), Height = Dim.Auto (DimAutoStyle.Content, minimumContentDim: 10) }; - dimAutoFrameView.Margin.Thickness = new Thickness (1); + dimAutoFrameView.Margin!.Thickness = new Thickness (1); dimAutoFrameView.ValidatePosDim = true; var textEdit = new TextView diff --git a/Examples/UICatalog/Scenarios/EditorsAndHelpers/AdornmentsEditor.cs b/Examples/UICatalog/Scenarios/EditorsAndHelpers/AdornmentsEditor.cs index 74d7acee8..30c3ffda4 100644 --- a/Examples/UICatalog/Scenarios/EditorsAndHelpers/AdornmentsEditor.cs +++ b/Examples/UICatalog/Scenarios/EditorsAndHelpers/AdornmentsEditor.cs @@ -99,7 +99,7 @@ public class AdornmentsEditor : EditorBase SuperViewRendersLineCanvas = true, BorderStyle = LineStyle.Single }; - MarginEditor.Border!.Thickness = MarginEditor.Border.Thickness with { Bottom = 0 }; + MarginEditor.Border!.Thickness = MarginEditor.Border!.Thickness with { Bottom = 0 }; Add (MarginEditor); BorderEditor = new () @@ -109,7 +109,7 @@ public class AdornmentsEditor : EditorBase SuperViewRendersLineCanvas = true, BorderStyle = LineStyle.Single }; - BorderEditor.Border!.Thickness = BorderEditor.Border.Thickness with { Bottom = 0 }; + BorderEditor.Border!.Thickness = BorderEditor.Border!.Thickness with { Bottom = 0 }; Add (BorderEditor); PaddingEditor = new () @@ -119,7 +119,7 @@ public class AdornmentsEditor : EditorBase SuperViewRendersLineCanvas = true, BorderStyle = LineStyle.Single }; - PaddingEditor.Border!.Thickness = PaddingEditor.Border.Thickness with { Bottom = 0 }; + PaddingEditor.Border!.Thickness = PaddingEditor.Border!.Thickness with { Bottom = 0 }; Add (PaddingEditor); Width = Dim.Auto (maximumContentDim: Dim.Func (_ => MarginEditor.Frame.Width - 2)); diff --git a/Examples/UICatalog/Scenarios/GraphViewExample.cs b/Examples/UICatalog/Scenarios/GraphViewExample.cs index 530acf8e4..507a0c475 100644 --- a/Examples/UICatalog/Scenarios/GraphViewExample.cs +++ b/Examples/UICatalog/Scenarios/GraphViewExample.cs @@ -144,8 +144,8 @@ public class GraphViewExample : Scenario Height = Dim.Fill (1), BorderStyle = LineStyle.Single }; - _graphView.Border.Thickness = _thickness; - _graphView.Margin.Thickness = _thickness; + _graphView.Border!.Thickness = _thickness; + _graphView.Margin!.Thickness = _thickness; _graphView.Padding.Thickness = _thickness; app.Add (_graphView); @@ -955,14 +955,14 @@ public class GraphViewExample : Scenario if (_miShowBorder.Checked == true) { _graphView.BorderStyle = LineStyle.Single; - _graphView.Border.Thickness = _thickness; - _graphView.Margin.Thickness = _thickness; + _graphView.Border!.Thickness = _thickness; + _graphView.Margin!.Thickness = _thickness; _graphView.Padding.Thickness = _thickness; } else { _graphView.BorderStyle = LineStyle.None; - _graphView.Margin.Thickness = Thickness.Empty; + _graphView.Margin!.Thickness = Thickness.Empty; _graphView.Padding.Thickness = Thickness.Empty; } } diff --git a/Examples/UICatalog/Scenarios/LineCanvasExperiment.cs b/Examples/UICatalog/Scenarios/LineCanvasExperiment.cs index 85786a40e..6a826e4be 100644 --- a/Examples/UICatalog/Scenarios/LineCanvasExperiment.cs +++ b/Examples/UICatalog/Scenarios/LineCanvasExperiment.cs @@ -130,9 +130,9 @@ public class LineCanvasExperiment : Scenario // //Scheme = Colors.Schemes ["Error"], // SuperViewRendersLineCanvas = true //}; - //marginWindow.Margin.Scheme = Colors.Schemes ["Error"]; - //marginWindow.Margin.Thickness = new (1); - //marginWindow.Border.Thickness = new (1, 2, 1, 1); + //marginWindow.Margin!.Scheme = Colors.Schemes ["Error"]; + //marginWindow.Margin!.Thickness = new (1); + //marginWindow.Border!.Thickness = new (1, 2, 1, 1); //frame1.Add (marginWindow); diff --git a/Examples/UICatalog/Scenarios/PosAlignDemo.cs b/Examples/UICatalog/Scenarios/PosAlignDemo.cs index 5a9317aac..205a3c535 100644 --- a/Examples/UICatalog/Scenarios/PosAlignDemo.cs +++ b/Examples/UICatalog/Scenarios/PosAlignDemo.cs @@ -248,13 +248,13 @@ public sealed class PosAlignDemo : Scenario { addedViewsUpDown.X = Pos.Align (_horizAligner.Alignment); addedViewsUpDown.Y = Pos.Top (alignRadioGroup); - addedViewsUpDown.Border.Thickness = new (0, 1, 0, 0); + addedViewsUpDown.Border!.Thickness = new (0, 1, 0, 0); } else { addedViewsUpDown.X = Pos.Left (alignRadioGroup); addedViewsUpDown.Y = Pos.Align (_vertAligner.Alignment); - addedViewsUpDown.Border.Thickness = new (1, 0, 0, 0); + addedViewsUpDown.Border!.Thickness = new (1, 0, 0, 0); } addedViewsUpDown.ValueChanging += (s, e) => @@ -319,7 +319,7 @@ public sealed class PosAlignDemo : Scenario aligner.Alignment, aligner.AlignmentModes, posAlign!.GroupId); - view.Margin.Thickness = new (_leftMargin, view.Margin.Thickness.Top, view.Margin.Thickness.Right, view.Margin.Thickness.Bottom); + view.Margin!.Thickness = new (_leftMargin, view.Margin!.Thickness.Top, view.Margin!.Thickness.Right, view.Margin!.Thickness.Bottom); } else { @@ -330,7 +330,7 @@ public sealed class PosAlignDemo : Scenario aligner.AlignmentModes, posAlign!.GroupId); - view.Margin.Thickness = new (view.Margin.Thickness.Left, _topMargin, view.Margin.Thickness.Right, view.Margin.Thickness.Bottom); + view.Margin!.Thickness = new (view.Margin!.Thickness.Left, _topMargin, view.Margin!.Thickness.Right, view.Margin!.Thickness.Bottom); } } } diff --git a/Examples/UICatalog/Scenarios/Shortcuts.cs b/Examples/UICatalog/Scenarios/Shortcuts.cs index fc9843b3c..a4443146e 100644 --- a/Examples/UICatalog/Scenarios/Shortcuts.cs +++ b/Examples/UICatalog/Scenarios/Shortcuts.cs @@ -337,7 +337,7 @@ public class Shortcuts : Scenario if (framedShortcut.CommandView.Margin is { }) { - framedShortcut.CommandView.Margin.SchemeName = framedShortcut.CommandView.SchemeName = "Error"; + framedShortcut.CommandView.Margin!.SchemeName = framedShortcut.CommandView.SchemeName = "Error"; framedShortcut.HelpView.Margin!.SchemeName = framedShortcut.HelpView.SchemeName = "Dialog"; framedShortcut.KeyView.Margin!.SchemeName = framedShortcut.KeyView.SchemeName = "Menu"; } diff --git a/Examples/UICatalog/Scenarios/ViewportSettings.cs b/Examples/UICatalog/Scenarios/ViewportSettings.cs index fa801e37c..4e430171c 100644 --- a/Examples/UICatalog/Scenarios/ViewportSettings.cs +++ b/Examples/UICatalog/Scenarios/ViewportSettings.cs @@ -178,10 +178,10 @@ public class ViewportSettings : Scenario buttonAnchored.Accepting += (sender, args) => MessageBox.Query ("Hi", $"You pressed {((Button)sender)?.Text}", "_Ok"); view.Margin!.Data = "Margin"; - view.Margin.Thickness = new (0); + view.Margin!.Thickness = new (0); view.Border!.Data = "Border"; - view.Border.Thickness = new (3); + view.Border!.Thickness = new (3); view.Padding.Data = "Padding"; diff --git a/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs b/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs index 0a28a74fa..fcf57343a 100644 --- a/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs +++ b/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs @@ -43,7 +43,7 @@ public class WindowsAndFrameViews : Scenario Arrangement = ViewArrangement.Overlapped | ViewArrangement.Movable | ViewArrangement.Resizable }; win.Padding.Thickness = new (padding); - win.Margin.Thickness = new (margin); + win.Margin!.Thickness = new (margin); var paddingButton = new Button { diff --git a/Examples/UICatalog/UICatalog.cs b/Examples/UICatalog/UICatalog.cs index acbba9711..e7b6d2340 100644 --- a/Examples/UICatalog/UICatalog.cs +++ b/Examples/UICatalog/UICatalog.cs @@ -451,7 +451,7 @@ public class UICatalog scenario.StartBenchmark (); } - Application.Init (driverName: _forceDriver); + Application.ForceDriver = _forceDriver!; scenario.Main (); @@ -517,7 +517,7 @@ public class UICatalog if (benchmarkWindow.Border is { }) { - benchmarkWindow.Border.Thickness = new (0, 0, 0, 0); + benchmarkWindow.Border!.Thickness = new (0, 0, 0, 0); } TableView resultsTableView = new () diff --git a/Terminal.Gui.Analyzers.Tests/HandledEventArgsAnalyzerTests.cs b/Terminal.Gui.Analyzers.Tests/HandledEventArgsAnalyzerTests.cs index 6eeab40dd..e50992546 100644 --- a/Terminal.Gui.Analyzers.Tests/HandledEventArgsAnalyzerTests.cs +++ b/Terminal.Gui.Analyzers.Tests/HandledEventArgsAnalyzerTests.cs @@ -1,7 +1,8 @@ -using Terminal.Gui.Input; +using Terminal.Gui.Analyzers; +using Terminal.Gui.Input; using Terminal.Gui.Views; -namespace Terminal.Gui.Analyzers.Tests; +namespace Analyzers.Tests; public class HandledEventArgsAnalyzerTests { diff --git a/Terminal.Gui/App/Application.Lifecycle.cs b/Terminal.Gui/App/Application.Lifecycle.cs index 4aa915d0f..9cd2b7996 100644 --- a/Terminal.Gui/App/Application.Lifecycle.cs +++ b/Terminal.Gui/App/Application.Lifecycle.cs @@ -40,26 +40,6 @@ public static partial class Application // Lifecycle (Init/Shutdown) [RequiresDynamicCode ("AOT")] public static void Init (IConsoleDriver? driver = null, string? driverName = null) { - // Check if this is a request for a legacy driver (like FakeDriver) - // that isn't supported by the modern application architecture - if (driver is null) - { - var driverNameToCheck = string.IsNullOrWhiteSpace (driverName) ? ForceDriver : driverName; - if (!string.IsNullOrEmpty (driverNameToCheck)) - { - (List drivers, List driverTypeNames) = GetDriverTypes (); - Type? driverType = drivers.FirstOrDefault (t => t!.Name.Equals (driverNameToCheck, StringComparison.InvariantCultureIgnoreCase)); - - // If it's a legacy IConsoleDriver (not a Facade), use InternalInit which supports legacy drivers - if (driverType is { } && !typeof (IConsoleDriverFacade).IsAssignableFrom (driverType)) - { - InternalInit (driver, driverName); - return; - } - } - } - - // Otherwise delegate to the ApplicationImpl instance (which uses the modern architecture) ApplicationImpl.Instance.Init (driver, driverName ?? ForceDriver); } @@ -69,96 +49,6 @@ public static partial class Application // Lifecycle (Init/Shutdown) set => ((ApplicationImpl)ApplicationImpl.Instance).MainThreadId = value; } - // INTERNAL function for initializing an app with a Toplevel factory object, driver, and mainloop. - // - // Called from: - // - // Init() - When the user wants to use the default Toplevel. calledViaRunT will be false, causing all state to be reset. - // Run() - When the user wants to use a custom Toplevel. calledViaRunT will be true, enabling Run() to be called without calling Init first. - // Unit Tests - To initialize the app with a custom Toplevel, using the FakeDriver. calledViaRunT will be false, causing all state to be reset. - // - // calledViaRunT: If false (default) all state will be reset. If true the state will not be reset. - [RequiresUnreferencedCode ("AOT")] - [RequiresDynamicCode ("AOT")] - internal static void InternalInit ( - IConsoleDriver? driver = null, - string? driverName = null, - bool calledViaRunT = false - ) - { - if (Initialized && driver is null) - { - return; - } - - if (Initialized) - { - throw new InvalidOperationException ("Init has already been called and must be bracketed by Shutdown."); - } - - if (!calledViaRunT) - { - // Reset all class variables (Application is a singleton). - ResetState (ignoreDisposed: true); - } - - // For UnitTests - if (driver is { }) - { - Driver = driver; - } - - // Ignore Configuration for ForceDriver if driverName is specified - if (!string.IsNullOrEmpty (driverName)) - { - ForceDriver = driverName; - } - - // Check if we need to use a legacy driver (like FakeDriver) - // or go through the modern application architecture - if (Driver is null) - { - ApplicationImpl.Instance.Init (driver, driverName); - Debug.Assert (Driver is { }); - return; - } - - Debug.Assert (Navigation is null); - Navigation = new (); - - Debug.Assert (Popover is null); - Popover = new (); - - try - { - Driver!.Init (); - SubscribeDriverEvents (); - } - catch (InvalidOperationException ex) - { - // This is a case where the driver is unable to initialize the console. - // This can happen if the console is already in use by another process or - // if running in unit tests. - // In this case, we want to throw a more specific exception. - throw new InvalidOperationException ( - "Unable to initialize the console. This can happen if the console is already in use by another process or in unit tests.", - ex - ); - } - - SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext ()); - - // TODO: This is probably not needed - if (Popover.GetActivePopover () is View popover) - { - popover.Visible = false; - } - - MainThreadId = Thread.CurrentThread.ManagedThreadId; - bool init = Initialized = true; - InitializedChanged?.Invoke (null, new (init)); - } - internal static void SubscribeDriverEvents () { ArgumentNullException.ThrowIfNull (Driver); @@ -179,7 +69,10 @@ public static partial class Application // Lifecycle (Init/Shutdown) Driver.MouseEvent -= Driver_MouseEvent; } - private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { OnSizeChanging (e); } + private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) + { + RaiseScreenChangedEvent (new Rectangle (new (0, 0), e.Size!.Value)); + } private static void Driver_KeyDown (object? sender, Key e) { RaiseKeyDownEvent (e); } private static void Driver_KeyUp (object? sender, Key e) { RaiseKeyUpEvent (e); } private static void Driver_MouseEvent (object? sender, MouseEventArgs e) { RaiseMouseEvent (e); } diff --git a/Terminal.Gui/App/Application.Run.cs b/Terminal.Gui/App/Application.Run.cs index fc6920ba9..7477325aa 100644 --- a/Terminal.Gui/App/Application.Run.cs +++ b/Terminal.Gui/App/Application.Run.cs @@ -22,10 +22,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E set => Keyboard.ArrangeKey = value; } - // When `End ()` is called, it is possible `RunState.Toplevel` is a different object than `Top`. - // This variable is set in `End` in this case so that `Begin` correctly sets `Top`. - private static Toplevel? _cachedRunStateToplevel; - /// /// Notify that a new was created ( was called). The token is /// created in and this event will be fired before that function exits. @@ -43,7 +39,11 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E /// must also subscribe to and manually dispose of the token /// when the application is done. /// +#pragma warning disable CS0067 // Event is never used +#pragma warning disable CS0414 // Event is never used public static event EventHandler? NotifyStopRunState; +#pragma warning restore CS0414 // Event is never used +#pragma warning restore CS0067 // Event is never used /// Building block API: Prepares the provided for execution. /// @@ -74,7 +74,7 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E { // This assertion confirm if the Top was already disposed Debug.Assert (Top.WasDisposed); - Debug.Assert (Top == _cachedRunStateToplevel); + Debug.Assert (Top == CachedRunStateToplevel); } #endif @@ -84,7 +84,7 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E { // If Top was already disposed and isn't on the Toplevels Stack, // clean it up here if is the same as _cachedRunStateToplevel - if (Top == _cachedRunStateToplevel) + if (Top == CachedRunStateToplevel) { Top = null; } @@ -493,7 +493,7 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E Top.SetFocus (); } - _cachedRunStateToplevel = runState.Toplevel; + CachedRunStateToplevel = runState.Toplevel; runState.Toplevel = null; runState.Dispose (); diff --git a/Terminal.Gui/App/Application.Screen.cs b/Terminal.Gui/App/Application.Screen.cs index 879141412..92522c235 100644 --- a/Terminal.Gui/App/Application.Screen.cs +++ b/Terminal.Gui/App/Application.Screen.cs @@ -19,38 +19,26 @@ public static partial class Application // Screen related stuff; intended to hid } /// Invoked when the terminal's size changed. The new size of the terminal is provided. - /// - /// Event handlers can set to to prevent - /// from changing it's size to match the new terminal size. - /// - public static event EventHandler? SizeChanging; + public static event EventHandler>? ScreenChanged; /// - /// Called when the application's size changes. Sets the size of all s and fires the - /// event. + /// Called when the application's size has changed. Sets the size of all s and fires the + /// event. /// - /// The new size. - /// if the size was changed. - public static bool OnSizeChanging (SizeChangedEventArgs args) + /// The new screen size and position. + public static void RaiseScreenChangedEvent (Rectangle screen) { - SizeChanging?.Invoke (null, args); + Screen = new (Point.Empty, screen.Size); - if (args.Cancel || args.Size is null) - { - return false; - } - - Screen = new (Point.Empty, args.Size.Value); + ScreenChanged?.Invoke (ApplicationImpl.Instance, new (screen)); foreach (Toplevel t in TopLevels) { - t.OnSizeChanging (new (args.Size)); + t.OnSizeChanging (new (screen.Size)); t.SetNeedsLayout (); } LayoutAndDraw (true); - - return true; } /// diff --git a/Terminal.Gui/App/Application.Toplevel.cs b/Terminal.Gui/App/Application.Toplevel.cs index cea9818ef..eeb70522a 100644 --- a/Terminal.Gui/App/Application.Toplevel.cs +++ b/Terminal.Gui/App/Application.Toplevel.cs @@ -17,4 +17,12 @@ public static partial class Application // Toplevel handling get => ApplicationImpl.Instance.Top; internal set => ApplicationImpl.Instance.Top = value; } + + internal static Toplevel? CachedRunStateToplevel + { + get => ApplicationImpl.Instance.CachedRunStateToplevel; + private set => ApplicationImpl.Instance.CachedRunStateToplevel = value; + } + + } diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs index 944c07918..99991bded 100644 --- a/Terminal.Gui/App/Application.cs +++ b/Terminal.Gui/App/Application.cs @@ -206,15 +206,15 @@ public static partial class Application Debug.Assert (Top.WasDisposed, $"Title = {Top.Title}, Id = {Top.Id}"); // If End wasn't called _cachedRunStateToplevel may be null - if (_cachedRunStateToplevel is { }) + if (CachedRunStateToplevel is { }) { - Debug.Assert (_cachedRunStateToplevel.WasDisposed); - Debug.Assert (_cachedRunStateToplevel == Top); + Debug.Assert (CachedRunStateToplevel.WasDisposed); + Debug.Assert (CachedRunStateToplevel == Top); } } #endif Top = null; - _cachedRunStateToplevel = null; + CachedRunStateToplevel = null; MainThreadId = -1; Iteration = null; @@ -257,7 +257,7 @@ public static partial class Application // Keyboard events and bindings are now managed by the Keyboard instance - SizeChanging = null; + ScreenChanged = null; Navigation = null; diff --git a/Terminal.Gui/App/ApplicationImpl.cs b/Terminal.Gui/App/ApplicationImpl.cs index 0eb2a35f8..e167876dd 100644 --- a/Terminal.Gui/App/ApplicationImpl.cs +++ b/Terminal.Gui/App/ApplicationImpl.cs @@ -63,11 +63,6 @@ public class ApplicationImpl : IApplication set => _mouse = value ?? throw new ArgumentNullException (nameof (value)); } - /// - /// Handles which (if any) has captured the mouse - /// - public IMouseGrabHandler MouseGrabHandler { get; set; } = new MouseGrabHandler (); - private IKeyboard? _keyboard; /// @@ -134,7 +129,7 @@ public class ApplicationImpl : IApplication } set { - if (value is {} && (value.X != 0 || value.Y != 0)) + if (value is { } && (value.X != 0 || value.Y != 0)) { throw new NotImplementedException ($"Screen locations other than 0, 0 are not yet supported"); } @@ -177,6 +172,11 @@ public class ApplicationImpl : IApplication /// public ConcurrentStack TopLevels => _topLevels; + // When `End ()` is called, it is possible `RunState.Toplevel` is a different object than `Top`. + // This variable is set in `End` in this case so that `Begin` correctly sets `Top`. + /// + public Toplevel? CachedRunStateToplevel { get; set; } + /// /// Gets or sets the main thread ID for the application. /// @@ -231,10 +231,10 @@ public class ApplicationImpl : IApplication if (string.IsNullOrWhiteSpace (_driverName)) { - _driverName = Application.ForceDriver; + _driverName = ForceDriver; } - Debug.Assert(_navigation is null); + Debug.Assert (_navigation is null); _navigation = new (); Debug.Assert (_popover is null); @@ -264,7 +264,7 @@ public class ApplicationImpl : IApplication } CreateDriver (driverName ?? _driverName); - + Screen = Driver!.Screen; _initialized = true; Application.OnInitializedChanged (this, new (true)); @@ -276,23 +276,6 @@ public class ApplicationImpl : IApplication private void CreateDriver (string? driverName) { - // When running unit tests, always use FakeDriver unless explicitly specified - if (ConsoleDriver.RunningUnitTests && - string.IsNullOrEmpty (driverName) && - _componentFactory is null) - { - Logging.Logger.LogDebug ("Unit test safeguard: forcing FakeDriver (RunningUnitTests=true, driverName=null, componentFactory=null)"); - _coordinator = CreateSubcomponents (() => new FakeComponentFactory ()); - _coordinator.StartAsync ().Wait (); - - if (_driver == null) - { - throw new ("Driver was null even after booting MainLoopCoordinator"); - } - - return; - } - PlatformID p = Environment.OSVersion.Platform; // Check component factory type first - this takes precedence over driverName @@ -310,7 +293,10 @@ public class ApplicationImpl : IApplication // Decide which driver to use - component factory type takes priority if (factoryIsFake || (!factoryIsWindows && !factoryIsDotNet && !factoryIsUnix && nameIsFake)) { - _coordinator = CreateSubcomponents (() => new FakeComponentFactory ()); + FakeConsoleOutput fakeOutput = new (); + fakeOutput.SetConsoleSize (80, 25); + + _coordinator = CreateSubcomponents (() => new FakeComponentFactory (null, fakeOutput)); } else if (factoryIsWindows || (!factoryIsDotNet && !factoryIsUnix && nameIsWindows)) { @@ -410,7 +396,7 @@ public class ApplicationImpl : IApplication if (_driver == null) { - throw new InvalidOperationException ("Driver was inexplicably null when trying to Run view"); + throw new InvalidOperationException ("Driver was inexplicably null when trying to Run view"); } _top = view; @@ -437,17 +423,17 @@ public class ApplicationImpl : IApplication public void Shutdown () { _coordinator?.Stop (); - + bool wasInitialized = _initialized; - + // Reset Screen before calling Application.ResetState to avoid circular reference ResetScreen (); - + // Call ResetState FIRST so it can properly dispose Popover and other resources // that are accessed via Application.* static properties that now delegate to instance fields Application.ResetState (); ConfigurationManager.PrintJsonErrors (); - + // Clear instance fields after ResetState has disposed everything _driver = null; _mouse = null; @@ -455,6 +441,7 @@ public class ApplicationImpl : IApplication _initialized = false; _navigation = null; _popover = null; + CachedRunStateToplevel = null; _top = null; _topLevels.Clear (); _mainThreadId = -1; @@ -475,7 +462,7 @@ public class ApplicationImpl : IApplication /// public void RequestStop (Toplevel? top) { - Logging.Logger.LogInformation ($"RequestStop '{(top is {} ? top : "null")}'"); + Logging.Logger.LogInformation ($"RequestStop '{(top is { } ? top : "null")}'"); top ??= _top; @@ -499,7 +486,7 @@ public class ApplicationImpl : IApplication public void Invoke (Action action) { // If we are already on the main UI thread - if (Application.Top is { Running: true } && _mainThreadId == Thread.CurrentThread.ManagedThreadId) + if (Top is { Running: true } && _mainThreadId == Thread.CurrentThread.ManagedThreadId) { action (); return; diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs index 93a4c7c22..1fb9e84f0 100644 --- a/Terminal.Gui/App/IApplication.cs +++ b/Terminal.Gui/App/IApplication.cs @@ -74,6 +74,11 @@ public interface IApplication /// Gets the stack of all Toplevels. System.Collections.Concurrent.ConcurrentStack TopLevels { get; } + /// + /// Caches the Toplevel associated with the current RunState. + /// + Toplevel? CachedRunStateToplevel { get; set; } + /// Requests that the application stop running. void RequestStop (); diff --git a/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs b/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs index 97aff25d6..c64ff95b4 100644 --- a/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs +++ b/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs @@ -27,7 +27,7 @@ public class ApplicationMainLoop : IApplicationMainLoop private IInputProcessor? _inputProcessor; private IConsoleOutput? _out; private AnsiRequestScheduler? _ansiRequestScheduler; - private IWindowSizeMonitor? _windowSizeMonitor; + private IConsoleSizeMonitor? _consoleSizeMonitor; /// public ITimedEvents TimedEvents @@ -74,10 +74,10 @@ public class ApplicationMainLoop : IApplicationMainLoop } /// - public IWindowSizeMonitor WindowSizeMonitor + public IConsoleSizeMonitor ConsoleSizeMonitor { - get => _windowSizeMonitor ?? throw new NotInitializedException (nameof (WindowSizeMonitor)); - private set => _windowSizeMonitor = value; + get => _consoleSizeMonitor ?? throw new NotInitializedException (nameof (ConsoleSizeMonitor)); + private set => _consoleSizeMonitor = value; } /// @@ -114,7 +114,7 @@ public class ApplicationMainLoop : IApplicationMainLoop TimedEvents = timedEvents; AnsiRequestScheduler = new (InputProcessor.GetParser ()); - WindowSizeMonitor = componentFactory.CreateWindowSizeMonitor (Out, OutputBuffer); + ConsoleSizeMonitor = componentFactory.CreateConsoleSizeMonitor (Out, OutputBuffer); } /// @@ -152,7 +152,7 @@ public class ApplicationMainLoop : IApplicationMainLoop || AnySubViewsNeedDrawn (Application.Top) || (Application.Mouse.MouseGrabView != null && AnySubViewsNeedDrawn (Application.Mouse.MouseGrabView)); - bool sizeChanged = WindowSizeMonitor.Poll (); + bool sizeChanged = ConsoleSizeMonitor.Poll (); if (needsDrawOrLayout || sizeChanged) { diff --git a/Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs b/Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs index eaaa95e07..f4069315e 100644 --- a/Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs +++ b/Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs @@ -48,7 +48,7 @@ public interface IApplicationMainLoop : IDisposable /// /// Gets the class responsible for determining the current console size /// - public IWindowSizeMonitor WindowSizeMonitor { get; } + public IConsoleSizeMonitor ConsoleSizeMonitor { get; } /// /// Initializes the loop with a buffer from which data can be read diff --git a/Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs b/Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs index 512a340be..60f6ced3e 100644 --- a/Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs +++ b/Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs @@ -149,7 +149,7 @@ internal class MainLoopCoordinator : IMainLoopCoordinator _loop.OutputBuffer, _output, _loop.AnsiRequestScheduler, - _loop.WindowSizeMonitor); + _loop.ConsoleSizeMonitor); Application.Driver = _facade; diff --git a/Terminal.Gui/App/Timeout/ITimedEvents.cs b/Terminal.Gui/App/Timeout/ITimedEvents.cs index 501da77bc..225301ffe 100644 --- a/Terminal.Gui/App/Timeout/ITimedEvents.cs +++ b/Terminal.Gui/App/Timeout/ITimedEvents.cs @@ -27,18 +27,6 @@ public interface ITimedEvents /// event EventHandler? Added; - /// - /// Called from to check if there are any outstanding timer handlers. - /// - /// - /// Returns the number of milliseconds remaining in the current timer (if any). Will be -1 if - /// there are no active timers. - /// - /// - /// if there is a timer active; otherwise, . - /// - bool CheckTimers (out int waitTimeout); - /// /// Removes a previously scheduled timeout. /// diff --git a/Terminal.Gui/Drawing/Ruler.cs b/Terminal.Gui/Drawing/Ruler.cs index 258ce8c12..b7e91485d 100644 --- a/Terminal.Gui/Drawing/Ruler.cs +++ b/Terminal.Gui/Drawing/Ruler.cs @@ -23,7 +23,8 @@ internal class Ruler /// Draws the . /// The location to start drawing the ruler, in screen-relative coordinates. /// The start value of the ruler. - public void Draw (Point location, int start = 0) + /// Optional Driver. If not provided, driver will be used. + public void Draw (Point location, int start = 0, IConsoleDriver? driver = null) { if (start < 0) { @@ -35,14 +36,16 @@ internal class Ruler return; } + driver ??= driver; + if (Orientation == Orientation.Horizontal) { string hrule = _hTemplate.Repeat ((int)Math.Ceiling (Length + 2 / (double)_hTemplate.Length))! [start..(Length + start)]; // Top - Application.Driver?.Move (location.X, location.Y); - Application.Driver?.AddStr (hrule); + driver?.Move (location.X, location.Y); + driver?.AddStr (hrule); } else { @@ -52,8 +55,8 @@ internal class Ruler for (int r = location.Y; r < location.Y + Length; r++) { - Application.Driver?.Move (location.X, r); - Application.Driver?.AddRune ((Rune)vrule [r - location.Y]); + driver?.Move (location.X, r); + driver?.AddRune ((Rune)vrule [r - location.Y]); } } } diff --git a/Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs b/Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs index 1d9c78cab..01df783a4 100644 --- a/Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs +++ b/Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs @@ -53,21 +53,21 @@ public class SixelSupportDetector private void TryComputeResolution (SixelSupportResult result, Action resultCallback) { - string windowSize; + string consoleSize; string sizeInChars; QueueRequest ( EscSeqUtils.CSI_RequestWindowSizeInPixels, r1 => { - windowSize = r1; + consoleSize = r1; QueueRequest ( - EscSeqUtils.CSI_ReportTerminalSizeInChars, + EscSeqUtils.CSI_ReportWindowSizeInChars, r2 => { sizeInChars = r2; - ComputeResolution (result, windowSize, sizeInChars); + ComputeResolution (result, consoleSize, sizeInChars); resultCallback (result); }, () => resultCallback (result)); @@ -75,11 +75,11 @@ public class SixelSupportDetector () => resultCallback (result)); } - private void ComputeResolution (SixelSupportResult result, string windowSize, string sizeInChars) + private void ComputeResolution (SixelSupportResult result, string consoleSize, string sizeInChars) { // Fallback to window size in pixels and characters // Example [4;600;1200t - Match pixelMatch = Regex.Match (windowSize, @"\[\d+;(\d+);(\d+)t$"); + Match pixelMatch = Regex.Match (consoleSize, @"\[\d+;(\d+);(\d+)t$"); // Example [8;30;120t Match charMatch = Regex.Match (sizeInChars, @"\[\d+;(\d+);(\d+)t$"); diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs index b8690a060..72b0f6ff8 100644 --- a/Terminal.Gui/Drawing/Thickness.cs +++ b/Terminal.Gui/Drawing/Thickness.cs @@ -88,14 +88,17 @@ public record struct Thickness /// The location and size of the rectangle that bounds the thickness rectangle, in screen coordinates. /// /// The diagnostics label to draw on the bottom of the . + /// Optional driver. If not specified, will be used. /// The inner rectangle remaining to be drawn. - public Rectangle Draw (Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string? label = null) + public Rectangle Draw (Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string? label = null, IConsoleDriver? driver = null) { if (rect.Size.Width < 1 || rect.Size.Height < 1) { return Rectangle.Empty; } + driver ??= Application.Driver; + var clearChar = (Rune)' '; Rune leftChar = clearChar; Rune rightChar = clearChar; @@ -118,71 +121,71 @@ public record struct Thickness // Draw the Top side if (Top > 0) { - Application.Driver?.FillRect (rect with { Height = Math.Min (rect.Height, Top) }, topChar); + driver?.FillRect (rect with { Height = Math.Min (rect.Height, Top) }, topChar); } // Draw the Left side // Draw the Left side if (Left > 0) { - Application.Driver?.FillRect (rect with { Width = Math.Min (rect.Width, Left) }, leftChar); + driver?.FillRect (rect with { Width = Math.Min (rect.Width, Left) }, leftChar); } // Draw the Right side if (Right > 0) { - Application.Driver?.FillRect ( - rect with - { - X = Math.Max (0, rect.X + rect.Width - Right), - Width = Math.Min (rect.Width, Right) - }, - rightChar - ); + driver?.FillRect ( + rect with + { + X = Math.Max (0, rect.X + rect.Width - Right), + Width = Math.Min (rect.Width, Right) + }, + rightChar + ); } // Draw the Bottom side if (Bottom > 0) { - Application.Driver?.FillRect ( - rect with - { - Y = rect.Y + Math.Max (0, rect.Height - Bottom), - Height = Bottom - }, - bottomChar - ); + driver?.FillRect ( + rect with + { + Y = rect.Y + Math.Max (0, rect.Height - Bottom), + Height = Bottom + }, + bottomChar + ); } if (diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler)) { // PERF: This can almost certainly be simplified down to a single point offset and fewer calls to Draw // Top - var hruler = new Ruler { Length = rect.Width, Orientation = Orientation.Horizontal }; + Ruler hRuler = new () { Length = rect.Width, Orientation = Orientation.Horizontal }; if (Top > 0) { - hruler.Draw (rect.Location); + hRuler.Draw (rect.Location, driver: driver); } //Left - var vruler = new Ruler { Length = rect.Height - 2, Orientation = Orientation.Vertical }; + Ruler vRuler = new () { Length = rect.Height - 2, Orientation = Orientation.Vertical }; if (Left > 0) { - vruler.Draw (rect.Location with { Y = rect.Y + 1 }, 1); + vRuler.Draw (rect.Location with { Y = rect.Y + 1 }, 1, driver); } // Bottom if (Bottom > 0) { - hruler.Draw (rect.Location with { Y = rect.Y + rect.Height - 1 }); + hRuler.Draw (rect.Location with { Y = rect.Y + rect.Height - 1 }, driver: driver); } // Right if (Right > 0) { - vruler.Draw (new (rect.X + rect.Width - 1, rect.Y + 1), 1); + vRuler.Draw (new (rect.X + rect.Width - 1, rect.Y + 1), 1, driver); } } @@ -191,7 +194,7 @@ public record struct Thickness // Draw the diagnostics label on the bottom string text = label is null ? string.Empty : $"{label} {this}"; - var tf = new TextFormatter + TextFormatter tf = new () { Text = text, Alignment = Alignment.Center, @@ -200,9 +203,9 @@ public record struct Thickness ConstrainToHeight = 1 }; - if (Application.Driver?.CurrentAttribute is { }) + if (driver?.CurrentAttribute is { }) { - tf.Draw (rect, Application.Driver!.CurrentAttribute, Application.Driver!.CurrentAttribute, rect); + tf.Draw (rect, driver!.CurrentAttribute, driver!.CurrentAttribute, rect, driver); } } @@ -242,7 +245,7 @@ public record struct Thickness /// public Region AsRegion (Rectangle rect) { - Region region = new Region (rect); + var region = new Region (rect); region.Exclude (GetInside (rect)); return region; diff --git a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs index 97dbc933e..8e4c5e9e8 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs @@ -1,14 +1,10 @@ #nullable enable +using System.Diagnostics; using System.Globalization; -namespace Terminal.Gui.Drivers; +// ReSharper disable InconsistentNaming -// QUESTION: Should this class be refactored into separate classes for: -// QUESTION: CSI definitions -// QUESTION: Primitives like DecodeEsqReq -// QUESTION: Screen/Color/Cursor handling -// QUESTION: Mouse handling -// QUESTION: Keyboard handling +namespace Terminal.Gui.Drivers; /// /// Provides a platform-independent API for managing ANSI escape sequences. @@ -21,7 +17,18 @@ namespace Terminal.Gui.Drivers; /// public static class EscSeqUtils { - // TODO: One type per file - Move this enum to a separate file. + /// + /// Escape key code (ASCII 27/0x1B). + /// + public const char KeyEsc = (char)KeyCode.Esc; + + /// + /// ESC [ - The CSI (Control Sequence Introducer). + /// + public const string CSI = "\u001B["; + + #region Screen Window Buffer + /// /// Options for ANSI ESC "[xJ" - Clears part of the screen. /// @@ -48,19 +55,6 @@ public static class EscSeqUtils EntireScreenAndScrollbackBuffer = 3 } - // QUESTION: I wonder if EscSeqUtils.CSI_... should be more strongly typed such that this (and Terminator could be - // QUESTION: public required CSIRequests Request { get; init; } - // QUESTION: public required CSITerminators Terminator { get; init; } - /// - /// Escape key code (ASCII 27/0x1B). - /// - public const char KeyEsc = (char)KeyCode.Esc; - - /// - /// ESC [ - The CSI (Control Sequence Introducer). - /// - public const string CSI = "\u001B["; - /// /// ESC [ ? 1047 h - Activate xterm alternative buffer (no backscroll) /// @@ -71,36 +65,6 @@ public static class EscSeqUtils /// public static readonly string CSI_ActivateAltBufferNoBackscroll = CSI + "?1047h"; - /// - /// ESC [ ? 1003 l - Disable any mouse event tracking. - /// - public static readonly string CSI_DisableAnyEventMouse = CSI + "?1003l"; - - /// - /// ESC [ ? 1006 l - Disable SGR (Select Graphic Rendition). - /// - public static readonly string CSI_DisableSgrExtModeMouse = CSI + "?1006l"; - - /// - /// ESC [ ? 1015 l - Disable URXVT (Unicode Extended Virtual Terminal). - /// - public static readonly string CSI_DisableUrxvtExtModeMouse = CSI + "?1015l"; - - /// - /// ESC [ ? 1003 h - Enable mouse event tracking. - /// - public static readonly string CSI_EnableAnyEventMouse = CSI + "?1003h"; - - /// - /// ESC [ ? 1006 h - Enable SGR (Select Graphic Rendition). - /// - public static readonly string CSI_EnableSgrExtModeMouse = CSI + "?1006h"; - - /// - /// ESC [ ? 1015 h - Enable URXVT (Unicode Extended Virtual Terminal). - /// - public static readonly string CSI_EnableUrxvtExtModeMouse = CSI + "?1015h"; - /// /// ESC [ ? 1047 l - Restore xterm working buffer (with backscroll) /// @@ -135,20 +99,52 @@ public static class EscSeqUtils /// public static readonly string CSI_SaveCursorAndActivateAltBufferNoBackscroll = CSI + "?1049h"; - //private static bool isButtonReleased; - private static bool _isButtonClicked; + /// + /// ESC [ x J - Clears part of the screen. See . + /// + /// + /// + public static string CSI_ClearScreen (ClearScreenOptions option) { return $"{CSI}{(int)option}J"; } - private static bool _isButtonDoubleClicked; + /// + /// ESC [ 8 ; height ; width t - Set Terminal Window Size + /// https://terminalguide.namepad.de/seq/csi_st-8/ + /// + public static string CSI_SetTerminalWindowSize (int height, int width) { return $"{CSI}8;{height};{width}t"; } - //private static MouseFlags? lastMouseButtonReleased; - // QUESTION: What's the difference between isButtonClicked and isButtonPressed? - // Some clarity or comments would be handy, here. - // It also seems like some enforcement of valid states might be a good idea. - private static bool _isButtonPressed; - private static bool _isButtonTripleClicked; + #endregion Screen Window Buffer - private static MouseFlags? _lastMouseButtonPressed; - private static Point? _point; + #region Mouse + + /// + /// ESC [ ? 1003 l - Disable any mouse event tracking. + /// + public static readonly string CSI_DisableAnyEventMouse = CSI + "?1003l"; + + /// + /// ESC [ ? 1006 l - Disable SGR (Select Graphic Rendition). + /// + public static readonly string CSI_DisableSgrExtModeMouse = CSI + "?1006l"; + + /// + /// ESC [ ? 1015 l - Disable URXVT (Unicode Extended Virtual Terminal). + /// + public static readonly string CSI_DisableUrxvtExtModeMouse = CSI + "?1015l"; + + /// + /// ESC [ ? 1003 h - Enable mouse event tracking. + /// + public static readonly string CSI_EnableAnyEventMouse = CSI + "?1003h"; + + /// + /// ESC [ ? 1006 h - Enable SGR (Select Graphic Rendition). + /// + public static readonly string CSI_EnableSgrExtModeMouse = CSI + "?1006h"; + + /// + /// ESC [ ? 1015 h - Enable URXVT (Unicode Extended Virtual Terminal). + /// + public static readonly string CSI_EnableUrxvtExtModeMouse = CSI + "?1015h"; /// /// Control sequence for disabling mouse events. @@ -162,882 +158,16 @@ public static class EscSeqUtils public static readonly string CSI_EnableMouseEvents = CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse; - /// - /// ESC [ x J - Clears part of the screen. See . - /// - /// - /// - public static string CSI_ClearScreen (ClearScreenOptions option) { return $"{CSI}{(int)option}J"; } + #endregion Mouse + + #region Keyboard /// - /// Specify the incomplete array not yet recognized as valid ANSI escape sequence. - /// - public static ConsoleKeyInfo []? IncompleteCkInfos { get; set; } - - /// - /// Decodes an ANSI escape sequence. - /// - /// The which may change. - /// The which may change. - /// The array. - /// The which may change. - /// The control returned by the method. - /// The code returned by the method. - /// The values returned by the method. - /// The terminator returned by the method. - /// Indicates if the escape sequence is a mouse event. - /// 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 ( - ref ConsoleKeyInfo newConsoleKeyInfo, - ref ConsoleKey key, - ConsoleKeyInfo [] cki, - ref ConsoleModifiers mod, - out string c1Control, - out string code, - out string [] values, - out string terminator, - out bool isMouse, - out List buttonState, - out Point pos, - out bool isResponse, - Action? continuousButtonPressedHandler - ) - { - char [] kChars = GetKeyCharArray (cki); - (c1Control, code, values, terminator) = GetEscapeResult (kChars); - isMouse = false; - buttonState = [0]; - pos = default (Point); - isResponse = false; - var keyChar = '\0'; - - switch (c1Control) - { - case "ESC": - if (values is null && string.IsNullOrEmpty (terminator)) - { - key = ConsoleKey.Escape; - - newConsoleKeyInfo = new ( - 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 && (uint)cki [1].KeyChar != '\n' && (uint)cki [1].KeyChar != '\r') - { - key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); - mod = ConsoleModifiers.Alt | ConsoleModifiers.Control; - - newConsoleKeyInfo = new ( - cki [1].KeyChar, - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } - else if (cki [1].KeyChar >= 65 && cki [1].KeyChar <= 90) - { - key = (ConsoleKey)cki [1].KeyChar; - mod = ConsoleModifiers.Shift | ConsoleModifiers.Alt; - - newConsoleKeyInfo = new ( - cki [1].KeyChar, - (ConsoleKey)Math.Min ((uint)key, 255), - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } - else if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) - { - key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; - mod = ConsoleModifiers.Alt; - - newConsoleKeyInfo = new ( - cki [1].KeyChar, - (ConsoleKey)Math.Min ((uint)key, 255), - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } - else if (cki [1].KeyChar is '\0' or ' ') - { - key = ConsoleKey.Spacebar; - - if (kChars.Length > 1 && kChars [1] == '\0') - { - mod = ConsoleModifiers.Alt | ConsoleModifiers.Control; - } - else - { - mod = ConsoleModifiers.Shift | ConsoleModifiers.Alt; - } - - newConsoleKeyInfo = new ( - cki [1].KeyChar, - (ConsoleKey)Math.Min ((uint)key, 255), - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } - else if (cki [1].KeyChar is '\n' or '\r') - { - key = ConsoleKey.Enter; - - if (kChars.Length > 1 && kChars [1] == '\n') - { - mod = ConsoleModifiers.Alt | ConsoleModifiers.Control; - } - else - { - mod = ConsoleModifiers.Shift | ConsoleModifiers.Alt; - } - - newConsoleKeyInfo = new ( - cki [1].KeyChar, - (ConsoleKey)Math.Min ((uint)key, 255), - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } - else - { - key = (ConsoleKey)cki [1].KeyChar; - mod = ConsoleModifiers.Alt; - - newConsoleKeyInfo = new ( - cki [1].KeyChar, - (ConsoleKey)Math.Min ((uint)key, 255), - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } - - break; - case "SS3": - key = GetConsoleKey (terminator [0], values [0], ref mod, ref keyChar); - - newConsoleKeyInfo = new ( - keyChar, - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - - break; - case "CSI": - // Reset always IncompleteCkInfos - if (IncompleteCkInfos is { }) - { - IncompleteCkInfos = null; - } - - if (!string.IsNullOrEmpty (code) && code == "<") - { - GetMouse (cki, out buttonState, out pos, continuousButtonPressedHandler); - isMouse = true; - - return; - } - - if (EscSeqRequests.HasResponse (terminator)) - { - isResponse = true; - EscSeqRequests.Remove (terminator); - - return; - } - - if (!string.IsNullOrEmpty (terminator)) - { - System.Diagnostics.Debug.Assert (terminator.Length == 1); - - key = GetConsoleKey (terminator [0], values [0], ref mod, ref keyChar); - - if (key != 0 && values.Length > 1) - { - mod |= GetConsoleModifiers (values [1]); - } - - if (keyChar != 0 || key != 0 || mod != 0) - { - newConsoleKeyInfo = new ( - keyChar, - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } - else - { - // It's request response that wasn't handled by a valid request terminator - System.Diagnostics.Debug.Assert (EscSeqRequests.Statuses.Count > 0); - - isResponse = true; - EscSeqRequests.Remove (terminator); - } - } - else - { - // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/2803 - // This is caused by DotNetDriver depending on Console.KeyAvailable? - //throw new InvalidOperationException ("CSI response, but there's no terminator"); - - IncompleteCkInfos = cki; - } - - break; - default: - newConsoleKeyInfo = MapConsoleKeyInfo (cki [0]); - key = newConsoleKeyInfo.Key; - mod = newConsoleKeyInfo.Modifiers; - - break; - } - } - - /// - /// Gets the c1Control used in the called escape sequence. - /// - /// The char used. - /// The c1Control. - [Pure] - public static string GetC1ControlChar (in char c) - { - // These control characters are used in the vtXXX emulation. - return c switch - { - 'D' => "IND", // Index - 'E' => "NEL", // Next Line - 'H' => "HTS", // Tab Set - 'M' => "RI", // Reverse Index - 'N' => "SS2", // Single Shift Select of G2 Character Set: affects next character only - 'O' => "SS3", // Single Shift Select of G3 Character Set: affects next character only - 'P' => "DCS", // Device Control String - 'V' => "SPA", // Start of Guarded Area - 'W' => "EPA", // End of Guarded Area - 'X' => "SOS", // Start of String - 'Z' => "DECID", // Return Terminal ID Obsolete form of CSI c (DA) - '[' => "CSI", // Control Sequence Introducer - '\\' => "ST", // String Terminator - ']' => "OSC", // Operating System Command - '^' => "PM", // Privacy Message - '_' => "APC", // Application Program Command - _ => string.Empty - }; - } - - - /// - /// Gets the depending on terminating and value. - /// - /// - /// The terminator indicating a reply to or - /// . - /// - /// The value. - /// The which may change. - /// Normally is '\0' but on some cases may need other value. - /// The and probably the . - public static ConsoleKey GetConsoleKey (char terminator, string? value, ref ConsoleModifiers mod, ref char keyChar) - { - if (terminator == 'Z') - { - mod |= ConsoleModifiers.Shift; - } - - if (terminator == 'l') - { - keyChar = '+'; - } - - if (terminator == 'm') - { - keyChar = '-'; - } - - return (terminator, value) switch - { - ('A', _) => ConsoleKey.UpArrow, - ('B', _) => ConsoleKey.DownArrow, - ('C', _) => ConsoleKey.RightArrow, - ('D', _) => ConsoleKey.LeftArrow, - ('E', _) => ConsoleKey.Clear, - ('F', _) => ConsoleKey.End, - ('H', _) => ConsoleKey.Home, - ('P', _) => ConsoleKey.F1, - ('Q', _) => ConsoleKey.F2, - ('R', _) => ConsoleKey.F3, - ('S', _) => ConsoleKey.F4, - ('Z', _) => ConsoleKey.Tab, - ('~', "2") => ConsoleKey.Insert, - ('~', "3") => ConsoleKey.Delete, - ('~', "5") => ConsoleKey.PageUp, - ('~', "6") => ConsoleKey.PageDown, - ('~', "15") => ConsoleKey.F5, - ('~', "17") => ConsoleKey.F6, - ('~', "18") => ConsoleKey.F7, - ('~', "19") => ConsoleKey.F8, - ('~', "20") => ConsoleKey.F9, - ('~', "21") => ConsoleKey.F10, - ('~', "23") => ConsoleKey.F11, - ('~', "24") => ConsoleKey.F12, - // These terminators are used by macOS on a numeric keypad without keys modifiers - ('l', null) => ConsoleKey.Add, - ('m', null) => ConsoleKey.Subtract, - ('p', null) => ConsoleKey.Insert, - ('q', null) => ConsoleKey.End, - ('r', null) => ConsoleKey.DownArrow, - ('s', null) => ConsoleKey.PageDown, - ('t', null) => ConsoleKey.LeftArrow, - ('u', null) => ConsoleKey.Clear, - ('v', null) => ConsoleKey.RightArrow, - ('w', null) => ConsoleKey.Home, - ('x', null) => ConsoleKey.UpArrow, - ('y', null) => ConsoleKey.PageUp, - (_, _) => 0 - }; - } - - /// - /// Gets the from the value. - /// - /// The value. - /// The or zero. - public static ConsoleModifiers GetConsoleModifiers (string? value) - { - return value switch - { - "2" => ConsoleModifiers.Shift, - "3" => ConsoleModifiers.Alt, - "4" => ConsoleModifiers.Shift | ConsoleModifiers.Alt, - "5" => ConsoleModifiers.Control, - "6" => ConsoleModifiers.Shift | ConsoleModifiers.Control, - "7" => ConsoleModifiers.Alt | ConsoleModifiers.Control, - "8" => ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control, - _ => 0 - }; - } -#nullable restore - - /// - /// Gets all the needed information about an 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 is null || kChar.Length == 0 || (kChar.Length == 1 && kChar [0] != KeyEsc)) - { - return (null, null, null, null); - } - - if (kChar [0] != KeyEsc) - { - 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 (static x => x == ';') + 1; - var values = new string [nSep]; - var valueIdx = 0; - var terminating = string.Empty; - - for (var i = 2; i < kChar.Length; i++) - { - char c = kChar [i]; - - if (char.IsDigit (c)) - { - // PERF: Ouch - values [valueIdx] += c.ToString (); - } - else if (c == ';') - { - valueIdx++; - } - else if (valueIdx == nSep - 1 || i == kChar.Length - 1) - { - // PERF: Ouch - terminating += c.ToString (); - } - else - { - // PERF: Ouch - code += c.ToString (); - } - } - - return (c1Control, code, values, terminating); - } - - /// - /// A helper to get only the from the array. - /// - /// - /// The char array of the escape sequence. - // PERF: This is expensive - public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki) - { - char [] kChar = []; - var length = 0; - - foreach (ConsoleKeyInfo kc in cki) - { - length++; - Array.Resize (ref kChar, length); - kChar [length - 1] = kc.KeyChar; - } - - return kChar; - } - - /// - /// 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 = Point.Empty; - var buttonCode = 0; - var foundButtonCode = false; - var foundPoint = 0; - string value = string.Empty; - char [] kChar = GetKeyCharArray (cki); - - // PERF: This loop could benefit from use of Spans and other strategies to avoid copies. - //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}"); - for (var i = 0; i < kChar.Length; i++) - { - // PERF: Copy - char c = kChar [i]; - - if (c == '<') - { - foundButtonCode = true; - } - else if (foundButtonCode && c != ';') - { - // PERF: Ouch - value += c.ToString (); - } - else if (c == ';') - { - if (foundButtonCode) - { - foundButtonCode = false; - buttonCode = int.Parse (value); - } - - if (foundPoint == 1) - { - pos.X = int.Parse (value) - 1; - } - - value = string.Empty; - 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 = [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 is null) - || (_isButtonPressed && _lastMouseButtonPressed is { } && buttonState.HasFlag (MouseFlags.ReportMousePosition))) - { - mouseFlags [0] = buttonState; - _lastMouseButtonPressed = buttonState; - _isButtonPressed = true; - - _point = pos; - - - if (mouseFlags [0].HasFlag (MouseFlags.ReportMousePosition)) - { - _point = pos; - - // The isButtonPressed must always be true, otherwise we can lose the feature - // If mouse flags has ReportMousePosition this feature won't run - // but is always prepared with the new location - //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; - - ApplicationImpl.Instance.TimedEvents?.Add (TimeSpan.Zero, - () => - { - 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.AddTimeout (() => { - // 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; - - ApplicationImpl.Instance.TimedEvents?.Add (TimeSpan.Zero, - () => - { - 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}"); - //} - } - - /// - /// Helper to set the Control key states based on the char. + /// Helper to set the Control key states based on the char. /// /// The char value. /// - public static ConsoleKeyInfo MapChar (char ch) - { - return MapConsoleKeyInfo (new (ch, ConsoleKey.None, false, false, false)); - } + public static ConsoleKeyInfo MapChar (char ch) { return MapConsoleKeyInfo (new (ch, ConsoleKey.None, false, false, false)); } /// /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences. @@ -1047,7 +177,7 @@ public static class EscSeqUtils public static ConsoleKeyInfo MapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo; - ConsoleKey key = ConsoleKey.None; + var key = ConsoleKey.None; char keyChar = consoleKeyInfo.KeyChar; switch ((uint)keyChar) @@ -1142,7 +272,7 @@ public static class EscSeqUtils } break; - case uint n when n is >= '\u001c' and <= '\u001f': + case uint n when n is >= '\u001c' and <= '\u001f': key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + 24); newConsoleKeyInfo = new ( @@ -1195,203 +325,6 @@ public static class EscSeqUtils } } - private static MouseFlags _lastMouseFlags; - - /// - /// Provides a handler to be invoked when mouse continuous button pressed is processed. - /// - public static event EventHandler ContinuousButtonPressed; - - /// - /// Provides a default mouse event handler that can be used by any driver. - /// - /// The mouse flags event. - /// The mouse position. - public static void ProcessMouseEvent (MouseFlags mouseFlag, Point pos) - { - bool WasButtonReleased (MouseFlags flag) - { - return flag.HasFlag (MouseFlags.Button1Released) - || flag.HasFlag (MouseFlags.Button2Released) - || flag.HasFlag (MouseFlags.Button3Released) - || flag.HasFlag (MouseFlags.Button4Released); - } - - bool IsButtonNotPressed (MouseFlags flag) - { - return !flag.HasFlag (MouseFlags.Button1Pressed) - && !flag.HasFlag (MouseFlags.Button2Pressed) - && !flag.HasFlag (MouseFlags.Button3Pressed) - && !flag.HasFlag (MouseFlags.Button4Pressed); - } - - bool IsButtonClickedOrDoubleClicked (MouseFlags flag) - { - return flag.HasFlag (MouseFlags.Button1Clicked) - || flag.HasFlag (MouseFlags.Button2Clicked) - || flag.HasFlag (MouseFlags.Button3Clicked) - || flag.HasFlag (MouseFlags.Button4Clicked) - || flag.HasFlag (MouseFlags.Button1DoubleClicked) - || flag.HasFlag (MouseFlags.Button2DoubleClicked) - || flag.HasFlag (MouseFlags.Button3DoubleClicked) - || flag.HasFlag (MouseFlags.Button4DoubleClicked); - } - - if ((WasButtonReleased (mouseFlag) && IsButtonNotPressed (_lastMouseFlags)) || (IsButtonClickedOrDoubleClicked (mouseFlag) && _lastMouseFlags == 0)) - { - return; - } - - _lastMouseFlags = mouseFlag; - - var me = new MouseEventArgs { Flags = mouseFlag, Position = pos }; - - ContinuousButtonPressed?.Invoke ((mouseFlag, pos), me); - } - - /// - /// 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 is null ? 1 : cki.Length + 1); - cki [^1] = consoleKeyInfo; - - return cki; - } - - /// - /// Insert a array into the another array at the specified - /// index. - /// - /// The array to insert. - /// The array where will be added the array. - /// The start index to insert the array, default is 0. - /// The array with another array inserted. - public static ConsoleKeyInfo [] InsertArray ([CanBeNull] ConsoleKeyInfo [] toInsert, ConsoleKeyInfo [] cki, int index = 0) - { - if (toInsert is null) - { - return cki; - } - - if (cki is null) - { - return toInsert; - } - - if (index < 0) - { - index = 0; - } - - ConsoleKeyInfo [] backupCki = cki.Clone () as ConsoleKeyInfo []; - - Array.Resize (ref cki, cki.Length + toInsert.Length); - - for (var i = 0; i < cki.Length; i++) - { - if (i == index) - { - for (var j = 0; j < toInsert.Length; j++) - { - cki [i] = toInsert [j]; - i++; - } - - for (int k = index; k < backupCki!.Length; k++) - { - cki [i] = backupCki [k]; - i++; - } - } - else - { - cki [i] = backupCki! [i]; - } - } - - return cki; - } - - 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; - } - internal static KeyCode MapKey (ConsoleKeyInfo keyInfo) { switch (keyInfo.Key) @@ -1441,7 +374,7 @@ public static class EscSeqUtils && keyInfo.Key != ConsoleKey.Oem102) { // If the keyChar is 0, keyInfo.Key value is not a printable character. - System.Diagnostics.Debug.Assert (keyInfo.Key == 0); + Debug.Assert (keyInfo.Key == 0); } return KeyCode.Null; // MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode)keyInfo.Key); @@ -1522,143 +455,7 @@ public static class EscSeqUtils return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar); } - 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 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; - } - - /// - /// Split a raw string into a list of string with the correct ansi escape sequence. - /// - /// The raw string containing one or many ansi escape sequence. - /// A list with a valid ansi escape sequence. - public static List SplitEscapeRawString (string rawData) - { - List splitList = []; - var isEscSeq = false; - var split = string.Empty; - char previousChar = '\0'; - - for (var i = 0; i < rawData.Length; i++) - { - char c = rawData [i]; - - if (c == '\u001B') - { - isEscSeq = true; - - split = AddAndClearSplit (); - - split += c.ToString (); - } - else if (!isEscSeq && c >= Key.Space) - { - split = AddAndClearSplit (); - splitList.Add (c.ToString ()); - } - else if ((previousChar != '\u001B' && c <= Key.Space) || (previousChar != '\u001B' && c == 127) - || (char.IsLetter (previousChar) && char.IsLower (c) && char.IsLetter (c)) - || (!string.IsNullOrEmpty (split) && split.Length > 2 && char.IsLetter (previousChar) && char.IsLetterOrDigit (c)) - || (!string.IsNullOrEmpty (split) && split.Length > 2 && char.IsLetter (previousChar) && char.IsPunctuation (c)) - || (!string.IsNullOrEmpty (split) && split.Length > 2 && char.IsLetter (previousChar) && char.IsSymbol (c))) - { - isEscSeq = false; - split = AddAndClearSplit (); - splitList.Add (c.ToString ()); - } - else - { - split += c.ToString (); - } - - if (!string.IsNullOrEmpty (split) && i == rawData.Length - 1) - { - splitList.Add (split); - } - - previousChar = c; - } - - return splitList; - - string AddAndClearSplit () - { - if (!string.IsNullOrEmpty (split)) - { - splitList.Add (split); - split = string.Empty; - } - - return split; - } - } - - /// - /// Convert a array to string. - /// - /// - /// The string representing the array. - public static string ToString (ConsoleKeyInfo [] consoleKeyInfos) - { - StringBuilder sb = new (); - - foreach (ConsoleKeyInfo keyChar in consoleKeyInfos) - { - sb.Append (keyChar.KeyChar); - } - - return sb.ToString (); - } - - /// - /// Convert a string to array. - /// - /// - /// The representing the string. - public static ConsoleKeyInfo [] ToConsoleKeyInfoArray (string ansi) - { - if (ansi is null) - { - return null; - } - - ConsoleKeyInfo [] cki = new ConsoleKeyInfo [ansi.Length]; - - for (var i = 0; i < ansi.Length; i++) - { - char c = ansi [i]; - cki [i] = new (c, 0, false, false, false); - } - - return cki; - } + #endregion Keyboard #region Cursor @@ -1674,12 +471,6 @@ public static class EscSeqUtils /// public static readonly string CSI_RestoreCursorPosition = CSI + "8"; - /// - /// ESC [ 8 ; height ; width t - Set Terminal Window Size - /// https://terminalguide.namepad.de/seq/csi_st-8/ - /// - public static string CSI_SetTerminalWindowSize (int height, int width) { return $"{CSI}8;{height};{width}t"; } - //ESC [ < n > A - CUU - Cursor Up Cursor up by < n > //ESC [ < n > B - CUD - Cursor Down Cursor down by < n > //ESC [ < n > C - CUF - Cursor Forward Cursor forward (Right) by < n > @@ -1721,19 +512,25 @@ public static class EscSeqUtils public static void CSI_WriteCursorPosition (TextWriter writer, int row, int col) { const int maxInputBufferSize = + // CSI (2) + ';' + 'H' - 4 + + 4 + + + // row + col (2x int sign + int max value) - 2 + 20; - Span buffer = stackalloc char[maxInputBufferSize]; + 2 + + 20; + Span buffer = stackalloc char [maxInputBufferSize]; + if (!buffer.TryWrite (CultureInfo.InvariantCulture, $"{CSI}{row};{col}H", out int charsWritten)) { - string tooLongCursorPositionSequence = $"{CSI}{row};{col}H"; + var tooLongCursorPositionSequence = $"{CSI}{row};{col}H"; + throw new InvalidOperationException ( - $"{nameof(CSI_WriteCursorPosition)} buffer (len: {buffer.Length}) is too short for cursor position sequence '{tooLongCursorPositionSequence}' (len: {tooLongCursorPositionSequence.Length})."); + $"{nameof (CSI_WriteCursorPosition)} buffer (len: {buffer.Length}) is too short for cursor position sequence '{tooLongCursorPositionSequence}' (len: {tooLongCursorPositionSequence.Length})."); } - ReadOnlySpan cursorPositionSequence = buffer[..charsWritten]; + ReadOnlySpan cursorPositionSequence = buffer [..charsWritten]; writer.Write (cursorPositionSequence); } @@ -1806,7 +603,7 @@ public static class EscSeqUtils /// public static string CSI_SetCursorStyle (DECSCUSR_Style style) { return $"{CSI}{(int)style} q"; } - #endregion + #endregion Cursor #region Colors @@ -1870,25 +667,27 @@ public static class EscSeqUtils builder.Append ($"{CSI}48;2;{r};{g};{b}m"); } - - - #endregion + #endregion Colors #region Text Styles /// - /// Appends an ANSI SGR (Select Graphic Rendition) escape sequence to switch printed text from one to another. + /// Appends an ANSI SGR (Select Graphic Rendition) escape sequence to switch printed text from one + /// to another. /// /// to add escape sequence to. /// Previous to change away from. /// Next to change to. /// - /// - /// Unlike colors, most text styling options are not mutually exclusive with each other, and can be applied independently. This creates a problem when - /// switching from one style to another: For instance, if your previous style is just bold, and your next style is just italic, then simply adding the - /// sequence to enable italic text would cause the text to remain bold. This method automatically handles this problem, enabling and disabling styles as - /// necessary to apply exactly the next style. - /// + /// + /// Unlike colors, most text styling options are not mutually exclusive with each other, and can be applied + /// independently. This creates a problem when + /// switching from one style to another: For instance, if your previous style is just bold, and your next style is + /// just italic, then simply adding the + /// sequence to enable italic text would cause the text to remain bold. This method automatically handles this + /// problem, enabling and disabling styles as + /// necessary to apply exactly the next style. + /// /// internal static void CSI_AppendTextStyleChange (StringBuilder output, TextStyle prev, TextStyle next) { @@ -1901,12 +700,12 @@ public static class EscSeqUtils // Bitwise operations to determine flag changes. A ^ B are the flags different between two flag sets. These different flags that exist in the next flag // set (diff & next) are the ones that were enabled in the switch, those that exist in the previous flag set (diff & prev) are the ones that were // disabled. - var diff = prev ^ next; - var enabled = diff & next; - var disabled = diff & prev; + TextStyle diff = prev ^ next; + TextStyle enabled = diff & next; + TextStyle disabled = diff & prev; // List of escape codes to apply. - var sgr = new List (); + List sgr = new (); if (disabled != TextStyle.None) { @@ -2059,25 +858,24 @@ public static class EscSeqUtils /// /// CSI 1 8 t | yes | yes | yes | report window size in chars /// https://terminalguide.namepad.de/seq/csi_st-18/ - /// The terminator indicating a reply to : ESC [ 8 ; height ; width t + /// The terminator indicating a reply to : ESC [ 8 ; height ; width t /// - public static readonly AnsiEscapeSequence CSI_ReportTerminalSizeInChars = new () { Request = CSI + "18t", Terminator = "t", Value = "8" }; - + public static readonly AnsiEscapeSequence CSI_ReportWindowSizeInChars = new () { Request = CSI + "18t", Terminator = "t", Value = "8" }; /// - /// The terminator indicating a reply to : ESC [ 8 ; height ; width t + /// The terminator indicating a reply to : ESC [ 8 ; height ; width t /// - public const string CSI_ReportTerminalSizeInChars_Terminator = "t"; + public const string CSI_ReportWindowSizeInChars_Terminator = "t"; /// - /// The value of the response to indicating value 1 and 2 are the terminal + /// The value of the response to indicating value 1 and 2 are the terminal /// size in chars. /// - public const string CSI_ReportTerminalSizeInChars_ResponseValue = "8"; + public const string CSI_ReportWindowSizeInChars_ResponseValue = "8"; - #endregion + #endregion Requests - #region OSC 8 Hyperlinks + #region OSC /// /// OSC (Operating System Command) escape sequence prefix. @@ -2112,6 +910,7 @@ public static class EscSeqUtils // Format: ESC ] 8 ; params ; URL ST // params can include "id=value" for matching start/end string parameters = string.IsNullOrEmpty (id) ? "" : $"id={id}"; + return $"{OSC}8;{parameters};{url}{ST}"; } @@ -2129,5 +928,22 @@ public static class EscSeqUtils return $"{OSC}8;;{ST}"; } - #endregion + #endregion OSC + + /// + /// Convert a array to string. + /// + /// + /// The string representing the array. + public static string ToString (ConsoleKeyInfo [] consoleKeyInfos) + { + StringBuilder sb = new (); + + foreach (ConsoleKeyInfo keyChar in consoleKeyInfos) + { + sb.Append (keyChar.KeyChar); + } + + return sb.ToString (); + } } diff --git a/Terminal.Gui/Drivers/AnsiHandling/Osc8UrlLinker.cs b/Terminal.Gui/Drivers/AnsiHandling/Osc8UrlLinker.cs index 6e030e9bd..7a3e41a6d 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/Osc8UrlLinker.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/Osc8UrlLinker.cs @@ -33,7 +33,7 @@ internal static class Osc8UrlLinker internal static StringBuilder WrapOsc8 (StringBuilder input, Options options) { - if (input is null || input.Length == 0) + if (input.Length == 0) { return input; } diff --git a/Terminal.Gui/Drivers/ComponentFactory.cs b/Terminal.Gui/Drivers/ComponentFactory.cs index 3c5adddd2..3a80fd2e6 100644 --- a/Terminal.Gui/Drivers/ComponentFactory.cs +++ b/Terminal.Gui/Drivers/ComponentFactory.cs @@ -16,9 +16,9 @@ public abstract class ComponentFactory : IComponentFactory public abstract IInputProcessor CreateInputProcessor (ConcurrentQueue inputBuffer); /// - public virtual IWindowSizeMonitor CreateWindowSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer) + public virtual IConsoleSizeMonitor CreateConsoleSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer) { - return new WindowSizeMonitor (consoleOutput, outputBuffer); + return new ConsoleSizeMonitor (consoleOutput, outputBuffer); } /// diff --git a/Terminal.Gui/Drivers/ConsoleDriver.cs b/Terminal.Gui/Drivers/ConsoleDriver.cs index 534adefb7..a4c668f74 100644 --- a/Terminal.Gui/Drivers/ConsoleDriver.cs +++ b/Terminal.Gui/Drivers/ConsoleDriver.cs @@ -60,6 +60,19 @@ public abstract class ConsoleDriver : IConsoleDriver /// Gets the location and size of the terminal screen. public Rectangle Screen => new (0, 0, Cols, Rows); + /// + /// Sets the screen size for testing purposes. Only supported by FakeDriver. + /// is the source of truth for screen dimensions. + /// and are read-only and derived from . + /// + /// The new width in columns. + /// The new height in rows. + /// Thrown when called on non-FakeDriver instances. + public virtual void SetScreenSize (int width, int height) + { + throw new NotSupportedException ("SetScreenSize is only supported by FakeDriver for test scenarios."); + } + private Region? _clip; /// @@ -508,9 +521,17 @@ public abstract class ConsoleDriver : IConsoleDriver } } - /// Called when the terminal size changes. Fires the event. - /// - public void OnSizeChanged (SizeChangedEventArgs args) { SizeChanged?.Invoke (this, args); } + /// + /// Called when the terminal screen changes (size, position, etc.). Fires the event. + /// reflects the source of truth for screen dimensions. + /// and are derived from and are read-only. + /// + /// Event arguments containing the new screen size. + public void OnSizeChanged (SizeChangedEventArgs args) + { + SizeChanged?.Invoke (this, args); + } + /// Updates the screen to reflect all the changes that have been done to the display buffer public void Refresh () @@ -531,13 +552,17 @@ public abstract class ConsoleDriver : IConsoleDriver /// upon success public abstract bool SetCursorVisibility (CursorVisibility visibility); - /// The event fired when the terminal is resized. + /// + /// The event fired when the screen changes (size, position, etc.). + /// is the source of truth for screen dimensions. + /// and are read-only and derived from . + /// public event EventHandler? SizeChanged; #endregion Cursor Handling /// Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver. - /// This is only implemented in . + /// This is only implemented in the Unix driver. public abstract void Suspend (); /// Sets the position of the terminal cursor to and . @@ -603,20 +628,7 @@ public abstract class ConsoleDriver : IConsoleDriver /// Gets the current . /// The current attribute. public Attribute GetAttribute () { return CurrentAttribute; } - - /// Makes an . - /// The foreground color. - /// The background color. - /// The attribute for the foreground and background colors. - public virtual Attribute MakeColor (in Color foreground, in Color background) - { - // Encode the colors into the int value. - return new ( - foreground, - background - ); - } - + #endregion Color Handling #region Mouse Handling diff --git a/Terminal.Gui/Drivers/ConsoleDriverFacade.cs b/Terminal.Gui/Drivers/ConsoleDriverFacade.cs index ef12ec04e..cc7a1c118 100644 --- a/Terminal.Gui/Drivers/ConsoleDriverFacade.cs +++ b/Terminal.Gui/Drivers/ConsoleDriverFacade.cs @@ -10,13 +10,15 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade private readonly AnsiRequestScheduler _ansiRequestScheduler; private CursorVisibility _lastCursor = CursorVisibility.Default; - /// The event fired when the terminal is resized. + /// + /// The event fired when the screen changes (size, position, etc.). + /// public event EventHandler? SizeChanged; public IInputProcessor InputProcessor { get; } public IOutputBuffer OutputBuffer => _outputBuffer; - public IWindowSizeMonitor WindowSizeMonitor { get; } + public IConsoleSizeMonitor ConsoleSizeMonitor { get; } public ConsoleDriverFacade ( @@ -24,7 +26,7 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade IOutputBuffer outputBuffer, IConsoleOutput output, AnsiRequestScheduler ansiRequestScheduler, - IWindowSizeMonitor windowSizeMonitor + IConsoleSizeMonitor sizeMonitor ) { InputProcessor = inputProcessor; @@ -40,8 +42,12 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade MouseEvent?.Invoke (s, e); }; - WindowSizeMonitor = windowSizeMonitor; - windowSizeMonitor.SizeChanging += (_,e) => SizeChanged?.Invoke (this, e); + ConsoleSizeMonitor = sizeMonitor; + sizeMonitor.SizeChanged += (_, e) => + { + SetScreenSize(e.Size!.Value.Width, e.Size.Value.Height); + //SizeChanged?.Invoke (this, e); + }; CreateClipboard (); } @@ -50,7 +56,7 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade { if (FakeDriver.FakeBehaviors.UseFakeClipboard) { - Clipboard = new FakeDriver.FakeClipboard ( + Clipboard = new FakeClipboard ( FakeDriver.FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeDriver.FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse); @@ -73,7 +79,7 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade } else { - Clipboard = new FakeDriver.FakeClipboard (); + Clipboard = new FakeClipboard (); } } @@ -88,10 +94,23 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade return Rectangle.Empty; } - return new (new (0, 0), _output.GetWindowSize ()); + return new (0, 0, _outputBuffer.Cols, _outputBuffer.Rows); } } + /// + /// Sets the screen size for testing purposes. Only supported by FakeDriver. + /// + /// The new width in columns. + /// The new height in rows. + /// Thrown when called on non-FakeDriver instances. + public virtual void SetScreenSize (int width, int height) + { + _outputBuffer.SetSize (width, height); + _output.SetSize (width, height); + SizeChanged?.Invoke(this, new (new (width, height))); + } + /// /// Gets or sets the clip rectangle that and are subject /// to. @@ -104,7 +123,7 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade } /// Get the operating system clipboard. - public IClipboard Clipboard { get; private set; } = new FakeDriver.FakeClipboard (); + public IClipboard Clipboard { get; private set; } = new FakeClipboard (); /// /// Gets the column last set by . and are used by @@ -364,7 +383,6 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade public void UpdateCursor () { _output.SetCursorPosition (Col, Row); } /// Initializes the driver - /// Returns an instance of using the for the driver. public void Init () { throw new NotSupportedException (); } /// Ends the execution of the console driver. @@ -375,26 +393,20 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade /// Selects the specified attribute as the attribute to use for future calls to AddRune and AddString. /// Implementations should call base.SetAttribute(c). - /// C. - public Attribute SetAttribute (Attribute c) { return _outputBuffer.CurrentAttribute = c; } + /// C. + /// The previously set Attribute. + public Attribute SetAttribute (Attribute newAttribute) + { + Attribute currentAttribute = _outputBuffer.CurrentAttribute; + _outputBuffer.CurrentAttribute = newAttribute; + + return currentAttribute; + } /// Gets the current . /// The current attribute. public Attribute GetAttribute () { return _outputBuffer.CurrentAttribute; } - /// Makes an . - /// The foreground color. - /// The background color. - /// The attribute for the foreground and background colors. - public Attribute MakeColor (in Color foreground, in Color background) - { - // TODO: what even is this? why Attribute constructor wants to call Driver method which must return an instance of Attribute? ?!?!?! - return new ( - foreground, - background - ); - } - /// Event fired when a key is pressed down. This is a precursor to . public event EventHandler? KeyDown; diff --git a/Terminal.Gui/Drivers/ConsoleKeyMapping.cs b/Terminal.Gui/Drivers/ConsoleKeyMapping.cs index 8e9964147..ca8fd6d6f 100644 --- a/Terminal.Gui/Drivers/ConsoleKeyMapping.cs +++ b/Terminal.Gui/Drivers/ConsoleKeyMapping.cs @@ -533,7 +533,7 @@ public static class ConsoleKeyMapping /// /// Get the output character from the , with the correct - /// and the scan code used on . + /// and the scan code used on Windows. /// /// The unicode character. /// The modifiers keys. diff --git a/Terminal.Gui/Drivers/ConsoleSizeMonitor.cs b/Terminal.Gui/Drivers/ConsoleSizeMonitor.cs new file mode 100644 index 000000000..e5999dd39 --- /dev/null +++ b/Terminal.Gui/Drivers/ConsoleSizeMonitor.cs @@ -0,0 +1,35 @@ +#nullable enable +using Microsoft.Extensions.Logging; + +namespace Terminal.Gui.Drivers; + +/// +internal class ConsoleSizeMonitor (IConsoleOutput consoleOut, IOutputBuffer _) : IConsoleSizeMonitor +{ + private Size _lastSize = Size.Empty; + + /// Invoked when the terminal's size changed. The new size of the terminal is provided. + public event EventHandler? SizeChanged; + + /// + public bool Poll () + { + if (ConsoleDriver.RunningUnitTests) + { + return false; + } + + Size size = consoleOut.GetSize (); + + if (size != _lastSize) + { + Logging.Logger.LogInformation ($"Console size changes from '{_lastSize}' to {size}"); + _lastSize = size; + SizeChanged?.Invoke (this, new (size)); + + return true; + } + + return false; + } +} diff --git a/Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs b/Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs index d1c70526d..a97ec407b 100644 --- a/Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs +++ b/Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs @@ -35,7 +35,7 @@ public class NetOutput : OutputBase, IConsoleOutput /// - public Size GetWindowSize () + public Size GetSize () { if (ConsoleDriver.RunningUnitTests) { @@ -49,6 +49,12 @@ public class NetOutput : OutputBase, IConsoleOutput /// public void SetCursorPosition (int col, int row) { SetCursorPositionImpl (col, row); } + /// + public void SetSize (int width, int height) + { + // Do Nothing. + } + private Point? _lastCursorPosition; /// diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeClipboard.cs b/Terminal.Gui/Drivers/FakeDriver/FakeClipboard.cs new file mode 100644 index 000000000..6951b7923 --- /dev/null +++ b/Terminal.Gui/Drivers/FakeDriver/FakeClipboard.cs @@ -0,0 +1,59 @@ +#nullable enable +namespace Terminal.Gui.Drivers; + +/// +/// Implements a fake clipboard for testing purposes. +/// +public class FakeClipboard : ClipboardBase +{ + /// + /// Gets or sets an exception to be thrown by clipboard operations. + /// + public Exception? FakeException { get; set; } + + private readonly bool _isSupportedAlwaysFalse; + private string _contents = string.Empty; + + /// + /// Constructs a new instance of . + /// + /// + /// + public FakeClipboard ( + bool fakeClipboardThrowsNotSupportedException = false, + bool isSupportedAlwaysFalse = false + ) + { + _isSupportedAlwaysFalse = isSupportedAlwaysFalse; + + if (fakeClipboardThrowsNotSupportedException) + { + FakeException = new NotSupportedException ("Fake clipboard exception"); + } + } + + /// + public override bool IsSupported => !_isSupportedAlwaysFalse; + + /// + protected override string GetClipboardDataImpl () + { + if (FakeException is { }) + { + throw FakeException; + } + + return _contents; + } + + /// + protected override void SetClipboardDataImpl (string? text) + { + if (FakeException is { }) + { + throw FakeException; + } + + _contents = text ?? throw new ArgumentNullException (nameof (text)); + } +} diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs b/Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs index b3d6f4fec..2314649bc 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs @@ -42,8 +42,9 @@ public class FakeComponentFactory : ComponentFactory } /// - public override IWindowSizeMonitor CreateWindowSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer) + public override IConsoleSizeMonitor CreateConsoleSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer) { - return new FakeWindowSizeMonitor(consoleOutput, outputBuffer); + outputBuffer.SetSize(consoleOutput.GetSize().Width, consoleOutput.GetSize().Height); + return new ConsoleSizeMonitor (consoleOutput, outputBuffer); } } diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/Drivers/FakeDriver/FakeConsole.cs index 9eb566768..1b43a6b39 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeConsole.cs @@ -32,10 +32,10 @@ public static class FakeConsole #pragma warning disable RCS1138 // Add summary to documentation comment. /// Specifies the initial console width. - public const int WIDTH = 80; + public const int WIDTH = 0; /// Specifies the initial console height. - public const int HEIGHT = 25; + public const int HEIGHT = 0; /// public static int WindowWidth { get; set; } = WIDTH; @@ -971,7 +971,7 @@ public static class FakeConsole /// /// /// - public static void SetWindowSize (int width, int height) + public static void SetConsoleSize (int width, int height) { WindowWidth = width; WindowHeight = height; diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeConsoleOutput.cs b/Terminal.Gui/Drivers/FakeDriver/FakeConsoleOutput.cs index e36c6edac..07c59e224 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeConsoleOutput.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeConsoleOutput.cs @@ -1,33 +1,28 @@ #nullable enable -using System; -using System.Text; - namespace Terminal.Gui.Drivers; /// -/// Fake console output for testing that captures what would be written to the console. +/// Fake console output for testing that captures what would be written to the console. /// public class FakeConsoleOutput : OutputBase, IConsoleOutput { private readonly StringBuilder _output = new (); private int _cursorLeft; private int _cursorTop; - private Size _windowSize = new (80, 25); + private Size _consoleSize = new (80, 25); /// - /// Gets the captured output as a string. + /// Gets the captured output as a string. /// public string Output => _output.ToString (); - /// - /// Clears the captured output. - /// - public void ClearOutput () => _output.Clear (); - /// - public void SetCursorPosition (int col, int row) + public void SetCursorPosition (int col, int row) { SetCursorPositionImpl (col, row); } + + /// + public void SetSize (int width, int height) { - SetCursorPositionImpl (col, row); + _consoleSize = new (width, height); } /// @@ -35,30 +30,25 @@ public class FakeConsoleOutput : OutputBase, IConsoleOutput { _cursorLeft = col; _cursorTop = row; + return true; } /// - /// Sets the fake window size. + /// Sets the fake window size. /// - public void SetWindowSize (int width, int height) - { - _windowSize = new Size (width, height); - } + public void SetConsoleSize (int width, int height) { _consoleSize = new (width, height); } /// - /// Gets the current cursor position. + /// Gets the current cursor position. /// - public (int left, int top) GetCursorPosition () => (_cursorLeft, _cursorTop); + public (int left, int top) GetCursorPosition () { return (_cursorLeft, _cursorTop); } /// - public Size GetWindowSize () => _windowSize; + public Size GetSize () { return _consoleSize; } /// - public void Write (ReadOnlySpan text) - { - _output.Append (text); - } + public void Write (ReadOnlySpan text) { _output.Append (text); } /// public override void SetCursorVisibility (CursorVisibility visibility) @@ -80,9 +70,5 @@ public class FakeConsoleOutput : OutputBase, IConsoleOutput } /// - protected override void Write (StringBuilder output) - { - _output.Append (output); - } - + protected override void Write (StringBuilder output) { _output.Append (output); } } diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs index 916760c97..2c1f2d474 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs @@ -1,4 +1,5 @@ #nullable enable + // // FakeDriver.cs: A fake IConsoleDriver for unit tests. // @@ -6,8 +7,6 @@ using System.Diagnostics; using System.Runtime.InteropServices; -// Alias Console to MockConsole so we don't accidentally use Console - namespace Terminal.Gui.Drivers; /// Implements a mock IConsoleDriver for unit testing @@ -28,8 +27,8 @@ public class FakeDriver : ConsoleDriver FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; // double check usage is correct - Debug.Assert (useFakeClipboard == false && fakeClipboardAlwaysThrowsNotSupportedException == false); - Debug.Assert (useFakeClipboard == false && fakeClipboardIsSupportedAlwaysTrue == false); + Debug.Assert (!useFakeClipboard && !fakeClipboardAlwaysThrowsNotSupportedException); + Debug.Assert (!useFakeClipboard && !fakeClipboardIsSupportedAlwaysTrue); } public bool FakeClipboardAlwaysThrowsNotSupportedException { get; internal set; } @@ -40,19 +39,16 @@ public class FakeDriver : ConsoleDriver public static Behaviors FakeBehaviors { get; } = new (); public override bool SupportsTrueColor => false; - /// - public override void WriteRaw (string ansi) - { - - } + /// + public override void WriteRaw (string ansi) { } public FakeDriver () { // FakeDriver implies UnitTests RunningUnitTests = true; - base.Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; - base.Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; + //base.Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; + //base.Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; if (FakeBehaviors.UseFakeClipboard) { @@ -95,16 +91,19 @@ public class FakeDriver : ConsoleDriver { FakeConsole.MockKeyPresses.Clear (); - Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; - Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; + //Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; + //Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; FakeConsole.Clear (); + + SetScreenSize (80,25); ResizeScreen (); - CurrentAttribute = new Attribute (Color.White, Color.Black); + ClearContents (); + CurrentAttribute = new (Color.White, Color.Black); } public override bool UpdateScreen () { - bool updated = false; + var updated = false; int savedRow = FakeConsole.CursorTop; int savedCol = FakeConsole.CursorLeft; @@ -223,119 +222,10 @@ public class FakeDriver : ConsoleDriver FakeConsole.CursorTop = savedRow; FakeConsole.CursorLeft = savedCol; FakeConsole.CursorVisible = savedCursorVisible; + return updated; } - - #region Color Handling - - ///// - ///// In the FakeDriver, colors are encoded as an int; same as DotNetDriver - ///// However, the foreground color is stored in the most significant 16 bits, - ///// and the background color is stored in the least significant 16 bits. - ///// - //public override Attribute MakeColor (Color foreground, Color background) - //{ - // // Encode the colors into the int value. - // return new Attribute ( - // foreground: foreground, - // background: background - // ); - //} - - #endregion - - private KeyCode MapKey (ConsoleKeyInfo keyInfo) - { - switch (keyInfo.Key) - { - case ConsoleKey.Escape: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Esc); - case ConsoleKey.Tab: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Tab); - case ConsoleKey.Clear: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Clear); - case ConsoleKey.Home: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Home); - case ConsoleKey.End: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.End); - case ConsoleKey.LeftArrow: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorLeft); - case ConsoleKey.RightArrow: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorRight); - case ConsoleKey.UpArrow: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorUp); - case ConsoleKey.DownArrow: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorDown); - case ConsoleKey.PageUp: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PageUp); - case ConsoleKey.PageDown: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PageDown); - case ConsoleKey.Enter: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Enter); - case ConsoleKey.Spacebar: - return ConsoleKeyMapping.MapToKeyCodeModifiers ( - keyInfo.Modifiers, - keyInfo.KeyChar == 0 - ? KeyCode.Space - : (KeyCode)keyInfo.KeyChar - ); - case ConsoleKey.Backspace: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Backspace); - case ConsoleKey.Delete: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Delete); - case ConsoleKey.Insert: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Insert); - case ConsoleKey.PrintScreen: - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PrintScreen); - - case ConsoleKey.Oem1: - case ConsoleKey.Oem2: - case ConsoleKey.Oem3: - case ConsoleKey.Oem4: - case ConsoleKey.Oem5: - case ConsoleKey.Oem6: - case ConsoleKey.Oem7: - case ConsoleKey.Oem8: - case ConsoleKey.Oem102: - case ConsoleKey.OemPeriod: - case ConsoleKey.OemComma: - case ConsoleKey.OemPlus: - case ConsoleKey.OemMinus: - if (keyInfo.KeyChar == 0) - { - return KeyCode.Null; - } - - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar); - } - - ConsoleKey key = keyInfo.Key; - - if (key >= ConsoleKey.A && key <= ConsoleKey.Z) - { - int delta = key - ConsoleKey.A; - - if (keyInfo.KeyChar != (uint)key) - { - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.Key); - } - - if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control) - || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) - || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift)) - { - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)KeyCode.A + delta)); - } - - char alphaBase = keyInfo.Modifiers != ConsoleModifiers.Shift ? 'A' : 'a'; - - return (KeyCode)((uint)alphaBase + delta); - } - - return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar); - } - private CursorVisibility _savedCursorVisibility; /// @@ -356,7 +246,6 @@ public class FakeDriver : ConsoleDriver return FakeConsole.CursorVisible = visibility == CursorVisibility.Default; } - /// private bool EnsureCursorVisibility () { if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) @@ -373,23 +262,32 @@ public class FakeDriver : ConsoleDriver return FakeConsole.CursorVisible; } - private AnsiResponseParser _parser = new (); + private readonly AnsiResponseParser _parser = new (); - /// - internal override IAnsiResponseParser GetParser () => _parser; + /// + internal override IAnsiResponseParser GetParser () { return _parser; } + + /// + /// Sets the screen size for testing purposes. Only available in FakeDriver. + /// This method updates the driver's dimensions and triggers the ScreenChanged event. + /// + /// The new width in columns. + /// The new height in rows. + public override void SetScreenSize (int width, int height) { SetBufferSize (width, height); } public void SetBufferSize (int width, int height) { FakeConsole.SetBufferSize (width, height); Cols = width; Rows = height; - SetWindowSize (width, height); + SetConsoleSize (width, height); ProcessResize (); } - public void SetWindowSize (int width, int height) + public void SetConsoleSize (int width, int height) { - FakeConsole.SetWindowSize (width, height); + FakeConsole.SetConsoleSize (width, height); + FakeConsole.SetBufferSize (width, height); if (width != Cols || height != Rows) { @@ -416,7 +314,7 @@ public class FakeDriver : ConsoleDriver { ResizeScreen (); ClearContents (); - OnSizeChanged (new SizeChangedEventArgs (new (Cols, Rows))); + OnSizeChanged (new (new (Cols, Rows))); } public virtual void ResizeScreen () @@ -452,7 +350,7 @@ public class FakeDriver : ConsoleDriver return; } - // Prevents the exception of size changing during resizing. + // Prevents the exception to size changing during resizing. try { // BUGBUG: Why is this using BufferWidth/Height and now Cols/Rows? @@ -476,48 +374,6 @@ public class FakeDriver : ConsoleDriver #endregion - public class FakeClipboard : ClipboardBase - { - public Exception? FakeException { get; set; } - - private readonly bool _isSupportedAlwaysFalse; - private string _contents = string.Empty; - - public FakeClipboard ( - bool fakeClipboardThrowsNotSupportedException = false, - bool isSupportedAlwaysFalse = false - ) - { - _isSupportedAlwaysFalse = isSupportedAlwaysFalse; - - if (fakeClipboardThrowsNotSupportedException) - { - FakeException = new NotSupportedException ("Fake clipboard exception"); - } - } - - public override bool IsSupported => !_isSupportedAlwaysFalse; - - protected override string GetClipboardDataImpl () - { - if (FakeException is { }) - { - throw FakeException; - } - - return _contents; - } - - protected override void SetClipboardDataImpl (string? text) - { - if (FakeException is { }) - { - throw FakeException; - } - - _contents = text ?? throw new ArgumentNullException (nameof (text)); - } - } #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member -} +} \ No newline at end of file diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeWindowSizeMonitor.cs b/Terminal.Gui/Drivers/FakeDriver/FakeWindowSizeMonitor.cs deleted file mode 100644 index 329be232d..000000000 --- a/Terminal.Gui/Drivers/FakeDriver/FakeWindowSizeMonitor.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Terminal.Gui.Drivers; - -internal class FakeWindowSizeMonitor (IConsoleOutput consoleOut, IOutputBuffer outputBuffer) : IWindowSizeMonitor -{ - private Size _lastSize = new (0, 0); - - /// Invoked when the terminal's size changed. The new size of the terminal is provided. - public event EventHandler SizeChanging; - - /// Raises the event with the specified size. Used for testing. - /// The new size to report. - public void RaiseSizeChanging (Size newSize) - { - SizeChanging?.Invoke (this, new (newSize)); - } - - /// - public bool Poll () - { - if (ConsoleDriver.RunningUnitTests) - { - return false; - } - - Size size = consoleOut.GetWindowSize (); - - if (size != _lastSize) - { - Logging.Logger.LogInformation ($"Console size changes from '{_lastSize}' to {size}"); - outputBuffer.SetWindowSize (size.Width, size.Height); - _lastSize = size; - SizeChanging?.Invoke (this, new (size)); - - return true; - } - - return false; - } -} diff --git a/Terminal.Gui/Drivers/IComponentFactory.cs b/Terminal.Gui/Drivers/IComponentFactory.cs index 64bf7aaa9..f7120c274 100644 --- a/Terminal.Gui/Drivers/IComponentFactory.cs +++ b/Terminal.Gui/Drivers/IComponentFactory.cs @@ -41,11 +41,11 @@ public interface IComponentFactory : IComponentFactory IInputProcessor CreateInputProcessor (ConcurrentQueue inputBuffer); /// - /// Creates class for the current driver implementation i.e. the class responsible for - /// reporting the current size of the terminal window. + /// Creates class for the current driver implementation i.e. the class responsible for + /// reporting the current size of the terminal. /// /// /// /// - IWindowSizeMonitor CreateWindowSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer); + IConsoleSizeMonitor CreateConsoleSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer); } diff --git a/Terminal.Gui/Drivers/IConsoleDriver.cs b/Terminal.Gui/Drivers/IConsoleDriver.cs index a28671a4e..b65fca43f 100644 --- a/Terminal.Gui/Drivers/IConsoleDriver.cs +++ b/Terminal.Gui/Drivers/IConsoleDriver.cs @@ -14,6 +14,15 @@ public interface IConsoleDriver /// Gets the location and size of the terminal screen. Rectangle Screen { get; } + /// + /// Sets the screen size for testing purposes. Only supported by FakeDriver. + /// is the source of truth for screen dimensions. + /// + /// The new width in columns. + /// The new height in rows. + /// Thrown when called on non-FakeDriver instances. + void SetScreenSize (int width, int height); + /// /// Gets or sets the clip rectangle that and are subject /// to. @@ -200,11 +209,14 @@ public interface IConsoleDriver /// upon success bool SetCursorVisibility (CursorVisibility visibility); - /// The event fired when the terminal is resized. + /// + /// The event fired when the screen changes (size, position, etc.). + /// is the source of truth for screen dimensions. + /// event EventHandler? SizeChanged; /// Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver. - /// This is only implemented in . + /// This is only implemented in UnixDriver. void Suspend (); /// @@ -228,12 +240,6 @@ public interface IConsoleDriver /// The current attribute. Attribute GetAttribute (); - /// Makes an . - /// The foreground color. - /// The background color. - /// The attribute for the foreground and background colors. - Attribute MakeColor (in Color foreground, in Color background); - /// Event fired when a mouse event occurs. event EventHandler? MouseEvent; diff --git a/Terminal.Gui/Drivers/IConsoleDriverFacade.cs b/Terminal.Gui/Drivers/IConsoleDriverFacade.cs index 2d854e16b..bf150af74 100644 --- a/Terminal.Gui/Drivers/IConsoleDriverFacade.cs +++ b/Terminal.Gui/Drivers/IConsoleDriverFacade.cs @@ -22,5 +22,5 @@ public interface IConsoleDriverFacade /// Interface for classes responsible for reporting the current /// size of the terminal window. /// - IWindowSizeMonitor WindowSizeMonitor { get; } + IConsoleSizeMonitor ConsoleSizeMonitor { get; } } diff --git a/Terminal.Gui/Drivers/IConsoleOutput.cs b/Terminal.Gui/Drivers/IConsoleOutput.cs index aa35c06ee..4bb768c86 100644 --- a/Terminal.Gui/Drivers/IConsoleOutput.cs +++ b/Terminal.Gui/Drivers/IConsoleOutput.cs @@ -20,11 +20,11 @@ public interface IConsoleOutput : IDisposable void Write (IOutputBuffer buffer); /// - /// Returns the current size of the console window in rows/columns (i.e. + /// Returns the current size of the console in rows/columns (i.e. /// of characters not pixels). /// /// - public Size GetWindowSize (); + public Size GetSize (); /// /// Updates the console cursor (the blinking underscore) to be hidden, @@ -39,4 +39,11 @@ public interface IConsoleOutput : IDisposable /// /// void SetCursorPosition (int col, int row); + + /// + /// Sets the size of the console.. + /// + /// + /// + void SetSize (int width, int height); } diff --git a/Terminal.Gui/Drivers/IWindowSizeMonitor.cs b/Terminal.Gui/Drivers/IConsoleSizeMonitor.cs similarity index 77% rename from Terminal.Gui/Drivers/IWindowSizeMonitor.cs rename to Terminal.Gui/Drivers/IConsoleSizeMonitor.cs index 2cee6e8a8..b34503f17 100644 --- a/Terminal.Gui/Drivers/IWindowSizeMonitor.cs +++ b/Terminal.Gui/Drivers/IConsoleSizeMonitor.cs @@ -6,13 +6,13 @@ namespace Terminal.Gui.Drivers; /// Interface for classes responsible for reporting the current /// size of the terminal window. /// -public interface IWindowSizeMonitor +public interface IConsoleSizeMonitor { /// Invoked when the terminal's size changed. The new size of the terminal is provided. - event EventHandler? SizeChanging; + event EventHandler? SizeChanged; /// - /// Examines the current size of the terminal and raises if it is different + /// Examines the current size of the terminal and raises if it is different /// from last inspection. /// /// diff --git a/Terminal.Gui/Drivers/IInputProcessor.cs b/Terminal.Gui/Drivers/IInputProcessor.cs index 8366788af..007825f61 100644 --- a/Terminal.Gui/Drivers/IInputProcessor.cs +++ b/Terminal.Gui/Drivers/IInputProcessor.cs @@ -28,7 +28,7 @@ public interface IInputProcessor /// /// Gets the name of the driver associated with this input processor. /// - string DriverName { get; init; } + string? DriverName { get; init; } /// /// Called when a key is pressed down. Fires the event. This is a precursor to diff --git a/Terminal.Gui/Drivers/IOutputBuffer.cs b/Terminal.Gui/Drivers/IOutputBuffer.cs index 4ca6be377..66498f4f6 100644 --- a/Terminal.Gui/Drivers/IOutputBuffer.cs +++ b/Terminal.Gui/Drivers/IOutputBuffer.cs @@ -97,7 +97,7 @@ public interface IOutputBuffer /// /// /// - void SetWindowSize (int cols, int rows); + void SetSize (int cols, int rows); /// /// Fills the given with the given diff --git a/Terminal.Gui/Drivers/InputProcessor.cs b/Terminal.Gui/Drivers/InputProcessor.cs index 667d4d267..15d320ba2 100644 --- a/Terminal.Gui/Drivers/InputProcessor.cs +++ b/Terminal.Gui/Drivers/InputProcessor.cs @@ -31,7 +31,7 @@ public abstract class InputProcessor : IInputProcessor public ConcurrentQueue InputBuffer { get; } /// - public string DriverName { get; init; } + public string? DriverName { get; init; } /// public IAnsiResponseParser GetParser () { return Parser; } @@ -166,7 +166,7 @@ public abstract class InputProcessor : IInputProcessor /// protected abstract void ProcessAfterParsing (T input); - internal char _highSurrogate = '\0'; + private char _highSurrogate = '\0'; /// public bool IsValidInput (Key key, out Key result) diff --git a/Terminal.Gui/Drivers/MouseButtonStateEx.cs b/Terminal.Gui/Drivers/MouseButtonStateEx.cs index 099151c2c..e6e2a1e96 100644 --- a/Terminal.Gui/Drivers/MouseButtonStateEx.cs +++ b/Terminal.Gui/Drivers/MouseButtonStateEx.cs @@ -3,7 +3,7 @@ namespace Terminal.Gui.Drivers; /// -/// Not to be confused with +/// Not to be confused with NetEvents.MouseButtonState /// internal class MouseButtonStateEx { diff --git a/Terminal.Gui/Drivers/OutputBase.cs b/Terminal.Gui/Drivers/OutputBase.cs index 8393061b4..4222ec6e1 100644 --- a/Terminal.Gui/Drivers/OutputBase.cs +++ b/Terminal.Gui/Drivers/OutputBase.cs @@ -56,7 +56,7 @@ public abstract class OutputBase for (; col < cols; col++) { - if (!buffer.Contents [row, col].IsDirty) + if (!buffer.Contents! [row, col].IsDirty) { if (output.Length > 0) { diff --git a/Terminal.Gui/Drivers/OutputBuffer.cs b/Terminal.Gui/Drivers/OutputBuffer.cs index 039d3f22f..0d3efcfac 100644 --- a/Terminal.Gui/Drivers/OutputBuffer.cs +++ b/Terminal.Gui/Drivers/OutputBuffer.cs @@ -5,7 +5,7 @@ namespace Terminal.Gui.Drivers; /// /// Stores the desired output state for the whole application. This is updated during -/// draw operations before being flushed to the console as part of +/// draw operations before being flushed to the console as part of the main loop. /// operation /// public class OutputBuffer : IOutputBuffer @@ -15,7 +15,7 @@ public class OutputBuffer : IOutputBuffer /// UpdateScreen is called. /// The format of the array is rows, columns. The first index is the row, the second index is the column. /// - public Cell [,] Contents { get; set; } = new Cell[0, 0]; + public Cell [,]? Contents { get; set; } = new Cell[0, 0]; private int _cols; private int _rows; @@ -365,7 +365,7 @@ public class OutputBuffer : IOutputBuffer } /// - public void SetWindowSize (int cols, int rows) + public void SetSize (int cols, int rows) { Cols = cols; Rows = rows; diff --git a/Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs b/Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs index 9643223d3..9313e2bfd 100644 --- a/Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs +++ b/Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs @@ -121,7 +121,7 @@ internal class UnixOutput : OutputBase, IConsoleOutput } /// - public Size GetWindowSize () + public Size GetSize () { if (ConsoleDriver.RunningUnitTests) { @@ -168,6 +168,12 @@ internal class UnixOutput : OutputBase, IConsoleOutput SetCursorPositionImpl (col, row); } + /// + public void SetSize (int width, int height) + { + // Do nothing + } + /// public void Dispose () { diff --git a/Terminal.Gui/Drivers/WindowSizeMonitor.cs b/Terminal.Gui/Drivers/WindowSizeMonitor.cs deleted file mode 100644 index ee3841c6e..000000000 --- a/Terminal.Gui/Drivers/WindowSizeMonitor.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Terminal.Gui.Drivers; - -internal class WindowSizeMonitor : IWindowSizeMonitor -{ - private readonly IConsoleOutput _consoleOut; - private readonly IOutputBuffer _outputBuffer; - private Size _lastSize = new (0, 0); - - /// Invoked when the terminal's size changed. The new size of the terminal is provided. - public event EventHandler SizeChanging; - - public WindowSizeMonitor (IConsoleOutput consoleOut, IOutputBuffer outputBuffer) - { - _consoleOut = consoleOut; - _outputBuffer = outputBuffer; - } - - /// - public bool Poll () - { - if (ConsoleDriver.RunningUnitTests) - { - return false; - } - - Size size = _consoleOut.GetWindowSize (); - - if (size != _lastSize) - { - Logging.Logger.LogInformation ($"Console size changes from '{_lastSize}' to {size}"); - _outputBuffer.SetWindowSize (size.Width, size.Height); - _lastSize = size; - SizeChanging?.Invoke (this, new (size)); - - return true; - } - - return false; - } -} diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsConsole.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsConsole.cs index 46f9eb742..abff1ca0c 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsConsole.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsConsole.cs @@ -1,719 +1,25 @@ #nullable enable -using System.Collections.Concurrent; -using System.ComponentModel; using System.Runtime.InteropServices; -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member -#pragma warning disable IDE1006// Naming rule violation: Prefix '_' is not expected + +// ReSharper disable InconsistentNaming namespace Terminal.Gui.Drivers; -public partial class WindowsConsole -{ - private CancellationTokenSource? _inputReadyCancellationTokenSource; - private readonly BlockingCollection _inputQueue = new (new ConcurrentQueue ()); +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - public const int STD_OUTPUT_HANDLE = -11; +/// +/// Definitions for Windows Console API structures and constants. +/// +public class WindowsConsole +{ + /// + /// Standard input handle constant. + /// public const int STD_INPUT_HANDLE = -10; - private readonly nint _inputHandle; - private nint _outputHandle; - private nint _screenBuffer; - private readonly uint _originalConsoleMode; - private CursorVisibility? _initialCursorVisibility; - private CursorVisibility? _currentCursorVisibility; - private CursorVisibility? _pendingCursorVisibility; - private readonly StringBuilder _stringBuilder = new (256 * 1024); - private string _lastWrite = string.Empty; - - public WindowsConsole () - { - _inputHandle = GetStdHandle (STD_INPUT_HANDLE); - _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); - _originalConsoleMode = ConsoleMode; - uint newConsoleMode = _originalConsoleMode; - newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); - newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; - newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; - ConsoleMode = newConsoleMode; - - IsVirtualTerminal = GetConsoleMode (_outputHandle, out uint mode) && (mode & (uint)ConsoleModes.EnableVirtualTerminalProcessing) != 0; - - if (!IsVirtualTerminal) - { - CreateConsoleScreenBuffer (); - Size bufferSize = GetConsoleBufferWindow (out _); - SmallRect window = new () - { - Top = 0, - Left = 0, - Bottom = (short)bufferSize.Height, - Right = (short)bufferSize.Width - }; - - ReadFromConsoleOutput (bufferSize, new ((short)bufferSize.Width, (short)bufferSize.Height), ref window); - - if (!GetConsoleMode (_screenBuffer, out mode)) - { - throw new ApplicationException ($"Failed to get screenBuffer console mode, error code: {Marshal.GetLastWin32Error ()}."); - } - - const uint ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002; - - mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT; // Disable wrap - - if (!SetConsoleMode (_screenBuffer, mode)) - { - throw new ApplicationException ($"Failed to set screenBuffer console mode, error code: {Marshal.GetLastWin32Error ()}."); - } - } - - SetInitialCursorVisibility (); - - _inputReadyCancellationTokenSource = new (); - Task.Run (ProcessInputQueue, _inputReadyCancellationTokenSource.Token); - } - - private void CreateConsoleScreenBuffer () - { - _screenBuffer = CreateConsoleScreenBuffer ( - DesiredAccess.GenericRead | DesiredAccess.GenericWrite, - ShareMode.FileShareRead | ShareMode.FileShareWrite, - nint.Zero, - 1, - nint.Zero - ); - - if (_screenBuffer == INVALID_HANDLE_VALUE) - { - int err = Marshal.GetLastWin32Error (); - - if (err != 0) - { - throw new Win32Exception (err); - } - } - - if (!SetConsoleActiveScreenBuffer (_screenBuffer)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - } - - public InputRecord? DequeueInput () - { - while (_inputReadyCancellationTokenSource is { }) - { - try - { - return _inputQueue.Take (_inputReadyCancellationTokenSource.Token); - } - catch (OperationCanceledException) - { - return null; - } - } - - return null; - } - - public InputRecord? ReadConsoleInput () - { - const int BUFFER_SIZE = 1; - InputRecord inputRecord = default; - uint numberEventsRead = 0; - - while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false }) - { - try - { - // Peek to check if there is any input available - if (PeekConsoleInput (_inputHandle, out _, BUFFER_SIZE, out uint eventsRead) && eventsRead > 0) - { - // Read the input since it is available - ReadConsoleInput ( - _inputHandle, - out inputRecord, - BUFFER_SIZE, - out numberEventsRead); - } - - if (numberEventsRead > 0) - { - return inputRecord; - } - - try - { - Task.Delay (100, _inputReadyCancellationTokenSource.Token).Wait (_inputReadyCancellationTokenSource.Token); - } - catch (OperationCanceledException) - { - return null; - } - } - catch (Exception ex) - { - if (ex is OperationCanceledException or ObjectDisposedException) - { - return null; - } - - throw; - } - } - - return null; - } - - private void ProcessInputQueue () - { - while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false }) - { - try - { - if (_inputQueue.Count == 0) - { - while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false }) - { - try - { - InputRecord? inpRec = ReadConsoleInput (); - - if (inpRec is { }) - { - _inputQueue.Add (inpRec.Value); - - break; - } - } - catch (OperationCanceledException) - { - return; - } - } - } - } - catch (OperationCanceledException) - { - return; - } - } - } - - - // Last text style used, for updating style with EscSeqUtils.CSI_AppendTextStyleChange(). - private TextStyle _redrawTextStyle = TextStyle.None; - - private CharInfo []? _originalStdOutChars; - - private struct Run - { - public ushort attr; - public string text; - - public Run (ushort attr, string text) - { - this.attr = attr; - this.text = text; - } - } - - public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord bufferSize, SmallRect window, bool force16Colors) - { - //Debug.WriteLine ("WriteToConsole"); - - Attribute? prev = null; - var result = false; - - if (force16Colors) - { - _stringBuilder.Clear (); - - var i = 0; - List runs = []; - Run? current = null; - SetCursorPosition (new Coord (0, 0)); - - foreach (ExtendedCharInfo info in charInfoBuffer) - { - if (IsVirtualTerminal) - { - Attribute attr = info.Attribute; - AnsiColorCode fgColor = info.Attribute.Foreground.GetAnsiColorCode (); - AnsiColorCode bgColor = info.Attribute.Background.GetAnsiColorCode (); - - if (attr != prev) - { - prev = attr; - _stringBuilder.Append (EscSeqUtils.CSI_SetForegroundColor (fgColor)); - _stringBuilder.Append (EscSeqUtils.CSI_SetBackgroundColor (bgColor)); - - EscSeqUtils.CSI_AppendTextStyleChange (_stringBuilder, _redrawTextStyle, attr.Style); - _redrawTextStyle = attr.Style; - } - - if (info.Char [0] != '\x1b') - { - if (!info.Empty) - { - _stringBuilder.Append (info.Char); - } - } - else - { - _stringBuilder.Append (' '); - } - } - else - { - if (info.Empty) - { - i++; - continue; - } - - if (!info.Empty) - { - var attr = (ushort)((int)info.Attribute.Foreground.GetClosestNamedColor16 () - | ((int)info.Attribute.Background.GetClosestNamedColor16 () << 4)); - - // Start new run if needed - if (current == null || attr != current.Value.attr) - { - if (current != null) - { - runs.Add (new (current.Value.attr, _stringBuilder.ToString ())); - } - - _stringBuilder.Clear (); - current = new Run (attr, ""); - } - - _stringBuilder!.Append (info.Char); - } - - i++; - - if (i > 0 && i <= charInfoBuffer.Length && i % bufferSize.X == 0) - { - if (i < charInfoBuffer.Length) - { - _stringBuilder.AppendLine (); - } - - runs.Add (new (current!.Value.attr, _stringBuilder.ToString ())); - _stringBuilder.Clear (); - } - } - } - - if (IsVirtualTerminal) - { - _stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition); - _stringBuilder.Append (EscSeqUtils.CSI_HideCursor); - - var s = _stringBuilder.ToString (); - - // TODO: requires extensive testing if we go down this route - // If console output has changed - if (s != _lastWrite) - { - // supply console with the new content - result = WriteConsole (_outputHandle, s, (uint)s.Length, out uint _, nint.Zero); - } - - _lastWrite = s; - - foreach (var sixel in Application.Sixel) - { - SetCursorPosition (new Coord ((short)sixel.ScreenPosition.X, (short)sixel.ScreenPosition.Y)); - WriteConsole (IsVirtualTerminal ? _outputHandle : _screenBuffer, sixel.SixelData, (uint)sixel.SixelData.Length, out uint _, nint.Zero); - } - } - else - { - foreach (var run in runs) - { - SetConsoleTextAttribute (IsVirtualTerminal ? _outputHandle : _screenBuffer, run.attr); - result = WriteConsole (IsVirtualTerminal ? _outputHandle : _screenBuffer, run.text, (uint)run.text.Length, out _, nint.Zero); - } - } - } - else - { - _stringBuilder.Clear (); - - _stringBuilder.Append (EscSeqUtils.CSI_SaveCursorPosition); - EscSeqUtils.CSI_AppendCursorPosition (_stringBuilder, 0, 0); - - foreach (ExtendedCharInfo info in charInfoBuffer) - { - Attribute attr = info.Attribute; - - if (attr != prev) - { - prev = attr; - EscSeqUtils.CSI_AppendForegroundColorRGB (_stringBuilder, attr.Foreground.R, attr.Foreground.G, attr.Foreground.B); - EscSeqUtils.CSI_AppendBackgroundColorRGB (_stringBuilder, attr.Background.R, attr.Background.G, attr.Background.B); - EscSeqUtils.CSI_AppendTextStyleChange (_stringBuilder, _redrawTextStyle, attr.Style); - _redrawTextStyle = attr.Style; - } - - if (info.Char [0] != '\x1b') - { - if (!info.Empty) - { - _stringBuilder.Append (info.Char); - } - } - else - { - _stringBuilder.Append (' '); - } - } - - _stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition); - _stringBuilder.Append (EscSeqUtils.CSI_HideCursor); - - var s = _stringBuilder.ToString (); - - // TODO: requires extensive testing if we go down this route - // If console output has changed - if (s != _lastWrite) - { - // supply console with the new content - result = WriteConsole (_outputHandle, s, (uint)s.Length, out uint _, nint.Zero); - } - - _lastWrite = s; - - foreach (var sixel in Application.Sixel) - { - SetCursorPosition (new Coord ((short)sixel.ScreenPosition.X, (short)sixel.ScreenPosition.Y)); - WriteConsole (IsVirtualTerminal ? _outputHandle : _screenBuffer, sixel.SixelData, (uint)sixel.SixelData.Length, out uint _, nint.Zero); - } - } - - if (!result) - { - int err = Marshal.GetLastWin32Error (); - - if (err != 0) - { - throw new Win32Exception (err); - } - } - - return result; - } - - internal bool WriteANSI (string ansi) - { - if (WriteConsole (_outputHandle, ansi, (uint)ansi.Length, out uint _, nint.Zero)) - { - // Flush the output to make sure it's sent immediately - return FlushFileBuffers (_outputHandle); - } - - return false; - } - - public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window) - { - _originalStdOutChars = new CharInfo [size.Height * size.Width]; - - if (!ReadConsoleOutput (_screenBuffer, _originalStdOutChars, coords, new Coord { X = 0, Y = 0 }, ref window)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - } - - public bool SetCursorPosition (Coord position) - { - return SetConsoleCursorPosition (IsVirtualTerminal ? _outputHandle : _screenBuffer, position); - } - - public void SetInitialCursorVisibility () - { - if (_initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) - { - _initialCursorVisibility = visibility; - } - } - - public bool GetCursorVisibility (out CursorVisibility visibility) - { - if ((IsVirtualTerminal ? _outputHandle : _screenBuffer) == nint.Zero) - { - visibility = CursorVisibility.Invisible; - - return false; - } - - if (!GetConsoleCursorInfo (IsVirtualTerminal ? _outputHandle : _screenBuffer, out ConsoleCursorInfo info)) - { - int err = Marshal.GetLastWin32Error (); - - if (err != 0) - { - throw new Win32Exception (err); - } - - visibility = CursorVisibility.Default; - - return false; - } - - if (!info.bVisible) - { - visibility = CursorVisibility.Invisible; - } - else if (info.dwSize > 50) - { - visibility = CursorVisibility.Default; - } - else - { - visibility = CursorVisibility.Default; - } - - return visibility != CursorVisibility.Invisible; - } - - public bool EnsureCursorVisibility () - { - if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility (_pendingCursorVisibility.Value)) - { - _pendingCursorVisibility = null; - - return true; - } - - return false; - } - - public void ForceRefreshCursorVisibility () - { - if (_currentCursorVisibility.HasValue) - { - _pendingCursorVisibility = _currentCursorVisibility; - _currentCursorVisibility = null; - } - } - - public bool SetCursorVisibility (CursorVisibility visibility) - { - if (_initialCursorVisibility.HasValue == false) - { - _pendingCursorVisibility = visibility; - - return false; - } - - if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) - { - var info = new ConsoleCursorInfo - { - dwSize = (uint)visibility & 0x00FF, - bVisible = ((uint)visibility & 0xFF00) != 0 - }; - - if (!SetConsoleCursorInfo (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref info)) - { - return false; - } - - _currentCursorVisibility = visibility; - } - - return true; - } - - public void Cleanup () - { - if (_initialCursorVisibility.HasValue) - { - SetCursorVisibility (_initialCursorVisibility.Value); - } - - //SetConsoleOutputWindow (out _); - - ConsoleMode = _originalConsoleMode; - - _outputHandle = CreateConsoleScreenBuffer ( - DesiredAccess.GenericRead | DesiredAccess.GenericWrite, - ShareMode.FileShareRead | ShareMode.FileShareWrite, - nint.Zero, - 1, - nint.Zero - ); - - if (!SetConsoleActiveScreenBuffer (_outputHandle)) - { - int err = Marshal.GetLastWin32Error (); - Console.WriteLine ("Error: {0}", err); - } - - if (_screenBuffer != nint.Zero) - { - CloseHandle (_screenBuffer); - } - - _screenBuffer = nint.Zero; - - _inputReadyCancellationTokenSource?.Cancel (); - _inputReadyCancellationTokenSource?.Dispose (); - _inputReadyCancellationTokenSource = null; - } - - internal Size GetConsoleBufferWindow (out Point position) - { - if ((IsVirtualTerminal ? _outputHandle : _screenBuffer) == nint.Zero) - { - position = Point.Empty; - - return Size.Empty; - } - - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - - if (!GetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi)) - { - //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - position = Point.Empty; - - return Size.Empty; - } - - Size sz = new ( - csbi.srWindow.Right - csbi.srWindow.Left + 1, - csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - position = new (csbi.srWindow.Left, csbi.srWindow.Top); - - return sz; - } - - internal Size GetConsoleOutputWindow (out Point position) - { - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - - if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - - Size sz = new ( - csbi.srWindow.Right - csbi.srWindow.Left + 1, - csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - position = new (csbi.srWindow.Left, csbi.srWindow.Top); - - return sz; - } - - internal Size SetConsoleWindow (short cols, short rows) - { - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - - if (!GetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - - Coord maxWinSize = GetLargestConsoleWindowSize (IsVirtualTerminal ? _outputHandle : _screenBuffer); - short newCols = Math.Min (cols, maxWinSize.X); - short newRows = Math.Min (rows, maxWinSize.Y); - csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1)); - csbi.srWindow = new SmallRect (0, 0, newCols, newRows); - csbi.dwMaximumWindowSize = new Coord (newCols, newRows); - - if (!SetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - - var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0)); - - if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) - { - //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - return new (cols, rows); - } - - SetConsoleOutputWindow (csbi); - - return new (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1); - } - - internal Size GetLargestConsoleWindowSize () - { - Coord maxWinSize = GetLargestConsoleWindowSize (IsVirtualTerminal ? _outputHandle : _screenBuffer); - - return new (maxWinSize.X, maxWinSize.Y); - } - - private void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi) - { - if ((IsVirtualTerminal - ? _outputHandle - : _screenBuffer) != nint.Zero && !SetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - } - - internal Size SetConsoleOutputWindow (out Point position) - { - if ((IsVirtualTerminal ? _outputHandle : _screenBuffer) == nint.Zero) - { - position = Point.Empty; - - return Size.Empty; - } - - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - - if (!GetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - - Size sz = new ( - csbi.srWindow.Right - csbi.srWindow.Left + 1, - Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0)); - position = new (csbi.srWindow.Left, csbi.srWindow.Top); - SetConsoleOutputWindow (csbi); - var winRect = new SmallRect (0, 0, (short)(sz.Width - 1), (short)Math.Max (sz.Height - 1, 0)); - - if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - - if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - - return sz; - } - - internal bool IsVirtualTerminal { get; init; } - - private uint ConsoleMode - { - get - { - GetConsoleMode (_inputHandle, out uint v); - - return v; - } - set => SetConsoleMode (_inputHandle, value); - } - + /// + /// Windows Console mode flags. + /// [Flags] public enum ConsoleModes : uint { @@ -724,6 +30,9 @@ public partial class WindowsConsole EnableExtendedFlags = 128 } + /// + /// Key event record structure. + /// [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] public struct KeyEventRecord { @@ -811,11 +120,9 @@ public partial class WindowsConsole public readonly override string ToString () { return $"[Mouse{MousePosition},{ButtonState},{ControlKeyState},{EventFlags}]"; } } - public struct WindowBufferSizeRecord + public struct WindowBufferSizeRecord (short x, short y) { - public Coord _size; - - public WindowBufferSizeRecord (short x, short y) { _size = new Coord (x, y); } + public Coord _size = new (x, y); public readonly override string ToString () { return $"[WindowBufferSize{_size}"; } } @@ -865,41 +172,17 @@ public partial class WindowsConsole public readonly override string ToString () { return (EventType switch - { - EventType.Focus => FocusEvent.ToString (), - EventType.Key => KeyEvent.ToString (), - EventType.Menu => MenuEvent.ToString (), - EventType.Mouse => MouseEvent.ToString (), - EventType.WindowBufferSize => WindowBufferSizeEvent.ToString (), - _ => "Unknown event type: " + EventType - })!; + { + EventType.Focus => FocusEvent.ToString (), + EventType.Key => KeyEvent.ToString (), + EventType.Menu => MenuEvent.ToString (), + EventType.Mouse => MouseEvent.ToString (), + EventType.WindowBufferSize => WindowBufferSizeEvent.ToString (), + _ => "Unknown event type: " + EventType + })!; } } - [Flags] - private enum ShareMode : uint - { - FileShareRead = 1, - FileShareWrite = 2 - } - - [Flags] - private enum DesiredAccess : uint - { - GenericRead = 2147483648, - GenericWrite = 1073741824 - } - - [StructLayout (LayoutKind.Sequential)] - public struct ConsoleScreenBufferInfo - { - public Coord dwSize; - public Coord dwCursorPosition; - public ushort wAttributes; - public SmallRect srWindow; - public Coord dwMaximumWindowSize; - } - [StructLayout (LayoutKind.Sequential)] public struct Coord { @@ -935,20 +218,6 @@ public partial class WindowsConsole public ushort Attributes; } - public struct ExtendedCharInfo - { - public char [] Char { get; set; } - public Attribute Attribute { get; set; } - public bool Empty { get; set; } // TODO: Temp hack until virtual terminal sequences - - public ExtendedCharInfo (char [] character, Attribute attribute) - { - Char = character; - Attribute = attribute; - Empty = false; - } - } - [StructLayout (LayoutKind.Sequential)] public struct SmallRect { @@ -1045,147 +314,19 @@ public partial class WindowsConsole } } - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern nint GetStdHandle (int nStdHandle); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool CloseHandle (nint handle); - - [DllImport ("kernel32.dll", SetLastError = true)] - public static extern bool PeekConsoleInput (nint hConsoleInput, out InputRecord lpBuffer, uint nLength, out uint lpNumberOfEventsRead); - - [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] - public static extern bool ReadConsoleInput ( - nint hConsoleInput, - out InputRecord lpBuffer, - uint nLength, - out uint lpNumberOfEventsRead - ); - - [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern bool ReadConsoleOutput ( - nint hConsoleOutput, - [Out] CharInfo [] lpBuffer, - Coord dwBufferSize, - Coord dwBufferCoord, - ref SmallRect lpReadRegion - ); - - // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput - [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutputW", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool WriteConsoleOutput ( - nint hConsoleOutput, - CharInfo [] lpBuffer, - Coord dwBufferSize, - Coord dwBufferCoord, - ref SmallRect lpWriteRegion - ); - - [LibraryImport ("kernel32.dll", EntryPoint = "WriteConsoleW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs (UnmanagedType.Bool)] - private static partial bool WriteConsole ( - nint hConsoleOutput, - ReadOnlySpan lpbufer, - uint NumberOfCharsToWriten, - out uint lpNumberOfCharsWritten, - nint lpReserved - ); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool SetConsoleTextAttribute ( - nint hConsoleOutput, - ushort wAttributes - ); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool FlushFileBuffers (nint hFile); - - [DllImport ("kernel32.dll")] - private static extern bool SetConsoleCursorPosition (nint hConsoleOutput, Coord dwCursorPosition); - [StructLayout (LayoutKind.Sequential)] public struct ConsoleCursorInfo { /// - /// The percentage of the character cell that is filled by the cursor.This value is between 1 and 100. - /// The cursor appearance varies, ranging from completely filling the cell to showing up as a horizontal - /// line at the bottom of the cell. + /// The percentage of the character cell that is filled by the cursor.This value is between 1 and 100. + /// The cursor appearance varies, ranging from completely filling the cell to showing up as a horizontal + /// line at the bottom of the cell. /// public uint dwSize; + public bool bVisible; } - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool SetConsoleCursorInfo (nint hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool GetConsoleCursorInfo (nint hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo); - - [DllImport ("kernel32.dll")] - private static extern bool GetConsoleMode (nint hConsoleHandle, out uint lpMode); - - [DllImport ("kernel32.dll")] - private static extern bool SetConsoleMode (nint hConsoleHandle, uint dwMode); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern nint CreateConsoleScreenBuffer ( - DesiredAccess dwDesiredAccess, - ShareMode dwShareMode, - nint secutiryAttributes, - uint flags, - nint screenBufferData - ); - - internal static nint INVALID_HANDLE_VALUE = new (-1); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool SetConsoleActiveScreenBuffer (nint handle); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool GetNumberOfConsoleInputEvents (nint handle, out uint lpcNumberOfEvents); - - internal uint GetNumberOfConsoleInputEvents () - { - if (!GetNumberOfConsoleInputEvents (_inputHandle, out uint numOfEvents)) - { - Console.WriteLine ($"Error: {Marshal.GetLastWin32Error ()}"); - - return 0; - } - - return numOfEvents; - } - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool FlushConsoleInputBuffer (nint handle); - - internal void FlushConsoleInputBuffer () - { - if (!FlushConsoleInputBuffer (_inputHandle)) - { - Console.WriteLine ($"Error: {Marshal.GetLastWin32Error ()}"); - } - } - -#if false // Not needed on the constructor. Perhaps could be used on resizing. To study. - [DllImport ("kernel32.dll", ExactSpelling = true)] - static extern IntPtr GetConsoleWindow (); - - [DllImport ("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - static extern bool ShowWindow (IntPtr hWnd, int nCmdShow); - - public const int HIDE = 0; - public const int MAXIMIZE = 3; - public const int MINIMIZE = 6; - public const int RESTORE = 9; - - internal void ShowWindow (int state) - { - IntPtr thisConsole = GetConsoleWindow (); - ShowWindow (thisConsole, state); - } -#endif - // See: https://github.com/gui-cs/Terminal.Gui/issues/357 [StructLayout (LayoutKind.Sequential)] @@ -1235,22 +376,4 @@ public partial class WindowsConsole [FieldOffset (0)] public uint Value; } - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool GetConsoleScreenBufferInfoEx (nint hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool SetConsoleScreenBufferInfoEx (nint hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX consoleScreenBufferInfo); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern bool SetConsoleWindowInfo ( - nint hConsoleOutput, - bool bAbsolute, - [In] ref SmallRect lpConsoleWindow - ); - - [DllImport ("kernel32.dll", SetLastError = true)] - private static extern Coord GetLargestConsoleWindowSize ( - nint hConsoleOutput - ); } diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsInput.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsInput.cs index 94f2c5ec8..12ef0c15d 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsInput.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsInput.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +#nullable enable +using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; using static Terminal.Gui.Drivers.WindowsConsole; @@ -66,13 +67,13 @@ internal class WindowsInput : ConsoleInput, IWindowsInput return false; } - const int bufferSize = 1; // We only need to check if there's at least one event - nint pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize); + const int BUFFER_SIZE = 1; // We only need to check if there's at least one event + nint pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * BUFFER_SIZE); try { // Use PeekConsoleInput to inspect the input buffer without removing events - if (PeekConsoleInput (_inputHandle, pRecord, bufferSize, out uint numberOfEventsRead)) + if (PeekConsoleInput (_inputHandle, pRecord, BUFFER_SIZE, out uint numberOfEventsRead)) { // Return true if there's at least one event in the buffer return numberOfEventsRead > 0; @@ -86,7 +87,7 @@ internal class WindowsInput : ConsoleInput, IWindowsInput catch (Exception ex) { // Optionally log the exception - Console.WriteLine ($"Error in Peek: {ex.Message}"); + Console.WriteLine (@$"Error in Peek: {ex.Message}"); return false; } @@ -99,15 +100,15 @@ internal class WindowsInput : ConsoleInput, IWindowsInput protected override IEnumerable Read () { - const int bufferSize = 1; - nint pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize); + const int BUFFER_SIZE = 1; + nint pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * BUFFER_SIZE); try { ReadConsoleInput ( _inputHandle, pRecord, - bufferSize, + BUFFER_SIZE, out uint numberEventsRead); return numberEventsRead == 0 diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs index 6c5f806c7..b04b9faa1 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs @@ -356,7 +356,7 @@ internal partial class WindowsOutput : OutputBase, IConsoleOutput private Size? _lastWindowSizeBeforeMaximized; private bool _lockResize; - public Size GetWindowSize () + public Size GetSize () { if (_lockResize) { @@ -497,6 +497,12 @@ internal partial class WindowsOutput : OutputBase, IConsoleOutput } } + /// + public void SetSize (int width, int height) + { + // Do Nothing. + } + private bool _isDisposed; private bool _force16Colors; private nint _consoleBuffer; diff --git a/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs b/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs index 999011e3e..b55fec027 100644 --- a/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs +++ b/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs @@ -688,7 +688,7 @@ public partial class Border break; case ViewArrangement.BottomResizable: - Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1); + Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin!.Thickness.Bottom + 1); break; case ViewArrangement.LeftResizable: @@ -705,12 +705,12 @@ public partial class Border break; case ViewArrangement.RightResizable: - Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1); + Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin!.Thickness.Right + 1); break; case ViewArrangement.BottomResizable | ViewArrangement.RightResizable: - Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1); - Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1); + Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin!.Thickness.Right + 1); + Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin!.Thickness.Bottom + 1); break; case ViewArrangement.BottomResizable | ViewArrangement.LeftResizable: @@ -723,7 +723,7 @@ public partial class Border Parent.X = parentLoc.X - _startGrabPoint.X; } - Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1); + Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin!.Thickness.Bottom + 1); break; case ViewArrangement.TopResizable | ViewArrangement.RightResizable: @@ -736,7 +736,7 @@ public partial class Border Parent.Y = parentLoc.Y - _startGrabPoint.Y; } - Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1); + Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin!.Thickness.Right + 1); break; case ViewArrangement.TopResizable | ViewArrangement.LeftResizable: diff --git a/Terminal.Gui/ViewBase/Adornment/Border.cs b/Terminal.Gui/ViewBase/Adornment/Border.cs index b092aae1d..18a99b1c1 100644 --- a/Terminal.Gui/ViewBase/Adornment/Border.cs +++ b/Terminal.Gui/ViewBase/Adornment/Border.cs @@ -152,7 +152,7 @@ public partial class Border : Adornment #if SUBVIEW_BASED_BORDER private void OnLayoutStarted (object sender, LayoutEventArgs e) { - _left.Border.LineStyle = LineStyle; + _left.Border!.LineStyle = LineStyle; _left.X = Thickness.Left - 1; _left.Y = Thickness.Top - 1; diff --git a/Terminal.Gui/ViewBase/Adornment/Margin.cs b/Terminal.Gui/ViewBase/Adornment/Margin.cs index 4695d3ee5..59b39930f 100644 --- a/Terminal.Gui/ViewBase/Adornment/Margin.cs +++ b/Terminal.Gui/ViewBase/Adornment/Margin.cs @@ -79,12 +79,12 @@ public class Margin : Adornment if (view.Margin?.GetCachedClip () != null) { - view.Margin.NeedsDraw = true; + view.Margin!.NeedsDraw = true; Region? saved = GetClip (); - View.SetClip (view.Margin.GetCachedClip ()); - view.Margin.Draw (); + View.SetClip (view.Margin!.GetCachedClip ()); + view.Margin!.Draw (); View.SetClip (saved); - view.Margin.ClearCachedClip (); + view.Margin!.ClearCachedClip (); } view.NeedsDraw = false; @@ -292,14 +292,14 @@ public class Margin : Adornment { case ShadowStyle.Transparent: // BUGBUG: This doesn't work right for all Border.Top sizes - Need an API on Border that gives top-right location of line corner. - _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0; + _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border!.GetBorderRectangle ().Location).Y + 1 : 0; break; case ShadowStyle.Opaque: // BUGBUG: This doesn't work right for all Border.Top sizes - Need an API on Border that gives top-right location of line corner. - _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0; - _bottomShadow.X = Parent.Border.Thickness.Left > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).X + 1 : 0; + _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border!.GetBorderRectangle ().Location).Y + 1 : 0; + _bottomShadow.X = Parent.Border!.Thickness.Left > 0 ? ScreenToViewport (Parent.Border!.GetBorderRectangle ().Location).X + 1 : 0; break; diff --git a/Terminal.Gui/ViewBase/Adornment/ShadowView.cs b/Terminal.Gui/ViewBase/Adornment/ShadowView.cs index e2dbb7d46..151aa149c 100644 --- a/Terminal.Gui/ViewBase/Adornment/ShadowView.cs +++ b/Terminal.Gui/ViewBase/Adornment/ShadowView.cs @@ -95,7 +95,7 @@ internal class ShadowView : View { for (int c = Math.Max (0, screen.X + 1); c < screen.X + screen.Width; c++) { - Driver.Move (c, r); + Driver?.Move (c, r); SetAttribute (GetAttributeUnderLocation (new (c, r))); if (c < ScreenContents?.GetLength (1) && r < ScreenContents?.GetLength (0)) @@ -129,7 +129,7 @@ internal class ShadowView : View { for (int r = Math.Max (0, screen.Y); r < screen.Y + viewport.Height; r++) { - Driver.Move (c, r); + Driver?.Move (c, r); SetAttribute (GetAttributeUnderLocation (new (c, r))); if (ScreenContents is { } && screen.X < ScreenContents.GetLength (1) && r < ScreenContents.GetLength (0)) diff --git a/Terminal.Gui/ViewBase/Layout/DimAuto.cs b/Terminal.Gui/ViewBase/Layout/DimAuto.cs index 34ad05b19..b70aca542 100644 --- a/Terminal.Gui/ViewBase/Layout/DimAuto.cs +++ b/Terminal.Gui/ViewBase/Layout/DimAuto.cs @@ -35,7 +35,7 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt int screenX4 = dimension == Dimension.Width ? Application.Screen.Width * 4 : Application.Screen.Height * 4; int autoMax = MaximumContentDim?.GetAnchor (superviewContentSize) ?? screenX4; - Debug.WriteLineIf (autoMin > autoMax, "MinimumContentDim must be less than or equal to MaximumContentDim."); + //Debug.WriteLineIf (autoMin > autoMax, "MinimumContentDim must be less than or equal to MaximumContentDim."); if (Style.FastHasFlags (DimAutoStyle.Text)) { diff --git a/Terminal.Gui/ViewBase/View.Layout.cs b/Terminal.Gui/ViewBase/View.Layout.cs index 72bb409b3..b12e2b78c 100644 --- a/Terminal.Gui/ViewBase/View.Layout.cs +++ b/Terminal.Gui/ViewBase/View.Layout.cs @@ -37,7 +37,7 @@ public partial class View // Layout APIs /// /// Changing this property will result in and to be set, /// resulting in the - /// view being laid out and redrawn as appropriate in the next iteration of the . + /// view being laid out and redrawn as appropriate in the next iteration. /// /// public Rectangle Frame @@ -219,7 +219,7 @@ public partial class View // Layout APIs /// /// Changing this property will result in and to be set, /// resulting in the - /// view being laid out and redrawn as appropriate in the next iteration of the . + /// view being laid out and redrawn as appropriate in the next iteration. /// /// /// Changing this property will cause to be updated. @@ -264,7 +264,7 @@ public partial class View // Layout APIs /// /// Changing this property will result in and to be set, /// resulting in the - /// view being laid out and redrawn as appropriate in the next iteration of the . + /// view being laid out and redrawn as appropriate in the next iteration. /// /// /// Changing this property will cause to be updated. @@ -308,7 +308,7 @@ public partial class View // Layout APIs /// /// Changing this property will result in and to be set, /// resulting in the - /// view being laid out and redrawn as appropriate in the next iteration of the . + /// view being laid out and redrawn as appropriate in the next iteration. /// /// /// Changing this property will cause to be updated. @@ -396,7 +396,7 @@ public partial class View // Layout APIs /// /// Changing this property will result in and to be set, /// resulting in the - /// view being laid out and redrawn as appropriate in the next iteration of the . + /// view being laid out and redrawn as appropriate in the next iteration. /// /// /// Changing this property will cause to be updated. @@ -843,7 +843,7 @@ public partial class View // Layout APIs /// /// /// - /// The will cause to be called on the next + /// The next iteration will cause to be called on the next /// so there is normally no reason to call see . /// /// @@ -882,12 +882,12 @@ public partial class View // Layout APIs if (current.Margin is { SubViews.Count: > 0 }) { - current.Margin.SetNeedsLayout (); + current.Margin!.SetNeedsLayout (); } if (current.Border is { SubViews.Count: > 0 }) { - current.Border.SetNeedsLayout (); + current.Border!.SetNeedsLayout (); } if (current.Padding is { SubViews.Count: > 0 }) diff --git a/Terminal.Gui/Views/Menuv1/MenuBar.cs b/Terminal.Gui/Views/Menuv1/MenuBar.cs index 8b66a63dc..8f56ed76b 100644 --- a/Terminal.Gui/Views/Menuv1/MenuBar.cs +++ b/Terminal.Gui/Views/Menuv1/MenuBar.cs @@ -1441,8 +1441,8 @@ public class MenuBar : View, IDesignable if (SuperView is { }) { - locationOffset.X += SuperView.Border.Thickness.Left; - locationOffset.Y += SuperView.Border.Thickness.Top; + locationOffset.X += SuperView.Border!.Thickness.Left; + locationOffset.Y += SuperView.Border!.Thickness.Top; } int cx = me.Position.X - locationOffset.X; diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index e4fee2f2a..ccccc01bb 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -210,14 +210,14 @@ public class Shortcut : View, IOrientation, IDesignable case 0: case 1: // Scrunch it by removing both margins - HelpView.Margin.Thickness = new (t.Right - 1, t.Top, t.Left - 1, t.Bottom); + HelpView.Margin!.Thickness = new (t.Right - 1, t.Top, t.Left - 1, t.Bottom); break; case 2: // Scrunch just the right margin - HelpView.Margin.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom); + HelpView.Margin!.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom); break; } @@ -225,7 +225,7 @@ public class Shortcut : View, IOrientation, IDesignable else { // Reset to default - HelpView.Margin.Thickness = GetMarginThickness (); + HelpView.Margin!.Thickness = GetMarginThickness (); } } @@ -471,9 +471,9 @@ public class Shortcut : View, IOrientation, IDesignable { if (CommandView.Margin is { }) { - CommandView.Margin.Thickness = GetMarginThickness (); + CommandView.Margin!.Thickness = GetMarginThickness (); // strip off ViewportSettings.TransparentMouse - CommandView.Margin.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; + CommandView.Margin!.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; } CommandView.X = Pos.Align (Alignment.End, AlignmentModes); @@ -533,9 +533,9 @@ public class Shortcut : View, IOrientation, IDesignable { if (HelpView.Margin is { }) { - HelpView.Margin.Thickness = GetMarginThickness (); + HelpView.Margin!.Thickness = GetMarginThickness (); // strip off ViewportSettings.TransparentMouse - HelpView.Margin.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; + HelpView.Margin!.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; } HelpView.X = Pos.Align (Alignment.End, AlignmentModes); @@ -666,9 +666,9 @@ public class Shortcut : View, IOrientation, IDesignable { if (KeyView.Margin is { }) { - KeyView.Margin.Thickness = GetMarginThickness (); + KeyView.Margin!.Thickness = GetMarginThickness (); // strip off ViewportSettings.TransparentMouse - KeyView.Margin.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; + KeyView.Margin!.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; } KeyView.X = Pos.Align (Alignment.End, AlignmentModes); diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs index 9be738c7d..3d58a7afc 100644 --- a/Terminal.Gui/Views/StatusBar.cs +++ b/Terminal.Gui/Views/StatusBar.cs @@ -71,7 +71,7 @@ public class StatusBar : Bar, IDesignable if (barItem.Border is { }) { - barItem.Border.Thickness = index == SubViews.Count - 1 ? new Thickness (0, 0, 0, 0) : new Thickness (0, 0, 1, 0); + barItem.Border!.Thickness = index == SubViews.Count - 1 ? new Thickness (0, 0, 0, 0) : new Thickness (0, 0, 1, 0); } if (barItem is Shortcut shortcut) diff --git a/Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs b/Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs index 11dd5d811..f082807c6 100644 --- a/Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs +++ b/Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs @@ -61,7 +61,7 @@ public class NetMaskedTextProvider : ITextValidateProvider public bool Fixed => true; /// - public string DisplayText => _provider.ToDisplayString (); + public string DisplayText => _provider!.ToDisplayString (); /// public int Cursor (int pos) diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index 59b0475a6..811824dbf 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -451,7 +451,6 @@ internal class Branch where T : class /// Returns true if the given x offset on the branch line is the +/- symbol. Returns false if not showing /// expansion symbols or leaf node etc. /// - /// /// /// internal bool IsHitOnExpandableSymbol (int x) diff --git a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationFactory.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationFactory.cs index 65a8f3a70..3485ecf08 100644 --- a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationFactory.cs +++ b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationFactory.cs @@ -16,11 +16,11 @@ public class FakeApplicationFactory var cts = new CancellationTokenSource (); var fakeInput = new FakeNetInput (cts.Token); FakeOutput output = new (); - output.Size = new (25, 25); + output.Size = new (80, 25); IApplication origApp = ApplicationImpl.Instance; - var sizeMonitor = new FakeSizeMonitor (); + var sizeMonitor = new FakeSizeMonitor (output, output.LastBuffer!); var impl = new ApplicationImpl (new FakeNetComponentFactory (fakeInput, output, sizeMonitor)); @@ -32,13 +32,13 @@ public class FakeApplicationFactory // Handle different facade types - cast to common interface instead var d = (IConsoleDriverFacade)Application.Driver!; - sizeMonitor.SizeChanging += (_, e) => + sizeMonitor.SizeChanged += (_, e) => { if (e.Size != null) { Size s = e.Size.Value; output.Size = s; - d.OutputBuffer.SetWindowSize (s.Width, s.Height); + d.OutputBuffer.SetSize (s.Width, s.Height); } }; diff --git a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeConsoleDriver.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeConsoleDriver.cs deleted file mode 100644 index 4b42dee78..000000000 --- a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeConsoleDriver.cs +++ /dev/null @@ -1,58 +0,0 @@ -#nullable enable -using System.Collections.Concurrent; -using System.Drawing; -using TerminalGuiFluentTesting; - -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - -namespace Terminal.Gui.Drivers; - -/// -/// Implementation of that uses fake input/output. -/// This is a lightweight alternative to (if you don't -/// need the entire application main loop running). -/// -internal class FakeConsoleDriver : ConsoleDriverFacade, IFakeConsoleDriver -{ - internal FakeConsoleDriver ( - ConcurrentQueue inputBuffer, - OutputBuffer outputBuffer, - FakeOutput fakeOutput, - Func datetimeFunc, - FakeSizeMonitor sizeMonitor - ) : - base ( - new NetInputProcessor (inputBuffer), - outputBuffer, - fakeOutput, - new (new AnsiResponseParser (), datetimeFunc), - sizeMonitor) - { - FakeOutput fakeOutput1; - InputBuffer = inputBuffer; - SizeMonitor = sizeMonitor; - OutputBuffer = outputBuffer; - ConsoleOutput = fakeOutput1 = fakeOutput; - - SizeChanged += (_, e) => - { - if (e.Size != null) - { - Size s = e.Size.Value; - fakeOutput1.Size = s; - OutputBuffer.SetWindowSize (s.Width, s.Height); - } - }; - } - - public void SetBufferSize (int width, int height) - { - SizeMonitor.RaiseSizeChanging (new (width, height)); - OutputBuffer.SetWindowSize (width, height); - } - - public IConsoleOutput ConsoleOutput { get; } - public ConcurrentQueue InputBuffer { get; } - public new OutputBuffer OutputBuffer { get; } - public FakeSizeMonitor SizeMonitor { get; } -} \ No newline at end of file diff --git a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeDriverFactory.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeDriverFactory.cs deleted file mode 100644 index 793b3362e..000000000 --- a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeDriverFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -#nullable enable -namespace Terminal.Gui.Drivers; - -#pragma warning disable CS1591 -public class FakeDriverFactory -{ - /// - /// Creates a new instance of using default options - /// - /// - public IFakeConsoleDriver Create () - { - return new FakeConsoleDriver ( - new (), - new (), - new (), - () => DateTime.Now, - new ()); - } -} diff --git a/Tests/TerminalGuiFluentTesting/FakeInput.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeInput.cs similarity index 100% rename from Tests/TerminalGuiFluentTesting/FakeInput.cs rename to Tests/TerminalGuiFluentTesting/FakeDriver/FakeInput.cs diff --git a/Tests/TerminalGuiFluentTesting/FakeOutput.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeOutput.cs similarity index 83% rename from Tests/TerminalGuiFluentTesting/FakeOutput.cs rename to Tests/TerminalGuiFluentTesting/FakeDriver/FakeOutput.cs index 9f747e8f7..00b8bd1b7 100644 --- a/Tests/TerminalGuiFluentTesting/FakeOutput.cs +++ b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeOutput.cs @@ -17,7 +17,7 @@ internal class FakeOutput : IConsoleOutput public void Write (IOutputBuffer buffer) { LastBuffer = buffer; } /// - public Size GetWindowSize () { return Size; } + public Size GetSize () { return Size; } /// public void SetCursorVisibility (CursorVisibility visibility) { } @@ -25,6 +25,12 @@ internal class FakeOutput : IConsoleOutput /// public void SetCursorPosition (int col, int row) { CursorPosition = new Point (col, row); } + /// + public void SetSize (int width, int height) + { + Size = new (width, height); + } + /// /// The last value set by calling /// diff --git a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeSizeMonitor.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeSizeMonitor.cs index 2c4a982e9..cd2ac4f77 100644 --- a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeSizeMonitor.cs +++ b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeSizeMonitor.cs @@ -4,17 +4,17 @@ using System.Drawing; namespace Terminal.Gui.Drivers; #pragma warning disable CS1591 -public class FakeSizeMonitor : IWindowSizeMonitor +public class FakeSizeMonitor (IConsoleOutput consoleOut, IOutputBuffer _) : IConsoleSizeMonitor { - /// - public event EventHandler? SizeChanging; + /// + public event EventHandler? SizeChanged; /// public bool Poll () { return false; } /// - /// Raises the event. + /// Raises the event. /// /// - public void RaiseSizeChanging (Size newSize) { SizeChanging?.Invoke (this, new (newSize)); } -} + public void RaiseSizeChanged (Size newSize) { SizeChanged?.Invoke (this, new (newSize)); } +} \ No newline at end of file diff --git a/Tests/TerminalGuiFluentTesting/FakeWindowsInput.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeWindowsInput.cs similarity index 100% rename from Tests/TerminalGuiFluentTesting/FakeWindowsInput.cs rename to Tests/TerminalGuiFluentTesting/FakeDriver/FakeWindowsInput.cs diff --git a/Tests/TerminalGuiFluentTesting/FakeDriver/IFakeConsoleDriver.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/IFakeConsoleDriver.cs deleted file mode 100644 index b0186cb2e..000000000 --- a/Tests/TerminalGuiFluentTesting/FakeDriver/IFakeConsoleDriver.cs +++ /dev/null @@ -1,8 +0,0 @@ -#nullable enable -namespace Terminal.Gui.Drivers; - -#pragma warning disable CS1591 -public interface IFakeConsoleDriver : IConsoleDriver, IConsoleDriverFacade -{ - void SetBufferSize (int width, int height); -} diff --git a/Tests/TerminalGuiFluentTesting/GuiTestContext.cs b/Tests/TerminalGuiFluentTesting/GuiTestContext.cs index 9bec9caf0..754ac4886 100644 --- a/Tests/TerminalGuiFluentTesting/GuiTestContext.cs +++ b/Tests/TerminalGuiFluentTesting/GuiTestContext.cs @@ -43,7 +43,7 @@ public class GuiTestContext : IDisposable _winInput = new (_cts.Token); _output.Size = new (width, height); - _fakeSizeMonitor = new (); + _fakeSizeMonitor = new (_output, _output.LastBuffer!); IComponentFactory cf = driver == TestDriver.DotNet ? new FakeNetComponentFactory (_netInput, _output, _fakeSizeMonitor) @@ -238,11 +238,7 @@ public class GuiTestContext : IDisposable return WaitIteration ( () => { - _output.Size = new (width, height); - _fakeSizeMonitor.RaiseSizeChanging (_output.Size); - - var d = (IConsoleDriverFacade)Application.Driver!; - d.OutputBuffer.SetWindowSize (width, height); + Application.Driver!.SetScreenSize(width, height); }); } @@ -938,48 +934,35 @@ public class GuiTestContext : IDisposable public Point GetCursorPosition () { return _output.CursorPosition; } } -internal class FakeWindowsComponentFactory : WindowsComponentFactory +internal class FakeWindowsComponentFactory (FakeWindowsInput winInput, FakeOutput output, FakeSizeMonitor fakeSizeMonitor) + : WindowsComponentFactory { - private readonly FakeWindowsInput _winInput; - private readonly FakeOutput _output; - private readonly FakeSizeMonitor _fakeSizeMonitor; + /// + public override IConsoleInput CreateInput () { return winInput; } - public FakeWindowsComponentFactory (FakeWindowsInput winInput, FakeOutput output, FakeSizeMonitor fakeSizeMonitor) + /// + public override IConsoleOutput CreateOutput () { return output; } + + /// + public override IConsoleSizeMonitor CreateConsoleSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer) { - _winInput = winInput; - _output = output; - _fakeSizeMonitor = fakeSizeMonitor; + outputBuffer.SetSize (consoleOutput.GetSize ().Width, consoleOutput.GetSize ().Height); + return fakeSizeMonitor; } - - /// - public override IConsoleInput CreateInput () { return _winInput; } - - /// - public override IConsoleOutput CreateOutput () { return _output; } - - /// - public override IWindowSizeMonitor CreateWindowSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer) { return _fakeSizeMonitor; } } -internal class FakeNetComponentFactory : NetComponentFactory +internal class FakeNetComponentFactory (FakeNetInput netInput, FakeOutput output, FakeSizeMonitor fakeSizeMonitor) : NetComponentFactory { - private readonly FakeNetInput _netInput; - private readonly FakeOutput _output; - private readonly FakeSizeMonitor _fakeSizeMonitor; + /// + public override IConsoleInput CreateInput () { return netInput; } - public FakeNetComponentFactory (FakeNetInput netInput, FakeOutput output, FakeSizeMonitor fakeSizeMonitor) + /// + public override IConsoleOutput CreateOutput () { return output; } + + /// + public override IConsoleSizeMonitor CreateConsoleSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer) { - _netInput = netInput; - _output = output; - _fakeSizeMonitor = fakeSizeMonitor; + outputBuffer.SetSize (consoleOutput.GetSize ().Width, consoleOutput.GetSize ().Height); + return fakeSizeMonitor; } - - /// - public override IConsoleInput CreateInput () { return _netInput; } - - /// - public override IConsoleOutput CreateOutput () { return _output; } - - /// - public override IWindowSizeMonitor CreateWindowSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer) { return _fakeSizeMonitor; } } diff --git a/Tests/UnitTests/Application/Application.NavigationTests.cs b/Tests/UnitTests/Application/Application.NavigationTests.cs index cc61f8643..03f7b9235 100644 --- a/Tests/UnitTests/Application/Application.NavigationTests.cs +++ b/Tests/UnitTests/Application/Application.NavigationTests.cs @@ -1,7 +1,7 @@ using UnitTests; using Xunit.Abstractions; -namespace UnitTests.ApplicationTests.NavigationTests; +namespace UnitTests.ApplicationTests; public class ApplicationNavigationTests (ITestOutputHelper output) { diff --git a/Tests/UnitTests/Application/ApplicationImplTests.cs b/Tests/UnitTests/Application/ApplicationImplTests.cs index c04b6dfad..b0e41bf37 100644 --- a/Tests/UnitTests/Application/ApplicationImplTests.cs +++ b/Tests/UnitTests/Application/ApplicationImplTests.cs @@ -25,7 +25,7 @@ public class ApplicationImplTests m.Setup (f => f.CreateInput ()).Returns (netInput.Object); m.Setup (f => f.CreateInputProcessor (It.IsAny> ())).Returns (Mock.Of ()); m.Setup (f => f.CreateOutput ()).Returns (Mock.Of ()); - m.Setup (f => f.CreateWindowSizeMonitor (It.IsAny (),It.IsAny ())).Returns (Mock.Of ()); + m.Setup (f => f.CreateConsoleSizeMonitor (It.IsAny (),It.IsAny ())).Returns (Mock.Of ()); return new (m.Object); } @@ -38,7 +38,7 @@ public class ApplicationImplTests m.Setup (f => f.CreateInput ()).Returns (winInput.Object); m.Setup (f => f.CreateInputProcessor (It.IsAny> ())).Returns (Mock.Of ()); m.Setup (f => f.CreateOutput ()).Returns (Mock.Of ()); - m.Setup (f => f.CreateWindowSizeMonitor (It.IsAny (), It.IsAny ())).Returns (Mock.Of ()); + m.Setup (f => f.CreateConsoleSizeMonitor (It.IsAny (), It.IsAny ())).Returns (Mock.Of ()); return new (m.Object); } } diff --git a/Tests/UnitTests/Application/ApplicationPopoverTests.cs b/Tests/UnitTests/Application/ApplicationPopoverTests.cs index 53a0e4651..c068a3723 100644 --- a/Tests/UnitTests/Application/ApplicationPopoverTests.cs +++ b/Tests/UnitTests/Application/ApplicationPopoverTests.cs @@ -1,4 +1,5 @@ -namespace UnitTests.ApplicationTests; +#nullable enable +namespace UnitTests.ApplicationTests; public class ApplicationPopoverTests { @@ -276,7 +277,7 @@ public class ApplicationPopoverTests public PopoverTestClass () { CanFocus = true; - AddCommand (Command.New, NewCommandHandler); + AddCommand (Command.New, NewCommandHandler!); HotKeyBindings.Add (Key.N.WithCtrl, Command.New); return; diff --git a/Tests/UnitTests/Application/ApplicationScreenTests.cs b/Tests/UnitTests/Application/ApplicationScreenTests.cs index 4fb8b03d9..0e7cac0c4 100644 --- a/Tests/UnitTests/Application/ApplicationScreenTests.cs +++ b/Tests/UnitTests/Application/ApplicationScreenTests.cs @@ -89,7 +89,7 @@ public class ApplicationScreenTests } [Fact] - public void Screen_Changes_OnSizeChanged_Without_Call_Application_Init () + public void Screen_Changes_OnScreenChanged_Without_Call_Application_Init () { // Arrange Application.ResetState (true); @@ -99,7 +99,7 @@ public class ApplicationScreenTests Assert.Equal (new (0, 0, 25, 25), Application.Screen); // Act - ((FakeDriver)Application.Driver)!.SetBufferSize (120, 30); + Application.Driver.SetScreenSize(120,30); // Assert Assert.Equal (new (0, 0, 120, 30), Application.Screen); diff --git a/Tests/UnitTests/Application/ApplicationTests.cs b/Tests/UnitTests/Application/ApplicationTests.cs index 8de8b43b5..56e39c1aa 100644 --- a/Tests/UnitTests/Application/ApplicationTests.cs +++ b/Tests/UnitTests/Application/ApplicationTests.cs @@ -1,8 +1,4 @@ using System.Diagnostics; -using System.Reflection; -using JetBrains.Annotations; -using Terminal.Gui.Drivers; -using UnitTests; using Xunit.Abstractions; using static Terminal.Gui.Configuration.ConfigurationManager; @@ -81,8 +77,8 @@ public class ApplicationTests _timeoutLock = null; } - a.After (null); + return; void OnApplicationOnInitializedChanged (object s, EventArgs a) @@ -167,11 +163,11 @@ public class ApplicationTests public void Begin_Sets_Application_Top_To_Console_Size () { Assert.Null (Application.Top); - AutoInitShutdownAttribute.FakeResize (new Size (80, 25)); + Application.Driver!.SetScreenSize (80, 25); Toplevel top = new (); Application.Begin (top); Assert.Equal (new (0, 0, 80, 25), Application.Top!.Frame); - AutoInitShutdownAttribute.FakeResize (new Size (5, 5)); + Application.Driver!.SetScreenSize (5, 5); Assert.Equal (new (0, 0, 5, 5), Application.Top!.Frame); top.Dispose (); } @@ -212,7 +208,7 @@ public class ApplicationTests public void Init_Begin_End_Cleans_Up () { // Start stopwatch - Stopwatch stopwatch = new Stopwatch (); + var stopwatch = new Stopwatch (); stopwatch.Start (); // Begin will cause Run() to be called, which will call Begin(). Thus will block the tests @@ -252,7 +248,6 @@ public class ApplicationTests stopwatch.Stop (); _output.WriteLine ($"Load took {stopwatch.ElapsedMilliseconds} ms"); - } // Legacy driver test - all InlineData commented out @@ -284,6 +279,7 @@ public class ApplicationTests [Theory] [InlineData (typeof (FakeDriver))] + //[InlineData (typeof (DotNetDriver))] //[InlineData (typeof (WindowsDriver))] //[InlineData (typeof (UnixDriver))] @@ -422,6 +418,7 @@ public class ApplicationTests [Theory] [InlineData (typeof (FakeDriver))] + //[InlineData (typeof (DotNetDriver))] //[InlineData (typeof (WindowsDriver))] //[InlineData (typeof (UnixDriver))] @@ -461,11 +458,8 @@ public class ApplicationTests [AutoInitShutdown] public void Init_Unbalanced_Throws () { - Assert.Throws ( - () => - Application.InternalInit ( - new FakeDriver () - ) + Assert.Throws (() => + Application.Init (null, "fake") ); Application.Shutdown (); @@ -473,7 +467,6 @@ public class ApplicationTests Assert.Null (Application.Driver); } - [Fact] [AutoInitShutdown] public void Init_Unbalanced_Throws2 () @@ -496,7 +489,7 @@ public class ApplicationTests // NOTE: Run, when called after Init has been called behaves differently than // when called if Init has not been called. Toplevel topLevel = new (); - Application.InternalInit (new FakeDriver ()); + Application.Init (null, "fake"); RunState runstate = null; @@ -532,6 +525,7 @@ public class ApplicationTests { Application.ForceDriver = "Fake"; Application.Init (); + //Assert.IsType(Application.Drive); //Assert.IsType (Application.Driver); Application.ResetState (); @@ -556,7 +550,7 @@ public class ApplicationTests } finally { - Application.ResetState (false); + Application.ResetState (); } } @@ -594,7 +588,7 @@ public class ApplicationTests { var iteration = 0; - Application.Init (null, driverName: "fake"); + Application.Init (null, "fake"); Application.Iteration += Application_Iteration; Application.Run ().Dispose (); @@ -620,9 +614,9 @@ public class ApplicationTests [AutoInitShutdown] public void Screen_Size_Changes () { - var driver = Application.Driver; + IConsoleDriver driver = Application.Driver; - AutoInitShutdownAttribute.FakeResize (new Size (80,25)); + Application.Driver!.SetScreenSize (80, 25); Assert.Equal (new (0, 0, 80, 25), driver.Screen); Assert.Equal (new (0, 0, 80, 25), Application.Screen); @@ -630,13 +624,14 @@ public class ApplicationTests // TODO: Should not be possible to manually change these at whim! driver.Cols = 100; driver.Rows = 30; + // IConsoleDriver.Screen isn't assignable //driver.Screen = new (0, 0, driver.Cols, Rows); - AutoInitShutdownAttribute.FakeResize (new Size (100, 30)); + Application.Driver!.SetScreenSize (100, 30); Assert.Equal (new (0, 0, 100, 30), driver.Screen); - + // Assert does not make sense // Assert.NotEqual (new (0, 0, 100, 30), Application.Screen); // Assert.Equal (new (0, 0, 80, 25), Application.Screen); @@ -647,11 +642,7 @@ public class ApplicationTests } [Fact] - public void InitState_Throws_If_Driver_Is_Null () - { - Assert.Throws (static () => Application.SubscribeDriverEvents ()); - } - + public void InitState_Throws_If_Driver_Is_Null () { Assert.Throws (static () => Application.SubscribeDriverEvents ()); } #region RunTests @@ -701,7 +692,6 @@ public class ApplicationTests [TestRespondersDisposed] public void Run_T_After_Init_Does_Not_Disposes_Application_Top () { - // Init doesn't create a Toplevel and assigned it to Application.Top // but Begin does var initTop = new Toplevel (); @@ -785,7 +775,7 @@ public class ApplicationTests Assert.Null (Application.Driver); } - [Fact(Skip = "FakeDriver is not allowed, use AutoInitShutdown attribute instead")] + [Fact (Skip = "FakeDriver is not allowed, use AutoInitShutdown attribute instead")] [TestRespondersDisposed] public void Run_T_NoInit_DoesNotThrow () { @@ -907,7 +897,7 @@ public class ApplicationTests Width = 5, Height = 5, Arrangement = ViewArrangement.Movable }; - AutoInitShutdownAttribute.FakeResize (new Size (10, 10)); + Application.Driver!.SetScreenSize (10, 10); RunState rs = Application.Begin (w); // Don't use visuals to test as style of border can change over time. @@ -1075,7 +1065,8 @@ public class ApplicationTests Assert.Null (Application.Top); } - private class TestToplevel : Toplevel { } + private class TestToplevel : Toplevel + { } private readonly object _forceDriverLock = new (); @@ -1155,19 +1146,15 @@ public class ApplicationTests Assert.False (Application.Initialized); Application.Init (null, "v2net"); Assert.True (Application.Initialized); - Task.Run (() => - { - Task.Delay (300).Wait (); - }).ContinueWith ( - (t, _) => - { - // no longer loading - Application.Invoke (() => - { - Application.RequestStop (); - }); - }, - TaskScheduler.FromCurrentSynchronizationContext ()); + + Task.Run (() => { Task.Delay (300).Wait (); }) + .ContinueWith ( + (t, _) => + { + // no longer loading + Application.Invoke (() => { Application.RequestStop (); }); + }, + TaskScheduler.FromCurrentSynchronizationContext ()); Application.Run (); Assert.NotNull (Application.Driver); Assert.NotNull (Application.Top); diff --git a/Tests/UnitTests/Application/TimedEventsTests.cs b/Tests/UnitTests/Application/TimedEventsTests.cs index e03c027c7..4877a6b2c 100644 --- a/Tests/UnitTests/Application/TimedEventsTests.cs +++ b/Tests/UnitTests/Application/TimedEventsTests.cs @@ -1,42 +1,50 @@ -using System.Diagnostics; - +#nullable enable namespace UnitTests.ApplicationTests; /// -/// Tests for TimedEvents class, focusing on high-resolution timing with Stopwatch. +/// Tests for TimedEvents class, focusing on high-resolution timing with Stopwatch. /// public class TimedEventsTests { [Fact] public void HighFrequency_Concurrent_Invocations_No_Lost_Timeouts () { - var timedEvents = new Terminal.Gui.App.TimedEvents (); + var timedEvents = new TimedEvents (); var counter = 0; var expected = 1000; var completed = new ManualResetEventSlim (false); // Add many timeouts with TimeSpan.Zero concurrently - Parallel.For (0, expected, i => - { - timedEvents.Add (TimeSpan.Zero, () => - { - var current = Interlocked.Increment (ref counter); - if (current == expected) - { - completed.Set (); - } - return false; // One-shot - }); - }); + Parallel.For ( + 0, + expected, + i => + { + timedEvents.Add ( + TimeSpan.Zero, + () => + { + int current = Interlocked.Increment (ref counter); + + if (current == expected) + { + completed.Set (); + } + + return false; // One-shot + }); + }); // Run timers multiple times to ensure all are processed - for (int i = 0; i < 10; i++) + for (var i = 0; i < 10; i++) { timedEvents.RunTimers (); + if (completed.IsSet) { break; } + Thread.Sleep (10); } @@ -46,51 +54,52 @@ public class TimedEventsTests [Fact] public void GetTimestampTicks_Provides_High_Resolution () { - var timedEvents = new Terminal.Gui.App.TimedEvents (); - + var timedEvents = new TimedEvents (); + // Add multiple timeouts with TimeSpan.Zero rapidly - var timestamps = new List (); - + List timestamps = new (); + // Single event handler to capture all timestamps - EventHandler? handler = null; - handler = (s, e) => - { - timestamps.Add (e.Ticks); - }; - + EventHandler? handler = null; + handler = (s, e) => { timestamps.Add (e.Ticks); }; + timedEvents.Added += handler; - - for (int i = 0; i < 100; i++) + + for (var i = 0; i < 100; i++) { timedEvents.Add (TimeSpan.Zero, () => false); } - + timedEvents.Added -= handler; // Verify that we got timestamps Assert.True (timestamps.Count > 0, $"Should have captured timestamps. Got {timestamps.Count}"); - + // Verify that we got unique timestamps (or very close) // With Stopwatch, we should have much better resolution than DateTime.UtcNow - var uniqueTimestamps = timestamps.Distinct ().Count (); - + int uniqueTimestamps = timestamps.Distinct ().Count (); + // We should have mostly unique timestamps // Allow some duplicates due to extreme speed, but should be > 50% unique - Assert.True (uniqueTimestamps > timestamps.Count / 2, - $"Expected more unique timestamps. Got {uniqueTimestamps} unique out of {timestamps.Count} total"); + Assert.True ( + uniqueTimestamps > timestamps.Count / 2, + $"Expected more unique timestamps. Got {uniqueTimestamps} unique out of {timestamps.Count} total"); } [Fact] public void TimeSpan_Zero_Executes_Immediately () { - var timedEvents = new Terminal.Gui.App.TimedEvents (); + var timedEvents = new TimedEvents (); var executed = false; - timedEvents.Add (TimeSpan.Zero, () => - { - executed = true; - return false; - }); + timedEvents.Add ( + TimeSpan.Zero, + () => + { + executed = true; + + return false; + }); Assert.True (timedEvents.Timeouts.Keys [0] > 0); @@ -104,17 +113,20 @@ public class TimedEventsTests [Fact] public void Multiple_TimeSpan_Zero_Timeouts_All_Execute () { - var timedEvents = new Terminal.Gui.App.TimedEvents (); + var timedEvents = new TimedEvents (); var executeCount = 0; var expected = 100; - for (int i = 0; i < expected; i++) + for (var i = 0; i < expected; i++) { - timedEvents.Add (TimeSpan.Zero, () => - { - Interlocked.Increment (ref executeCount); - return false; - }); + timedEvents.Add ( + TimeSpan.Zero, + () => + { + Interlocked.Increment (ref executeCount); + + return false; + }); } // Run timers once diff --git a/Tests/UnitTests/AutoInitShutdownAttribute.cs b/Tests/UnitTests/AutoInitShutdownAttribute.cs index b5e11ccbc..6361e1545 100644 --- a/Tests/UnitTests/AutoInitShutdownAttribute.cs +++ b/Tests/UnitTests/AutoInitShutdownAttribute.cs @@ -26,7 +26,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute /// is true. /// /// - /// If true, will force the use of . Only valid if + /// If true, will force the use of . Only valid if /// == and is true. /// /// @@ -107,7 +107,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute Debug.Assert (!CM.IsEnabled, "This test left ConfigurationManager enabled!"); // Force the ConfigurationManager to reset to its hardcoded defaults - CM.Disable(true); + CM.Disable (true); } public override void Before (MethodInfo methodUnderTest) @@ -138,12 +138,14 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute #endif if (_driverType == null) { - Application.Top = null; - Application.TopLevels.Clear (); + //Application.Init (null, "fake"); + //Application.Driver!.SetScreenSize (80, 25); + //Application.Top = null; + //Application.TopLevels.Clear (); var fa = new FakeApplicationFactory (); _v2Cleanup = fa.SetupFakeApplication (); - AutoInitShutdownAttribute.FakeResize (new Size (80,25)); + //Application.Driver!.SetScreenSize (80,25)); } else { @@ -154,35 +156,12 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute private bool AutoInit { get; } - /// - /// 'Resizes' the application and forces layout. Only works if your test uses - /// - /// - public static void FakeResize (Size size) - { - var d = (IConsoleDriverFacade)Application.Driver!; - d.OutputBuffer.SetWindowSize (size.Width, size.Height); - - // Handle both FakeSizeMonitor (from test project) and FakeWindowSizeMonitor (from main library) - if (d.WindowSizeMonitor is FakeSizeMonitor fakeSizeMonitor) - { - fakeSizeMonitor.RaiseSizeChanging (size); - } - else if (d.WindowSizeMonitor is FakeWindowSizeMonitor fakeWindowSizeMonitor) - { - // For FakeWindowSizeMonitor, use the RaiseSizeChanging method - fakeWindowSizeMonitor.RaiseSizeChanging (size); - } - - Application.LayoutAndDraw (true); - } - /// /// Runs a single iteration of the main loop (layout, draw, run timed events etc.) /// public static void RunIteration () { - var a = (ApplicationImpl)ApplicationImpl.Instance; + ApplicationImpl a = (ApplicationImpl)ApplicationImpl.Instance; a.Coordinator?.RunIteration (); } } \ No newline at end of file diff --git a/Tests/UnitTests/ConsoleDrivers/ClipRegionTests.cs b/Tests/UnitTests/ConsoleDrivers/ClipRegionTests.cs index 3cc5433c5..73cee1d11 100644 --- a/Tests/UnitTests/ConsoleDrivers/ClipRegionTests.cs +++ b/Tests/UnitTests/ConsoleDrivers/ClipRegionTests.cs @@ -15,119 +15,96 @@ public class ClipRegionTests this._output = output; } - [Theory] - [InlineData (typeof (FakeDriver))] - //[InlineData (typeof (DotNetDriver))] - - //[InlineData (typeof (ANSIDriver))] - //[InlineData (typeof (WindowsDriver))] - //[InlineData (typeof (UnixDriver))] - public void AddRune_Is_Clipped (Type driverType) + [Fact] + public void AddRune_Is_Clipped () { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - Application.Driver!.Rows = 25; - Application.Driver!.Cols = 80; + Application.Init (null, "fake"); - driver.Move (0, 0); - driver.AddRune ('x'); - Assert.Equal ((Rune)'x', driver.Contents [0, 0].Rune); + Application.Driver!.Move (0, 0); + Application.Driver!.AddRune ('x'); + Assert.Equal ((Rune)'x', Application.Driver!.Contents! [0, 0].Rune); - driver.Move (5, 5); - driver.AddRune ('x'); - Assert.Equal ((Rune)'x', driver.Contents [5, 5].Rune); + Application.Driver?.Move (5, 5); + Application.Driver?.AddRune ('x'); + Assert.Equal ((Rune)'x', Application.Driver!.Contents [5, 5].Rune); // Clear the contents - driver.FillRect (new Rectangle (0, 0, driver.Rows, driver.Cols), ' '); - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); + Application.Driver?.FillRect (new Rectangle (0, 0, Application.Driver.Rows, Application.Driver.Cols), ' '); + Assert.Equal ((Rune)' ', Application.Driver?.Contents [0, 0].Rune); // Setup the region with a single rectangle, fill screen with 'x' - driver.Clip = new (new Rectangle (5, 5, 5, 5)); - driver.FillRect (new Rectangle (0, 0, driver.Rows, driver.Cols), 'x'); - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [4, 9].Rune); - Assert.Equal ((Rune)'x', driver.Contents [5, 5].Rune); - Assert.Equal ((Rune)'x', driver.Contents [9, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [10, 10].Rune); + Application.Driver!.Clip = new (new Rectangle (5, 5, 5, 5)); + Application.Driver.FillRect (new Rectangle (0, 0, Application.Driver.Rows, Application.Driver.Cols), 'x'); + Assert.Equal ((Rune)' ', Application.Driver?.Contents [0, 0].Rune); + Assert.Equal ((Rune)' ', Application.Driver?.Contents [4, 9].Rune); + Assert.Equal ((Rune)'x', Application.Driver?.Contents [5, 5].Rune); + Assert.Equal ((Rune)'x', Application.Driver?.Contents [9, 9].Rune); + Assert.Equal ((Rune)' ', Application.Driver?.Contents [10, 10].Rune); Application.Shutdown (); } - [Theory] - [InlineData (typeof (FakeDriver))] - //[InlineData (typeof (DotNetDriver))] - - //[InlineData (typeof (ANSIDriver))] - //[InlineData (typeof (WindowsDriver))] - //[InlineData (typeof (UnixDriver))] - public void Clip_Set_To_Empty_AllInvalid (Type driverType) + [Fact] + public void Clip_Set_To_Empty_AllInvalid () { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - Application.Init (driver); + Application.Init (null, "fake"); // Define a clip rectangle - driver.Clip = new (Rectangle.Empty); + Application.Driver!.Clip = new (Rectangle.Empty); // negative - Assert.False (driver.IsValidLocation (default, 4, 5)); - Assert.False (driver.IsValidLocation (default, 5, 4)); - Assert.False (driver.IsValidLocation (default, 10, 9)); - Assert.False (driver.IsValidLocation (default, 9, 10)); - Assert.False (driver.IsValidLocation (default, -1, 0)); - Assert.False (driver.IsValidLocation (default, 0, -1)); - Assert.False (driver.IsValidLocation (default, -1, -1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows)); + Assert.False (Application.Driver.IsValidLocation (default, 4, 5)); + Assert.False (Application.Driver.IsValidLocation (default, 5, 4)); + Assert.False (Application.Driver.IsValidLocation (default, 10, 9)); + Assert.False (Application.Driver.IsValidLocation (default, 9, 10)); + Assert.False (Application.Driver.IsValidLocation (default, -1, 0)); + Assert.False (Application.Driver.IsValidLocation (default, 0, -1)); + Assert.False (Application.Driver.IsValidLocation (default, -1, -1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows - 1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows - 1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows)); Application.Shutdown (); } - [Theory] - [InlineData (typeof (FakeDriver))] - //[InlineData (typeof (DotNetDriver))] - - //[InlineData (typeof (ANSIDriver))] - //[InlineData (typeof (WindowsDriver))] - //[InlineData (typeof (UnixDriver))] - public void IsValidLocation (Type driverType) + [Fact] + public void IsValidLocation () { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - Application.Init (driver); + Application.Init (null, "fake"); Application.Driver!.Rows = 10; Application.Driver!.Cols = 10; // positive - Assert.True (driver.IsValidLocation (default, 0, 0)); - Assert.True (driver.IsValidLocation (default, 1, 1)); - Assert.True (driver.IsValidLocation (default, driver.Cols - 1, driver.Rows - 1)); + Assert.True (Application.Driver.IsValidLocation (default, 0, 0)); + Assert.True (Application.Driver.IsValidLocation (default, 1, 1)); + Assert.True (Application.Driver.IsValidLocation (default, Application.Driver.Cols - 1, Application.Driver.Rows - 1)); // negative - Assert.False (driver.IsValidLocation (default, -1, 0)); - Assert.False (driver.IsValidLocation (default, 0, -1)); - Assert.False (driver.IsValidLocation (default, -1, -1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows)); + Assert.False (Application.Driver.IsValidLocation (default, -1, 0)); + Assert.False (Application.Driver.IsValidLocation (default, 0, -1)); + Assert.False (Application.Driver.IsValidLocation (default, -1, -1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows - 1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows - 1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows)); // Define a clip rectangle - driver.Clip = new(new Rectangle(5, 5, 5, 5)); + Application.Driver.Clip = new (new Rectangle (5, 5, 5, 5)); // positive - Assert.True (driver.IsValidLocation (default, 5, 5)); - Assert.True (driver.IsValidLocation (default, 9, 9)); + Assert.True (Application.Driver.IsValidLocation (default, 5, 5)); + Assert.True (Application.Driver.IsValidLocation (default, 9, 9)); // negative - Assert.False (driver.IsValidLocation (default, 4, 5)); - Assert.False (driver.IsValidLocation (default, 5, 4)); - Assert.False (driver.IsValidLocation (default, 10, 9)); - Assert.False (driver.IsValidLocation (default, 9, 10)); - Assert.False (driver.IsValidLocation (default, -1, 0)); - Assert.False (driver.IsValidLocation (default, 0, -1)); - Assert.False (driver.IsValidLocation (default, -1, -1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (default, driver.Cols, driver.Rows)); + Assert.False (Application.Driver.IsValidLocation (default, 4, 5)); + Assert.False (Application.Driver.IsValidLocation (default, 5, 4)); + Assert.False (Application.Driver.IsValidLocation (default, 10, 9)); + Assert.False (Application.Driver.IsValidLocation (default, 9, 10)); + Assert.False (Application.Driver.IsValidLocation (default, -1, 0)); + Assert.False (Application.Driver.IsValidLocation (default, 0, -1)); + Assert.False (Application.Driver.IsValidLocation (default, -1, -1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows - 1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows - 1)); + Assert.False (Application.Driver.IsValidLocation (default, Application.Driver.Cols, Application.Driver.Rows)); Application.Shutdown (); } diff --git a/Tests/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs b/Tests/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs index b77a1905a..8cfdcf1e8 100644 --- a/Tests/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs +++ b/Tests/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs @@ -160,7 +160,7 @@ public class ConsoleDriverTests // { // var win = new Window (); // Application.Begin (win); - // AutoInitShutdownAttribute.FakeResize(new Size ( (20, 8); + // Application.Driver!.SetScreenSize ( (20, 8); // System.Threading.Tasks.Task.Run (() => { // System.Threading.Tasks.Task.Delay (500).Wait (); diff --git a/Tests/UnitTests/ConsoleDrivers/EscSeqUtilsTests.cs b/Tests/UnitTests/ConsoleDrivers/EscSeqUtilsTests.cs new file mode 100644 index 000000000..21d59bcbc --- /dev/null +++ b/Tests/UnitTests/ConsoleDrivers/EscSeqUtilsTests.cs @@ -0,0 +1,106 @@ +using System.Text; + +// ReSharper disable HeuristicUnreachableCode + +namespace UnitTests.DriverTests; + +public class EscSeqUtilsTests +{ + [Fact] + public void Defaults_Values () + { + Assert.Equal ('\x1b', EscSeqUtils.KeyEsc); + Assert.Equal ("\x1b[", EscSeqUtils.CSI); + 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.CSI_EnableMouseEvents); + Assert.Equal ("\x1b[?1003l\x1b[?1015l\u001b[?1006l", EscSeqUtils.CSI_DisableMouseEvents); + } + + [Fact] + public void GetConsoleInputKey_ConsoleKeyInfo () + { + var cki = new ConsoleKeyInfo ('r', 0, false, false, false); + var expectedCki = new ConsoleKeyInfo ('r', ConsoleKey.R, false, false, false); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('r', 0, true, false, false); + expectedCki = new ('r', ConsoleKey.R, true, false, false); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('r', 0, false, true, false); + expectedCki = new ('r', ConsoleKey.R, false, true, false); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('r', 0, false, false, true); + expectedCki = new ('r', ConsoleKey.R, false, false, true); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('r', 0, true, true, false); + expectedCki = new ('r', ConsoleKey.R, true, true, false); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('r', 0, false, true, true); + expectedCki = new ('r', ConsoleKey.R, false, true, true); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('r', 0, true, true, true); + expectedCki = new ('r', ConsoleKey.R, true, true, true); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('\u0012', 0, false, false, false); + expectedCki = new ('\u0012', ConsoleKey.R, false, false, true); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('\0', (ConsoleKey)64, false, false, true); + expectedCki = new ('\0', ConsoleKey.Spacebar, false, false, true); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('\r', 0, false, false, false); + expectedCki = new ('\r', ConsoleKey.Enter, false, false, false); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('\u007f', 0, false, false, false); + expectedCki = new ('\u007f', ConsoleKey.Backspace, false, false, false); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + + cki = new ('R', 0, false, false, false); + expectedCki = new ('R', ConsoleKey.R, true, false, false); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); + } + + [Theory] + [InlineData (0, 0, $"{EscSeqUtils.CSI}0;0H")] + [InlineData (int.MaxValue, int.MaxValue, $"{EscSeqUtils.CSI}2147483647;2147483647H")] + [InlineData (int.MinValue, int.MinValue, $"{EscSeqUtils.CSI}-2147483648;-2147483648H")] + public void CSI_WriteCursorPosition_ReturnsCorrectEscSeq (int row, int col, string expected) + { + StringBuilder builder = new(); + using StringWriter writer = new(builder); + + EscSeqUtils.CSI_WriteCursorPosition (writer, row, col); + + string actual = builder.ToString(); + Assert.Equal (expected, actual); + } + + [Theory] + [InlineData ('\u001B', KeyCode.Esc)] + [InlineData ('\r', KeyCode.Enter)] + [InlineData ('1', KeyCode.D1)] + [InlineData ('!', (KeyCode)'!')] + [InlineData ('a', KeyCode.A)] + [InlineData ('A', KeyCode.A | KeyCode.ShiftMask)] + public void MapChar_Returns_Modifiers_If_Needed (char ch, KeyCode keyCode) + { + ConsoleKeyInfo cki = EscSeqUtils.MapChar (ch); + Key key = EscSeqUtils.MapKey (cki); + Key expectedKey = keyCode; + + Assert.Equal (key, expectedKey); + } +} diff --git a/Tests/UnitTests/ConsoleDrivers/FakeDriverTests.cs b/Tests/UnitTests/ConsoleDrivers/FakeDriverTests.cs index 4314492d4..c79145227 100644 --- a/Tests/UnitTests/ConsoleDrivers/FakeDriverTests.cs +++ b/Tests/UnitTests/ConsoleDrivers/FakeDriverTests.cs @@ -1,12 +1,10 @@ -using UnitTests; -using Xunit; - +using System.Text; using Xunit.Abstractions; namespace UnitTests.DriverTests; /// -/// Tests for the FakeDriver to ensure it works properly with the modern component factory architecture. +/// Tests for the FakeDriver to ensure it works properly with the modern component factory architecture. /// public class FakeDriverTests (ITestOutputHelper output) { @@ -20,12 +18,13 @@ public class FakeDriverTests (ITestOutputHelper output) { // Verify Application was initialized Assert.True (Application.Initialized); - // Assert.NotNull (Application.Top); - + + // Assert.NotNull (Application.Top); + // Verify it's using a driver facade (modern architecture) Assert.IsAssignableFrom (Application.Driver); - - _output.WriteLine ($"Driver type: {Application.Driver.GetType().Name}"); + + _output.WriteLine ($"Driver type: {Application.Driver.GetType ().Name}"); _output.WriteLine ($"Screen size: {Application.Screen}"); } @@ -48,7 +47,7 @@ public class FakeDriverTests (ITestOutputHelper output) Assert.Equal (25, Application.Driver.Rows); // Resize to 100x30 - AutoInitShutdownAttribute.FakeResize (new (100, 30)); + Application.Driver?.SetScreenSize (100, 30); // Verify new size Assert.Equal (100, Application.Driver.Cols); @@ -60,7 +59,7 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_Top_Is_Created () { - Application.Top = new Toplevel (); + Application.Top = new (); Application.Begin (Application.Top); @@ -73,7 +72,7 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_Can_Add_View_To_Top () { - Application.Top = new Toplevel (); + Application.Top = new (); var label = new Label { Text = "Hello World" }; Application.Top!.Add (label); @@ -86,12 +85,11 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_RunIteration_Works () { - Application.Top = new Toplevel (); + Application.Top = new (); var label = new Label { Text = "Hello" }; Application.Top!.Add (label); - Application.Begin (Application.Top); // Run a single iteration - this should layout and draw @@ -106,43 +104,43 @@ public class FakeDriverTests (ITestOutputHelper output) #region AutoInitShutdown Attribute Tests - [Theory] - [InlineData (true)] - [InlineData (false)] - public void AutoInitShutdown_Attribute_Respects_AutoInit_Parameter (bool autoInit) - { - // When autoInit is false, Application should not be initialized - // When autoInit is true, Application should be initialized - - // This test will be called twice - once with autoInit=true, once with false - // We can't use the attribute directly in the test body, but we can verify - // the behavior by checking Application.Initialized - - // For this test to work properly, we need to call Application.Init manually when autoInit=false - bool wasInitialized = Application.Initialized; - - try - { - if (!wasInitialized) - { - Application.ResetState (); - var fa = new FakeApplicationFactory (); - using var cleanup = fa.SetupFakeApplication (); - Assert.True (Application.Initialized); - } - else - { - Assert.True (Application.Initialized); - } - } - finally - { - if (!wasInitialized) - { - Application.Shutdown (); - } - } - } + //[Theory] + //[InlineData (true)] + //[InlineData (false)] + //public void AutoInitShutdown_Attribute_Respects_AutoInit_Parameter (bool autoInit) + //{ + // // When autoInit is false, Application should not be initialized + // // When autoInit is true, Application should be initialized + + // // This test will be called twice - once with autoInit=true, once with false + // // We can't use the attribute directly in the test body, but we can verify + // // the behavior by checking Application.Initialized + + // // For this test to work properly, we need to call Application.Init manually when autoInit=false + // bool wasInitialized = Application.Initialized; + + // try + // { + // if (!wasInitialized) + // { + // Application.ResetState (); + // var fa = new FakeApplicationFactory (); + // using IDisposable cleanup = fa.SetupFakeApplication (); + // Assert.True (Application.Initialized); + // } + // else + // { + // Assert.True (Application.Initialized); + // } + // } + // finally + // { + // if (!wasInitialized) + // { + // Application.Shutdown (); + // } + // } + //} [Fact] public void Without_AutoInitShutdown_Application_Is_Not_Initialized () @@ -180,24 +178,24 @@ public class FakeDriverTests (ITestOutputHelper output) [Fact] [SetupFakeDriver] - public void SetupFakeDriver_Driver_Is_FakeConsoleDriver () + public void SetupFakeDriver_Driver_Is_IConsoleDriver () { Assert.NotNull (Application.Driver); - - // Should be IFakeConsoleDriver - Assert.IsAssignableFrom (Application.Driver); - - _output.WriteLine ($"Driver type: {Application.Driver.GetType().Name}"); + + // Should be IConsoleDriver + Assert.IsAssignableFrom (Application.Driver); + + _output.WriteLine ($"Driver type: {Application.Driver.GetType ().Name}"); } [Fact] [SetupFakeDriver] - public void SetupFakeDriver_Can_Set_Buffer_Size () + public void SetupFakeDriver_Can_Set_Screen_Size () { - var fakeDriver = Application.Driver as IFakeConsoleDriver; + IConsoleDriver fakeDriver = Application.Driver; Assert.NotNull (fakeDriver); - fakeDriver!.SetBufferSize (100, 50); + fakeDriver!.SetScreenSize (100, 50); Assert.Equal (100, Application.Driver!.Cols); Assert.Equal (50, Application.Driver.Rows); @@ -211,10 +209,10 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_Can_Draw_Simple_View () { - Application.Top = new Toplevel (); + Application.Top = new (); - var window = new Window - { + var window = new Window + { Title = "Test Window", X = 0, Y = 0, @@ -222,13 +220,13 @@ public class FakeDriverTests (ITestOutputHelper output) Height = 10 }; - var label = new Label - { + var label = new Label + { Text = "Hello World", X = 1, Y = 1 }; - + window.Add (label); Application.Top!.Add (window); @@ -248,13 +246,13 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_Multiple_RunIterations_Work () { - Application.Top = new Toplevel (); + Application.Top = new (); var label = new Label { Text = "Iteration Test" }; Application.Top!.Add (label); // Run multiple iterations - for (int i = 0; i < 5; i++) + for (var i = 0; i < 5; i++) { AutoInitShutdownAttribute.RunIteration (); } @@ -270,10 +268,10 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_Resize_Triggers_Layout () { - Application.Top = new Toplevel (); + Application.Top = new (); - var view = new View - { + var view = new View + { Width = Dim.Fill (), Height = Dim.Fill () }; @@ -281,16 +279,15 @@ public class FakeDriverTests (ITestOutputHelper output) Application.Begin (Application.Top); - AutoInitShutdownAttribute.FakeResize (new Size (80,25)); AutoInitShutdownAttribute.RunIteration (); // Check initial size - var initialFrame = view.Frame; + Rectangle initialFrame = view.Frame; Assert.Equal (80, initialFrame.Width); Assert.Equal (25, initialFrame.Height); // Resize - AutoInitShutdownAttribute.FakeResize (new (100, 40)); + Application.Driver?.SetScreenSize (100, 40); // Check new size Assert.Equal (100, view.Frame.Width); @@ -301,7 +298,7 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_Window_Can_Be_Shown_And_Closed () { - Application.Top = new Toplevel (); + Application.Top = new (); var window = new Window { Title = "Test" }; Application.Top!.Add (window); @@ -346,8 +343,8 @@ public class FakeDriverTests (ITestOutputHelper output) Assert.NotNull (Application.Driver!.Clipboard); // Should throw NotSupportedException - Assert.Throws (() => - Application.Driver.Clipboard.GetClipboardData ()); + Assert.Throws (() => + Application.Driver.Clipboard.GetClipboardData ()); } #endregion @@ -358,11 +355,11 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_Handles_Invalid_Coordinates_Gracefully () { - Application.Top = new Toplevel (); + Application.Top = new (); // Try to add a view with invalid coordinates - should not crash - var view = new View - { + var view = new View + { X = -1000, Y = -1000, Width = 10, @@ -370,10 +367,10 @@ public class FakeDriverTests (ITestOutputHelper output) }; Application.Top!.Add (view); - + // Should not throw AutoInitShutdownAttribute.RunIteration (); - + Assert.True (Application.Initialized); } @@ -381,8 +378,8 @@ public class FakeDriverTests (ITestOutputHelper output) [AutoInitShutdown] public void FakeDriver_Survives_Rapid_Resizes () { - var sizes = new[] - { + Size [] sizes = new [] + { new Size (80, 25), new Size (100, 30), new Size (60, 20), @@ -390,15 +387,155 @@ public class FakeDriverTests (ITestOutputHelper output) new Size (80, 25) }; - foreach (var size in sizes) + foreach (Size size in sizes) { - AutoInitShutdownAttribute.FakeResize (size); + Application.Driver!.SetScreenSize (size.Width, size.Height); AutoInitShutdownAttribute.RunIteration (); - + Assert.Equal (size.Width, Application.Driver!.Cols); Assert.Equal (size.Height, Application.Driver.Rows); } } #endregion + + #region Buffer and Fill Tests + + [Fact] + [AutoInitShutdown] + public void FakeDriver_Can_Fill_Rectangle () + { + // Verify driver is initialized with buffers + Assert.NotNull (Application.Driver); + Assert.NotNull (Application.Driver!.Contents); + + // Fill a rectangle + var rect = new Rectangle (5, 5, 10, 5); + Application.Driver.FillRect (rect, (Rune)'X'); + + // Verify the rectangle was filled + for (int row = rect.Y; row < rect.Y + rect.Height; row++) + { + for (int col = rect.X; col < rect.X + rect.Width; col++) + { + Assert.Equal ((Rune)'X', Application.Driver.Contents [row, col].Rune); + } + } + } + + [Fact] + [AutoInitShutdown] + public void FakeDriver_Buffer_Integrity_After_Multiple_Resizes () + { + // Start with default size + Assert.Equal (80, Application.Driver!.Cols); + Assert.Equal (25, Application.Driver.Rows); + + // Fill with a pattern + Application.Driver.FillRect (new (0, 0, 10, 5), (Rune)'A'); + + // Resize + Application.Driver?.SetScreenSize (100, 30); + + // Verify new size + Assert.Equal (100, Application.Driver.Cols); + Assert.Equal (30, Application.Driver.Rows); + + // Verify buffer is clean (no stale runes from previous size) + Assert.NotNull (Application.Driver.Contents); + Assert.Equal (30, Application.Driver.Contents!.GetLength (0)); + Assert.Equal (100, Application.Driver.Contents.GetLength (1)); + + // Fill with new pattern + Application.Driver.FillRect (new (0, 0, 20, 10), (Rune)'B'); + + // Resize back + Application.Driver?.SetScreenSize (80, 25); + + // Verify size is back + Assert.Equal (80, Application.Driver.Cols); + Assert.Equal (25, Application.Driver.Rows); + + // Verify buffer dimensions match + Assert.Equal (25, Application.Driver.Contents.GetLength (0)); + Assert.Equal (80, Application.Driver.Contents.GetLength (1)); + } + + #endregion + + #region ScreenChanged Event Tests + + [Fact] + [AutoInitShutdown] + public void ScreenChanged_Event_Fires_On_SetScreenSize () + { + var screenChangedFired = false; + Size? newSize = null; + + Application.Driver!.SizeChanged += (sender, args) => + { + screenChangedFired = true; + newSize = args.Size; + }; + + // Trigger resize using FakeResize which uses SetScreenSize internally + Application.Driver?.SetScreenSize (100, 30); + + // Verify event fired + Assert.True (screenChangedFired); + Assert.NotNull (newSize); + Assert.Equal (100, newSize!.Value.Width); + Assert.Equal (30, newSize.Value.Height); + } + + [Fact] + [AutoInitShutdown] + public void FakeResize_Triggers_ScreenChanged_And_Updates_Application_Screen () + { + var screenChangedFired = false; + Size? eventSize = null; + + Application.Driver!.SizeChanged += (sender, args) => + { + screenChangedFired = true; + eventSize = args.Size; + }; + + // Use FakeResize helper + Application.Driver?.SetScreenSize (120, 40); + + // Verify event fired + Assert.True (screenChangedFired); + Assert.NotNull (eventSize); + Assert.Equal (120, eventSize!.Value.Width); + Assert.Equal (40, eventSize.Value.Height); + + // Verify Application.Screen was updated + Assert.Equal (new (0, 0, 120, 40), Application.Screen); + Assert.Equal (120, Application.Driver.Cols); + Assert.Equal (40, Application.Driver.Rows); + } + + [Fact] + [AutoInitShutdown] + public void SizeChanged_Event_Still_Fires_For_Compatibility () + { + var sizeChangedFired = false; + var screenChangedFired = false; + +#pragma warning disable CS0618 // Type or member is obsolete + Application.Driver!.SizeChanged += (sender, args) => { sizeChangedFired = true; }; +#pragma warning restore CS0618 // Type or member is obsolete + + Application.Driver.SizeChanged += (sender, args) => { screenChangedFired = true; }; + + // Trigger resize using FakeResize + Application.Driver?.SetScreenSize (90, 35); + + // Both events should fire for compatibility + Assert.True (sizeChangedFired); + Assert.True (screenChangedFired); + } + + #endregion } diff --git a/Tests/UnitTests/ConsoleDrivers/WindowSizeMonitorTests.cs b/Tests/UnitTests/ConsoleDrivers/WindowSizeMonitorTests.cs index 1301087da..8753719b9 100644 --- a/Tests/UnitTests/ConsoleDrivers/WindowSizeMonitorTests.cs +++ b/Tests/UnitTests/ConsoleDrivers/WindowSizeMonitorTests.cs @@ -1,43 +1,41 @@ using Moq; namespace UnitTests.DriverTests; + public class WindowSizeMonitorTests { - public WindowSizeMonitorTests () - { - ConsoleDriver.RunningUnitTests = false; - } + public WindowSizeMonitorTests () { ConsoleDriver.RunningUnitTests = false; } [Fact] public void TestWindowSizeMonitor_RaisesEventWhenChanges () { - var consoleOutput = new Mock (); + Mock consoleOutput = new (); - var queue = new Queue(new []{ - new Size (30, 20), - new Size (20, 20) + Queue queue = new ( + [ + new (30, 20), + new (20, 20) + ]); - }); - - consoleOutput.Setup (m => m.GetWindowSize ()) + consoleOutput.Setup (m => m.GetSize ()) .Returns (queue.Dequeue); var outputBuffer = Mock.Of (); - var monitor = new WindowSizeMonitor (consoleOutput.Object, outputBuffer); + var monitor = new ConsoleSizeMonitor (consoleOutput.Object, outputBuffer); - var result = new List (); - monitor.SizeChanging += (s, e) => { result.Add (e);}; + List result = new (); + monitor.SizeChanged += (s, e) => { result.Add (e); }; Assert.Empty (result); monitor.Poll (); Assert.Single (result); - Assert.Equal (new Size (30,20),result [0].Size); + Assert.Equal (new Size (30, 20), result [0].Size); monitor.Poll (); - Assert.Equal (2,result.Count); + Assert.Equal (2, result.Count); Assert.Equal (new Size (30, 20), result [0].Size); Assert.Equal (new Size (20, 20), result [1].Size); } @@ -45,22 +43,24 @@ public class WindowSizeMonitorTests [Fact] public void TestWindowSizeMonitor_DoesNotRaiseEventWhen_NoChanges () { - var consoleOutput = new Mock (); + Mock consoleOutput = new (); - var queue = new Queue (new []{ - new Size (30, 20), - new Size (30, 20), - }); + Queue queue = new ( + new [] + { + new Size (30, 20), + new Size (30, 20) + }); - consoleOutput.Setup (m => m.GetWindowSize ()) + consoleOutput.Setup (m => m.GetSize ()) .Returns (queue.Dequeue); var outputBuffer = Mock.Of (); - var monitor = new WindowSizeMonitor (consoleOutput.Object, outputBuffer); + var monitor = new ConsoleSizeMonitor (consoleOutput.Object, outputBuffer); - var result = new List (); - monitor.SizeChanging += (s, e) => { result.Add (e); }; + List result = new (); + monitor.SizeChanged += (s, e) => { result.Add (e); }; // First poll always raises event because going from unknown size i.e. 0,0 Assert.Empty (result); diff --git a/Tests/UnitTests/Dialogs/DialogTests.cs b/Tests/UnitTests/Dialogs/DialogTests.cs index f3f008f9d..59a6de315 100644 --- a/Tests/UnitTests/Dialogs/DialogTests.cs +++ b/Tests/UnitTests/Dialogs/DialogTests.cs @@ -20,7 +20,7 @@ public class DialogTests (ITestOutputHelper output) // We test with one button first, but do this to get the width right for 2 int width = $@"{Glyphs.VLine} {btn1} {btn2} {Glyphs.VLine}".Length; - AutoInitShutdownAttribute.FakeResize (new (width, 1)); + Driver?.SetScreenSize (width, 1); // Override CM Dialog.DefaultButtonAlignment = Alignment.Center; @@ -163,7 +163,7 @@ public class DialogTests (ITestOutputHelper output) var buttonRow = $"{Glyphs.VLine} {btn1} {btn2} {btn3} {btn4} {Glyphs.VLine}"; int width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (buttonRow.Length, 3)); + Driver?.SetScreenSize (buttonRow.Length, 3); // Default - Center (runState, Dialog dlg) = BeginButtonTestDialog ( @@ -255,7 +255,7 @@ public class DialogTests (ITestOutputHelper output) var buttonRow = string.Empty; var width = 30; - AutoInitShutdownAttribute.FakeResize (new (width, 1)); + Driver?.SetScreenSize (width, 1); // Default - Center buttonRow = @@ -351,7 +351,7 @@ public class DialogTests (ITestOutputHelper output) // 123456 1234567 var buttonRow = $"{Glyphs.VLine} {btn1} {btn2} {btn3} {btn4} {Glyphs.VLine}"; int width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (buttonRow.Length, 1)); + Driver?.SetScreenSize (buttonRow.Length, 1); // Default - Center (runState, Dialog dlg) = BeginButtonTestDialog ( @@ -446,7 +446,7 @@ public class DialogTests (ITestOutputHelper output) // 123456 123456 var buttonRow = $"{Glyphs.VLine} {btn1} {btn2} {btn3} {btn4} {Glyphs.VLine}"; int width = buttonRow.GetColumns (); - AutoInitShutdownAttribute.FakeResize (new (width, 3)); + Driver?.SetScreenSize (width, 3); // Default - Center (runState, Dialog dlg) = BeginButtonTestDialog ( @@ -531,7 +531,7 @@ public class DialogTests (ITestOutputHelper output) $"{Glyphs.VLine} {Glyphs.LeftBracket} {btnText} {Glyphs.RightBracket} {Glyphs.VLine}"; int width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (width, 1)); + Driver?.SetScreenSize (width, 1); (runState, Dialog dlg) = BeginButtonTestDialog ( title, @@ -595,7 +595,7 @@ public class DialogTests (ITestOutputHelper output) $"{Glyphs.VLine} {Glyphs.LeftBracket} {btnText} {Glyphs.RightBracket} {Glyphs.VLine}"; width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (width, 1)); + Driver?.SetScreenSize (width, 1); (runState, dlg) = BeginButtonTestDialog ( title, @@ -675,7 +675,7 @@ public class DialogTests (ITestOutputHelper output) var buttonRow = $@"{Glyphs.VLine} {btn1} {btn2} {btn3} {Glyphs.VLine}"; int width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (buttonRow.Length, 3)); + Driver?.SetScreenSize (buttonRow.Length, 3); (runState, Dialog dlg) = BeginButtonTestDialog ( title, @@ -758,7 +758,7 @@ public class DialogTests (ITestOutputHelper output) var buttonRow = $@"{Glyphs.VLine} {btn1} {btn2} {Glyphs.VLine}"; int width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (buttonRow.Length, 3)); + Driver?.SetScreenSize (buttonRow.Length, 3); (runState, Dialog dlg) = BeginButtonTestDialog ( title, @@ -837,7 +837,7 @@ public class DialogTests (ITestOutputHelper output) var buttonRow = $@"{Glyphs.VLine} {btn1} {btn2} {Glyphs.VLine}"; int width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (buttonRow.Length, 3)); + Driver?.SetScreenSize (buttonRow.Length, 3); // Default (Center) Button button1 = new () { Text = btn1Text }; @@ -957,7 +957,7 @@ public class DialogTests (ITestOutputHelper output) [AutoInitShutdown] public void Dialog_In_Window_With_Size_One_Button_Aligns () { - AutoInitShutdownAttribute.FakeResize (new (20, 5)); + Driver?.SetScreenSize (20, 5); // Override CM Window.DefaultBorderStyle = LineStyle.Single; @@ -1062,7 +1062,7 @@ public class DialogTests (ITestOutputHelper output) )] public void Dialog_In_Window_Without_Size_One_Button_Aligns (int height, string expected) { - AutoInitShutdownAttribute.FakeResize (new (20, height)); + Driver?.SetScreenSize (20, height); var win = new Window (); int iterations = -1; @@ -1109,7 +1109,7 @@ public class DialogTests (ITestOutputHelper output) [AutoInitShutdown] public void Dialog_Opened_From_Another_Dialog () { - AutoInitShutdownAttribute.FakeResize (new (30, 10)); + Driver?.SetScreenSize (30, 10); // Override CM Dialog.DefaultButtonAlignment = Alignment.Center; @@ -1251,7 +1251,7 @@ public class DialogTests (ITestOutputHelper output) Height = Dim.Percent (85) }; Begin (d); - AutoInitShutdownAttribute.FakeResize (new (100, 100)); + Driver?.SetScreenSize (100, 100); // Default location is centered, so 100 / 2 - 85 / 2 = 7 var expected = 7; @@ -1265,7 +1265,7 @@ public class DialogTests (ITestOutputHelper output) { var d = new Dialog { X = 1, Y = 1 }; Begin (d); - AutoInitShutdownAttribute.FakeResize (new (100, 100)); + Driver?.SetScreenSize (100, 100); // Default location is centered, so 100 / 2 - 85 / 2 = 7 var expected = 1; @@ -1287,7 +1287,7 @@ public class DialogTests (ITestOutputHelper output) var expected = 5; var d = new Dialog { X = expected, Y = expected, Height = 5, Width = 5 }; Begin (d); - AutoInitShutdownAttribute.FakeResize (new (20, 10)); + Driver?.SetScreenSize (20, 10); // Default location is centered, so 100 / 2 - 85 / 2 = 7 Assert.Equal (new (expected, expected), d.Frame.Location); @@ -1321,7 +1321,7 @@ public class DialogTests (ITestOutputHelper output) Y = 1 }; - AutoInitShutdownAttribute.FakeResize (new (20, 20)); + Driver?.SetScreenSize (20, 20); var iterations = 0; @@ -1340,7 +1340,7 @@ public class DialogTests (ITestOutputHelper output) else if (iterations == 2) { // Mouse click outside of dialog - RaiseMouseEvent (new() { Flags = MouseFlags.Button1Clicked, ScreenPosition = new (0, 0) }); + RaiseMouseEvent (new () { Flags = MouseFlags.Button1Clicked, ScreenPosition = new (0, 0) }); } }; @@ -1370,7 +1370,7 @@ public class DialogTests (ITestOutputHelper output) $"{Glyphs.VLine} {Glyphs.LeftBracket} {btnText} {Glyphs.RightBracket} {Glyphs.VLine}"; int width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (buttonRow.Length, 10)); + Driver?.SetScreenSize (buttonRow.Length, 10); (runState, Dialog dlg) = BeginButtonTestDialog ( title, @@ -1435,7 +1435,7 @@ public class DialogTests (ITestOutputHelper output) }; Begin (d); - AutoInitShutdownAttribute.FakeResize (new (100, 100)); + Driver?.SetScreenSize (100, 100); // Default size is Percent(85) Assert.Equal (new ((int)(100 * .85), (int)(100 * .85)), d.Frame.Size); @@ -1451,7 +1451,7 @@ public class DialogTests (ITestOutputHelper output) var d = new Dialog { Width = 50, Height = 50 }; Begin (d); - AutoInitShutdownAttribute.FakeResize (new (100, 100)); + Driver?.SetScreenSize (100, 100); // Default size is Percent(85) Assert.Equal (new (50, 50), d.Frame.Size); @@ -1468,9 +1468,9 @@ public class DialogTests (ITestOutputHelper output) var buttonRow = $"{Glyphs.VLine} {Glyphs.VLine}"; int width = buttonRow.Length; - AutoInitShutdownAttribute.FakeResize (new (buttonRow.Length, 3)); + Driver?.SetScreenSize (buttonRow.Length, 3); - (runState, Dialog dlg) = BeginButtonTestDialog (title, width, Alignment.Center, []); + (runState, Dialog dlg) = BeginButtonTestDialog (title, width, Alignment.Center); DriverAssert.AssertDriverContentsWithFrameAre ($"{buttonRow}", output); diff --git a/Tests/UnitTests/Dialogs/MessageBoxTests.cs b/Tests/UnitTests/Dialogs/MessageBoxTests.cs index a3033c998..0fd0954e5 100644 --- a/Tests/UnitTests/Dialogs/MessageBoxTests.cs +++ b/Tests/UnitTests/Dialogs/MessageBoxTests.cs @@ -155,7 +155,7 @@ public class MessageBoxTests { int iterations = -1; - AutoInitShutdownAttribute.FakeResize(new Size(15, 15)); // 15 x 15 gives us enough room for a button with one char (9x1) + Application.Driver!.SetScreenSize(15, 15); // 15 x 15 gives us enough room for a button with one char (9x1) Dialog.DefaultShadow = ShadowStyle.None; Button.DefaultShadow = ShadowStyle.None; @@ -189,7 +189,7 @@ public class MessageBoxTests int iterations = -1; var top = new Toplevel (); top.BorderStyle = LineStyle.None; - AutoInitShutdownAttribute.FakeResize(new Size(20, 10)); + Application.Driver!.SetScreenSize(20, 10); var btn = $"{Glyphs.LeftBracket}{Glyphs.LeftDefaultIndicator} btn {Glyphs.RightDefaultIndicator}{Glyphs.RightBracket}"; @@ -259,7 +259,7 @@ public class MessageBoxTests int iterations = -1; var top = new Toplevel (); top.BorderStyle = LineStyle.None; - AutoInitShutdownAttribute.FakeResize(new Size(20, 10)); + Application.Driver!.SetScreenSize (20, 10); var btn = $"{Glyphs.LeftBracket}{Glyphs.LeftDefaultIndicator} btn {Glyphs.RightDefaultIndicator}{Glyphs.RightBracket}"; @@ -343,7 +343,7 @@ public class MessageBoxTests public void Size_Not_Default_Message (int height, int width, string message) { int iterations = -1; - AutoInitShutdownAttribute.FakeResize(new Size(100, 100)); + Application.Driver!.SetScreenSize(100, 100); Application.Iteration += (s, a) => { @@ -380,7 +380,7 @@ public class MessageBoxTests public void Size_Not_Default_Message_Button (int height, int width, string message) { int iterations = -1; - AutoInitShutdownAttribute.FakeResize(new Size(100, 100)); + Application.Driver?.SetScreenSize(100, 100); Application.Iteration += (s, a) => { @@ -413,7 +413,7 @@ public class MessageBoxTests public void Size_Not_Default_No_Message (int height, int width) { int iterations = -1; - AutoInitShutdownAttribute.FakeResize(new Size(100, 100)); + Application.Driver?.SetScreenSize(100, 100); Application.Iteration += (s, a) => { @@ -442,7 +442,7 @@ public class MessageBoxTests public void UICatalog_AboutBox () { int iterations = -1; - AutoInitShutdownAttribute.FakeResize (new Size (70, 15)); + Application.Driver!.SetScreenSize (70, 15); // Override CM MessageBox.DefaultButtonAlignment = Alignment.End; diff --git a/Tests/UnitTests/Dialogs/WizardTests.cs b/Tests/UnitTests/Dialogs/WizardTests.cs index c85ae53be..edf8a1692 100644 --- a/Tests/UnitTests/Dialogs/WizardTests.cs +++ b/Tests/UnitTests/Dialogs/WizardTests.cs @@ -1,9 +1,6 @@ -using UnitTests; -using Xunit.Abstractions; +namespace UnitTests.DialogTests; -namespace UnitTests.DialogTests; - -public class WizardTests () +public class WizardTests { // =========== Wizard Tests [Fact] @@ -43,7 +40,7 @@ public class WizardTests () // Same test, but with two steps wizard = new (); - step1 = new() { Title = "step1" }; + step1 = new () { Title = "step1" }; wizard.AddStep (step1); WizardStep step2 = new () { Title = "step2" }; wizard.AddStep (step2); @@ -75,9 +72,9 @@ public class WizardTests () // Same test, but with two steps but the 1st one disabled wizard = new (); - step1 = new() { Title = "step1" }; + step1 = new () { Title = "step1" }; wizard.AddStep (step1); - step2 = new() { Title = "step2" }; + step2 = new () { Title = "step2" }; wizard.AddStep (step2); step1.Enabled = false; @@ -389,43 +386,22 @@ public class WizardTests () // and that the title is correct public void OneStepWizard_Shows () { - var title = "1234"; var stepTitle = "ABCD"; var width = 30; var height = 7; - AutoInitShutdownAttribute.FakeResize (new Size (width, height)); + Application.Driver?.SetScreenSize (width, height); // var btnBackText = "Back"; var btnBack = string.Empty; // $"{Glyphs.LeftBracket} {btnBackText} {Glyphs.RightBracket}"; var btnNextText = "Finish"; // "Next"; var btnNext = - $"{ - Glyphs.LeftBracket - }{ - Glyphs.LeftDefaultIndicator - } { - btnNextText - } { - Glyphs.RightDefaultIndicator - }{ - Glyphs.RightBracket - }"; + $"{Glyphs.LeftBracket}{Glyphs.LeftDefaultIndicator} {btnNextText} {Glyphs.RightDefaultIndicator}{Glyphs.RightBracket}"; var topRow = - $"{ - Glyphs.ULCornerDbl - }╡{ - title - } - { - stepTitle - }╞{ - new (Glyphs.HLineDbl.ToString () [0], width - title.Length - stepTitle.Length - 7) - }{ - Glyphs.URCornerDbl - }"; + $"{Glyphs.ULCornerDbl}╡{title} - {stepTitle}╞{new (Glyphs.HLineDbl.ToString () [0], width - title.Length - stepTitle.Length - 7)}{Glyphs.URCornerDbl}"; var row2 = $"{Glyphs.VLineDbl}{new (' ', width - 2)}{Glyphs.VLineDbl}"; string row3 = row2; string row4 = row3; @@ -434,29 +410,13 @@ public class WizardTests () $"{Glyphs.VLineDbl}{new (Glyphs.HLine.ToString () [0], width - 2)}{Glyphs.VLineDbl}"; var buttonRow = - $"{ - Glyphs.VLineDbl - }{ - btnBack - }{ - new (' ', width - btnBack.Length - btnNext.Length - 2) - }{ - btnNext - }{ - Glyphs.VLineDbl - }"; + $"{Glyphs.VLineDbl}{btnBack}{new (' ', width - btnBack.Length - btnNext.Length - 2)}{btnNext}{Glyphs.VLineDbl}"; var bottomRow = - $"{ - Glyphs.LLCornerDbl - }{ - new (Glyphs.HLineDbl.ToString () [0], width - 2) - }{ - Glyphs.LRCornerDbl - }"; + $"{Glyphs.LLCornerDbl}{new (Glyphs.HLineDbl.ToString () [0], width - 2)}{Glyphs.LRCornerDbl}"; Wizard wizard = new () { Title = title, Width = width, Height = height }; - wizard.AddStep (new() { Title = stepTitle }); + wizard.AddStep (new () { Title = stepTitle }); //wizard.LayoutSubViews (); RunState runstate = Application.Begin (wizard); @@ -477,42 +437,22 @@ public class WizardTests () // this test is needed because Wizard overrides Dialog's title behavior ("Title - StepTitle") public void Setting_Title_Works () { - var d = (IConsoleDriverFacade)Application.Driver; - + var d = Application.Driver; + var title = "1234"; var stepTitle = " - ABCD"; var width = 40; var height = 4; - d.OutputBuffer.SetWindowSize (width,height); + d.SetScreenSize (width, height); var btnNextText = "Finish"; var btnNext = - $"{ - Glyphs.LeftBracket - }{ - Glyphs.LeftDefaultIndicator - } { - btnNextText - } { - Glyphs.RightDefaultIndicator - }{ - Glyphs.RightBracket - }"; + $"{Glyphs.LeftBracket}{Glyphs.LeftDefaultIndicator} {btnNextText} {Glyphs.RightDefaultIndicator}{Glyphs.RightBracket}"; var topRow = - $"{ - Glyphs.ULCornerDbl - }╡{ - title - }{ - stepTitle - }╞{ - new (Glyphs.HLineDbl.ToString () [0], width - title.Length - stepTitle.Length - 4) - }{ - Glyphs.URCornerDbl - }"; + $"{Glyphs.ULCornerDbl}╡{title}{stepTitle}╞{new (Glyphs.HLineDbl.ToString () [0], width - title.Length - stepTitle.Length - 4)}{Glyphs.URCornerDbl}"; var separatorRow = $"{Glyphs.VLineDbl}{new (Glyphs.HLine.ToString () [0], width - 2)}{Glyphs.VLineDbl}"; @@ -523,16 +463,10 @@ public class WizardTests () //var buttonRow = $"{Glyphs.VDLine}{new String (' ', width - btnNext.Length - 2)}{btnNext}{Glyphs.VDLine}"; var bottomRow = - $"{ - Glyphs.LLCornerDbl - }{ - new (Glyphs.HLineDbl.ToString () [0], width - 2) - }{ - Glyphs.LRCornerDbl - }"; + $"{Glyphs.LLCornerDbl}{new (Glyphs.HLineDbl.ToString () [0], width - 2)}{Glyphs.LRCornerDbl}"; var wizard = new Wizard { Title = title, Width = width, Height = height }; - wizard.AddStep (new() { Title = "ABCD" }); + wizard.AddStep (new () { Title = "ABCD" }); Application.End (Application.Begin (wizard)); wizard.Dispose (); @@ -646,37 +580,17 @@ public class WizardTests () var width = 30; var height = 6; - AutoInitShutdownAttribute.FakeResize (new Size (width, height)); + Application.Driver?.SetScreenSize (width, height); var btnBackText = "Back"; var btnBack = $"{Glyphs.LeftBracket} {btnBackText} {Glyphs.RightBracket}"; var btnNextText = "Finish"; var btnNext = - $"{ - Glyphs.LeftBracket - }{ - Glyphs.LeftDefaultIndicator - } { - btnNextText - } { - Glyphs.RightDefaultIndicator - }{ - Glyphs.RightBracket - }"; + $"{Glyphs.LeftBracket}{Glyphs.LeftDefaultIndicator} {btnNextText} {Glyphs.RightDefaultIndicator}{Glyphs.RightBracket}"; var topRow = - $"{ - Glyphs.ULCornerDbl - }╡{ - title - }{ - stepTitle - }╞{ - new (Glyphs.HLineDbl.ToString () [0], width - title.Length - stepTitle.Length - 4) - }{ - Glyphs.URCornerDbl - }"; + $"{Glyphs.ULCornerDbl}╡{title}{stepTitle}╞{new (Glyphs.HLineDbl.ToString () [0], width - title.Length - stepTitle.Length - 4)}{Glyphs.URCornerDbl}"; var row2 = $"{Glyphs.VLineDbl}{new (' ', width - 2)}{Glyphs.VLineDbl}"; string row3 = row2; @@ -684,26 +598,10 @@ public class WizardTests () $"{Glyphs.VLineDbl}{new (Glyphs.HLine.ToString () [0], width - 2)}{Glyphs.VLineDbl}"; var buttonRow = - $"{ - Glyphs.VLineDbl - }{ - btnBack - }{ - new (' ', width - btnBack.Length - btnNext.Length - 2) - }{ - btnNext - }{ - Glyphs.VLineDbl - }"; + $"{Glyphs.VLineDbl}{btnBack}{new (' ', width - btnBack.Length - btnNext.Length - 2)}{btnNext}{Glyphs.VLineDbl}"; var bottomRow = - $"{ - Glyphs.LLCornerDbl - }{ - new (Glyphs.HLineDbl.ToString () [0], width - 2) - }{ - Glyphs.LRCornerDbl - }"; + $"{Glyphs.LLCornerDbl}{new (Glyphs.HLineDbl.ToString () [0], width - 2)}{Glyphs.LRCornerDbl}"; var wizard = new Wizard { Title = title, Width = width, Height = height }; RunState runstate = Application.Begin (wizard); diff --git a/Tests/UnitTests/Drawing/LineCanvasTests.cs b/Tests/UnitTests/Drawing/LineCanvasTests.cs deleted file mode 100644 index 46f90658d..000000000 --- a/Tests/UnitTests/Drawing/LineCanvasTests.cs +++ /dev/null @@ -1,1263 +0,0 @@ -using System.Text; -using UnitTests; -using Xunit.Abstractions; - -namespace UnitTests.DrawingTests; - -public class LineCanvasTests (ITestOutputHelper output) -{ - [Theory] - - // Horizontal lines with a vertical zero-length - [InlineData ( - 0, - 0, - 1, - Orientation.Horizontal, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Single, - "╞" - )] - [InlineData ( - 0, - 0, - -1, - Orientation.Horizontal, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Single, - "╡" - )] - [InlineData ( - 0, - 0, - 1, - Orientation.Horizontal, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Double, - "╟" - )] - [InlineData ( - 0, - 0, - -1, - Orientation.Horizontal, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Double, - "╢" - )] - [InlineData ( - 0, - 0, - 1, - Orientation.Horizontal, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Single, - "├" - )] - [InlineData ( - 0, - 0, - -1, - Orientation.Horizontal, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Single, - "┤" - )] - [InlineData ( - 0, - 0, - 1, - Orientation.Horizontal, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Double, - "╠" - )] - [InlineData ( - 0, - 0, - -1, - Orientation.Horizontal, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Double, - "╣" - )] - - // Vertical lines with a horizontal zero-length - [InlineData ( - 0, - 0, - 1, - Orientation.Vertical, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Single, - "╥" - )] - [InlineData ( - 0, - 0, - -1, - Orientation.Vertical, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Single, - "╨" - )] - [InlineData ( - 0, - 0, - 1, - Orientation.Vertical, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Double, - "╤" - )] - [InlineData ( - 0, - 0, - -1, - Orientation.Vertical, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Double, - "╧" - )] - [InlineData ( - 0, - 0, - 1, - Orientation.Vertical, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Single, - "┬" - )] - [InlineData ( - 0, - 0, - -1, - Orientation.Vertical, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Single, - "┴" - )] - [InlineData ( - 0, - 0, - 1, - Orientation.Vertical, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Double, - "╦" - )] - [InlineData ( - 0, - 0, - -1, - Orientation.Vertical, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Double, - "╩" - )] - - // Crosses (two zero-length) - [InlineData ( - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Single, - "╫" - )] - [InlineData ( - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Double, - "╪" - )] - [InlineData ( - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Single, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Single, - "┼" - )] - [InlineData ( - 0, - 0, - 0, - Orientation.Vertical, - LineStyle.Double, - 0, - 0, - 0, - Orientation.Horizontal, - LineStyle.Double, - "╬" - )] - public void Add_2_Lines ( - int x1, - int y1, - int len1, - Orientation o1, - LineStyle s1, - int x2, - int y2, - int len2, - Orientation o2, - LineStyle s2, - string expected - ) - { - View v = GetCanvas (out LineCanvas lc); - v.Width = 10; - v.Height = 10; - v.Viewport = new (0, 0, 10, 10); - - lc.AddLine (new (x1, y1), len1, o1, s1); - lc.AddLine (new (x2, y2), len2, o2, s2); - - OutputAssert.AssertEqual (output, expected, lc.ToString ()); - v.Dispose (); - } - - [Fact] - [SetupFakeDriver] - public void Viewport_Specific () - { - // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1) - // This proves we aren't drawing excess above - var x = 1; - var y = 2; - var width = 3; - var height = 2; - - var lc = new LineCanvas (); - - // 01230 - // ╔╡╞╗1 - // ║ ║2 - - // Add a short horiz line for ╔╡ - lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double); - Assert.Equal (new (x, y, 2, 1), lc.Bounds); - - //LHS line down - lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double); - Assert.Equal (new (x, y, 2, 2), lc.Bounds); - - //Vertical line before Title, results in a ╡ - lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single); - Assert.Equal (new (x, y, 2, 2), lc.Bounds); - - //Vertical line after Title, results in a ╞ - lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single); - Assert.Equal (new (x, y, 3, 2), lc.Bounds); - - // remainder of top line - lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double); - Assert.Equal (new (x, y, 4, 2), lc.Bounds); - - //RHS line down - lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double); - Assert.Equal (new (x, y, 4, 2), lc.Bounds); - - OutputAssert.AssertEqual ( - output, - @" -╔╡╞╗ -║ ║", - $"{Environment.NewLine}{lc}" - ); - } - - [Fact] - [SetupFakeDriver] - public void Viewport_Specific_With_Ustring () - { - // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1) - // This proves we aren't drawing excess above - var x = 1; - var y = 2; - var width = 3; - var height = 2; - - var lc = new LineCanvas (); - - // 01230 - // ╔╡╞╗1 - // ║ ║2 - - // Add a short horiz line for ╔╡ - lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double); - Assert.Equal (new (x, y, 2, 1), lc.Bounds); - - //LHS line down - lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double); - Assert.Equal (new (x, y, 2, 2), lc.Bounds); - - //Vertical line before Title, results in a ╡ - lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single); - Assert.Equal (new (x, y, 2, 2), lc.Bounds); - - //Vertical line after Title, results in a ╞ - lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single); - Assert.Equal (new (x, y, 3, 2), lc.Bounds); - - // remainder of top line - lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double); - Assert.Equal (new (x, y, 4, 2), lc.Bounds); - - //RHS line down - lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double); - Assert.Equal (new (x, y, 4, 2), lc.Bounds); - - OutputAssert.AssertEqual ( - output, - @" -╔╡╞╗ -║ ║", - $"{Environment.NewLine}{lc}" - ); - } - - [Fact] - [SetupFakeDriver] - public void Canvas_Updates_On_Changes () - { - var lc = new LineCanvas (); - - Assert.Equal (Rectangle.Empty, lc.Bounds); - - lc.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double); - Assert.NotEqual (Rectangle.Empty, lc.Bounds); - - lc.Clear (); - Assert.Equal (Rectangle.Empty, lc.Bounds); - } - - [InlineData (0, 0, Orientation.Horizontal, "─")] - [InlineData (1, 0, Orientation.Horizontal, "─")] - [InlineData (0, 1, Orientation.Horizontal, "─")] - [InlineData (-1, 0, Orientation.Horizontal, "─")] - [InlineData (0, -1, Orientation.Horizontal, "─")] - [InlineData (-1, -1, Orientation.Horizontal, "─")] - [InlineData (0, 0, Orientation.Vertical, "│")] - [InlineData (1, 0, Orientation.Vertical, "│")] - [InlineData (0, 1, Orientation.Vertical, "│")] - [InlineData (0, -1, Orientation.Vertical, "│")] - [InlineData (-1, 0, Orientation.Vertical, "│")] - [InlineData (-1, -1, Orientation.Vertical, "│")] - [Theory] - [SetupFakeDriver] - public void Length_0_Is_1_Long (int x, int y, Orientation orientation, string expected) - { - var canvas = new LineCanvas (); - - // Add a line at 5, 5 that's has length of 1 - canvas.AddLine (new (x, y), 1, orientation, LineStyle.Single); - OutputAssert.AssertEqual (output, $"{expected}", $"{canvas}"); - } - - // X is offset by 2 - [InlineData (0, 0, 1, Orientation.Horizontal, "─")] - [InlineData (1, 0, 1, Orientation.Horizontal, "─")] - [InlineData (0, 1, 1, Orientation.Horizontal, "─")] - [InlineData (0, 0, 1, Orientation.Vertical, "│")] - [InlineData (1, 0, 1, Orientation.Vertical, "│")] - [InlineData (0, 1, 1, Orientation.Vertical, "│")] - [InlineData (-1, 0, 1, Orientation.Horizontal, "─")] - [InlineData (0, -1, 1, Orientation.Horizontal, "─")] - [InlineData (-1, 0, 1, Orientation.Vertical, "│")] - [InlineData (0, -1, 1, Orientation.Vertical, "│")] - [InlineData (0, 0, -1, Orientation.Horizontal, "─")] - [InlineData (1, 0, -1, Orientation.Horizontal, "─")] - [InlineData (0, 1, -1, Orientation.Horizontal, "─")] - [InlineData (0, 0, -1, Orientation.Vertical, "│")] - [InlineData (1, 0, -1, Orientation.Vertical, "│")] - [InlineData (0, 1, -1, Orientation.Vertical, "│")] - [InlineData (-1, 0, -1, Orientation.Horizontal, "─")] - [InlineData (0, -1, -1, Orientation.Horizontal, "─")] - [InlineData (-1, 0, -1, Orientation.Vertical, "│")] - [InlineData (0, -1, -1, Orientation.Vertical, "│")] - [InlineData (0, 0, 2, Orientation.Horizontal, "──")] - [InlineData (1, 0, 2, Orientation.Horizontal, "──")] - [InlineData (0, 1, 2, Orientation.Horizontal, "──")] - [InlineData (1, 1, 2, Orientation.Horizontal, "──")] - [InlineData (0, 0, 2, Orientation.Vertical, "│\r\n│")] - [InlineData (1, 0, 2, Orientation.Vertical, "│\r\n│")] - [InlineData (0, 1, 2, Orientation.Vertical, "│\r\n│")] - [InlineData (1, 1, 2, Orientation.Vertical, "│\r\n│")] - [InlineData (-1, 0, 2, Orientation.Horizontal, "──")] - [InlineData (0, -1, 2, Orientation.Horizontal, "──")] - [InlineData (-1, 0, 2, Orientation.Vertical, "│\r\n│")] - [InlineData (0, -1, 2, Orientation.Vertical, "│\r\n│")] - [InlineData (-1, -1, 2, Orientation.Vertical, "│\r\n│")] - [InlineData (0, 0, -2, Orientation.Horizontal, "──")] - [InlineData (1, 0, -2, Orientation.Horizontal, "──")] - [InlineData (0, 1, -2, Orientation.Horizontal, "──")] - [InlineData (0, 0, -2, Orientation.Vertical, "│\r\n│")] - [InlineData (1, 0, -2, Orientation.Vertical, "│\r\n│")] - [InlineData (0, 1, -2, Orientation.Vertical, "│\r\n│")] - [InlineData (1, 1, -2, Orientation.Vertical, "│\r\n│")] - [InlineData (-1, 0, -2, Orientation.Horizontal, "──")] - [InlineData (0, -1, -2, Orientation.Horizontal, "──")] - [InlineData (-1, 0, -2, Orientation.Vertical, "│\r\n│")] - [InlineData (0, -1, -2, Orientation.Vertical, "│\r\n│")] - [InlineData (-1, -1, -2, Orientation.Vertical, "│\r\n│")] - [Theory] - [SetupFakeDriver] - public void Length_n_Is_n_Long (int x, int y, int length, Orientation orientation, string expected) - { - var canvas = new LineCanvas (); - canvas.AddLine (new (x, y), length, orientation, LineStyle.Single); - - var result = canvas.ToString (); - OutputAssert.AssertEqual (output, expected, result); - } - - [Fact] - [SetupFakeDriver] - public void Length_Negative () - { - var offset = new Point (5, 5); - - var canvas = new LineCanvas (); - canvas.AddLine (offset, -3, Orientation.Horizontal, LineStyle.Single); - - var looksLike = "───"; - - Assert.Equal (looksLike, $"{canvas}"); - } - - [InlineData (Orientation.Horizontal, "─")] - [InlineData (Orientation.Vertical, "│")] - [Theory] - [SetupFakeDriver] - public void Length_Zero_Alone_Is_Line (Orientation orientation, string expected) - { - var lc = new LineCanvas (); - - // Add a line at 0, 0 that's has length of 0 - lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); - OutputAssert.AssertEqual (output, expected, $"{lc}"); - } - - [InlineData (Orientation.Horizontal, "┼")] - [InlineData (Orientation.Vertical, "┼")] - [Theory] - [SetupFakeDriver] - public void Length_Zero_Cross_Is_Cross (Orientation orientation, string expected) - { - var lc = new LineCanvas (); - - // Add point at opposite orientation - lc.AddLine ( - Point.Empty, - 0, - orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal, - LineStyle.Single - ); - - // Add a line at 0, 0 that's has length of 0 - lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); - OutputAssert.AssertEqual (output, expected, $"{lc}"); - } - - [InlineData (Orientation.Horizontal, "╥")] - [InlineData (Orientation.Vertical, "╞")] - [Theory] - [SetupFakeDriver] - public void Length_Zero_NextTo_Opposite_Is_T (Orientation orientation, string expected) - { - var lc = new LineCanvas (); - - // Add line with length of 1 in opposite orientation starting at same location - if (orientation == Orientation.Horizontal) - { - lc.AddLine (Point.Empty, 1, Orientation.Vertical, LineStyle.Double); - } - else - { - lc.AddLine (Point.Empty, 1, Orientation.Horizontal, LineStyle.Double); - } - - // Add a line at 0, 0 that's has length of 0 - lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); - OutputAssert.AssertEqual (output, expected, $"{lc}"); - } - - [Fact] - public void TestLineCanvas_LeaveMargin_Top1_Left1 () - { - var canvas = new LineCanvas (); - - // Upper box - canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single); - canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single); - - var looksLike = - @" -┌─ -│ "; - OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); - } - - [Fact] - [SetupFakeDriver] - public void TestLineCanvas_Window_Heavy () - { - View v = GetCanvas (out LineCanvas canvas); - - // outer box - canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Heavy); - canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Heavy); - canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Heavy); - canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Heavy); - - canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Heavy); - canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Heavy); - - v.Draw (); - - var looksLike = - @" -┏━━━━┳━━━┓ -┃ ┃ ┃ -┣━━━━╋━━━┫ -┃ ┃ ┃ -┗━━━━┻━━━┛"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Theory] - [SetupFakeDriver] - [InlineData (LineStyle.Single)] - [InlineData (LineStyle.Rounded)] - public void TestLineCanvas_Window_HeavyTop_ThinSides (LineStyle thinStyle) - { - View v = GetCanvas (out LineCanvas canvas); - - // outer box - canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Heavy); - canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle); - canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Heavy); - canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle); - - canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle); - canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Heavy); - - v.Draw (); - - var looksLike = - @" -┍━━━━┯━━━┑ -│ │ │ -┝━━━━┿━━━┥ -│ │ │ -┕━━━━┷━━━┙ -"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Theory] - [SetupFakeDriver] - [InlineData (LineStyle.Single)] - [InlineData (LineStyle.Rounded)] - public void TestLineCanvas_Window_ThinTop_HeavySides (LineStyle thinStyle) - { - View v = GetCanvas (out LineCanvas canvas); - - // outer box - canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle); - canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Heavy); - canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle); - canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Heavy); - - canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Heavy); - canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle); - - v.Draw (); - - var looksLike = - @" -┎────┰───┒ -┃ ┃ ┃ -┠────╂───┨ -┃ ┃ ┃ -┖────┸───┚ - -"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Fact] - [SetupFakeDriver] - public void Top_Left_From_TopRight_LeftUp () - { - var canvas = new LineCanvas (); - - // Upper box - canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single); - canvas.AddLine (new (0, 1), -2, Orientation.Vertical, LineStyle.Single); - - var looksLike = - @" -┌─ -│ "; - OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); - } - - [Fact] - [SetupFakeDriver] - public void Top_With_1Down () - { - var canvas = new LineCanvas (); - - // Top ─ - canvas.AddLine (Point.Empty, 1, Orientation.Horizontal, LineStyle.Single); - - // Bottom ─ - canvas.AddLine (new (1, 1), -1, Orientation.Horizontal, LineStyle.Single); - - //// Right down - //canvas.AddLine (new Point (9, 0), 3, Orientation.Vertical, LineStyle.Single); - - //// Bottom - //canvas.AddLine (new Point (9, 3), -10, Orientation.Horizontal, LineStyle.Single); - - //// Left Up - //canvas.AddLine (new Point (0, 3), -3, Orientation.Vertical, LineStyle.Single); - - Assert.Equal (new (0, 0, 2, 2), canvas.Bounds); - - Dictionary map = canvas.GetMap (); - Assert.Equal (2, map.Count); - - OutputAssert.AssertEqual ( - output, - @" -─ - ─", - $"{Environment.NewLine}{canvas}" - ); - } - - [Fact] - [SetupFakeDriver] - public void ToString_Empty () - { - var lc = new LineCanvas (); - OutputAssert.AssertEqual (output, string.Empty, lc.ToString ()); - } - - // 012 - [InlineData (0, 0, "═══")] - [InlineData (1, 0, "═══")] - [InlineData (0, 1, "═══")] - [InlineData (1, 1, "═══")] - [InlineData (2, 2, "═══")] - [InlineData (-1, 0, "═══")] - [InlineData (0, -1, "═══")] - [InlineData (-1, -1, "═══")] - [InlineData (-2, -2, "═══")] - [Theory] - [SetupFakeDriver] - public void ToString_Positive_Horizontal_1Line_Offset (int x, int y, string expected) - { - var lc = new LineCanvas (); - lc.AddLine (new (x, y), 3, Orientation.Horizontal, LineStyle.Double); - OutputAssert.AssertEqual (output, expected, $"{lc}"); - } - - [InlineData (0, 0, 0, 0, "═══")] - [InlineData (1, 0, 1, 0, "═══")] - [InlineData (-1, 0, -1, 0, "═══")] - [InlineData (0, 0, 1, 0, "════")] - [InlineData (1, 0, 3, 0, "═════")] - [InlineData (1, 0, 4, 0, "══════")] - [InlineData (1, 0, 5, 0, "═══ ═══")] - [InlineData (0, 0, 0, 1, "\u2550\u2550\u2550\r\n\u2550\u2550\u2550")] - [InlineData (0, 0, 1, 1, "═══ \r\n ═══")] - [InlineData (0, 0, 2, 1, "═══ \r\n ═══")] - [InlineData (1, 0, 0, 1, " ═══\r\n═══ ")] - [InlineData (0, 1, 0, 1, "═══")] - [InlineData (1, 1, 0, 1, "════")] - [InlineData (2, 2, 0, 1, "═══ \r\n ═══")] - [Theory] - [SetupFakeDriver] - public void ToString_Positive_Horizontal_2Line_Offset (int x1, int y1, int x2, int y2, string expected) - { - var lc = new LineCanvas (); - lc.AddLine (new (x1, y1), 3, Orientation.Horizontal, LineStyle.Double); - lc.AddLine (new (x2, y2), 3, Orientation.Horizontal, LineStyle.Double); - - OutputAssert.AssertEqual (output, expected, $"{lc}"); - } - - // [Fact, SetupFakeDriver] - // public void LeaveMargin_Top1_Left1 () - // { - // var canvas = new LineCanvas (); - - // // Upper box - // canvas.AddLine (Point.Empty, 9, Orientation.Horizontal, LineStyle.Single); - // canvas.AddLine (new Point (8, 0), 3, Orientation.Vertical, LineStyle.Single); - // canvas.AddLine (new Point (8, 3), -9, Orientation.Horizontal, LineStyle.Single); - // canvas.AddLine (new Point (0, 2), -3, Orientation.Vertical, LineStyle.Single); - - // // Lower Box - // canvas.AddLine (new Point (5, 0), 2, Orientation.Vertical, LineStyle.Single); - // canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Single); - - // string looksLike = - //@" - //┌────┬──┐ - //│ │ │ - //├────┼──┤ - //└────┴──┘ - //"; - // Assert.Equal (looksLike, $"{Environment.NewLine}{canvas}"); - // } - - [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Double, "═")] - [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Double, "║")] - [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Single, "─")] - [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Single, "│")] - [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Double, "═")] - [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Double, "║")] - [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Single, "─")] - [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Single, "│")] - [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Double, "══")] - [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Double, "║\n║")] - [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Single, "──")] - [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Single, "│\n│")] - [SetupFakeDriver] - [Theory] - public void View_Draws_1LineTests ( - int x1, - int y1, - int length, - Orientation o1, - LineStyle s1, - string expected - ) - { - View v = GetCanvas (out LineCanvas lc); - v.Width = 10; - v.Height = 10; - v.Viewport = new (0, 0, 10, 10); - - lc.AddLine (new (x1, y1), length, o1, s1); - - v.Draw (); - - DriverAssert.AssertDriverContentsAre (expected, output); - v.Dispose (); - } - - /// This test demonstrates how to correctly trigger a corner. By overlapping the lines in the same cell - [Fact] - [SetupFakeDriver] - public void View_Draws_Corner_Correct () - { - View v = GetCanvas (out LineCanvas canvas); - canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single); - canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single); - - v.Draw (); - - var looksLike = - @" -┌─ -│"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - /// - /// This test demonstrates that corners are only drawn when lines overlap. Not when they terminate adjacent to one - /// another. - /// - [Fact] - [SetupFakeDriver] - public void View_Draws_Corner_NoOverlap () - { - View v = GetCanvas (out LineCanvas canvas); - canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single); - canvas.AddLine (new (0, 1), 2, Orientation.Vertical, LineStyle.Single); - - v.Draw (); - - var looksLike = - @" -── -│ -│"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [InlineData (LineStyle.Single)] - [InlineData (LineStyle.Rounded)] - [Theory] - [SetupFakeDriver] - public void View_Draws_Horizontal (LineStyle style) - { - View v = GetCanvas (out LineCanvas canvas); - canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, style); - - v.Draw (); - - var looksLike = - @" -──"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Fact] - [SetupFakeDriver] - public void View_Draws_Horizontal_Double () - { - View v = GetCanvas (out LineCanvas canvas); - canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double); - - v.Draw (); - - var looksLike = - @" -══"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [InlineData (LineStyle.Single)] - [InlineData (LineStyle.Rounded)] - [Theory] - [SetupFakeDriver] - public void View_Draws_Vertical (LineStyle style) - { - View v = GetCanvas (out LineCanvas canvas); - canvas.AddLine (Point.Empty, 2, Orientation.Vertical, style); - - v.Draw (); - - var looksLike = - @" -│ -│"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Fact] - [SetupFakeDriver] - public void View_Draws_Vertical_Double () - { - View v = GetCanvas (out LineCanvas canvas); - canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Double); - - v.Draw (); - - var looksLike = - @" -║ -║"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Fact] - [SetupFakeDriver] - public void View_Draws_Window_Double () - { - View v = GetCanvas (out LineCanvas canvas); - - // outer box - canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double); - canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double); - canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double); - canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double); - - canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double); - canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double); - - v.Draw (); - - var looksLike = - @" -╔════╦═══╗ -║ ║ ║ -╠════╬═══╣ -║ ║ ║ -╚════╩═══╝"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Theory] - [SetupFakeDriver] - [InlineData (LineStyle.Single)] - [InlineData (LineStyle.Rounded)] - public void View_Draws_Window_DoubleTop_SingleSides (LineStyle thinStyle) - { - View v = GetCanvas (out LineCanvas canvas); - - // outer box - canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double); - canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle); - canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double); - canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle); - - canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle); - canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double); - - v.Draw (); - - var looksLike = - @" -╒════╤═══╕ -│ │ │ -╞════╪═══╡ -│ │ │ -╘════╧═══╛ -"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - /// - /// Demonstrates when corners are used. Notice how not all lines declare rounded. - /// If there are 1+ lines intersecting and a corner is to be used then if any of them are rounded a rounded corner is - /// used. - /// - [Fact] - [SetupFakeDriver] - public void View_Draws_Window_Rounded () - { - View v = GetCanvas (out LineCanvas canvas); - - // outer box - canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Rounded); - - // LineStyle.Single is ignored because corner overlaps with the above line which is Rounded - // this results in a rounded corner being used. - canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single); - canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Rounded); - canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single); - - // These lines say rounded but they will result in the T sections which are never rounded. - canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Rounded); - canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Rounded); - - v.Draw (); - - var looksLike = - @" -╭────┬───╮ -│ │ │ -├────┼───┤ -│ │ │ -╰────┴───╯"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Theory] - [InlineData (LineStyle.Single)] - [InlineData (LineStyle.Rounded)] - [SetupFakeDriver] - public void View_Draws_Window_SingleTop_DoubleSides (LineStyle thinStyle) - { - View v = GetCanvas (out LineCanvas canvas); - - // outer box - canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle); - canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double); - canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle); - canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double); - - canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double); - canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle); - - v.Draw (); - - var looksLike = - @" -╓────╥───╖ -║ ║ ║ -╟────╫───╢ -║ ║ ║ -╙────╨───╜ - -"; - DriverAssert.AssertDriverContentsAre (looksLike, output); - v.Dispose (); - } - - [Fact] - [SetupFakeDriver] - public void Window () - { - var canvas = new LineCanvas (); - - // Frame - canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Single); - canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single); - canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Single); - canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single); - - // Cross - canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Single); - canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Single); - - var looksLike = - @" -┌────┬───┐ -│ │ │ -├────┼───┤ -│ │ │ -└────┴───┘"; - OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); - } - - [Fact] - [SetupFakeDriver] - public void Zero_Length_Intersections () - { - // Draw at 1,2 within client area of View (i.e. leave a top and left margin of 1) - // This proves we aren't drawing excess above - var x = 1; - var y = 2; - var width = 5; - var height = 2; - - var lc = new LineCanvas (); - - // ╔╡╞═════╗ - // Add a short horiz line for ╔╡ - lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double); - - //LHS line down - lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double); - - //Vertical line before Title, results in a ╡ - lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single); - - //Vertical line after Title, results in a ╞ - lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single); - - // remainder of top line - lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double); - - //RHS line down - lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double); - - var looksLike = @" -╔╡╞══╗ -║ ║"; - OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{lc}"); - } - - [Fact] - public void LineCanvas_UsesFillCorrectly () - { - // Arrange - var foregroundColor = new Color (255, 0); // Red - var backgroundColor = new Color (0, 0); // Black - var foregroundFill = new SolidFill (foregroundColor); - var backgroundFill = new SolidFill (backgroundColor); - var fillPair = new FillPair (foregroundFill, backgroundFill); - - var lineCanvas = new LineCanvas - { - Fill = fillPair - }; - - // Act - lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single); - Dictionary cellMap = lineCanvas.GetCellMap (); - - // Assert - foreach (Cell? cell in cellMap.Values) - { - Assert.NotNull (cell); - Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); - Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); - } - } - - [Fact] - public void LineCanvas_LineColorIgnoredBecauseOfFill () - { - // Arrange - var foregroundColor = new Color (255, 0); // Red - var backgroundColor = new Color (0, 0); // Black - var lineColor = new Attribute (new Color (0, 255), new Color (255, 255, 255)); // Green on White - var foregroundFill = new SolidFill (foregroundColor); - var backgroundFill = new SolidFill (backgroundColor); - var fillPair = new FillPair (foregroundFill, backgroundFill); - - var lineCanvas = new LineCanvas - { - Fill = fillPair - }; - - // Act - lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single, lineColor); - Dictionary cellMap = lineCanvas.GetCellMap (); - - // Assert - foreach (Cell? cell in cellMap.Values) - { - Assert.NotNull (cell); - Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); - Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); - } - } - - [Fact] - public void LineCanvas_IntersectingLinesUseFillCorrectly () - { - // Arrange - var foregroundColor = new Color (255, 0); // Red - var backgroundColor = new Color (0, 0); // Black - var foregroundFill = new SolidFill (foregroundColor); - var backgroundFill = new SolidFill (backgroundColor); - var fillPair = new FillPair (foregroundFill, backgroundFill); - - var lineCanvas = new LineCanvas - { - Fill = fillPair - }; - - // Act - lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single); - lineCanvas.AddLine (new (2, -2), 5, Orientation.Vertical, LineStyle.Single); - Dictionary cellMap = lineCanvas.GetCellMap (); - - // Assert - foreach (Cell? cell in cellMap.Values) - { - Assert.NotNull (cell); - Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); - Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); - } - } - - // TODO: Remove this and make all LineCanvas tests independent of View - /// - /// Creates a new into which a is rendered at - /// time. - /// - /// The you can draw into. - /// How far to offset drawing in X - /// How far to offset drawing in Y - /// - private View GetCanvas (out LineCanvas canvas, int offsetX = 0, int offsetY = 0) - { - var v = new View { Width = 10, Height = 5, Viewport = new (0, 0, 10, 5) }; - - LineCanvas canvasCopy = canvas = new (); - - v.DrawComplete += (s, e) => - { - v.FillRect (v.Viewport); - - foreach (KeyValuePair p in canvasCopy.GetMap ()) - { - v.AddRune ( - offsetX + p.Key.X, - offsetY + p.Key.Y, - p.Value - ); - } - - canvasCopy.Clear (); - }; - - return v; - } -} diff --git a/Tests/UnitTests/Drawing/RulerTests.cs b/Tests/UnitTests/Drawing/RulerTests.cs deleted file mode 100644 index 00874d523..000000000 --- a/Tests/UnitTests/Drawing/RulerTests.cs +++ /dev/null @@ -1,142 +0,0 @@ -using UnitTests; -using Xunit.Abstractions; - -namespace UnitTests.DrawingTests; - -public class RulerTests -{ - private readonly ITestOutputHelper _output; - public RulerTests (ITestOutputHelper output) { _output = output; } - - [Fact] - [AutoInitShutdown] - public void Draw_Default () - { - AutoInitShutdownAttribute.FakeResize(new Size(25, 25)); - - var r = new Ruler (); - r.Draw (Point.Empty); - DriverAssert.AssertDriverContentsWithFrameAre (@"", _output); - } - - [Fact] - [SetupFakeDriver] - public void Draw_Horizontal () - { - var len = 15; - - var r = new Ruler (); - Assert.Equal (Orientation.Horizontal, r.Orientation); - - r.Length = len; - r.Draw (Point.Empty); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -|123456789|1234", - _output - ); - - // Postive offset - r.Draw (new (1, 1)); - - DriverAssert.AssertDriverContentsAre ( - @" -|123456789|1234 - |123456789|1234 -", - _output - ); - - // Negative offset - r.Draw (new (-1, 3)); - - DriverAssert.AssertDriverContentsAre ( - @" -|123456789|1234 - |123456789|1234 -123456789|1234 -", - _output - ); - } - - [Fact] - [SetupFakeDriver] - public void Draw_Vertical () - { - var len = 15; - - var r = new Ruler (); - r.Orientation = Orientation.Vertical; - r.Length = len; - r.Draw (Point.Empty); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -- -1 -2 -3 -4 -5 -6 -7 -8 -9 -- -1 -2 -3 -4", - _output - ); - - r.Draw (new (1, 1)); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -- -1- -21 -32 -43 -54 -65 -76 -87 -98 --9 -1- -21 -32 -43 - 4", - _output - ); - - // Negative offset - r.Draw (new (2, -1)); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -- 1 -1-2 -213 -324 -435 -546 -657 -768 -879 -98- --91 -1-2 -213 -324 -43 - 4 ", - _output - ); - } -} diff --git a/Tests/UnitTests/Drawing/ThicknessTests.cs b/Tests/UnitTests/Drawing/ThicknessTests.cs deleted file mode 100644 index 86bd32996..000000000 --- a/Tests/UnitTests/Drawing/ThicknessTests.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System.Text; -using UnitTests; -using Xunit.Abstractions; - -namespace UnitTests.DrawingTests; - -public class ThicknessTests (ITestOutputHelper output) -{ - [Fact] - [AutoInitShutdown] - public void DrawTests () - { - AutoInitShutdownAttribute.FakeResize(new Size(60, 60)); - var t = new Thickness (0, 0, 0, 0); - var r = new Rectangle (5, 5, 40, 15); - - Application.Driver?.FillRect ( - new (0, 0, Application.Driver!.Cols, Application.Driver!.Rows), - (Rune)' ' - ); - t.Draw (r, ViewDiagnosticFlags.Thickness, "Test"); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - Test (Left=0,Top=0,Right=0,Bottom=0)", - output - ); - - t = new (1, 1, 1, 1); - r = new (5, 5, 40, 15); - - Application.Driver?.FillRect ( - new (0, 0, Application.Driver!.Cols, Application.Driver!.Rows), - (Rune)' ' - ); - t.Draw (r, ViewDiagnosticFlags.Thickness, "Test"); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT - T T - T T - T T - T T - T T - T T - T T - T T - T T - T T - T T - T T - T T - TTTest (Left=1,Top=1,Right=1,Bottom=1)TT", - output - ); - - t = new (1, 2, 3, 4); - r = new (5, 5, 40, 15); - - Application.Driver?.FillRect ( - new (0, 0, Application.Driver!.Cols, Application.Driver!.Rows), - (Rune)' ' - ); - t.Draw (r, ViewDiagnosticFlags.Thickness, "Test"); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT - TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT - T TTT - T TTT - T TTT - T TTT - T TTT - T TTT - T TTT - T TTT - T TTT - TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT - TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT - TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT - TTTest (Left=1,Top=2,Right=3,Bottom=4)TT", - output - ); - - t = new (-1, 1, 1, 1); - r = new (5, 5, 40, 15); - - Application.Driver?.FillRect ( - new (0, 0, Application.Driver!.Cols, Application.Driver!.Rows), - (Rune)' ' - ); - t.Draw (r, ViewDiagnosticFlags.Thickness, "Test"); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT - T - T - T - T - T - T - T - T - T - T - T - T - T - TTest (Left=-1,Top=1,Right=1,Bottom=1)TT", - output - ); - } - - [Fact] - [AutoInitShutdown] - public void DrawTests_Ruler () - { - // Add a frame so we can see the ruler - var f = new FrameView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single}; - - var top = new Toplevel (); - top.Add (f); - RunState rs = Application.Begin (top); - - AutoInitShutdownAttribute.FakeResize(new Size(45, 20)); - var t = new Thickness (0, 0, 0, 0); - var r = new Rectangle (2, 2, 40, 15); - - AutoInitShutdownAttribute.RunIteration (); - - t.Draw (r, ViewDiagnosticFlags.Ruler, "Test"); - - DriverAssert.AssertDriverContentsAre ( - @" -┌───────────────────────────────────────────┐ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -└───────────────────────────────────────────┘", - output - ); - - t = new (1, 1, 1, 1); - r = new (1, 1, 40, 15); - top.SetNeedsDraw (); - AutoInitShutdownAttribute.RunIteration (); - t.Draw (r, ViewDiagnosticFlags.Ruler, "Test"); - - DriverAssert.AssertDriverContentsAre ( - @" -┌───────────────────────────────────────────┐ -│|123456789|123456789|123456789|123456789 │ -│1 1 │ -│2 2 │ -│3 3 │ -│4 4 │ -│5 5 │ -│6 6 │ -│7 7 │ -│8 8 │ -│9 9 │ -│- - │ -│1 1 │ -│2 2 │ -│3 3 │ -│|123456789|123456789|123456789|123456789 │ -│ │ -│ │ -│ │ -└───────────────────────────────────────────┘", - output - ); - - t = new (1, 2, 3, 4); - r = new (2, 2, 40, 15); - top.SetNeedsDraw (); - AutoInitShutdownAttribute.RunIteration (); - t.Draw (r, ViewDiagnosticFlags.Ruler, "Test"); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌───────────────────────────────────────────┐ -│ │ -│ |123456789|123456789|123456789|123456789 │ -│ 1 1 │ -│ 2 2 │ -│ 3 3 │ -│ 4 4 │ -│ 5 5 │ -│ 6 6 │ -│ 7 7 │ -│ 8 8 │ -│ 9 9 │ -│ - - │ -│ 1 1 │ -│ 2 2 │ -│ 3 3 │ -│ |123456789|123456789|123456789|123456789 │ -│ │ -│ │ -└───────────────────────────────────────────┘", - output - ); - - t = new (-1, 1, 1, 1); - r = new (5, 5, 40, 15); - top.SetNeedsDraw (); - AutoInitShutdownAttribute.RunIteration (); - t.Draw (r, ViewDiagnosticFlags.Ruler, "Test"); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌───────────────────────────────────────────┐ -│ │ -│ │ -│ │ -│ │ -│ |123456789|123456789|123456789|123456789 -│ 1 -│ 2 -│ 3 -│ 4 -│ 5 -│ 6 -│ 7 -│ 8 -│ 9 -│ - -│ 1 -│ 2 -│ 3 -└────|123456789|123456789|123456789|123456789", - output - ); - top.Dispose (); - } -} diff --git a/Tests/UnitTests/DriverAssert.cs b/Tests/UnitTests/DriverAssert.cs index fe79a3fb3..d9e9e2184 100644 --- a/Tests/UnitTests/DriverAssert.cs +++ b/Tests/UnitTests/DriverAssert.cs @@ -1,4 +1,5 @@ -using System.Text; +#nullable enable +using System.Text; using System.Text.RegularExpressions; using Xunit.Abstractions; @@ -9,8 +10,9 @@ namespace UnitTests; /// internal partial class DriverAssert { - private const char SpaceChar = ' '; - private static readonly Rune SpaceRune = (Rune)SpaceChar; + private const char SPACE_CHAR = ' '; + private static readonly Rune _spaceRune = (Rune)SPACE_CHAR; + #pragma warning disable xUnit1013 // Public method should be marked as test /// /// Verifies are found at the locations specified by @@ -28,7 +30,7 @@ internal partial class DriverAssert public static void AssertDriverAttributesAre ( string expectedLook, ITestOutputHelper output, - IConsoleDriver driver = null, + IConsoleDriver? driver = null, params Attribute [] expectedAttributes ) { @@ -42,7 +44,7 @@ internal partial class DriverAssert expectedLook = expectedLook.Trim (); driver ??= Application.Driver; - Cell [,] contents = driver!.Contents; + Cell [,] contents = driver!.Contents!; var line = 0; @@ -107,7 +109,6 @@ internal partial class DriverAssert // // get the index of the actual attribute in `expectedAttributes` - // if (index == -1) // { // sb.Append ("x:x "); @@ -146,7 +147,7 @@ internal partial class DriverAssert public static void AssertDriverContentsAre ( string expectedLook, ITestOutputHelper output, - IConsoleDriver driver = null, + IConsoleDriver? driver = null, bool ignoreLeadingWhitespace = false ) { @@ -192,18 +193,19 @@ internal partial class DriverAssert public static Rectangle AssertDriverContentsWithFrameAre ( string expectedLook, ITestOutputHelper output, - IConsoleDriver driver = null + IConsoleDriver? driver = null ) { - List> lines = new (); + List> lines = []; var sb = new StringBuilder (); driver ??= Application.Driver; + int x = -1; int y = -1; int w = -1; int h = -1; - Cell [,] contents = driver.Contents; + Cell [,] contents = driver!.Contents!; for (var rowIndex = 0; rowIndex < driver.Rows; rowIndex++) { @@ -211,9 +213,9 @@ internal partial class DriverAssert for (var colIndex = 0; colIndex < driver.Cols; colIndex++) { - Rune runeAtCurrentLocation = contents [rowIndex, colIndex].Rune; + Rune runeAtCurrentLocation = contents! [rowIndex, colIndex].Rune; - if (runeAtCurrentLocation != SpaceRune) + if (runeAtCurrentLocation != _spaceRune) { if (x == -1) { @@ -222,7 +224,7 @@ internal partial class DriverAssert for (var i = 0; i < colIndex; i++) { - runes.InsertRange (i, [SpaceRune]); + runes.InsertRange (i, [_spaceRune]); } } @@ -330,7 +332,6 @@ internal partial class DriverAssert return new (x > -1 ? x : 0, y > -1 ? y : 0, w > -1 ? w : 0, h > -1 ? h : 0); } - /// /// Verifies the console used all the when rendering. If one or more of the /// expected colors are not used then the failure will output both the colors that were found to be used and which of @@ -338,17 +339,17 @@ internal partial class DriverAssert /// /// if null uses /// - internal static void AssertDriverUsedColors (IConsoleDriver driver = null, params Attribute [] expectedColors) + internal static void AssertDriverUsedColors (IConsoleDriver? driver = null, params Attribute [] expectedColors) { driver ??= Application.Driver; - Cell [,] contents = driver.Contents; + Cell [,] contents = driver?.Contents!; List toFind = expectedColors.ToList (); // Contents 3rd column is an Attribute HashSet colorsUsed = new (); - for (var r = 0; r < driver.Rows; r++) + for (var r = 0; r < driver!.Rows; r++) { for (var c = 0; c < driver.Cols; c++) { @@ -381,11 +382,9 @@ internal partial class DriverAssert throw new (sb.ToString ()); } - [GeneratedRegex ("^\\s+", RegexOptions.Multiline)] private static partial Regex LeadingWhitespaceRegEx (); - [GeneratedRegex ("\\s+$", RegexOptions.Multiline)] private static partial Regex TrailingWhiteSpaceRegEx (); } diff --git a/Tests/UnitTests/Input/EscSeqUtilsTests.cs b/Tests/UnitTests/Input/EscSeqUtilsTests.cs deleted file mode 100644 index 7a1731e1e..000000000 --- a/Tests/UnitTests/Input/EscSeqUtilsTests.cs +++ /dev/null @@ -1,1582 +0,0 @@ -using System.Text; -using UnitTests; - -// ReSharper disable HeuristicUnreachableCode - -namespace UnitTests.DriverTests; - -public class EscSeqUtilsTests -{ - private bool _actionStarted; - private MouseFlags _arg1; - private Point _arg2; - private string _c1Control, _code, _terminating; - private ConsoleKeyInfo [] _cki; - private bool _isKeyMouse; - private bool _isResponse; - private ConsoleKey _key; - private ConsoleModifiers _mod; - private List _mouseFlags; - private ConsoleKeyInfo _newConsoleKeyInfo; - private Point _pos; - private string [] _values; - - [Fact] - [AutoInitShutdown] - public void DecodeEscSeq_Multiple_Tests () - { - // ESC - _cki = new ConsoleKeyInfo [] { new ('\u001b', 0, false, false, false) }; - var expectedCki = new ConsoleKeyInfo ('\u001b', ConsoleKey.Escape, false, false, false); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - _cki = new ConsoleKeyInfo [] { new ('\u001b', 0, false, false, false), new ('\u0012', 0, false, false, false) }; - expectedCki = new ('\u0012', ConsoleKey.R, false, true, true); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - Assert.Equal (expectedCki, _newConsoleKeyInfo); - Assert.Equal (ConsoleKey.R, _key); - Assert.Equal (ConsoleModifiers.Alt | ConsoleModifiers.Control, _mod); - Assert.Equal ("ESC", _c1Control); - Assert.Null (_code); - Assert.Null (_values); - Assert.Equal ("\u0012", _terminating); - Assert.False (_isKeyMouse); - Assert.Equal (new () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - _cki = new ConsoleKeyInfo [] { new ('\u001b', 0, false, false, false), new ('r', 0, false, false, false) }; - expectedCki = new ('r', ConsoleKey.R, false, true, false); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - Assert.Equal (expectedCki, _newConsoleKeyInfo); - Assert.Equal (ConsoleKey.R, _key); - Assert.Equal (ConsoleModifiers.Alt, _mod); - Assert.Equal ("ESC", _c1Control); - Assert.Null (_code); - Assert.Null (_values); - Assert.Equal ("r", _terminating); - Assert.False (_isKeyMouse); - Assert.Equal (new () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - // SS3 - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), new ('O', 0, false, false, false), new ('R', 0, false, false, false) - }; - expectedCki = new ('\0', ConsoleKey.F3, false, false, false); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - // CSI - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('1', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new ('R', 0, false, false, false) - }; - expectedCki = new ('\0', ConsoleKey.F3, true, false, false); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('1', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('R', 0, false, false, false) - }; - expectedCki = new ('\0', ConsoleKey.F3, false, true, false); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('1', 0, false, false, false), - new (';', 0, false, false, false), - new ('4', 0, false, false, false), - new ('R', 0, false, false, false) - }; - expectedCki = new ('\0', ConsoleKey.F3, true, true, false); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('1', 0, false, false, false), - new (';', 0, false, false, false), - new ('5', 0, false, false, false), - new ('R', 0, false, false, false) - }; - expectedCki = new ('\0', ConsoleKey.F3, false, false, true); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('1', 0, false, false, false), - new (';', 0, false, false, false), - new ('6', 0, false, false, false), - new ('R', 0, false, false, false) - }; - expectedCki = new ('\0', ConsoleKey.F3, true, false, true); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('1', 0, false, false, false), - new (';', 0, false, false, false), - new ('7', 0, false, false, false), - new ('R', 0, false, false, false) - }; - expectedCki = new ('\0', ConsoleKey.F3, false, true, true); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('1', 0, false, false, false), - new (';', 0, false, false, false), - new ('8', 0, false, false, false), - new ('R', 0, false, false, false) - }; - expectedCki = new ('\0', ConsoleKey.F3, true, true, true); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('M', 0, false, false, false) - }; - expectedCki = default (ConsoleKeyInfo); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { MouseFlags.Button1Pressed }, _mouseFlags); - Assert.Equal (new (1, 2), _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('m', 0, false, false, false) - }; - expectedCki = default (ConsoleKeyInfo); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { MouseFlags.Button1Released, MouseFlags.Button1Clicked }, - _mouseFlags - ); - Assert.Equal (new (1, 2), _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('M', 0, false, false, false) - }; - expectedCki = default (ConsoleKeyInfo); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { MouseFlags.Button1DoubleClicked }, _mouseFlags); - Assert.Equal (new (1, 2), _pos); - Assert.False (_isResponse); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('M', 0, false, false, false) - }; - expectedCki = default (ConsoleKeyInfo); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { MouseFlags.Button1TripleClicked }, _mouseFlags); - Assert.Equal (new (1, 2), _pos); - Assert.False (_isResponse); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('M', 0, false, false, false) - }; - expectedCki = default (ConsoleKeyInfo); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { MouseFlags.Button1Pressed }, _mouseFlags); - Assert.Equal (new (1, 2), _pos); - Assert.False (_isResponse); - - Assert.Equal (MouseFlags.None, _arg1); - Assert.Equal (new (0, 0), _arg2); - - ClearAll (); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('m', 0, false, false, false) - }; - expectedCki = default (ConsoleKeyInfo); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { MouseFlags.Button1Released }, _mouseFlags); - Assert.Equal (new (1, 2), _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - - Assert.Empty (EscSeqRequests.Statuses); - EscSeqRequests.Clear (); - EscSeqRequests.Add ("t"); - - _cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('8', 0, false, false, false), - new (';', 0, false, false, false), - new ('1', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new ('0', 0, false, false, false), - new ('t', 0, false, false, false) - }; - expectedCki = default (ConsoleKeyInfo); - Assert.Single (EscSeqRequests.Statuses); - Assert.Equal ("t", EscSeqRequests.Statuses.ToArray () [^1].Terminator); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - 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 () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.True (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - } - - [Theory] - [InlineData ('A', ConsoleKey.A, true, true, false, "ESC", '\u001b', 'A')] - [InlineData ('a', ConsoleKey.A, false, true, false, "ESC", '\u001b', 'a')] - [InlineData ('\0', ConsoleKey.Spacebar, false, true, true, "ESC", '\u001b', '\0')] - [InlineData (' ', ConsoleKey.Spacebar, true, true, false, "ESC", '\u001b', ' ')] - [InlineData ('\n', ConsoleKey.Enter, false, true, true, "ESC", '\u001b', '\n')] - [InlineData ('\r', ConsoleKey.Enter, true, true, false, "ESC", '\u001b', '\r')] - public void DecodeEscSeq_More_Multiple_Tests ( - char keyChar, - ConsoleKey consoleKey, - bool shift, - bool alt, - bool control, - string c1Control, - params char [] kChars - ) - { - _cki = new ConsoleKeyInfo [kChars.Length]; - - for (var i = 0; i < kChars.Length; i++) - { - char kChar = kChars [i]; - _cki [i] = new (kChar, 0, false, false, false); - } - - var expectedCki = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - Assert.Equal (expectedCki, _newConsoleKeyInfo); - Assert.Equal (consoleKey, _key); - - ConsoleModifiers mods = new (); - - if (shift) - { - mods = ConsoleModifiers.Shift; - } - - if (alt) - { - mods |= ConsoleModifiers.Alt; - } - - if (control) - { - mods |= ConsoleModifiers.Control; - } - - Assert.Equal (mods, _mod); - Assert.Equal (c1Control, _c1Control); - Assert.Null (_code); - Assert.Null (_values); - Assert.Equal (keyChar.ToString (), _terminating); - Assert.False (_isKeyMouse); - Assert.Equal (new () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - } - - [Fact] - public void DecodeEscSeq_IncompleteCKInfos () - { - // This is simulated response from a CSI_ReportTerminalSizeInChars - _cki = - [ - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('8', 0, false, false, false), - new (';', 0, false, false, false), - new ('1', 0, false, false, false), - ]; - - ConsoleKeyInfo expectedCki = default; - - Assert.Null (EscSeqUtils.IncompleteCkInfos); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - Assert.Equal (expectedCki, _newConsoleKeyInfo); - Assert.Equal (ConsoleKey.None, _key); - Assert.Equal (ConsoleModifiers.None, _mod); - Assert.Equal ("CSI", _c1Control); - Assert.Null (_code); - Assert.Equal (2, _values.Length); - Assert.Equal ("", _terminating); - Assert.False (_isKeyMouse); - Assert.Equal ([0], _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - Assert.NotNull (EscSeqUtils.IncompleteCkInfos); - Assert.Equal (_cki, EscSeqUtils.IncompleteCkInfos); - Assert.Contains (EscSeqUtils.ToString (EscSeqUtils.IncompleteCkInfos), EscSeqUtils.ToString (_cki)); - - _cki = EscSeqUtils.InsertArray ( - EscSeqUtils.IncompleteCkInfos, - [ - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new ('0', 0, false, false, false), - new ('t', 0, false, false, false) - ]); - - expectedCki = default; - - // Add a request to avoid assert failure in the DecodeEscSeq method - EscSeqRequests.Add ("t"); - Assert.Single (EscSeqRequests.Statuses); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - Assert.Equal (expectedCki, _newConsoleKeyInfo); - Assert.Equal (ConsoleKey.None, _key); - - Assert.Equal (ConsoleModifiers.None, _mod); - Assert.Equal ("CSI", _c1Control); - Assert.Null (_code); - Assert.Equal (3, _values.Length); - Assert.Equal ("t", _terminating); - Assert.False (_isKeyMouse); - Assert.Equal ([0], _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (EscSeqRequests.HasResponse ("t")); - Assert.True (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - Assert.Null (EscSeqUtils.IncompleteCkInfos); - Assert.NotEqual (_cki, EscSeqUtils.IncompleteCkInfos); - - ClearAll (); - } - - [Theory] - [InlineData ('\u001B', ConsoleKey.Escape, false, false, false)] - [InlineData ('\r', ConsoleKey.Enter, false, false, false)] - [InlineData ('1', ConsoleKey.D1, false, false, false)] - [InlineData ('!', ConsoleKey.None, false, false, false)] - [InlineData ('a', ConsoleKey.A, false, false, false)] - [InlineData ('A', ConsoleKey.A, true, false, false)] - [InlineData ('\u0001', ConsoleKey.A, false, false, true)] - [InlineData ('\0', ConsoleKey.Spacebar, false, false, true)] - [InlineData ('\n', ConsoleKey.Enter, false, false, true)] - [InlineData ('\t', ConsoleKey.Tab, false, false, false)] - public void DecodeEscSeq_Single_Tests (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control) - { - _cki = [new (keyChar, 0, false, false, false)]; - var expectedCki = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control); - - EscSeqUtils.DecodeEscSeq ( - ref _newConsoleKeyInfo, - ref _key, - _cki, - ref _mod, - out _c1Control, - out _code, - out _values, - out _terminating, - out _isKeyMouse, - out _mouseFlags, - out _pos, - out _isResponse, - ProcessContinuousButtonPressed - ); - Assert.Empty (EscSeqRequests.Statuses); - Assert.Equal (expectedCki, _newConsoleKeyInfo); - Assert.Equal (consoleKey, _key); - - ConsoleModifiers mods = new (); - - if (shift) - { - mods = ConsoleModifiers.Shift; - } - - if (alt) - { - mods |= ConsoleModifiers.Alt; - } - - if (control) - { - mods |= ConsoleModifiers.Control; - } - - Assert.Equal (mods, _mod); - - if (keyChar == '\u001B') - { - Assert.Equal ("ESC", _c1Control); - } - else - { - Assert.Null (_c1Control); - } - - Assert.Null (_code); - Assert.Null (_values); - Assert.Null (_terminating); - Assert.False (_isKeyMouse); - Assert.Equal (new () { 0 }, _mouseFlags); - Assert.Equal (Point.Empty, _pos); - Assert.False (_isResponse); - Assert.Equal (0, (int)_arg1); - Assert.Equal (Point.Empty, _arg2); - - ClearAll (); - } - - [Fact] - public void Defaults_Values () - { - Assert.Equal ('\x1b', EscSeqUtils.KeyEsc); - Assert.Equal ("\x1b[", EscSeqUtils.CSI); - 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.CSI_EnableMouseEvents); - Assert.Equal ("\x1b[?1003l\x1b[?1015l\u001b[?1006l", EscSeqUtils.CSI_DisableMouseEvents); - } - - [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 GetConsoleInputKey_ConsoleKeyInfo () - { - var cki = new ConsoleKeyInfo ('r', 0, false, false, false); - var expectedCki = new ConsoleKeyInfo ('r', ConsoleKey.R, false, false, false); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('r', 0, true, false, false); - expectedCki = new ('r', ConsoleKey.R, true, false, false); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('r', 0, false, true, false); - expectedCki = new ('r', ConsoleKey.R, false, true, false); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('r', 0, false, false, true); - expectedCki = new ('r', ConsoleKey.R, false, false, true); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('r', 0, true, true, false); - expectedCki = new ('r', ConsoleKey.R, true, true, false); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('r', 0, false, true, true); - expectedCki = new ('r', ConsoleKey.R, false, true, true); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('r', 0, true, true, true); - expectedCki = new ('r', ConsoleKey.R, true, true, true); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('\u0012', 0, false, false, false); - expectedCki = new ('\u0012', ConsoleKey.R, false, false, true); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('\0', (ConsoleKey)64, false, false, true); - expectedCki = new ('\0', ConsoleKey.Spacebar, false, false, true); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('\r', 0, false, false, false); - expectedCki = new ('\r', ConsoleKey.Enter, false, false, false); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('\u007f', 0, false, false, false); - expectedCki = new ('\u007f', ConsoleKey.Backspace, false, false, false); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - - cki = new ('R', 0, false, false, false); - expectedCki = new ('R', ConsoleKey.R, true, false, false); - Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); - } - - [Fact] - public void GetConsoleKey_Tests () - { - ConsoleModifiers mod = 0; - char keyChar = '\0'; - Assert.Equal (ConsoleKey.UpArrow, EscSeqUtils.GetConsoleKey ('A', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.DownArrow, EscSeqUtils.GetConsoleKey ('B', "", ref mod, ref keyChar)); - Assert.Equal (_key = ConsoleKey.RightArrow, EscSeqUtils.GetConsoleKey ('C', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.LeftArrow, EscSeqUtils.GetConsoleKey ('D', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.End, EscSeqUtils.GetConsoleKey ('F', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.Home, EscSeqUtils.GetConsoleKey ('H', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F1, EscSeqUtils.GetConsoleKey ('P', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F2, EscSeqUtils.GetConsoleKey ('Q', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F3, EscSeqUtils.GetConsoleKey ('R', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F4, EscSeqUtils.GetConsoleKey ('S', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.Tab, EscSeqUtils.GetConsoleKey ('Z', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleModifiers.Shift, mod); - Assert.Equal (0, (int)EscSeqUtils.GetConsoleKey ('\0', "", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.Insert, EscSeqUtils.GetConsoleKey ('~', "2", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.Delete, EscSeqUtils.GetConsoleKey ('~', "3", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.PageUp, EscSeqUtils.GetConsoleKey ('~', "5", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.PageDown, EscSeqUtils.GetConsoleKey ('~', "6", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F5, EscSeqUtils.GetConsoleKey ('~', "15", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F6, EscSeqUtils.GetConsoleKey ('~', "17", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F7, EscSeqUtils.GetConsoleKey ('~', "18", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F8, EscSeqUtils.GetConsoleKey ('~', "19", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F9, EscSeqUtils.GetConsoleKey ('~', "20", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F10, EscSeqUtils.GetConsoleKey ('~', "21", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F11, EscSeqUtils.GetConsoleKey ('~', "23", ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.F12, EscSeqUtils.GetConsoleKey ('~', "24", ref mod, ref keyChar)); - Assert.Equal (0, (int)EscSeqUtils.GetConsoleKey ('~', "", ref mod, ref keyChar)); - // These terminators are used by macOS on a numeric keypad without keys modifiers - Assert.Equal (ConsoleKey.Add, EscSeqUtils.GetConsoleKey ('l', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.Subtract, EscSeqUtils.GetConsoleKey ('m', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.Insert, EscSeqUtils.GetConsoleKey ('p', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.End, EscSeqUtils.GetConsoleKey ('q', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.DownArrow, EscSeqUtils.GetConsoleKey ('r', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.PageDown, EscSeqUtils.GetConsoleKey ('s', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.LeftArrow, EscSeqUtils.GetConsoleKey ('t', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.Clear, EscSeqUtils.GetConsoleKey ('u', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.RightArrow, EscSeqUtils.GetConsoleKey ('v', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.Home, EscSeqUtils.GetConsoleKey ('w', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.UpArrow, EscSeqUtils.GetConsoleKey ('x', null, ref mod, ref keyChar)); - Assert.Equal (ConsoleKey.PageUp, EscSeqUtils.GetConsoleKey ('y', null, ref mod, ref keyChar)); - } - - [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 GetEscapeResult_Multiple_Tests () - { - char [] kChars = ['\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); - - ClearAll (); - } - - [Theory] - [InlineData ('\u001B')] - [InlineData (['\r'])] - [InlineData (['1'])] - [InlineData (['!'])] - [InlineData (['a'])] - [InlineData (['A'])] - public void GetEscapeResult_Single_Tests (params char [] kChars) - { - (_c1Control, _code, _values, _terminating) = EscSeqUtils.GetEscapeResult (kChars); - - if (kChars [0] == '\u001B') - { - Assert.Equal ("ESC", _c1Control); - } - else - { - Assert.Null (_c1Control); - } - - Assert.Null (_code); - Assert.Null (_values); - Assert.Null (_terminating); - - ClearAll (); - } - - [Fact] - public void GetKeyCharArray_Tests () - { - ConsoleKeyInfo [] cki = - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('5', 0, false, false, false), - new (';', 0, false, false, false), - new ('1', 0, false, false, false), - new ('0', 0, false, false, false), - new ('r', 0, false, false, false) - }; - - Assert.Equal (new [] { '\u001b', '[', '5', ';', '1', '0', 'r' }, EscSeqUtils.GetKeyCharArray (cki)); - } - - [Fact] - [AutoInitShutdown] - public void GetMouse_Tests () - { - ConsoleKeyInfo [] cki = - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('M', 0, false, false, false) - }; - EscSeqUtils.GetMouse (cki, out List mouseFlags, out Point pos, ProcessContinuousButtonPressed); - Assert.Equal (new () { MouseFlags.Button1Pressed }, mouseFlags); - Assert.Equal (new (1, 2), pos); - - cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('m', 0, false, false, false) - }; - EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed); - Assert.Equal (2, mouseFlags.Count); - - Assert.Equal ( - new () { MouseFlags.Button1Released, MouseFlags.Button1Clicked }, - mouseFlags - ); - Assert.Equal (new (1, 2), pos); - - cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('M', 0, false, false, false) - }; - EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed); - Assert.Equal (new () { MouseFlags.Button1DoubleClicked }, mouseFlags); - Assert.Equal (new (1, 2), pos); - - cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('M', 0, false, false, false) - }; - EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed); - Assert.Equal (new () { MouseFlags.Button1TripleClicked }, mouseFlags); - Assert.Equal (new (1, 2), pos); - - cki = new ConsoleKeyInfo [] - { - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('<', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new (';', 0, false, false, false), - new ('3', 0, false, false, false), - new ('m', 0, false, false, false) - }; - EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed); - Assert.Equal (new () { MouseFlags.Button1Released }, mouseFlags); - Assert.Equal (new (1, 2), pos); - } - - [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]); - } - - [Theory] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC\b", "\b")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC\t", "\t")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC\n", "\n")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC\r", "\r")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOCe", "e")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOCV", "V")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC\u007f", "\u007f")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC ", " ")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC\\", "\\")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC|", "|")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC1", "1")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC!", "!")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC\"", "\"")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC@", "@")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC#", "#")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC£", "£")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC$", "$")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC§", "§")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC%", "%")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC€", "€")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC&", "&")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC/", "/")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC{", "{")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC(", "(")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC[", "[")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC)", ")")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC]", "]")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC=", "=")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC}", "}")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC'", "'")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC?", "?")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC«", "«")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC»", "»")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC+", "+")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC*", "*")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC¨", "¨")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC´", "´")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC`", "`")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOCç", "ç")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOCº", "º")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOCª", "ª")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC~", "~")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC^", "^")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC<", "<")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC>", ">")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC,", ",")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC;", ";")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC.", ".")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC:", ":")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC-", "-")] - [InlineData ("\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC_", "_")] - public void SplitEscapeRawString_Multiple_Tests (string rawData, string expectedLast) - { - List splitList = EscSeqUtils.SplitEscapeRawString (rawData); - Assert.Equal (18, splitList.Count); - Assert.Equal ("\r", splitList [0]); - Assert.Equal ("\u001b[<35;50;1m", splitList [1]); - Assert.Equal ("\u001b[<35;49;1m", splitList [2]); - Assert.Equal ("\u001b[<35;47;1m", splitList [3]); - Assert.Equal ("\u001b[<35;46;1m", splitList [4]); - Assert.Equal ("\u001b[<35;45;2m", splitList [5]); - Assert.Equal ("\u001b[<35;44;2m", splitList [6]); - Assert.Equal ("\u001b[<35;43;3m", splitList [7]); - Assert.Equal ("\u001b[<35;42;3m", splitList [8]); - Assert.Equal ("\u001b[<35;41;4m", splitList [9]); - Assert.Equal ("\u001b[<35;40;5m", splitList [10]); - Assert.Equal ("\u001b[<35;39;6m", splitList [11]); - Assert.Equal ("\u001b[<35;49;1m", splitList [12]); - Assert.Equal ("\u001b[<35;48;2m", splitList [13]); - Assert.Equal ("\u001b[<0;33;6M", splitList [14]); - Assert.Equal ("\u001b[<0;33;6m", splitList [15]); - Assert.Equal ("\u001bOC", splitList [16]); - Assert.Equal (expectedLast, splitList [^1]); - } - - [Theory] - [InlineData ("[<35;50;1m")] - [InlineData ("\r")] - [InlineData ("1")] - [InlineData ("!")] - [InlineData ("a")] - [InlineData ("A")] - public void SplitEscapeRawString_Single_Tests (string rawData) - { - List splitList = EscSeqUtils.SplitEscapeRawString (rawData); - Assert.Single (splitList); - Assert.Equal (rawData, splitList [0]); - } - - [Theory] - [InlineData (null, null, null, null)] - [InlineData ("\u001b[8;1", null, null, "\u001b[8;1")] - [InlineData (null, "\u001b[8;1", 5, "\u001b[8;1")] - [InlineData ("\u001b[8;1", null, 5, "\u001b[8;1")] - [InlineData ("\u001b[8;1", "0;20t", -1, "\u001b[8;10;20t")] - [InlineData ("\u001b[8;1", "0;20t", 0, "\u001b[8;10;20t")] - [InlineData ("0;20t", "\u001b[8;1", 5, "\u001b[8;10;20t")] - [InlineData ("0;20t", "\u001b[8;1", 3, "\u001b[80;20t;1")] - public void InsertArray_Tests (string toInsert, string current, int? index, string expected) - { - ConsoleKeyInfo [] toIns = EscSeqUtils.ToConsoleKeyInfoArray (toInsert); - ConsoleKeyInfo [] cki = EscSeqUtils.ToConsoleKeyInfoArray (current); - ConsoleKeyInfo [] result = EscSeqUtils.ToConsoleKeyInfoArray (expected); - - if (index is null) - { - cki = EscSeqUtils.InsertArray (toIns, cki); - } - else - { - cki = EscSeqUtils.InsertArray (toIns, cki, (int)index); - } - - Assert.Equal (result, cki); - } - - [Theory] - [InlineData (0, 0, $"{EscSeqUtils.CSI}0;0H")] - [InlineData (int.MaxValue, int.MaxValue, $"{EscSeqUtils.CSI}2147483647;2147483647H")] - [InlineData (int.MinValue, int.MinValue, $"{EscSeqUtils.CSI}-2147483648;-2147483648H")] - public void CSI_WriteCursorPosition_ReturnsCorrectEscSeq (int row, int col, string expected) - { - StringBuilder builder = new(); - using StringWriter writer = new(builder); - - EscSeqUtils.CSI_WriteCursorPosition (writer, row, col); - - string actual = builder.ToString(); - Assert.Equal (expected, actual); - } - - [Theory] - [InlineData ('\u001B', KeyCode.Esc)] - [InlineData ('\r', KeyCode.Enter)] - [InlineData ('1', KeyCode.D1)] - [InlineData ('!', (KeyCode)'!')] - [InlineData ('a', KeyCode.A)] - [InlineData ('A', KeyCode.A | KeyCode.ShiftMask)] - public void MapChar_Returns_Modifiers_If_Needed (char ch, KeyCode keyCode) - { - ConsoleKeyInfo cki = EscSeqUtils.MapChar (ch); - Key key = EscSeqUtils.MapKey (cki); - Key expectedKey = keyCode; - - Assert.Equal (key, expectedKey); - } - - private void ClearAll () - { - EscSeqRequests.Clear (); - _newConsoleKeyInfo = default (ConsoleKeyInfo); - _key = default (ConsoleKey); - _cki = default (ConsoleKeyInfo []); - _mod = default (ConsoleModifiers); - _c1Control = default (string); - _code = default (string); - _terminating = default (string); - _values = default (string []); - _isKeyMouse = default (bool); - _isResponse = false; - _mouseFlags = default (List); - _pos = default (Point); - _arg1 = default (MouseFlags); - _arg2 = default (Point); - } - - private void ProcessContinuousButtonPressed (MouseFlags arg1, Point arg2) - { - _arg1 = arg1; - _arg2 = arg2; - _actionStarted = true; - } -} diff --git a/Tests/UnitTests/SetupFakeDriverAttribute.cs b/Tests/UnitTests/SetupFakeDriverAttribute.cs index 9d1cadeb4..c63ca267d 100644 --- a/Tests/UnitTests/SetupFakeDriverAttribute.cs +++ b/Tests/UnitTests/SetupFakeDriverAttribute.cs @@ -36,11 +36,10 @@ public class SetupFakeDriverAttribute : BeforeAfterTestAttribute Application.ResetState (true); Assert.Null (Application.Driver); - var ff = new FakeDriverFactory (); - var driver = ff.Create (); + var driver = new FakeDriver (); Application.Driver = driver; - driver.SetBufferSize (25, 25); + driver.SetScreenSize (25, 25); Assert.Equal (new (0, 0, 25, 25), Application.Screen); // Ensures subscribing events, at least for the SizeChanged event diff --git a/Tests/UnitTests/TestsAllViews.cs b/Tests/UnitTests/TestsAllViews.cs index 056a5050c..02bbc1c2f 100644 --- a/Tests/UnitTests/TestsAllViews.cs +++ b/Tests/UnitTests/TestsAllViews.cs @@ -1,6 +1,6 @@ #nullable enable -using System.Drawing; using System.Reflection; +using System.Drawing; namespace UnitTests; @@ -15,13 +15,12 @@ public class TestsAllViews public static IEnumerable AllViewTypes => typeof (View).Assembly .GetTypes () - .Where ( - type => type is { IsClass: true, IsAbstract: false, IsPublic: true } + .Where (type => type is { IsClass: true, IsAbstract: false, IsPublic: true } && (type.IsSubclassOf (typeof (View)) || type == typeof (View))) .Select (type => new object [] { type }); /// - /// Creates an instance of a view if it is not a generic type. + /// Creates an instance of a view if it is not a generic type. /// /// /// @@ -43,8 +42,7 @@ public class TestsAllViews public static List GetAllViewClasses () { return typeof (View).Assembly.GetTypes () - .Where ( - myType => myType is { IsClass: true, IsAbstract: false, IsPublic: true } + .Where (myType => myType is { IsClass: true, IsAbstract: false, IsPublic: true } && myType.IsSubclassOf (typeof (View)) ) .ToList (); @@ -83,6 +81,7 @@ public class TestsAllViews if (type.ContainsGenericParameters) { Logging.Warning ($"Cannot create an instance of {type} because it contains generic parameters."); + //throw new ArgumentException ($"Cannot create an instance of {type} because it contains generic parameters."); return null; } diff --git a/Tests/UnitTests/Text/AutocompleteTests.cs b/Tests/UnitTests/Text/AutocompleteTests.cs index deaf1d38f..eceb5cd33 100644 --- a/Tests/UnitTests/Text/AutocompleteTests.cs +++ b/Tests/UnitTests/Text/AutocompleteTests.cs @@ -1,5 +1,4 @@ using System.Text.RegularExpressions; -using UnitTests; using Xunit.Abstractions; namespace UnitTests.TextTests; @@ -10,7 +9,8 @@ public class AutocompleteTests (ITestOutputHelper output) [AutoInitShutdown] public void CursorLeft_CursorRight_Mouse_Button_Pressed_Does_Not_Show_Popup () { - AutoInitShutdownAttribute.FakeResize (new Size (50,5)); + Application.Driver?.SetScreenSize (50, 5); + var tv = new TextView { Width = 50, Height = 5, Text = "This a long line and against TextView." }; var g = (SingleWordSuggestionGenerator)tv.Autocomplete.SuggestionGenerator; @@ -279,6 +279,4 @@ This an long line and against TextView.", Assert.Equal (new (Color.Black), tv.Autocomplete.Scheme.Focus.Foreground); Assert.Equal (new (Color.Cyan), tv.Autocomplete.Scheme.Focus.Background); } - - } diff --git a/Tests/UnitTests/Text/TextFormatterTests.cs b/Tests/UnitTests/Text/TextFormatterTests.cs index 1f1e7819c..b4385ec60 100644 --- a/Tests/UnitTests/Text/TextFormatterTests.cs +++ b/Tests/UnitTests/Text/TextFormatterTests.cs @@ -3828,7 +3828,7 @@ ssb [SetupFakeDriver] public void FillRemaining_True_False () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (22, 5); + Application.Driver!.SetScreenSize (22, 5); Attribute [] attrs = { @@ -4050,7 +4050,7 @@ Nice Work")] Size tfSize = tf.FormatAndGetSize (); Assert.Equal (new (59, 13), tfSize); - ((IFakeConsoleDriver)Application.Driver).SetBufferSize (tfSize.Width, tfSize.Height); + Application.Driver!.SetScreenSize (tfSize.Width, tfSize.Height); Application.Driver.FillRect (Application.Screen, (Rune)'*'); tf.Draw (Application.Screen, Attribute.Default, Attribute.Default); diff --git a/Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs b/Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs index d1ac1cebe..52d5bc01e 100644 --- a/Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs +++ b/Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs @@ -19,9 +19,9 @@ public class AdornmentSubViewTests (ITestOutputHelper output) Width = 10, Height = 10 }; - Application.Top.Margin.Thickness = new Thickness (viewMargin); + Application.Top.Margin!.Thickness = new Thickness (viewMargin); // Turn of TransparentMouse for the test - Application.Top.Margin.ViewportSettings = ViewportSettingsFlags.None; + Application.Top.Margin!.ViewportSettings = ViewportSettingsFlags.None; var subView = new View () { @@ -30,11 +30,11 @@ public class AdornmentSubViewTests (ITestOutputHelper output) Width = 5, Height = 5 }; - subView.Margin.Thickness = new Thickness (subViewMargin); + subView.Margin!.Thickness = new Thickness (subViewMargin); // Turn of TransparentMouse for the test - subView.Margin.ViewportSettings = ViewportSettingsFlags.None; + subView.Margin!.ViewportSettings = ViewportSettingsFlags.None; - Application.Top.Margin.Add (subView); + Application.Top.Margin!.Add (subView); Application.Top.Layout (); var foundView = View.GetViewsUnderLocation (new Point(0, 0), ViewportSettingsFlags.None).LastOrDefault (); diff --git a/Tests/UnitTests/View/Adornment/BorderTests.cs b/Tests/UnitTests/View/Adornment/BorderTests.cs index 3ada74ef6..65820dcfe 100644 --- a/Tests/UnitTests/View/Adornment/BorderTests.cs +++ b/Tests/UnitTests/View/Adornment/BorderTests.cs @@ -1,5 +1,4 @@ -using UnitTests; -using Xunit.Abstractions; +using Xunit.Abstractions; namespace UnitTests.ViewTests; @@ -17,17 +16,18 @@ public class BorderTests (ITestOutputHelper output) superView.Add (view); view.Border!.Thickness = new (0, 1, 0, 0); - view.Border.LineStyle = LineStyle.Single; + view.Border!.LineStyle = LineStyle.Single; - view.SetScheme (new () - { - Normal = new (Color.Red, Color.Green), - Focus = new (Color.Green, Color.Red) - }); + view.SetScheme ( + new () + { + Normal = new (Color.Red, Color.Green), + Focus = new (Color.Green, Color.Red) + }); Assert.NotEqual (view.GetScheme ().Normal.Foreground, view.GetScheme ().Focus.Foreground); - Assert.Equal (ColorName16.Red, view.Border.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); - Assert.Equal (ColorName16.Green, view.Border.GetAttributeForRole (VisualRole.Focus).Foreground.GetClosestNamedColor16 ()); - Assert.Equal (view.GetAttributeForRole (VisualRole.Focus), view.Border.GetAttributeForRole (VisualRole.Focus)); + Assert.Equal (ColorName16.Red, view.Border!.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); + Assert.Equal (ColorName16.Green, view.Border!.GetAttributeForRole (VisualRole.Focus).Foreground.GetClosestNamedColor16 ()); + Assert.Equal (view.GetAttributeForRole (VisualRole.Focus), view.Border!.GetAttributeForRole (VisualRole.Focus)); superView.BeginInit (); superView.EndInit (); @@ -41,9 +41,9 @@ public class BorderTests (ITestOutputHelper output) view.SetFocus (); View.SetClipToScreen (); view.Draw (); - Assert.Equal (view.GetAttributeForRole (VisualRole.Focus), view.Border.GetAttributeForRole (VisualRole.Focus)); - Assert.Equal (view.GetScheme ().Focus.Foreground, view.Border.GetAttributeForRole (VisualRole.Focus).Foreground); - Assert.Equal (view.GetScheme ().Normal.Foreground, view.Border.GetAttributeForRole (VisualRole.Normal).Foreground); + Assert.Equal (view.GetAttributeForRole (VisualRole.Focus), view.Border!.GetAttributeForRole (VisualRole.Focus)); + Assert.Equal (view.GetScheme ().Focus.Foreground, view.Border!.GetAttributeForRole (VisualRole.Focus).Foreground); + Assert.Equal (view.GetScheme ().Normal.Foreground, view.Border!.GetAttributeForRole (VisualRole.Normal).Foreground); DriverAssert.AssertDriverAttributesAre ("00100", output, null, view.GetScheme ().Normal, view.GetAttributeForRole (VisualRole.Focus)); } @@ -52,17 +52,18 @@ public class BorderTests (ITestOutputHelper output) public void Border_Uses_Parent_Scheme () { var view = new View { Title = "A", Height = 2, Width = 5 }; - view.Border.Thickness = new (0, 1, 0, 0); - view.Border.LineStyle = LineStyle.Single; + view.Border!.Thickness = new (0, 1, 0, 0); + view.Border!.LineStyle = LineStyle.Single; - view.SetScheme (new () - { - Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red) - }); - Assert.Equal (ColorName16.Red, view.Border.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); - Assert.Equal (ColorName16.Green, view.Border.GetAttributeForRole (VisualRole.Focus).Foreground.GetClosestNamedColor16 ()); - Assert.Equal (view.GetAttributeForRole (VisualRole.Normal), view.Border.GetAttributeForRole (VisualRole.Normal)); - Assert.Equal (view.GetAttributeForRole (VisualRole.Focus), view.Border.GetAttributeForRole (VisualRole.Focus)); + view.SetScheme ( + new () + { + Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red) + }); + Assert.Equal (ColorName16.Red, view.Border!.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); + Assert.Equal (ColorName16.Green, view.Border!.GetAttributeForRole (VisualRole.Focus).Foreground.GetClosestNamedColor16 ()); + Assert.Equal (view.GetAttributeForRole (VisualRole.Normal), view.Border!.GetAttributeForRole (VisualRole.Normal)); + Assert.Equal (view.GetAttributeForRole (VisualRole.Focus), view.Border!.GetAttributeForRole (VisualRole.Focus)); view.BeginInit (); view.EndInit (); @@ -92,12 +93,11 @@ public class BorderTests (ITestOutputHelper output) { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double }; - win.Border.Thickness = win.Border.Thickness with { Top = 4 }; + win.Border!.Thickness = win.Border!.Thickness with { Top = 4 }; RunState rs = Application.Begin (win); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(width, 5)); + Application.Driver!.SetScreenSize (width, 5); AutoInitShutdownAttribute.RunIteration (); var expected = string.Empty; @@ -226,11 +226,11 @@ public class BorderTests (ITestOutputHelper output) { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double }; - win.Border.Thickness = win.Border.Thickness with { Top = 3 }; + win.Border!.Thickness = win.Border!.Thickness with { Top = 3 }; RunState rs = Application.Begin (win); - AutoInitShutdownAttribute.FakeResize(new Size(width, 4)); + Application.Driver!.SetScreenSize (width, 4); AutoInitShutdownAttribute.RunIteration (); var expected = string.Empty; @@ -359,12 +359,11 @@ public class BorderTests (ITestOutputHelper output) { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double }; - win.Border.Thickness = win.Border.Thickness with { Top = 2 }; + win.Border!.Thickness = win.Border!.Thickness with { Top = 2 }; RunState rs = Application.Begin (win); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(width, 4)); + Application.Driver!.SetScreenSize (width, 4); AutoInitShutdownAttribute.RunIteration (); var expected = string.Empty; @@ -485,9 +484,8 @@ public class BorderTests (ITestOutputHelper output) var win = new Window { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill () }; RunState rs = Application.Begin (win); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(20, height)); + Application.Driver!.SetScreenSize (20, height); AutoInitShutdownAttribute.RunIteration (); var expected = string.Empty; @@ -547,9 +545,8 @@ public class BorderTests (ITestOutputHelper output) var win = new Window { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill () }; RunState rs = Application.Begin (win); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(width, 3)); + Application.Driver!.SetScreenSize (width, 3); AutoInitShutdownAttribute.RunIteration (); var expected = string.Empty; @@ -727,13 +724,12 @@ public class BorderTests (ITestOutputHelper output) var top = new Toplevel (); top.BorderStyle = LineStyle.Double; - var frame = new FrameView { Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single}; + var frame = new FrameView { Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single }; top.Add (frame); RunState rs = Application.Begin (top); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(5, 5)); + Application.Driver!.SetScreenSize (5, 5); AutoInitShutdownAttribute.RunIteration (); var expected = @" @@ -759,9 +755,8 @@ public class BorderTests (ITestOutputHelper output) top.Add (frame); RunState rs = Application.Begin (top); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(10, 4)); + Application.Driver!.SetScreenSize (10, 4); AutoInitShutdownAttribute.RunIteration (); var expected = @" @@ -782,9 +777,8 @@ public class BorderTests (ITestOutputHelper output) var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () }; RunState rs = Application.Begin (win); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(3, 3)); + Application.Driver!.SetScreenSize (3, 3); AutoInitShutdownAttribute.RunIteration (); var expected = @" @@ -801,7 +795,7 @@ public class BorderTests (ITestOutputHelper output) { var view = new View (); Assert.Equal (LineStyle.None, view.BorderStyle); - Assert.Equal (Thickness.Empty, view.Border.Thickness); + Assert.Equal (Thickness.Empty, view.Border!.Thickness); view.Dispose (); } @@ -811,26 +805,30 @@ public class BorderTests (ITestOutputHelper output) var view = new View (); view.BorderStyle = LineStyle.Single; Assert.Equal (LineStyle.Single, view.BorderStyle); - Assert.Equal (new (1), view.Border.Thickness); + Assert.Equal (new (1), view.Border!.Thickness); view.BorderStyle = LineStyle.Double; Assert.Equal (LineStyle.Double, view.BorderStyle); - Assert.Equal (new (1), view.Border.Thickness); + Assert.Equal (new (1), view.Border!.Thickness); view.BorderStyle = LineStyle.None; Assert.Equal (LineStyle.None, view.BorderStyle); - Assert.Equal (Thickness.Empty, view.Border.Thickness); + Assert.Equal (Thickness.Empty, view.Border!.Thickness); view.Dispose (); } [Theory] - [InlineData (false, @" + [InlineData ( + false, + @" ┌───┐ │ ║ │ │═┌┄│ │ ┊ │ └───┘")] - [InlineData (true, @" + [InlineData ( + true, + @" ╔═╦─┐ ║ ║ │ ╠═╬┄┤ @@ -839,7 +837,7 @@ public class BorderTests (ITestOutputHelper output) [SetupFakeDriver] public void SuperViewRendersLineCanvas_No_SubViews_AutoJoinsLines (bool superViewRendersLineCanvas, string expected) { - View superView = new View () + var superView = new View { Id = "superView", Width = 5, @@ -847,7 +845,7 @@ public class BorderTests (ITestOutputHelper output) BorderStyle = LineStyle.Single }; - View view1 = new View () + var view1 = new View { Id = "view1", Width = 3, @@ -858,7 +856,7 @@ public class BorderTests (ITestOutputHelper output) SuperViewRendersLineCanvas = superViewRendersLineCanvas }; - View view2 = new View () + var view2 = new View { Id = "view2", Width = 3, @@ -878,9 +876,10 @@ public class BorderTests (ITestOutputHelper output) DriverAssert.AssertDriverContentsAre (expected, output); } - [Theory] - [InlineData (false, @" + [InlineData ( + false, + @" ┌┤A├──────┐ │ ║ │ │ ║ │ @@ -888,7 +887,9 @@ public class BorderTests (ITestOutputHelper output) │ ┊ │ │ ┊ │ └─────────┘")] - [InlineData (true, @" + [InlineData ( + true, + @" ╔╡A╞═╦────┐ ║ ║ │ ║ ║ │ @@ -899,17 +900,17 @@ public class BorderTests (ITestOutputHelper output) [SetupFakeDriver] public void SuperViewRendersLineCanvas_Title_AutoJoinsLines (bool superViewRendersLineCanvas, string expected) { - View superView = new View () + var superView = new View { Id = "superView", Title = "A", Width = 11, Height = 7, CanFocus = true, - BorderStyle = LineStyle.Single, + BorderStyle = LineStyle.Single }; - View view1 = new View () + var view1 = new View { Id = "view1", Title = "B", @@ -922,7 +923,7 @@ public class BorderTests (ITestOutputHelper output) SuperViewRendersLineCanvas = superViewRendersLineCanvas }; - View view2 = new View () + var view2 = new View { Id = "view2", Title = "C", diff --git a/Tests/UnitTests/View/Adornment/MarginTests.cs b/Tests/UnitTests/View/Adornment/MarginTests.cs index ff678a311..99c1a9159 100644 --- a/Tests/UnitTests/View/Adornment/MarginTests.cs +++ b/Tests/UnitTests/View/Adornment/MarginTests.cs @@ -9,7 +9,7 @@ public class MarginTests (ITestOutputHelper output) [SetupFakeDriver] public void Margin_Is_Transparent () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (5, 5); + Application.Driver!.SetScreenSize (5, 5); var view = new View { Height = 3, Width = 3 }; view.Margin!.Diagnostics = ViewDiagnosticFlags.Thickness; @@ -44,7 +44,7 @@ public class MarginTests (ITestOutputHelper output) [SetupFakeDriver] public void Margin_ViewPortSettings_Not_Transparent_Is_NotTransparent () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (5, 5); + Application.Driver!.SetScreenSize (5, 5); var view = new View { Height = 3, Width = 3 }; view.Margin!.Diagnostics = ViewDiagnosticFlags.Thickness; diff --git a/Tests/UnitTests/View/Adornment/PaddingTests.cs b/Tests/UnitTests/View/Adornment/PaddingTests.cs index d9a9a6141..b82a9e721 100644 --- a/Tests/UnitTests/View/Adornment/PaddingTests.cs +++ b/Tests/UnitTests/View/Adornment/PaddingTests.cs @@ -9,7 +9,7 @@ public class PaddingTests (ITestOutputHelper output) [SetupFakeDriver] public void Padding_Uses_Parent_Scheme () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (5, 5); + Application.Driver!.SetScreenSize (5, 5); var view = new View { Height = 3, Width = 3 }; view.Padding!.Thickness = new (1); view.Padding.Diagnostics = ViewDiagnosticFlags.Thickness; diff --git a/Tests/UnitTests/View/Adornment/ShadowStyleTests.cs b/Tests/UnitTests/View/Adornment/ShadowStyleTests.cs index b8ff317e6..cbd11f0a8 100644 --- a/Tests/UnitTests/View/Adornment/ShadowStyleTests.cs +++ b/Tests/UnitTests/View/Adornment/ShadowStyleTests.cs @@ -30,7 +30,7 @@ public class ShadowStyleTests (ITestOutputHelper output) [SetupFakeDriver] public void ShadowView_Colors (ShadowStyle style, string expectedAttrs) { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (5, 5); + Application.Driver!.SetScreenSize (5, 5); Color fg = Color.Red; Color bg = Color.Green; @@ -100,7 +100,7 @@ public class ShadowStyleTests (ITestOutputHelper output) [SetupFakeDriver] public void Visual_Test (ShadowStyle style, string expected) { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (5, 5); + Application.Driver!.SetScreenSize (5, 5); var superView = new Toplevel { diff --git a/Tests/UnitTests/View/Draw/ClearViewportTests.cs b/Tests/UnitTests/View/Draw/ClearViewportTests.cs index 4341fbdd6..5058f3df4 100644 --- a/Tests/UnitTests/View/Draw/ClearViewportTests.cs +++ b/Tests/UnitTests/View/Draw/ClearViewportTests.cs @@ -209,7 +209,7 @@ public class ClearViewportTests (ITestOutputHelper output) var top = new Toplevel (); top.Add (view); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(20, 10)); + Application.Driver!.SetScreenSize (20, 10); var expected = @" ┌──────────────────┐ @@ -274,7 +274,7 @@ public class ClearViewportTests (ITestOutputHelper output) var top = new Toplevel (); top.Add (view); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(20, 10)); + Application.Driver!.SetScreenSize (20, 10); var expected = @" ┌──────────────────┐ diff --git a/Tests/UnitTests/View/Draw/ClipTests.cs b/Tests/UnitTests/View/Draw/ClipTests.cs index 05f62a4d0..46397654c 100644 --- a/Tests/UnitTests/View/Draw/ClipTests.cs +++ b/Tests/UnitTests/View/Draw/ClipTests.cs @@ -171,7 +171,7 @@ public class ClipTests (ITestOutputHelper _output) [Trait ("Category", "Unicode")] public void Clipping_Wide_Runes () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (30, 1); + Application.Driver!.SetScreenSize (30, 1); var top = new View { @@ -190,7 +190,7 @@ public class ClipTests (ITestOutputHelper _output) """ }; frameView.Border!.LineStyle = LineStyle.Single; - frameView.Border.Thickness = new (1, 0, 0, 0); + frameView.Border!.Thickness = new (1, 0, 0, 0); top.Add (frameView); View.SetClipToScreen (); diff --git a/Tests/UnitTests/View/Draw/DrawTests.cs b/Tests/UnitTests/View/Draw/DrawTests.cs index 5c1843ce2..6ef42470c 100644 --- a/Tests/UnitTests/View/Draw/DrawTests.cs +++ b/Tests/UnitTests/View/Draw/DrawTests.cs @@ -32,7 +32,7 @@ public class DrawTests (ITestOutputHelper output) top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(10, 4)) ; + Application.Driver!.SetScreenSize (10, 4); const string expectedOutput = """ @@ -75,7 +75,7 @@ public class DrawTests (ITestOutputHelper output) top.Add (viewRight, viewBottom); var rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(7, 7)); + Application.Driver!.SetScreenSize (7, 7); AutoInitShutdownAttribute.RunIteration (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -616,7 +616,7 @@ public class DrawTests (ITestOutputHelper output) top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(10, 4)); + Application.Driver!.SetScreenSize (10, 4); var expected = """ diff --git a/Tests/UnitTests/View/Draw/NeedsDrawTests.cs b/Tests/UnitTests/View/Draw/NeedsDrawTests.cs index 8e2370fd1..7ec2ec4ce 100644 --- a/Tests/UnitTests/View/Draw/NeedsDrawTests.cs +++ b/Tests/UnitTests/View/Draw/NeedsDrawTests.cs @@ -39,7 +39,7 @@ public class NeedsDrawTests () RunState runState = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (80,25)); + Application.Driver!.SetScreenSize (80,25); top.SubViewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 80, 25), top.NeedsDrawRect); }; diff --git a/Tests/UnitTests/View/Layout/Dim.Tests.cs b/Tests/UnitTests/View/Layout/Dim.Tests.cs index b15f1175b..efe0bc333 100644 --- a/Tests/UnitTests/View/Layout/Dim.Tests.cs +++ b/Tests/UnitTests/View/Layout/Dim.Tests.cs @@ -127,7 +127,7 @@ public class DimTests Assert.Equal (49, f2.Frame.Width); // 50-1=49 Assert.Equal (5, f2.Frame.Height); - Assert.Equal ($"Combine(View(Width,FrameView(){f1.Border.Frame})-Absolute(2))", v1.Width.ToString ()); + Assert.Equal ($"Combine(View(Width,FrameView(){f1.Border!.Frame})-Absolute(2))", v1.Width.ToString ()); Assert.Equal ("Combine(Fill(Absolute(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 diff --git a/Tests/UnitTests/View/Layout/GetViewsUnderLocationTests.cs b/Tests/UnitTests/View/Layout/GetViewsUnderLocationTests.cs index 5fe8e91fd..ec1fcdd08 100644 --- a/Tests/UnitTests/View/Layout/GetViewsUnderLocationTests.cs +++ b/Tests/UnitTests/View/Layout/GetViewsUnderLocationTests.cs @@ -76,9 +76,9 @@ public class GetViewsUnderLocationTests Frame = new (frameX, frameY, 10, 10) }; Application.Top.Margin!.Thickness = new (marginThickness); - Application.Top.Margin.Id = "Margin"; + Application.Top.Margin!.Id = "Margin"; Application.Top.Border!.Thickness = new (borderThickness); - Application.Top.Border.Id = "Border"; + Application.Top.Border!.Id = "Border"; Application.Top.Padding!.Thickness = new (paddingThickness); Application.Top.Padding.Id = "Padding"; @@ -353,9 +353,9 @@ public class GetViewsUnderLocationTests Width = 10, Height = 10 }; Application.Top.Margin!.Thickness = new (1); - Application.Top.Margin.Id = "Margin"; + Application.Top.Margin!.Id = "Margin"; Application.Top.Border!.Thickness = new (1); - Application.Top.Border.Id = "Border"; + Application.Top.Border!.Id = "Border"; Application.Top.Padding!.Thickness = new (1); Application.Top.Padding.Id = "Padding"; @@ -401,7 +401,7 @@ public class GetViewsUnderLocationTests Width = 5, Height = 5 }; subview.Border!.Thickness = new (1); - subview.Border.Id = "border"; + subview.Border!.Id = "border"; Application.Top.Add (subview); List viewsUnderMouse = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse); @@ -438,8 +438,8 @@ public class GetViewsUnderLocationTests Width = 5, Height = 5 }; subview.Border!.Thickness = new (1); - subview.Border.ViewportSettings = ViewportSettingsFlags.TransparentMouse; - subview.Border.Id = "border"; + subview.Border!.ViewportSettings = ViewportSettingsFlags.TransparentMouse; + subview.Border!.Id = "border"; Application.Top.Add (subview); List viewsUnderMouse = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse); @@ -786,14 +786,14 @@ public class GetViewsUnderLocationTests Application.TopLevels.Push (secondaryToplevel); Application.Top = secondaryToplevel; - secondaryToplevel.Margin.ViewportSettings = ViewportSettingsFlags.None; + secondaryToplevel.Margin!.ViewportSettings = ViewportSettingsFlags.None; List found = View.GetViewsUnderLocation (new (5, 5), ViewportSettingsFlags.TransparentMouse); Assert.Contains (found, v => v == secondaryToplevel); Assert.Contains (found, v => v == secondaryToplevel.Margin); Assert.DoesNotContain (found, v => v?.Id == topToplevel.Id); - secondaryToplevel.Margin.ViewportSettings = ViewportSettingsFlags.TransparentMouse; + secondaryToplevel.Margin!.ViewportSettings = ViewportSettingsFlags.TransparentMouse; found = View.GetViewsUnderLocation (new (5, 5), ViewportSettingsFlags.TransparentMouse); Assert.DoesNotContain (found, v => v == secondaryToplevel); Assert.DoesNotContain (found, v => v == secondaryToplevel.Margin); diff --git a/Tests/UnitTests/View/Layout/Pos.AnchorEndTests.cs b/Tests/UnitTests/View/Layout/Pos.AnchorEndTests.cs index bf5d19d47..80579a2c7 100644 --- a/Tests/UnitTests/View/Layout/Pos.AnchorEndTests.cs +++ b/Tests/UnitTests/View/Layout/Pos.AnchorEndTests.cs @@ -26,7 +26,7 @@ public class PosAnchorEndTests () top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (80,25)); + Application.Driver!.SetScreenSize (80,25); Assert.Equal (new (0, 0, 80, 25), top.Frame); Assert.Equal (new (0, 0, 80, 25), win.Frame); diff --git a/Tests/UnitTests/View/Layout/Pos.CenterTests.cs b/Tests/UnitTests/View/Layout/Pos.CenterTests.cs index 2a4405a1d..0b094f262 100644 --- a/Tests/UnitTests/View/Layout/Pos.CenterTests.cs +++ b/Tests/UnitTests/View/Layout/Pos.CenterTests.cs @@ -33,9 +33,8 @@ public class PosCenterTests (ITestOutputHelper output) win.Add (subview); RunState rs = Application.Begin (win); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(20, height)); + Application.Driver!.SetScreenSize (20, height); AutoInitShutdownAttribute.RunIteration (); var expected = string.Empty; @@ -180,9 +179,8 @@ public class PosCenterTests (ITestOutputHelper output) win.Add (subview); RunState rs = Application.Begin (win); - var firstIteration = false; - AutoInitShutdownAttribute.FakeResize(new Size(width, 7)); + Application.Driver!.SetScreenSize (width, 7); AutoInitShutdownAttribute.RunIteration (); var expected = string.Empty; diff --git a/Tests/UnitTests/View/Layout/SetLayoutTests.cs b/Tests/UnitTests/View/Layout/SetLayoutTests.cs index d259837b3..ac3654e29 100644 --- a/Tests/UnitTests/View/Layout/SetLayoutTests.cs +++ b/Tests/UnitTests/View/Layout/SetLayoutTests.cs @@ -25,13 +25,13 @@ public class SetLayoutTests (ITestOutputHelper output) Application.Top.Add (view); var rs = Application.Begin (Application.Top); - AutoInitShutdownAttribute.FakeResize (new Size (80,25)); + Application.Driver!.SetScreenSize (80, 25); Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, Application.Screen.Width, Application.Screen.Height)); Assert.Equal (new (0, 0, Application.Screen.Width, Application.Screen.Height), Application.Top.Frame); Assert.Equal (new (0, 0, 80, 25), Application.Top.Frame); - AutoInitShutdownAttribute.FakeResize(new Size(20, 10)) ; + Application.Driver!.SetScreenSize (20, 10); Assert.Equal (new (0, 0, Application.Screen.Width, Application.Screen.Height), Application.Top.Frame); Assert.Equal (new (0, 0, 20, 10), Application.Top.Frame); diff --git a/Tests/UnitTests/View/Mouse/MouseTests.cs b/Tests/UnitTests/View/Mouse/MouseTests.cs index 8b9732b37..792cbb216 100644 --- a/Tests/UnitTests/View/Mouse/MouseTests.cs +++ b/Tests/UnitTests/View/Mouse/MouseTests.cs @@ -1,5 +1,4 @@ -using Moq; -using UnitTests; +using Timeout = Terminal.Gui.App.Timeout; namespace UnitTests.ViewMouseTests; @@ -35,8 +34,8 @@ public class MouseTests : TestsAllViews Height = 10, Arrangement = ViewArrangement.Movable }; - testView.Margin.Thickness = new (marginThickness); - testView.Border.Thickness = new (borderThickness); + testView.Margin!.Thickness = new (marginThickness); + testView.Border!.Thickness = new (borderThickness); testView.Padding.Thickness = new (paddingThickness); var top = new Toplevel (); @@ -187,7 +186,7 @@ public class MouseTests : TestsAllViews view.MouseHeldDown.MouseIsHeldDownTick += (_, _) => clickedCount++; // Mouse is currently not held down so should be no timers running - Assert.Empty(timed.Timeouts); + Assert.Empty (timed.Timeouts); // When mouse is held down me.Flags = pressed; @@ -196,14 +195,14 @@ public class MouseTests : TestsAllViews me.Handled = false; // A timer should begin - var t = Assert.Single (timed.Timeouts); + KeyValuePair t = Assert.Single (timed.Timeouts); // Invoke the timer t.Value.Callback.Invoke (); // Event should have been raised Assert.Equal (1, clickedCount); - Assert.NotEmpty(timed.Timeouts); + Assert.NotEmpty (timed.Timeouts); // When mouse is released me.Flags = released; @@ -253,7 +252,7 @@ public class MouseTests : TestsAllViews Assert.Equal (0, clickedCount); me.Handled = false; - Assert.NotEmpty(timed.Timeouts); + Assert.NotEmpty (timed.Timeouts); Assert.Single (timed.Timeouts).Value.Callback.Invoke (); me.Flags = pressed; @@ -308,7 +307,7 @@ public class MouseTests : TestsAllViews // Mouse is held down so timer should be ticking Assert.NotEmpty (timed.Timeouts); - Assert.Equal (clickedCount,0); + Assert.Equal (clickedCount, 0); // Don't wait, just force it to expire Assert.Single (timed.Timeouts).Value.Callback.Invoke (); @@ -571,7 +570,6 @@ public class MouseTests : TestsAllViews Application.ResetState (true); } - [Theory] [InlineData (0)] [InlineData (1)] @@ -634,6 +632,7 @@ public class MouseTests : TestsAllViews // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set Application.ResetState (true); } + private class MouseEventTestView : View { public int MouseEnterCount { get; private set; } diff --git a/Tests/UnitTests/View/TextTests.cs b/Tests/UnitTests/View/TextTests.cs index f8062c744..ab220c8f0 100644 --- a/Tests/UnitTests/View/TextTests.cs +++ b/Tests/UnitTests/View/TextTests.cs @@ -121,11 +121,11 @@ Y top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(15, 15)) ; + Application.Driver!.SetScreenSize (15, 15); Assert.Equal (new (0, 0, 15, 15), win.Frame); - Assert.Equal (new (0, 0, 15, 15), win.Margin.Frame); - Assert.Equal (new (0, 0, 15, 15), win.Border.Frame); + Assert.Equal (new (0, 0, 15, 15), win.Margin!.Frame); + Assert.Equal (new (0, 0, 15, 15), win.Border!.Frame); Assert.Equal (new (1, 1, 13, 13), win.Padding.Frame); Assert.Equal (TextDirection.LeftRight_TopBottom, view.TextDirection); @@ -386,7 +386,7 @@ Y var top = new Toplevel (); top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(4, 10)); + Application.Driver!.SetScreenSize (4, 10); Assert.Equal (5, text.Length); @@ -511,7 +511,7 @@ w "; var top = new Toplevel (); top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(20, 20)); + Application.Driver!.SetScreenSize (20, 20); Assert.Equal (new (0, 0, 11, 2), horizontalView.Frame); Assert.Equal (new (0, 3, 2, 11), verticalView.Frame); @@ -599,7 +599,7 @@ w "; var top = new Toplevel (); top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(22, 22)); + Application.Driver!.SetScreenSize (22, 22); Assert.Equal (new (text.GetColumns (), 1), horizontalView.TextFormatter.ConstrainToSize); Assert.Equal (new (2, 8), verticalView.TextFormatter.ConstrainToSize); @@ -783,7 +783,7 @@ w "; var top = new Toplevel (); top.Add (frame); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size(width + 2, 6)); + Application.Driver!.SetScreenSize (width + 2, 6); // frame.Width is width + border wide (20 + 2) and 6 high @@ -913,7 +913,7 @@ w "; var top = new Toplevel (); top.Add (frame); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(9, height + 2)); + Application.Driver!.SetScreenSize (9, height + 2); if (autoSize) { @@ -998,7 +998,7 @@ w "; [SetupFakeDriver] public void Narrow_Wide_Runes () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (32, 32); + Application.Driver!.SetScreenSize (32, 32); var top = new View { Width = 32, Height = 32 }; var text = $"First line{Environment.NewLine}Second line"; diff --git a/Tests/UnitTests/View/ViewTests.cs b/Tests/UnitTests/View/ViewTests.cs index c2b62059a..466a292d1 100644 --- a/Tests/UnitTests/View/ViewTests.cs +++ b/Tests/UnitTests/View/ViewTests.cs @@ -456,7 +456,7 @@ public class ViewTests AutoInitShutdownAttribute.RunIteration (); Assert.Equal ("Testing visibility.".Length, view.Frame.Width); Assert.True (view.Visible); - AutoInitShutdownAttribute.FakeResize(new Size(30, 5)); + Application.Driver!.SetScreenSize (30, 5); DriverAssert.AssertDriverContentsWithFrameAre ( @" diff --git a/Tests/UnitTests/Views/ButtonTests.cs b/Tests/UnitTests/Views/ButtonTests.cs index 002c1e7a3..6c958d538 100644 --- a/Tests/UnitTests/Views/ButtonTests.cs +++ b/Tests/UnitTests/Views/ButtonTests.cs @@ -1,3 +1,4 @@ +using TerminalGuiFluentTesting; using UnitTests; using Xunit.Abstractions; @@ -243,7 +244,7 @@ public class ButtonTests (ITestOutputHelper output) Assert.False (btn.IsInitialized); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(30, 5)); + Application.Driver?.SetScreenSize (30, 5); Assert.True (btn.IsInitialized); Assert.Equal ("Say Hello 你", btn.Text); diff --git a/Tests/UnitTests/Views/CheckBoxTests.cs b/Tests/UnitTests/Views/CheckBoxTests.cs index ff575a779..22166d6a9 100644 --- a/Tests/UnitTests/Views/CheckBoxTests.cs +++ b/Tests/UnitTests/Views/CheckBoxTests.cs @@ -96,7 +96,7 @@ public class CheckBoxTests (ITestOutputHelper output) top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (30, 5)); + Application.Driver?.SetScreenSize (30, 5); Assert.Equal (Alignment.Center, checkBox.TextAlignment); Assert.Equal (new (1, 1, 25, 1), checkBox.Frame); @@ -156,7 +156,7 @@ public class CheckBoxTests (ITestOutputHelper output) top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(30, 6)); + Application.Driver!.SetScreenSize (30, 6); Assert.Equal (Alignment.Fill, checkBox1.TextAlignment); Assert.Equal (new (1, 1, 25, 1), checkBox1.Frame); @@ -217,7 +217,7 @@ public class CheckBoxTests (ITestOutputHelper output) top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(30, 5)); + Application.Driver!.SetScreenSize (30, 5); Assert.Equal (Alignment.Start, checkBox.TextAlignment); Assert.Equal (new (1, 1, 25, 1), checkBox.Frame); @@ -268,7 +268,7 @@ public class CheckBoxTests (ITestOutputHelper output) top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(30, 5)); + Application.Driver!.SetScreenSize (30, 5); Assert.Equal (Alignment.End, checkBox.TextAlignment); Assert.Equal (new (1, 1, 25, 1), checkBox.Frame); diff --git a/Tests/UnitTests/Views/FrameViewTests.cs b/Tests/UnitTests/Views/FrameViewTests.cs index 5f6bda9c1..2b78d216b 100644 --- a/Tests/UnitTests/Views/FrameViewTests.cs +++ b/Tests/UnitTests/Views/FrameViewTests.cs @@ -13,12 +13,12 @@ public class FrameViewTests (ITestOutputHelper output) Assert.Equal (string.Empty, fv.Text); Assert.Equal (LineStyle.Rounded, fv.BorderStyle); - fv = new() { Title = "Test" }; + fv = new () { Title = "Test" }; Assert.Equal ("Test", fv.Title); Assert.Equal (string.Empty, fv.Text); Assert.Equal (LineStyle.Rounded, fv.BorderStyle); - fv = new() + fv = new () { X = 1, Y = 2, @@ -38,7 +38,7 @@ public class FrameViewTests (ITestOutputHelper output) [AutoInitShutdown] public void Draw_Defaults () { - AutoInitShutdownAttribute.FakeResize(new Size(10, 10)); + Application.Driver!.SetScreenSize (10, 10); var fv = new FrameView () { BorderStyle = LineStyle.Single }; Assert.Equal (string.Empty, fv.Title); Assert.Equal (string.Empty, fv.Text); diff --git a/Tests/UnitTests/Views/LabelTests.cs b/Tests/UnitTests/Views/LabelTests.cs index aacc98efd..9ea2daca2 100644 --- a/Tests/UnitTests/Views/LabelTests.cs +++ b/Tests/UnitTests/Views/LabelTests.cs @@ -18,7 +18,7 @@ public class LabelTests (ITestOutputHelper output) top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (30, 5)); + Application.Driver!.SetScreenSize (30, 5); AutoInitShutdownAttribute.RunIteration (); var expected = @" @@ -59,7 +59,7 @@ public class LabelTests (ITestOutputHelper output) top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (30, 5)); + Application.Driver!.SetScreenSize (30, 5); var expected = @" ┌────────────────────────────┐ @@ -245,7 +245,7 @@ e Assert.False (label.IsInitialized); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (30, 5)); + Application.Driver!.SetScreenSize (30, 5); Assert.True (label.IsInitialized); Assert.Equal ("Say Hello 你", label.Text); @@ -277,7 +277,7 @@ e Assert.False (label.IsInitialized); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (30, 5)); + Application.Driver!.SetScreenSize (30, 5); Assert.True (label.IsInitialized); Assert.Equal ("Say Hello 你", label.Text); @@ -326,8 +326,8 @@ e public void With_Top_Margin_Without_Top_Border () { var label = new Label { Text = "Test", /*Width = 6, Height = 3,*/ BorderStyle = LineStyle.Single }; - label.Margin.Thickness = new (0, 1, 0, 0); - label.Border.Thickness = new (1, 0, 1, 1); + label.Margin!.Thickness = new (0, 1, 0, 0); + label.Border!.Thickness = new (1, 0, 1, 1); var top = new Toplevel (); top.Add (label); Application.Begin (top); @@ -351,7 +351,7 @@ e public void Without_Top_Border () { var label = new Label { Text = "Test", /* Width = 6, Height = 3, */BorderStyle = LineStyle.Single }; - label.Border.Thickness = new (1, 0, 1, 1); + label.Border!.Thickness = new (1, 0, 1, 1); var top = new Toplevel (); top.Add (label); Application.Begin (top); @@ -709,7 +709,7 @@ e Toplevel top = new (); top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (40, 10)); + Application.Driver!.SetScreenSize (40, 10); Assert.Equal (29, label.Text.Length); Assert.Equal (new (0, 0, 40, 10), top.Frame); @@ -756,7 +756,7 @@ e Toplevel top = new (); top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (40, 10)); + Application.Driver!.SetScreenSize (40, 10); Assert.Equal (new (0, 0, 40, 10), top.Frame); Assert.Equal (new (0, 0, 40, 10), win.Frame); @@ -828,7 +828,7 @@ e { if (k.KeyCode == KeyCode.Enter) { - AutoInitShutdownAttribute.FakeResize (new Size (22, count + 4)); + Application.Driver!.SetScreenSize (22, count + 4); Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (_expecteds [count], output); Assert.Equal (new (0, 0, 22, count + 4), pos); @@ -892,7 +892,7 @@ e [SetupFakeDriver] public void Label_Height_Zero_Stays_Zero () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (10, 4); + Application.Driver!.SetScreenSize (10, 4); var text = "Label"; var label = new Label @@ -979,7 +979,7 @@ e { if (k.KeyCode == KeyCode.Enter) { - AutoInitShutdownAttribute.FakeResize (new Size (22, count + 4)); + Application.Driver!.SetScreenSize (22, count + 4); Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (_expecteds [count], output); Assert.Equal (new (0, 0, 22, count + 4), pos); @@ -1054,7 +1054,7 @@ e var top = new Toplevel (); top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (10, 4)); + Application.Driver!.SetScreenSize (10, 4); Assert.Equal (5, text.Length); Assert.Equal (new (0, 0, 5, 1), label.Frame); @@ -1113,7 +1113,7 @@ e var top = new Toplevel (); top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize (new Size (10, 4)); + Application.Driver!.SetScreenSize (10, 4); Assert.Equal (5, text.Length); Assert.Equal (new (0, 0, 5, 1), label.Frame); diff --git a/Tests/UnitTests/Views/ListViewTests.cs b/Tests/UnitTests/Views/ListViewTests.cs index 411538b46..2676dc1db 100644 --- a/Tests/UnitTests/Views/ListViewTests.cs +++ b/Tests/UnitTests/Views/ListViewTests.cs @@ -58,7 +58,7 @@ public class ListViewTests (ITestOutputHelper output) var top = new Toplevel (); top.Add (win); RunState rs = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(12, 12)); + Application.Driver!.SetScreenSize (12, 12); AutoInitShutdownAttribute.RunIteration (); Assert.Equal (-1, lv.SelectedItem); @@ -790,7 +790,7 @@ Item 6", Application.Begin (top); AutoInitShutdownAttribute.RunIteration (); - Assert.Equal (new (1), lv.Border.Thickness); + Assert.Equal (new (1), lv.Border!.Thickness); Assert.Equal (-1, lv.SelectedItem); Assert.Equal ("", lv.Text); diff --git a/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs b/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs index e81512510..c334d32f6 100644 --- a/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs +++ b/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs @@ -470,7 +470,7 @@ public class MenuBarv1Tests (ITestOutputHelper output) Window win = new (); top.Add (win); RunState rsTop = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(40, 15)) ; + Application.Driver!.SetScreenSize (40, 15); Assert.Equal (new (0, 0, 40, 15), win.Frame); @@ -656,7 +656,7 @@ public class MenuBarv1Tests (ITestOutputHelper output) Assert.Equal (items [i], menu.Menus [0].Title); } - AutoInitShutdownAttribute.FakeResize(new Size(20, 15)); + Application.Driver!.SetScreenSize (20, 15); menu.OpenMenu (); AutoInitShutdownAttribute.RunIteration (); @@ -689,7 +689,7 @@ public class MenuBarv1Tests (ITestOutputHelper output) [AutoInitShutdown] public void Draw_A_Menu_Over_A_Top_Dialog () { - ((FakeDriver)Application.Driver).SetBufferSize (40, 15); + Application.Driver!.SetScreenSize (40, 15); // Override CM Window.DefaultBorderStyle = LineStyle.Single; @@ -836,7 +836,7 @@ public class MenuBarv1Tests (ITestOutputHelper output) Assert.Equal (items [i], menu.Menus [0].Title); } - AutoInitShutdownAttribute.FakeResize(new Size(20, 15)); + Application.Driver!.SetScreenSize (20, 15); menu.OpenMenu (); AutoInitShutdownAttribute.RunIteration (); @@ -907,7 +907,7 @@ public class MenuBarv1Tests (ITestOutputHelper output) menu.CloseAllMenus (); menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height); - AutoInitShutdownAttribute.FakeResize(new Size(7, 5)); + Application.Driver!.SetScreenSize (7, 5); menu.OpenMenu (); AutoInitShutdownAttribute.RunIteration (); @@ -923,7 +923,7 @@ public class MenuBarv1Tests (ITestOutputHelper output) menu.CloseAllMenus (); menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height); - AutoInitShutdownAttribute.FakeResize(new Size(7, 3)); + Application.Driver!.SetScreenSize (7, 3); menu.OpenMenu (); AutoInitShutdownAttribute.RunIteration (); @@ -981,7 +981,7 @@ wo menu.CloseAllMenus (); menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height); - AutoInitShutdownAttribute.FakeResize(new Size(3, 2)); + Application.Driver!.SetScreenSize (3, 2); menu.OpenMenu (); AutoInitShutdownAttribute.RunIteration (); @@ -994,7 +994,7 @@ wo menu.CloseAllMenus (); menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height); - AutoInitShutdownAttribute.FakeResize(new Size(3, 1)); + Application.Driver!.SetScreenSize (3, 1); menu.OpenMenu (); AutoInitShutdownAttribute.RunIteration (); @@ -1628,7 +1628,7 @@ wo Toplevel top = new (); top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(40, 8)); + Application.Driver!.SetScreenSize (40, 8); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -1740,7 +1740,7 @@ wo Application.AddTimeout (TimeSpan.Zero, () => { - AutoInitShutdownAttribute.FakeResize(new Size (40, 8)); + Application.Driver!.SetScreenSize (40, 8); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -1854,7 +1854,7 @@ wo ] }; win.Add (menu); - AutoInitShutdownAttribute.FakeResize(new Size(40, 8)); + Application.Driver!.SetScreenSize (40, 8); RunState rs = Application.Begin (win); AutoInitShutdownAttribute.RunIteration (); @@ -1941,7 +1941,7 @@ wo [AutoInitShutdown] public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init_With_Run_T () { - AutoInitShutdownAttribute.FakeResize(new Size(40, 8)); + Application.Driver!.SetScreenSize (40, 8); Application.AddTimeout (TimeSpan.Zero, () => { @@ -2897,7 +2897,7 @@ Edit output ); - AutoInitShutdownAttribute.FakeResize(new Size(20, 15)); + Application.Driver!.SetScreenSize (20, 15); AutoInitShutdownAttribute.RunIteration (); diff --git a/Tests/UnitTests/Views/RadioGroupTests.cs b/Tests/UnitTests/Views/RadioGroupTests.cs index cf10e5bab..07f0adf6a 100644 --- a/Tests/UnitTests/Views/RadioGroupTests.cs +++ b/Tests/UnitTests/Views/RadioGroupTests.cs @@ -533,7 +533,7 @@ public class RadioGroupTests (ITestOutputHelper output) top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(30, 5)); + Application.Driver!.SetScreenSize (30, 5); Assert.Equal (Orientation.Vertical, rg.Orientation); Assert.Equal (2, rg.RadioLabels.Length); diff --git a/Tests/UnitTests/Views/TableViewTests.cs b/Tests/UnitTests/Views/TableViewTests.cs index 27d77328c..dccc58d46 100644 --- a/Tests/UnitTests/Views/TableViewTests.cs +++ b/Tests/UnitTests/Views/TableViewTests.cs @@ -2206,7 +2206,7 @@ public class TableViewTests (ITestOutputHelper output) [SetupFakeDriver] public void TestEnumerableDataSource_BasicTypes () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (100, 100); + Application.Driver!.SetScreenSize (100, 100); var tv = new TableView (); tv.SchemeName = "TopLevel"; tv.Viewport = new (0, 0, 50, 6); diff --git a/Tests/UnitTests/Views/TextFieldTests.cs b/Tests/UnitTests/Views/TextFieldTests.cs index 123201f49..d6cbae340 100644 --- a/Tests/UnitTests/Views/TextFieldTests.cs +++ b/Tests/UnitTests/Views/TextFieldTests.cs @@ -10,73 +10,6 @@ public class TextFieldTests (ITestOutputHelper output) { private static TextField _textField; - [Fact] - [SetupFakeDriver] - public void Accented_Letter_With_Three_Combining_Unicode_Chars () - { - var tf = new TextField { Width = 3, Text = "ắ" }; - tf.Layout (); - tf.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -ắ", - output - ); - - tf.Text = "\u1eaf"; - tf.Layout (); - tf.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -ắ", - output - ); - - tf.Text = "\u0103\u0301"; - tf.Layout (); - tf.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -ắ", - output - ); - - tf.Text = "\u0061\u0306\u0301"; - tf.Layout (); - tf.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -ắ", - output - ); - } - - [Fact] - [SetupFakeDriver] - public void Adjust_First () - { - var tf = new TextField { Width = Dim.Fill (), Text = "This is a test." }; - tf.SetRelativeLayout (new (20, 20)); - tf.Draw (); - - Assert.Equal ("This is a test. ", GetContents ()); - - string GetContents () - { - var item = ""; - - for (var i = 0; i < 16; i++) - { - item += Application.Driver?.Contents [0, i].Rune; - } - - return item; - } - } [Fact] [TextFieldTestsAutoInitShutdown] diff --git a/Tests/UnitTests/Views/TextViewTests.cs b/Tests/UnitTests/Views/TextViewTests.cs index 6d63b3b07..d09f7e8b2 100644 --- a/Tests/UnitTests/Views/TextViewTests.cs +++ b/Tests/UnitTests/Views/TextViewTests.cs @@ -5262,7 +5262,7 @@ TAB to jump between text field", var top = new Toplevel (); top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(15, 15)); + Application.Driver!.SetScreenSize (15, 15); AutoInitShutdownAttribute.RunIteration (); //this passes @@ -5340,7 +5340,7 @@ TAB to jump between text field", var top = new Toplevel (); top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size (15, 15)); + Application.Driver!.SetScreenSize (15, 15); AutoInitShutdownAttribute.RunIteration (); //this passes @@ -5453,7 +5453,7 @@ This is the second line. _output ); - AutoInitShutdownAttribute.FakeResize(new Size (6, 25)); + Application.Driver!.SetScreenSize (6, 25); tv.SetRelativeLayout (Application.Screen.Size); tv.Draw (); Assert.Equal (new (4, 2), tv.CursorPosition); diff --git a/Tests/UnitTests/Views/ToplevelTests.cs b/Tests/UnitTests/Views/ToplevelTests.cs index 3160fb314..7aba89b6e 100644 --- a/Tests/UnitTests/Views/ToplevelTests.cs +++ b/Tests/UnitTests/Views/ToplevelTests.cs @@ -1,6 +1,4 @@ -using UnitTests; - -namespace UnitTests.ViewsTests; +namespace UnitTests.ViewsTests; public class ToplevelTests { @@ -285,7 +283,7 @@ public class ToplevelTests if (iterations == 0) { - AutoInitShutdownAttribute.FakeResize(new Size(15, 7)); + Application.Driver?.SetScreenSize (15, 7); // Don't use MessageBox here; it's too complicated for this unit test; just use Window testWindow = new () @@ -405,7 +403,7 @@ public class ToplevelTests if (iterations == 0) { - AutoInitShutdownAttribute.FakeResize(new Size(30, 10)); + Application.Driver?.SetScreenSize (30, 10); } else if (iterations == 1) { @@ -507,10 +505,10 @@ public class ToplevelTests top.BeginInit (); top.EndInit (); - Exception exception = Record.Exception (() => ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (0, 10)); + Exception exception = Record.Exception (() => Application.Driver!.SetScreenSize (0, 10)); Assert.Null (exception); - exception = Record.Exception (() => ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (10, 0)); + exception = Record.Exception (() => Application.Driver!.SetScreenSize (10, 0)); Assert.Null (exception); } @@ -596,7 +594,8 @@ public class ToplevelTests Toplevel top = new (); var window = new Window { Width = 20, Height = 3, Arrangement = ViewArrangement.Movable }; RunState rsTop = Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(40, 10)); + Application.Driver?.SetScreenSize (40, 10); + RunState rsWindow = Application.Begin (window); AutoInitShutdownAttribute.RunIteration (); Assert.Equal (new (0, 0, 40, 10), top.Frame); @@ -619,7 +618,7 @@ public class ToplevelTests Assert.Equal (new (-11, -4, 20, 3), window.Frame); // Changes Top size to same size as Dialog more menu and scroll bar - AutoInitShutdownAttribute.FakeResize(new Size(20, 3)); + Application.Driver?.SetScreenSize (20, 3); Application.RaiseMouseEvent ( new () @@ -632,7 +631,7 @@ public class ToplevelTests Assert.Equal (new (-1, -1, 20, 3), window.Frame); // Changes Top size smaller than Dialog size - AutoInitShutdownAttribute.FakeResize(new Size(19, 2)); + Application.Driver?.SetScreenSize (19, 2); Application.RaiseMouseEvent ( new () @@ -699,7 +698,6 @@ public class ToplevelTests Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed }); - var firstIteration = false; AutoInitShutdownAttribute.RunIteration (); Assert.Equal (window.Border, Application.Mouse.MouseGrabView); @@ -711,7 +709,6 @@ public class ToplevelTests ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition }); - firstIteration = false; AutoInitShutdownAttribute.RunIteration (); Assert.Equal (window.Border, Application.Mouse.MouseGrabView); Assert.Equal (new (1, 1, 10, 3), window.Frame); diff --git a/Tests/UnitTests/Views/TreeTableSourceTests.cs b/Tests/UnitTests/Views/TreeTableSourceTests.cs index 6d3c972be..bd73e0452 100644 --- a/Tests/UnitTests/Views/TreeTableSourceTests.cs +++ b/Tests/UnitTests/Views/TreeTableSourceTests.cs @@ -30,7 +30,7 @@ public class TreeTableSourceTests : IDisposable [SetupFakeDriver] public void TestTreeTableSource_BasicExpanding_WithKeyboard () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (100, 100); + Application.Driver!.SetScreenSize (100, 100); TableView tv = GetTreeTable (out _); tv.Style.GetOrCreateColumnStyle (1).MinAcceptableWidth = 1; @@ -91,7 +91,7 @@ public class TreeTableSourceTests : IDisposable [SetupFakeDriver] public void TestTreeTableSource_BasicExpanding_WithMouse () { - ((IFakeConsoleDriver)Application.Driver!).SetBufferSize (100, 100); + Application.Driver!.SetScreenSize (100, 100); TableView tv = GetTreeTable (out _); diff --git a/Tests/UnitTests/Views/WindowTests.cs b/Tests/UnitTests/Views/WindowTests.cs index a53aa43aa..32d524549 100644 --- a/Tests/UnitTests/Views/WindowTests.cs +++ b/Tests/UnitTests/Views/WindowTests.cs @@ -51,7 +51,7 @@ public class WindowTests (ITestOutputHelper output) Toplevel top = new (); top.Add (win); Application.Begin (top); - AutoInitShutdownAttribute.FakeResize(new Size(20, 10)); + Application.Driver!.SetScreenSize (20, 10); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -68,7 +68,7 @@ public class WindowTests (ITestOutputHelper output) output ); - AutoInitShutdownAttribute.FakeResize(new Size(40, 20)); + Application.Driver!.SetScreenSize (40, 20); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -95,7 +95,7 @@ public class WindowTests (ITestOutputHelper output) output ); - AutoInitShutdownAttribute.FakeResize(new Size(20, 10)); + Application.Driver!.SetScreenSize (20, 10); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -168,8 +168,8 @@ public class WindowTests (ITestOutputHelper output) Assert.Equal (TextDirection.LeftRight_TopBottom, windowWithFrameRectEmpty.TextDirection); // Rect with values - using var windowWithFrame1234 = new Window ( ); - windowWithFrame1234.Frame = new (1, 2, 3, 4); + using var windowWithFrame1234 = new Window (); + windowWithFrame1234.Frame = new (1, 2, 3, 4); windowWithFrame1234.Title = "title"; Assert.Equal ("title", windowWithFrame1234.Title); Assert.NotNull (windowWithFrame1234); diff --git a/Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs b/Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs index 6cdc8d378..69b64f51a 100644 --- a/Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs +++ b/Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs @@ -1,3 +1,4 @@ +#nullable enable using Terminal.Gui.App; using Xunit.Abstractions; @@ -89,7 +90,7 @@ public class MouseInterfaceTests (ITestOutputHelper output) // Arrange MouseImpl mouse = new (); var eventFired = false; - MouseEventArgs capturedArgs = null; + MouseEventArgs? capturedArgs = null; mouse.MouseEvent += (sender, args) => { @@ -120,7 +121,7 @@ public class MouseInterfaceTests (ITestOutputHelper output) MouseImpl mouse = new (); var eventCount = 0; - void Handler (object sender, MouseEventArgs args) => eventCount++; + void Handler (object? sender, MouseEventArgs args) => eventCount++; mouse.MouseEvent += Handler; diff --git a/Tests/UnitTestsParallelizable/Input/EscSeqRequestsTests.cs b/Tests/UnitTestsParallelizable/ConsoleDrivers/EscSeqRequestsTests.cs similarity index 59% rename from Tests/UnitTestsParallelizable/Input/EscSeqRequestsTests.cs rename to Tests/UnitTestsParallelizable/ConsoleDrivers/EscSeqRequestsTests.cs index 0d86281a9..abb12e20c 100644 --- a/Tests/UnitTestsParallelizable/Input/EscSeqRequestsTests.cs +++ b/Tests/UnitTestsParallelizable/ConsoleDrivers/EscSeqRequestsTests.cs @@ -1,6 +1,8 @@ -namespace UnitTests_Parallelizable.DriverTests; +using UnitTests.Parallelizable; -public class EscSeqRequestsTests : UnitTests.Parallelizable.ParallelizableBase +namespace UnitTests_Parallelizable.DriverTests; + +public class EscSeqRequestsTests : ParallelizableBase { [Fact] public void Add_Tests () @@ -122,67 +124,4 @@ public class EscSeqRequestsTests : UnitTests.Parallelizable.ParallelizableBase EscSeqRequests.Clear (); } - - [Fact] - public void Requests_Responses_Tests () - { - // This is simulated response from a CSI_ReportTerminalSizeInChars - ConsoleKeyInfo [] cki = - [ - new ('\u001b', 0, false, false, false), - new ('[', 0, false, false, false), - new ('8', 0, false, false, false), - new (';', 0, false, false, false), - new ('1', 0, false, false, false), - new ('0', 0, false, false, false), - new (';', 0, false, false, false), - new ('2', 0, false, false, false), - new ('0', 0, false, false, false), - new ('t', 0, false, false, false) - ]; - ConsoleKeyInfo newConsoleKeyInfo = default; - ConsoleKey key = default; - ConsoleModifiers mod = default; - - Assert.Empty (EscSeqRequests.Statuses); - - EscSeqRequests.Add ("t"); - Assert.Single (EscSeqRequests.Statuses); - Assert.Equal ("t", EscSeqRequests.Statuses [^1].Terminator); - Assert.Equal (1, EscSeqRequests.Statuses [^1].NumRequests); - Assert.Equal (1, EscSeqRequests.Statuses [^1].NumOutstanding); - - EscSeqUtils.DecodeEscSeq ( - ref newConsoleKeyInfo, - ref key, - cki, - ref mod, - out string c1Control, - out string code, - out string [] values, - out string terminating, - out bool isKeyMouse, - out List mouseFlags, - out Point pos, - out bool isResponse, - null - ); - - Assert.Empty (EscSeqRequests.Statuses); - Assert.Equal (default, newConsoleKeyInfo); - Assert.Equal (default, key); - Assert.Equal (10, cki.Length); - Assert.Equal (default, mod); - Assert.Equal ("CSI", c1Control); - Assert.Null (code); - // ReSharper disable once HeuristicUnreachableCode - Assert.Equal (3, values.Length); - Assert.Equal ("8", values [0]); - Assert.Equal ("t", terminating); - Assert.False (isKeyMouse); - Assert.Single (mouseFlags); - Assert.Equal (default, mouseFlags [^1]); - Assert.Equal (Point.Empty, pos); - Assert.True (isResponse); - } } diff --git a/Tests/UnitTestsParallelizable/ConsoleDrivers/UrlHyperlinkerTests.cs b/Tests/UnitTestsParallelizable/ConsoleDrivers/UrlHyperlinkerTests.cs index 5d9e46b5c..b09eb5555 100644 --- a/Tests/UnitTestsParallelizable/ConsoleDrivers/UrlHyperlinkerTests.cs +++ b/Tests/UnitTestsParallelizable/ConsoleDrivers/UrlHyperlinkerTests.cs @@ -2,7 +2,7 @@ using System.Text; using Xunit.Abstractions; -namespace Terminal.Gui.DriverTests; +namespace UnitTests_Parallelizable.ConoleDriverTests; public class Osc8UrlLinkerTests (ITestOutputHelper output) { diff --git a/Tests/UnitTestsParallelizable/Drawing/AttributeTests.cs b/Tests/UnitTestsParallelizable/Drawing/AttributeTests.cs index 7dd6858ec..ac3d1d70c 100644 --- a/Tests/UnitTestsParallelizable/Drawing/AttributeTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/AttributeTests.cs @@ -217,24 +217,6 @@ public class AttributeTests Assert.Equal (attr1.GetHashCode (), attr2.GetHashCode ()); } - [Fact] - public void Implicit_Assign () - { - var driver = new FakeDriver (); - driver.Init (); - - var attr = new Attribute (); - - var value = 42; - var fg = new Color (); - fg = new (Color.Red); - - var bg = new Color (); - bg = new (Color.Blue); - - driver.End (); - } - [Fact] public void InequalityOperator_ShouldReturnFalseForEqualAttributes () { diff --git a/Tests/UnitTestsParallelizable/Drawing/LineCanvasTests.cs b/Tests/UnitTestsParallelizable/Drawing/LineCanvasTests.cs index 065037028..d86e03d13 100644 --- a/Tests/UnitTestsParallelizable/Drawing/LineCanvasTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/LineCanvasTests.cs @@ -1,13 +1,17 @@ +using System.Text; +using UnitTests; +using UnitTests.Parallelizable; +using Xunit.Abstractions; + namespace UnitTests_Parallelizable.DrawingTests; /// -/// Pure unit tests for that don't require Application.Driver or View context. -/// These tests focus on properties and behavior that don't depend on glyph rendering. -/// -/// Note: Tests that verify rendered output (ToString()) cannot be parallelized because LineCanvas -/// depends on Application.Driver for glyph resolution and configuration. Those tests remain in UnitTests. +/// Pure unit tests for that don't require Application.Driver or View context. +/// These tests focus on properties and behavior that don't depend on glyph rendering. +/// Note: Tests that verify rendered output (ToString()) cannot be parallelized because LineCanvas +/// depends on Application.Driver for glyph resolution and configuration. Those tests remain in UnitTests. /// -public class LineCanvasTests : UnitTests.Parallelizable.ParallelizableBase +public class LineCanvasTests (ITestOutputHelper output) : ParallelizableBase { #region Basic API Tests @@ -58,7 +62,7 @@ public class LineCanvasTests : UnitTests.Parallelizable.ParallelizableBase [Fact] public void Constructor_With_Lines_Creates_Canvas_With_Lines () { - var lines = new[] + StraightLine [] lines = new [] { new StraightLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single), new StraightLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single) @@ -188,12 +192,16 @@ public class LineCanvasTests : UnitTests.Parallelizable.ParallelizableBase public void Bounds_Complex_Box () { var canvas = new LineCanvas (); + // top canvas.AddLine (new (0, 0), 3, Orientation.Horizontal, LineStyle.Single); + // left canvas.AddLine (new (0, 0), 2, Orientation.Vertical, LineStyle.Single); + // right canvas.AddLine (new (2, 0), 2, Orientation.Vertical, LineStyle.Single); + // bottom canvas.AddLine (new (0, 2), 3, Orientation.Horizontal, LineStyle.Single); @@ -210,12 +218,12 @@ public class LineCanvasTests : UnitTests.Parallelizable.ParallelizableBase var canvas = new LineCanvas (); canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single); - var region = new Region (new Rectangle (0, 0, 2, 1)); + var region = new Region (new (0, 0, 2, 1)); canvas.Exclude (region); canvas.ClearExclusions (); // After clearing exclusions, GetMap should return all points - var map = canvas.GetMap (); + Dictionary map = canvas.GetMap (); Assert.Equal (5, map.Count); } @@ -225,10 +233,11 @@ public class LineCanvasTests : UnitTests.Parallelizable.ParallelizableBase var canvas = new LineCanvas (); canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single); - var region = new Region (new Rectangle (0, 0, 2, 1)); + var region = new Region (new (0, 0, 2, 1)); canvas.Exclude (region); - var map = canvas.GetMap (); + Dictionary map = canvas.GetMap (); + // Should have 5 - 2 = 3 points (excluding the first 2) Assert.Equal (3, map.Count); } @@ -240,8 +249,8 @@ public class LineCanvasTests : UnitTests.Parallelizable.ParallelizableBase [Fact] public void Fill_Property_Can_Be_Set () { - var foregroundFill = new SolidFill (new Color (255, 0)); - var backgroundFill = new SolidFill (new Color (0, 0)); + var foregroundFill = new SolidFill (new (255, 0)); + var backgroundFill = new SolidFill (new (0, 0)); var fillPair = new FillPair (foregroundFill, backgroundFill); var canvas = new LineCanvas { Fill = fillPair }; @@ -257,4 +266,1246 @@ public class LineCanvasTests : UnitTests.Parallelizable.ParallelizableBase } #endregion + + [Theory] + + // Horizontal lines with a vertical zero-length + [InlineData ( + 0, + 0, + 1, + Orientation.Horizontal, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Single, + "╞" + )] + [InlineData ( + 0, + 0, + -1, + Orientation.Horizontal, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Single, + "╡" + )] + [InlineData ( + 0, + 0, + 1, + Orientation.Horizontal, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Double, + "╟" + )] + [InlineData ( + 0, + 0, + -1, + Orientation.Horizontal, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Double, + "╢" + )] + [InlineData ( + 0, + 0, + 1, + Orientation.Horizontal, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Single, + "├" + )] + [InlineData ( + 0, + 0, + -1, + Orientation.Horizontal, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Single, + "┤" + )] + [InlineData ( + 0, + 0, + 1, + Orientation.Horizontal, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Double, + "╠" + )] + [InlineData ( + 0, + 0, + -1, + Orientation.Horizontal, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Double, + "╣" + )] + + // Vertical lines with a horizontal zero-length + [InlineData ( + 0, + 0, + 1, + Orientation.Vertical, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Single, + "╥" + )] + [InlineData ( + 0, + 0, + -1, + Orientation.Vertical, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Single, + "╨" + )] + [InlineData ( + 0, + 0, + 1, + Orientation.Vertical, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Double, + "╤" + )] + [InlineData ( + 0, + 0, + -1, + Orientation.Vertical, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Double, + "╧" + )] + [InlineData ( + 0, + 0, + 1, + Orientation.Vertical, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Single, + "┬" + )] + [InlineData ( + 0, + 0, + -1, + Orientation.Vertical, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Single, + "┴" + )] + [InlineData ( + 0, + 0, + 1, + Orientation.Vertical, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Double, + "╦" + )] + [InlineData ( + 0, + 0, + -1, + Orientation.Vertical, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Double, + "╩" + )] + + // Crosses (two zero-length) + [InlineData ( + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Single, + "╫" + )] + [InlineData ( + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Double, + "╪" + )] + [InlineData ( + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Single, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Single, + "┼" + )] + [InlineData ( + 0, + 0, + 0, + Orientation.Vertical, + LineStyle.Double, + 0, + 0, + 0, + Orientation.Horizontal, + LineStyle.Double, + "╬" + )] + public void Add_2_Lines ( + int x1, + int y1, + int len1, + Orientation o1, + LineStyle s1, + int x2, + int y2, + int len2, + Orientation o2, + LineStyle s2, + string expected + ) + { + IConsoleDriver driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas lc); + v.Width = 10; + v.Height = 10; + v.Viewport = new (0, 0, 10, 10); + + lc.AddLine (new (x1, y1), len1, o1, s1); + lc.AddLine (new (x2, y2), len2, o2, s2); + + OutputAssert.AssertEqual (output, expected, lc.ToString ()); + v.Dispose (); + } + + + [Fact] + public void Viewport_Specific () + { + // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1) + // This proves we aren't drawing excess above + var x = 1; + var y = 2; + var width = 3; + var height = 2; + + var lc = new LineCanvas (); + + // 01230 + // ╔╡╞╗1 + // ║ ║2 + + // Add a short horiz line for ╔╡ + lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double); + Assert.Equal (new (x, y, 2, 1), lc.Bounds); + + //LHS line down + lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double); + Assert.Equal (new (x, y, 2, 2), lc.Bounds); + + //Vertical line before Title, results in a ╡ + lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single); + Assert.Equal (new (x, y, 2, 2), lc.Bounds); + + //Vertical line after Title, results in a ╞ + lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single); + Assert.Equal (new (x, y, 3, 2), lc.Bounds); + + // remainder of top line + lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double); + Assert.Equal (new (x, y, 4, 2), lc.Bounds); + + //RHS line down + lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double); + Assert.Equal (new (x, y, 4, 2), lc.Bounds); + + OutputAssert.AssertEqual ( + output, + @" +╔╡╞╗ +║ ║", + $"{Environment.NewLine}{lc}" + ); + } + + [Fact] + public void Viewport_Specific_With_Ustring () + { + // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1) + // This proves we aren't drawing excess above + var x = 1; + var y = 2; + var width = 3; + var height = 2; + + var lc = new LineCanvas (); + + // 01230 + // ╔╡╞╗1 + // ║ ║2 + + // Add a short horiz line for ╔╡ + lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double); + Assert.Equal (new (x, y, 2, 1), lc.Bounds); + + //LHS line down + lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double); + Assert.Equal (new (x, y, 2, 2), lc.Bounds); + + //Vertical line before Title, results in a ╡ + lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single); + Assert.Equal (new (x, y, 2, 2), lc.Bounds); + + //Vertical line after Title, results in a ╞ + lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single); + Assert.Equal (new (x, y, 3, 2), lc.Bounds); + + // remainder of top line + lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double); + Assert.Equal (new (x, y, 4, 2), lc.Bounds); + + //RHS line down + lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double); + Assert.Equal (new (x, y, 4, 2), lc.Bounds); + + OutputAssert.AssertEqual ( + output, + @" +╔╡╞╗ +║ ║", + $"{Environment.NewLine}{lc}" + ); + } + + [Fact] + public void Canvas_Updates_On_Changes () + { + var lc = new LineCanvas (); + + Assert.Equal (Rectangle.Empty, lc.Bounds); + + lc.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double); + Assert.NotEqual (Rectangle.Empty, lc.Bounds); + + lc.Clear (); + Assert.Equal (Rectangle.Empty, lc.Bounds); + } + + [InlineData (0, 0, Orientation.Horizontal, "─")] + [InlineData (1, 0, Orientation.Horizontal, "─")] + [InlineData (0, 1, Orientation.Horizontal, "─")] + [InlineData (-1, 0, Orientation.Horizontal, "─")] + [InlineData (0, -1, Orientation.Horizontal, "─")] + [InlineData (-1, -1, Orientation.Horizontal, "─")] + [InlineData (0, 0, Orientation.Vertical, "│")] + [InlineData (1, 0, Orientation.Vertical, "│")] + [InlineData (0, 1, Orientation.Vertical, "│")] + [InlineData (0, -1, Orientation.Vertical, "│")] + [InlineData (-1, 0, Orientation.Vertical, "│")] + [InlineData (-1, -1, Orientation.Vertical, "│")] + [Theory] + public void Length_0_Is_1_Long (int x, int y, Orientation orientation, string expected) + { + var canvas = new LineCanvas (); + + // Add a line at 5, 5 that's has length of 1 + canvas.AddLine (new (x, y), 1, orientation, LineStyle.Single); + OutputAssert.AssertEqual (output, $"{expected}", $"{canvas}"); + } + + // X is offset by 2 + [InlineData (0, 0, 1, Orientation.Horizontal, "─")] + [InlineData (1, 0, 1, Orientation.Horizontal, "─")] + [InlineData (0, 1, 1, Orientation.Horizontal, "─")] + [InlineData (0, 0, 1, Orientation.Vertical, "│")] + [InlineData (1, 0, 1, Orientation.Vertical, "│")] + [InlineData (0, 1, 1, Orientation.Vertical, "│")] + [InlineData (-1, 0, 1, Orientation.Horizontal, "─")] + [InlineData (0, -1, 1, Orientation.Horizontal, "─")] + [InlineData (-1, 0, 1, Orientation.Vertical, "│")] + [InlineData (0, -1, 1, Orientation.Vertical, "│")] + [InlineData (0, 0, -1, Orientation.Horizontal, "─")] + [InlineData (1, 0, -1, Orientation.Horizontal, "─")] + [InlineData (0, 1, -1, Orientation.Horizontal, "─")] + [InlineData (0, 0, -1, Orientation.Vertical, "│")] + [InlineData (1, 0, -1, Orientation.Vertical, "│")] + [InlineData (0, 1, -1, Orientation.Vertical, "│")] + [InlineData (-1, 0, -1, Orientation.Horizontal, "─")] + [InlineData (0, -1, -1, Orientation.Horizontal, "─")] + [InlineData (-1, 0, -1, Orientation.Vertical, "│")] + [InlineData (0, -1, -1, Orientation.Vertical, "│")] + [InlineData (0, 0, 2, Orientation.Horizontal, "──")] + [InlineData (1, 0, 2, Orientation.Horizontal, "──")] + [InlineData (0, 1, 2, Orientation.Horizontal, "──")] + [InlineData (1, 1, 2, Orientation.Horizontal, "──")] + [InlineData (0, 0, 2, Orientation.Vertical, "│\r\n│")] + [InlineData (1, 0, 2, Orientation.Vertical, "│\r\n│")] + [InlineData (0, 1, 2, Orientation.Vertical, "│\r\n│")] + [InlineData (1, 1, 2, Orientation.Vertical, "│\r\n│")] + [InlineData (-1, 0, 2, Orientation.Horizontal, "──")] + [InlineData (0, -1, 2, Orientation.Horizontal, "──")] + [InlineData (-1, 0, 2, Orientation.Vertical, "│\r\n│")] + [InlineData (0, -1, 2, Orientation.Vertical, "│\r\n│")] + [InlineData (-1, -1, 2, Orientation.Vertical, "│\r\n│")] + [InlineData (0, 0, -2, Orientation.Horizontal, "──")] + [InlineData (1, 0, -2, Orientation.Horizontal, "──")] + [InlineData (0, 1, -2, Orientation.Horizontal, "──")] + [InlineData (0, 0, -2, Orientation.Vertical, "│\r\n│")] + [InlineData (1, 0, -2, Orientation.Vertical, "│\r\n│")] + [InlineData (0, 1, -2, Orientation.Vertical, "│\r\n│")] + [InlineData (1, 1, -2, Orientation.Vertical, "│\r\n│")] + [InlineData (-1, 0, -2, Orientation.Horizontal, "──")] + [InlineData (0, -1, -2, Orientation.Horizontal, "──")] + [InlineData (-1, 0, -2, Orientation.Vertical, "│\r\n│")] + [InlineData (0, -1, -2, Orientation.Vertical, "│\r\n│")] + [InlineData (-1, -1, -2, Orientation.Vertical, "│\r\n│")] + [Theory] public void Length_n_Is_n_Long (int x, int y, int length, Orientation orientation, string expected) + { + var canvas = new LineCanvas (); + canvas.AddLine (new (x, y), length, orientation, LineStyle.Single); + + var result = canvas.ToString (); + OutputAssert.AssertEqual (output, expected, result); + } + + [Fact] + public void Length_Negative () + { + var offset = new Point (5, 5); + + var canvas = new LineCanvas (); + canvas.AddLine (offset, -3, Orientation.Horizontal, LineStyle.Single); + + var looksLike = "───"; + + Assert.Equal (looksLike, $"{canvas}"); + } + + [InlineData (Orientation.Horizontal, "─")] + [InlineData (Orientation.Vertical, "│")] + [Theory] + public void Length_Zero_Alone_Is_Line (Orientation orientation, string expected) + { + var lc = new LineCanvas (); + + // Add a line at 0, 0 that's has length of 0 + lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); + OutputAssert.AssertEqual (output, expected, $"{lc}"); + } + + [InlineData (Orientation.Horizontal, "┼")] + [InlineData (Orientation.Vertical, "┼")] + [Theory] + public void Length_Zero_Cross_Is_Cross (Orientation orientation, string expected) + { + var lc = new LineCanvas (); + + // Add point at opposite orientation + lc.AddLine ( + Point.Empty, + 0, + orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal, + LineStyle.Single + ); + + // Add a line at 0, 0 that's has length of 0 + lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); + OutputAssert.AssertEqual (output, expected, $"{lc}"); + } + + [InlineData (Orientation.Horizontal, "╥")] + [InlineData (Orientation.Vertical, "╞")] + [Theory] + public void Length_Zero_NextTo_Opposite_Is_T (Orientation orientation, string expected) + { + var lc = new LineCanvas (); + + // Add line with length of 1 in opposite orientation starting at same location + if (orientation == Orientation.Horizontal) + { + lc.AddLine (Point.Empty, 1, Orientation.Vertical, LineStyle.Double); + } + else + { + lc.AddLine (Point.Empty, 1, Orientation.Horizontal, LineStyle.Double); + } + + // Add a line at 0, 0 that's has length of 0 + lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); + OutputAssert.AssertEqual (output, expected, $"{lc}"); + } + + [Fact] + public void TestLineCanvas_LeaveMargin_Top1_Left1 () + { + var canvas = new LineCanvas (); + + // Upper box + canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single); + canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single); + + var looksLike = + @" +┌─ +│ "; + OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); + } + + [Fact] + public void TestLineCanvas_Window_Heavy () + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + + // outer box + canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Heavy); + canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Heavy); + canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Heavy); + canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Heavy); + + canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Heavy); + canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Heavy); + + v.Draw (); + + var looksLike = + @" +┏━━━━┳━━━┓ +┃ ┃ ┃ +┣━━━━╋━━━┫ +┃ ┃ ┃ +┗━━━━┻━━━┛"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Theory] + [InlineData (LineStyle.Single)] + [InlineData (LineStyle.Rounded)] + public void TestLineCanvas_Window_HeavyTop_ThinSides (LineStyle thinStyle) + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + + // outer box + canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Heavy); + canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle); + canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Heavy); + canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle); + + canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle); + canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Heavy); + + v.Draw (); + + var looksLike = + @" +┍━━━━┯━━━┑ +│ │ │ +┝━━━━┿━━━┥ +│ │ │ +┕━━━━┷━━━┙ +"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Theory] + [InlineData (LineStyle.Single)] + [InlineData (LineStyle.Rounded)] + public void TestLineCanvas_Window_ThinTop_HeavySides (LineStyle thinStyle) + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + + // outer box + canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle); + canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Heavy); + canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle); + canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Heavy); + + canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Heavy); + canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle); + + v.Draw (); + + var looksLike = + @" +┎────┰───┒ +┃ ┃ ┃ +┠────╂───┨ +┃ ┃ ┃ +┖────┸───┚ + +"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Fact] + public void Top_Left_From_TopRight_LeftUp () + { + var canvas = new LineCanvas (); + + // Upper box + canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single); + canvas.AddLine (new (0, 1), -2, Orientation.Vertical, LineStyle.Single); + + var looksLike = + @" +┌─ +│ "; + OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); + } + + [Fact] + public void Top_With_1Down () + { + var canvas = new LineCanvas (); + + // Top ─ + canvas.AddLine (Point.Empty, 1, Orientation.Horizontal, LineStyle.Single); + + // Bottom ─ + canvas.AddLine (new (1, 1), -1, Orientation.Horizontal, LineStyle.Single); + + //// Right down + //canvas.AddLine (new Point (9, 0), 3, Orientation.Vertical, LineStyle.Single); + + //// Bottom + //canvas.AddLine (new Point (9, 3), -10, Orientation.Horizontal, LineStyle.Single); + + //// Left Up + //canvas.AddLine (new Point (0, 3), -3, Orientation.Vertical, LineStyle.Single); + + Assert.Equal (new (0, 0, 2, 2), canvas.Bounds); + + Dictionary map = canvas.GetMap (); + Assert.Equal (2, map.Count); + + OutputAssert.AssertEqual ( + output, + @" +─ + ─", + $"{Environment.NewLine}{canvas}" + ); + } + + [Fact] + public void ToString_Empty () + { + var lc = new LineCanvas (); + OutputAssert.AssertEqual (output, string.Empty, lc.ToString ()); + } + + // 012 + [InlineData (0, 0, "═══")] + [InlineData (1, 0, "═══")] + [InlineData (0, 1, "═══")] + [InlineData (1, 1, "═══")] + [InlineData (2, 2, "═══")] + [InlineData (-1, 0, "═══")] + [InlineData (0, -1, "═══")] + [InlineData (-1, -1, "═══")] + [InlineData (-2, -2, "═══")] + [Theory] + public void ToString_Positive_Horizontal_1Line_Offset (int x, int y, string expected) + { + var lc = new LineCanvas (); + lc.AddLine (new (x, y), 3, Orientation.Horizontal, LineStyle.Double); + OutputAssert.AssertEqual (output, expected, $"{lc}"); + } + + [InlineData (0, 0, 0, 0, "═══")] + [InlineData (1, 0, 1, 0, "═══")] + [InlineData (-1, 0, -1, 0, "═══")] + [InlineData (0, 0, 1, 0, "════")] + [InlineData (1, 0, 3, 0, "═════")] + [InlineData (1, 0, 4, 0, "══════")] + [InlineData (1, 0, 5, 0, "═══ ═══")] + [InlineData (0, 0, 0, 1, "\u2550\u2550\u2550\r\n\u2550\u2550\u2550")] + [InlineData (0, 0, 1, 1, "═══ \r\n ═══")] + [InlineData (0, 0, 2, 1, "═══ \r\n ═══")] + [InlineData (1, 0, 0, 1, " ═══\r\n═══ ")] + [InlineData (0, 1, 0, 1, "═══")] + [InlineData (1, 1, 0, 1, "════")] + [InlineData (2, 2, 0, 1, "═══ \r\n ═══")] + [Theory] + public void ToString_Positive_Horizontal_2Line_Offset (int x1, int y1, int x2, int y2, string expected) + { + var lc = new LineCanvas (); + lc.AddLine (new (x1, y1), 3, Orientation.Horizontal, LineStyle.Double); + lc.AddLine (new (x2, y2), 3, Orientation.Horizontal, LineStyle.Double); + + OutputAssert.AssertEqual (output, expected, $"{lc}"); + } + + // [Fact, SetupFakeDriver] + // public void LeaveMargin_Top1_Left1 () + // { + // var canvas = new LineCanvas (); + + // // Upper box + // canvas.AddLine (Point.Empty, 9, Orientation.Horizontal, LineStyle.Single); + // canvas.AddLine (new Point (8, 0), 3, Orientation.Vertical, LineStyle.Single); + // canvas.AddLine (new Point (8, 3), -9, Orientation.Horizontal, LineStyle.Single); + // canvas.AddLine (new Point (0, 2), -3, Orientation.Vertical, LineStyle.Single); + + // // Lower Box + // canvas.AddLine (new Point (5, 0), 2, Orientation.Vertical, LineStyle.Single); + // canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Single); + + // string looksLike = + //@" + //┌────┬──┐ + //│ │ │ + //├────┼──┤ + //└────┴──┘ + //"; + // Assert.Equal (looksLike, $"{Environment.NewLine}{canvas}"); + // } + + [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Double, "═")] + [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Double, "║")] + [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Single, "─")] + [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Single, "│")] + [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Double, "═")] + [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Double, "║")] + [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Single, "─")] + [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Single, "│")] + [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Double, "══")] + [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Double, "║\n║")] + [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Single, "──")] + [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Single, "│\n│")] + [Theory] + public void View_Draws_1LineTests ( + int x1, + int y1, + int length, + Orientation o1, + LineStyle s1, + string expected + ) + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas lc); + v.Width = 10; + v.Height = 10; + v.Viewport = new (0, 0, 10, 10); + + lc.AddLine (new (x1, y1), length, o1, s1); + + v.Draw (); + + DriverAssert.AssertDriverContentsAre (expected, output, driver); + v.Dispose (); + } + + /// This test demonstrates how to correctly trigger a corner. By overlapping the lines in the same cell + [Fact] + public void View_Draws_Corner_Correct () + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single); + canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single); + + v.Draw (); + + var looksLike = + @" +┌─ +│"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + /// + /// This test demonstrates that corners are only drawn when lines overlap. Not when they terminate adjacent to one + /// another. + /// + [Fact] + public void View_Draws_Corner_NoOverlap () + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single); + canvas.AddLine (new (0, 1), 2, Orientation.Vertical, LineStyle.Single); + + v.Draw (); + + var looksLike = + @" +── +│ +│"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [InlineData (LineStyle.Single)] + [InlineData (LineStyle.Rounded)] + [Theory] + public void View_Draws_Horizontal (LineStyle style) + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, style); + + v.Draw (); + + var looksLike = + @" +──"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Fact] + public void View_Draws_Horizontal_Double () + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double); + + v.Draw (); + + var looksLike = + @" +══"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [InlineData (LineStyle.Single)] + [InlineData (LineStyle.Rounded)] + [Theory] + public void View_Draws_Vertical (LineStyle style) + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + canvas.AddLine (Point.Empty, 2, Orientation.Vertical, style); + + v.Draw (); + + var looksLike = + @" +│ +│"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Fact] + + public void View_Draws_Vertical_Double () + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Double); + + v.Draw (); + + var looksLike = + @" +║ +║"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Fact] + public void View_Draws_Window_Double () + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + + // outer box + canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double); + canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double); + canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double); + canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double); + + canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double); + canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double); + + v.Draw (); + + var looksLike = + @" +╔════╦═══╗ +║ ║ ║ +╠════╬═══╣ +║ ║ ║ +╚════╩═══╝"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Theory] + [InlineData (LineStyle.Single)] + [InlineData (LineStyle.Rounded)] + public void View_Draws_Window_DoubleTop_SingleSides (LineStyle thinStyle) + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + + // outer box + canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double); + canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle); + canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double); + canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle); + + canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle); + canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double); + + v.Draw (); + + var looksLike = + @" +╒════╤═══╕ +│ │ │ +╞════╪═══╡ +│ │ │ +╘════╧═══╛ +"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + /// + /// Demonstrates when corners are used. Notice how not all lines declare rounded. + /// If there are 1+ lines intersecting and a corner is to be used then if any of them are rounded a rounded corner is + /// used. + /// + [Fact] + public void View_Draws_Window_Rounded () + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + + // outer box + canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Rounded); + + // LineStyle.Single is ignored because corner overlaps with the above line which is Rounded + // this results in a rounded corner being used. + canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single); + canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Rounded); + canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single); + + // These lines say rounded but they will result in the T sections which are never rounded. + canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Rounded); + canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Rounded); + + v.Draw (); + + var looksLike = + @" +╭────┬───╮ +│ │ │ +├────┼───┤ +│ │ │ +╰────┴───╯"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Theory] + [InlineData (LineStyle.Single)] + [InlineData (LineStyle.Rounded)] + public void View_Draws_Window_SingleTop_DoubleSides (LineStyle thinStyle) + { + var driver = CreateFakeDriver (); + View v = GetCanvas (driver, out LineCanvas canvas); + + // outer box + canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle); + canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double); + canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle); + canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double); + + canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double); + canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle); + + v.Draw (); + + var looksLike = + @" +╓────╥───╖ +║ ║ ║ +╟────╫───╢ +║ ║ ║ +╙────╨───╜ + +"; + DriverAssert.AssertDriverContentsAre (looksLike, output, driver); + v.Dispose (); + } + + [Fact] + public void Window () + { + var canvas = new LineCanvas (); + + // Frame + canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Single); + canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single); + canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Single); + canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single); + + // Cross + canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Single); + canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Single); + + var looksLike = + @" +┌────┬───┐ +│ │ │ +├────┼───┤ +│ │ │ +└────┴───┘"; + OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); + } + + [Fact] + public void Zero_Length_Intersections () + { + // Draw at 1,2 within client area of View (i.e. leave a top and left margin of 1) + // This proves we aren't drawing excess above + var x = 1; + var y = 2; + var width = 5; + var height = 2; + + var lc = new LineCanvas (); + + // ╔╡╞═════╗ + // Add a short horiz line for ╔╡ + lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double); + + //LHS line down + lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double); + + //Vertical line before Title, results in a ╡ + lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single); + + //Vertical line after Title, results in a ╞ + lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single); + + // remainder of top line + lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double); + + //RHS line down + lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double); + + var looksLike = @" +╔╡╞══╗ +║ ║"; + OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{lc}"); + } + + [Fact] + public void LineCanvas_UsesFillCorrectly () + { + // Arrange + var foregroundColor = new Color (255, 0); // Red + var backgroundColor = new Color (0, 0); // Black + var foregroundFill = new SolidFill (foregroundColor); + var backgroundFill = new SolidFill (backgroundColor); + var fillPair = new FillPair (foregroundFill, backgroundFill); + + var lineCanvas = new LineCanvas + { + Fill = fillPair + }; + + // Act + lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single); + Dictionary cellMap = lineCanvas.GetCellMap (); + + // Assert + foreach (Cell? cell in cellMap.Values) + { + Assert.NotNull (cell); + Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); + Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); + } + } + + [Fact] + public void LineCanvas_LineColorIgnoredBecauseOfFill () + { + // Arrange + var foregroundColor = new Color (255, 0); // Red + var backgroundColor = new Color (0, 0); // Black + var lineColor = new Attribute (new Color (0, 255), new Color (255, 255, 255)); // Green on White + var foregroundFill = new SolidFill (foregroundColor); + var backgroundFill = new SolidFill (backgroundColor); + var fillPair = new FillPair (foregroundFill, backgroundFill); + + var lineCanvas = new LineCanvas + { + Fill = fillPair + }; + + // Act + lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single, lineColor); + Dictionary cellMap = lineCanvas.GetCellMap (); + + // Assert + foreach (Cell? cell in cellMap.Values) + { + Assert.NotNull (cell); + Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); + Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); + } + } + + [Fact] + public void LineCanvas_IntersectingLinesUseFillCorrectly () + { + // Arrange + var foregroundColor = new Color (255, 0); // Red + var backgroundColor = new Color (0, 0); // Black + var foregroundFill = new SolidFill (foregroundColor); + var backgroundFill = new SolidFill (backgroundColor); + var fillPair = new FillPair (foregroundFill, backgroundFill); + + var lineCanvas = new LineCanvas + { + Fill = fillPair + }; + + // Act + lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single); + lineCanvas.AddLine (new (2, -2), 5, Orientation.Vertical, LineStyle.Single); + Dictionary cellMap = lineCanvas.GetCellMap (); + + // Assert + foreach (Cell? cell in cellMap.Values) + { + Assert.NotNull (cell); + Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); + Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); + } + } + + // TODO: Remove this and make all LineCanvas tests independent of View + /// + /// Creates a new into which a is rendered at + /// time. + /// + /// The you can draw into. + /// How far to offset drawing in X + /// How far to offset drawing in Y + /// + private View GetCanvas (IConsoleDriver driver, out LineCanvas canvas, int offsetX = 0, int offsetY = 0) + { + var v = new View { Width = 10, Height = 5, Viewport = new (0, 0, 10, 5) }; + v.Driver = driver; + + LineCanvas canvasCopy = canvas = new (); + + v.DrawComplete += (s, e) => + { + v.FillRect (v.Viewport); + + foreach (KeyValuePair p in canvasCopy.GetMap ()) + { + v.AddRune ( + offsetX + p.Key.X, + offsetY + p.Key.Y, + p.Value + ); + } + + canvasCopy.Clear (); + }; + + return v; + } } diff --git a/Tests/UnitTestsParallelizable/Drawing/RulerTests.cs b/Tests/UnitTestsParallelizable/Drawing/RulerTests.cs index e32bd7e94..5dbb8f032 100644 --- a/Tests/UnitTestsParallelizable/Drawing/RulerTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/RulerTests.cs @@ -1,3 +1,8 @@ +using Microsoft.VisualStudio.TestPlatform.Utilities; +using UnitTests; +using UnitTests.Parallelizable; +using Xunit.Abstractions; + namespace UnitTests_Parallelizable.DrawingTests; /// @@ -6,7 +11,7 @@ namespace UnitTests_Parallelizable.DrawingTests; /// /// Note: Tests that verify rendered output (Draw methods) require Application.Driver and remain in UnitTests as integration tests. /// -public class RulerTests : UnitTests.Parallelizable.ParallelizableBase +public class RulerTests (ITestOutputHelper output): ParallelizableBase { [Fact] public void Constructor_Defaults () @@ -43,4 +48,143 @@ public class RulerTests : UnitTests.Parallelizable.ParallelizableBase r.Orientation = Orientation.Vertical; Assert.Equal (Orientation.Vertical, r.Orientation); } + + [Fact] + public void Draw_Default () + { + IConsoleDriver driver = CreateFakeDriver (); + + var r = new Ruler (); + r.Draw (Point.Empty, driver: driver); + DriverAssert.AssertDriverContentsWithFrameAre (@"", output, driver); + } + + [Fact] + public void Draw_Horizontal () + { + IConsoleDriver driver = CreateFakeDriver (); + + var len = 15; + + var r = new Ruler (); + Assert.Equal (Orientation.Horizontal, r.Orientation); + + r.Length = len; + r.Draw (Point.Empty, driver: driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +|123456789|1234", + output, + driver + ); + + // Postive offset + r.Draw (new (1, 1), driver: driver); + + DriverAssert.AssertDriverContentsAre ( + @" +|123456789|1234 + |123456789|1234 +", + output, + driver + ); + + // Negative offset + r.Draw (new (-1, 3), driver: driver); + + DriverAssert.AssertDriverContentsAre ( + @" +|123456789|1234 + |123456789|1234 +123456789|1234 +", + output, + driver + ); + } + + [Fact] + public void Draw_Vertical () + { + IConsoleDriver driver = CreateFakeDriver (); + + var len = 15; + + var r = new Ruler (); + r.Orientation = Orientation.Vertical; + r.Length = len; + r.Draw (Point.Empty, driver: driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +- +1 +2 +3 +4 +5 +6 +7 +8 +9 +- +1 +2 +3 +4", + output, + driver + ); + + r.Draw (new (1, 1), driver: driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +- +1- +21 +32 +43 +54 +65 +76 +87 +98 +-9 +1- +21 +32 +43 + 4", + output, + driver + ); + + // Negative offset + r.Draw (new (2, -1), driver: driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +- 1 +1-2 +213 +324 +435 +546 +657 +768 +879 +98- +-91 +1-2 +213 +324 +43 + 4 ", + output, + driver + ); + } } diff --git a/Tests/UnitTests/Drawing/StraightLineExtensionsTests.cs b/Tests/UnitTestsParallelizable/Drawing/StraightLineExtensionsTests.cs similarity index 97% rename from Tests/UnitTests/Drawing/StraightLineExtensionsTests.cs rename to Tests/UnitTestsParallelizable/Drawing/StraightLineExtensionsTests.cs index 0573fec7e..47aedbcef 100644 --- a/Tests/UnitTests/Drawing/StraightLineExtensionsTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/StraightLineExtensionsTests.cs @@ -1,12 +1,10 @@ -using UnitTests; -using Xunit.Abstractions; +using Xunit.Abstractions; namespace UnitTests.DrawingTests; public class StraightLineExtensionsTests (ITestOutputHelper output) { [Fact] - [AutoInitShutdown] public void LineCanvasIntegrationTest () { var lc = new LineCanvas (); @@ -146,7 +144,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) #region Parallel Tests [Fact] - [AutoInitShutdown] public void TestExcludeParallel_HorizontalLines_LeftOnly () { // x=1 to x=10 @@ -165,7 +162,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludeParallel_HorizontalLines_RightOnly () { // x=1 to x=10 @@ -185,7 +181,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludeParallel_HorizontalLines_HorizontalSplit () { // x=1 to x=10 @@ -213,7 +208,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludeParallel_HorizontalLines_CoverCompletely () { // x=1 to x=10 @@ -228,7 +222,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludeParallel_VerticalLines_TopOnly () { // y=1 to y=10 @@ -247,7 +240,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludeParallel_HorizontalLines_BottomOnly () { // y=1 to y=10 @@ -267,7 +259,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludeParallel_VerticalLines_VerticalSplit () { // y=1 to y=10 @@ -295,7 +286,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludeParallel_VerticalLines_CoverCompletely () { // y=1 to y=10 @@ -314,7 +304,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) #region Perpendicular Intersection Tests [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_HorizontalLine_VerticalExclusion_Splits () { // x=1 to x=10 @@ -342,7 +331,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_HorizontalLine_VerticalExclusion_ClipLeft () { // x=1 to x=10 @@ -363,7 +351,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_HorizontalLine_VerticalExclusion_ClipRight () { // x=1 to x=10 @@ -384,7 +371,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_HorizontalLine_VerticalExclusion_MissLeft () { // x=1 to x=10 @@ -401,7 +387,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_HorizontalLine_VerticalExclusion_MissRight () { // x=1 to x=10 @@ -418,7 +403,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_VerticalLine_HorizontalExclusion_ClipTop () { // y=1 to y=10 @@ -439,7 +423,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_VerticalLine_HorizontalExclusion_ClipBottom () { // y=1 to y=10 @@ -460,7 +443,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_VerticalLine_HorizontalExclusion_MissTop () { // y=1 to y=10 @@ -477,7 +459,6 @@ public class StraightLineExtensionsTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void TestExcludePerpendicular_VerticalLine_HorizontalExclusion_MissBottom () { // y=1 to y=10 diff --git a/Tests/UnitTestsParallelizable/Drawing/ThicknessTests.cs b/Tests/UnitTestsParallelizable/Drawing/ThicknessTests.cs index d09d66f2c..333e58400 100644 --- a/Tests/UnitTestsParallelizable/Drawing/ThicknessTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/ThicknessTests.cs @@ -1,6 +1,11 @@ -namespace UnitTests_Parallelizable.DrawingTests; +using System.Text; +using UnitTests; +using UnitTests.Parallelizable; +using Xunit.Abstractions; -public class ThicknessTests +namespace UnitTests_Parallelizable.DrawingTests; + +public class ThicknessTests (ITestOutputHelper output) : ParallelizableBase { [Fact] public void Constructor_Defaults () @@ -616,4 +621,263 @@ public class ThicknessTests Assert.Equal (expectedRight, result.Right); Assert.Equal (expectedBottom, result.Bottom); } + + [Fact] + public void DrawTests () + { + IConsoleDriver driver = new FakeDriver (); + driver.SetScreenSize (60, 40); + + var t = new Thickness (0, 0, 0, 0); + var r = new Rectangle (5, 5, 40, 15); + + driver.FillRect ( + new (0, 0, driver!.Cols, driver!.Rows), + (Rune)' ' + ); + t.Draw (r, ViewDiagnosticFlags.Thickness, "Test", driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" + Test (Left=0,Top=0,Right=0,Bottom=0)", + output, + driver + ); + + t = new (1, 1, 1, 1); + r = new (5, 5, 40, 15); + + driver.FillRect ( + new (0, 0, driver!.Cols, driver!.Rows), + (Rune)' ' + ); + t.Draw (r, ViewDiagnosticFlags.Thickness, "Test", driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" + TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + T T + T T + T T + T T + T T + T T + T T + T T + T T + T T + T T + T T + T T + TTTest (Left=1,Top=1,Right=1,Bottom=1)TT", + output, + driver + ); + + t = new (1, 2, 3, 4); + r = new (5, 5, 40, 15); + + driver?.FillRect ( + new (0, 0, driver!.Cols, driver!.Rows), + (Rune)' ' + ); + t.Draw (r, ViewDiagnosticFlags.Thickness, "Test", driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" + TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + T TTT + T TTT + T TTT + T TTT + T TTT + T TTT + T TTT + T TTT + T TTT + TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + TTTest (Left=1,Top=2,Right=3,Bottom=4)TT", + output, + driver + ); + + t = new (-1, 1, 1, 1); + r = new (5, 5, 40, 15); + + driver?.FillRect ( + new (0, 0, driver!.Cols, driver!.Rows), + (Rune)' ' + ); + t.Draw (r, ViewDiagnosticFlags.Thickness, "Test", driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" + TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + T + T + T + T + T + T + T + T + T + T + T + T + T + TTest (Left=-1,Top=1,Right=1,Bottom=1)TT", + output, + driver + ); + } + + [Fact] + public void DrawTests_Ruler () + { + IConsoleDriver driver = new FakeDriver (); + + // Add a frame so we can see the ruler + var f = new FrameView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single }; + f.Driver = driver; + driver.SetScreenSize (45, 20); + + var top = new Toplevel () { Width = driver.Cols, Height = driver.Rows }; + top.Driver = driver; + top.Add (f); + + top.Layout (); + + var t = new Thickness (0, 0, 0, 0); + var r = new Rectangle (2, 2, 40, 15); + + top.Draw (); + t.Draw (r, ViewDiagnosticFlags.Ruler, "Test", driver); + + DriverAssert.AssertDriverContentsAre ( + @" +┌───────────────────────────────────────────┐ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└───────────────────────────────────────────┘", + output, + driver + ); + + t = new (1, 1, 1, 1); + r = new (1, 1, 40, 15); + top.SetNeedsDraw (); + top.Draw (); + t.Draw (r, ViewDiagnosticFlags.Ruler, "Test", driver); + + DriverAssert.AssertDriverContentsAre ( + @" +┌───────────────────────────────────────────┐ +│|123456789|123456789|123456789|123456789 │ +│1 1 │ +│2 2 │ +│3 3 │ +│4 4 │ +│5 5 │ +│6 6 │ +│7 7 │ +│8 8 │ +│9 9 │ +│- - │ +│1 1 │ +│2 2 │ +│3 3 │ +│|123456789|123456789|123456789|123456789 │ +│ │ +│ │ +│ │ +└───────────────────────────────────────────┘", + output, + driver + ); + + t = new (1, 2, 3, 4); + r = new (2, 2, 40, 15); + top.SetNeedsDraw (); + top.Draw (); + t.Draw (r, ViewDiagnosticFlags.Ruler, "Test", driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +┌───────────────────────────────────────────┐ +│ │ +│ |123456789|123456789|123456789|123456789 │ +│ 1 1 │ +│ 2 2 │ +│ 3 3 │ +│ 4 4 │ +│ 5 5 │ +│ 6 6 │ +│ 7 7 │ +│ 8 8 │ +│ 9 9 │ +│ - - │ +│ 1 1 │ +│ 2 2 │ +│ 3 3 │ +│ |123456789|123456789|123456789|123456789 │ +│ │ +│ │ +└───────────────────────────────────────────┘", + output, + driver + ); + + t = new (-1, 1, 1, 1); + r = new (5, 5, 40, 15); + top.SetNeedsDraw (); + top.Draw (); + t.Draw (r, ViewDiagnosticFlags.Ruler, "Test", driver); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +┌───────────────────────────────────────────┐ +│ │ +│ │ +│ │ +│ │ +│ |123456789|123456789|123456789|123456789 +│ 1 +│ 2 +│ 3 +│ 4 +│ 5 +│ 6 +│ 7 +│ 8 +│ 9 +│ - +│ 1 +│ 2 +│ 3 +└────|123456789|123456789|123456789|123456789", + output, + driver + ); + top.Dispose (); + } } diff --git a/Tests/UnitTestsParallelizable/Input/Keyboard/KeyTests.cs b/Tests/UnitTestsParallelizable/Input/Keyboard/KeyTests.cs index 778fbf11b..12e5044fb 100644 --- a/Tests/UnitTestsParallelizable/Input/Keyboard/KeyTests.cs +++ b/Tests/UnitTestsParallelizable/Input/Keyboard/KeyTests.cs @@ -573,4 +573,22 @@ public class KeyTests Key b = Key.A; Assert.False (a.Equals (b)); } + + + [Fact] + public void KeyPressed_Handled_True_Cancels_KeyPress () + { + var r = new View (); + var args = new Key { KeyCode = KeyCode.Null }; + + Assert.False (r.NewKeyDownEvent (args)); + Assert.False (args.Handled); + + r.KeyDown += (s, a) => a.Handled = true; + Assert.True (r.NewKeyDownEvent (args)); + Assert.True (args.Handled); + + r.Dispose (); + } + } diff --git a/Tests/UnitTestsParallelizable/Input/ResponderTests.cs b/Tests/UnitTestsParallelizable/Input/ResponderTests.cs deleted file mode 100644 index 94c4a00e5..000000000 --- a/Tests/UnitTestsParallelizable/Input/ResponderTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Alias Console to MockConsole so we don't accidentally use Console - -namespace UnitTests_Parallelizable.InputTests; - -public class ResponderTests -{ - [Fact] - public void KeyPressed_Handled_True_Cancels_KeyPress () - { - var r = new View (); - var args = new Key { KeyCode = KeyCode.Null }; - - Assert.False (r.NewKeyDownEvent (args)); - Assert.False (args.Handled); - - r.KeyDown += (s, a) => a.Handled = true; - Assert.True (r.NewKeyDownEvent (args)); - Assert.True (args.Handled); - - r.Dispose (); - } - - public class DerivedView : View - { - protected override bool OnKeyDown (Key keyEvent) { return true; } - } -} diff --git a/Tests/UnitTestsParallelizable/MockConsoleDriver.cs b/Tests/UnitTestsParallelizable/MockConsoleDriver.cs deleted file mode 100644 index 50f025834..000000000 --- a/Tests/UnitTestsParallelizable/MockConsoleDriver.cs +++ /dev/null @@ -1,203 +0,0 @@ -#nullable enable -using System.Text; - - -internal class MockConsoleDriver : IConsoleDriver -{ - public event EventHandler? AttributeSet; - - private IClipboard? _clipboard; - private Rectangle _screen; - private Region? _clip; - private int _col; - private int _cols; - private Cell [,]? _contents; - private int _left; - private int _row; - private int _rows; - private int _top; - private bool _supportsTrueColor; - private bool _force16Colors; - private Attribute _currentAttribute; - - /// - public IClipboard? Clipboard => _clipboard; - - /// - public Rectangle Screen => _screen; - - /// - public Region? Clip - { - get => _clip; - set => _clip = value; - } - - /// - public int Col => _col; - - /// - public int Cols - { - get => _cols; - set => _cols = value; - } - - /// - public Cell [,]? Contents - { - get => _contents; - set => _contents = value; - } - - /// - public int Left - { - get => _left; - set => _left = value; - } - - /// - public int Row => _row; - - /// - public int Rows - { - get => _rows; - set => _rows = value; - } - - /// - public int Top - { - get => _top; - set => _top = value; - } - - /// - public bool SupportsTrueColor => _supportsTrueColor; - - /// - public bool Force16Colors - { - get => _force16Colors; - set => _force16Colors = value; - } - - /// - public Attribute CurrentAttribute - { - get => _currentAttribute; - set => _currentAttribute = value; - } - - /// - public string GetVersionInfo () { return string.Empty; } - - /// - public void WriteRaw (string ansi) { } - - /// - public bool IsRuneSupported (Rune rune) { return true; } - - /// - public bool IsValidLocation (Rune rune, int col, int row) { return true; } - - /// - public void Move (int col, int row) - { - _col = col; - _row = row; - } - - /// - public void AddRune (Rune rune) { } - - /// - public void AddRune (char c) { } - - /// - public void AddStr (string str) { } - - /// - public void ClearContents () { } - - /// - public event EventHandler? ClearedContents; - - /// - public void FillRect (Rectangle rect, Rune rune = default) { } - - /// - public void FillRect (Rectangle rect, char c) { } - - /// - public bool GetCursorVisibility (out CursorVisibility visibility) - { - visibility = CursorVisibility.Invisible; - return false; - - } - - /// - public void Refresh () { } - - /// - public bool SetCursorVisibility (CursorVisibility visibility) { throw new NotImplementedException (); } - - /// - public event EventHandler? SizeChanged; - - /// - public void Suspend () { } - - /// - public void UpdateCursor () {} - - /// - public void Init () { } - - /// - public void End () { } - - /// - - /// - public Attribute SetAttribute (Attribute c) - { - Attribute oldAttribute = _currentAttribute; - _currentAttribute = c; - - AttributeSet?.Invoke (this, c); - - return oldAttribute; - } - - /// - public Attribute GetAttribute () - { - return _currentAttribute; - } - - - /// - public Attribute MakeColor (in Color foreground, in Color background) { throw new NotImplementedException (); } - - /// - public event EventHandler? MouseEvent; - - /// - public event EventHandler? KeyDown; - - /// - public event EventHandler? KeyUp; - - /// - public void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl) { throw new NotImplementedException (); } - - /// - public void QueueAnsiRequest (AnsiEscapeSequenceRequest request) { throw new NotImplementedException (); } - - /// - public AnsiRequestScheduler GetRequestScheduler () { throw new NotImplementedException (); } -} diff --git a/Tests/UnitTestsParallelizable/ParallelizableBase.cs b/Tests/UnitTestsParallelizable/ParallelizableBase.cs index 29fb4ae01..55b038472 100644 --- a/Tests/UnitTestsParallelizable/ParallelizableBase.cs +++ b/Tests/UnitTestsParallelizable/ParallelizableBase.cs @@ -20,11 +20,10 @@ public abstract class ParallelizableBase /// Width of the driver buffer /// Height of the driver buffer /// A configured IFakeConsoleDriver instance - protected static IFakeConsoleDriver CreateFakeDriver (int width = 25, int height = 25) + protected static IConsoleDriver CreateFakeDriver (int width = 25, int height = 25) { - var factory = new FakeDriverFactory (); - IFakeConsoleDriver driver = factory.Create (); - driver.SetBufferSize (width, height); + IConsoleDriver driver = new FakeDriver (); + driver.SetScreenSize (width, height); return driver; } } diff --git a/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj b/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj index 284585831..50e7e234a 100644 --- a/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj +++ b/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj @@ -31,6 +31,7 @@ + diff --git a/Tests/UnitTestsParallelizable/View/Adornment/AdornmentTests.cs b/Tests/UnitTestsParallelizable/View/Adornment/AdornmentTests.cs index 62b371df1..bbaad3a2b 100644 --- a/Tests/UnitTestsParallelizable/View/Adornment/AdornmentTests.cs +++ b/Tests/UnitTestsParallelizable/View/Adornment/AdornmentTests.cs @@ -76,20 +76,20 @@ public class AdornmentTests Assert.Equal (new (0, 0, 20, 20), view.Viewport); var marginThickness = 1; - view.Margin.Thickness = new (marginThickness); + view.Margin!.Thickness = new (marginThickness); Assert.Equal (new (0, 0, 18, 18), view.Viewport); var borderThickness = 2; - view.Border.Thickness = new (borderThickness); + view.Border!.Thickness = new (borderThickness); Assert.Equal (new (0, 0, 14, 14), view.Viewport); var paddingThickness = 3; - view.Padding.Thickness = new (paddingThickness); + view.Padding!.Thickness = new (paddingThickness); Assert.Equal (new (0, 0, 8, 8), view.Viewport); - Assert.Equal (new (0, 0, view.Margin.Frame.Width, view.Margin.Frame.Height), view.Margin.Viewport); + Assert.Equal (new (0, 0, view.Margin!.Frame.Width, view.Margin!.Frame.Height), view.Margin!.Viewport); - Assert.Equal (new (0, 0, view.Border.Frame.Width, view.Border.Frame.Height), view.Border.Viewport); + Assert.Equal (new (0, 0, view.Border!.Frame.Width, view.Border!.Frame.Height), view.Border!.Viewport); Assert.Equal (new (0, 0, view.Padding.Frame.Width, view.Padding.Frame.Height), view.Padding.Viewport); } @@ -131,7 +131,7 @@ public class AdornmentTests [InlineData (1, 1, 1, 0, 4)] public void Viewport_Width_Is_Frame_Width (int thickness, int x, int y, int w, int h) { - var adornment = new Adornment (null); + var adornment = new Adornment (null!); adornment.Thickness = new (thickness); adornment.Frame = new (x, y, w, h); Assert.Equal (new (x, y, w, h), adornment.Frame); @@ -151,11 +151,11 @@ public class AdornmentTests Assert.Equal (new (1, 2, 10, 10), parent.Frame); Assert.Equal (new (0, 0, 10, 10), parent.Viewport); - Assert.Equal (new (0, 0, 10, 10), parent.Margin.Frame); - Assert.Equal (new (0, 0, 10, 10), parent.Margin.Viewport); + Assert.Equal (new (0, 0, 10, 10), parent.Margin!.Frame); + Assert.Equal (new (0, 0, 10, 10), parent.Margin!.Viewport); - Assert.Null (parent.Margin.SuperView); - Rectangle boundsAsScreen = parent.Margin.ViewportToScreen (new Rectangle (1, 2, 5, 5)); + Assert.Null (parent.Margin!.SuperView); + Rectangle boundsAsScreen = parent.Margin!.ViewportToScreen (new Rectangle (1, 2, 5, 5)); Assert.Equal (new (2, 4, 5, 5), boundsAsScreen); } @@ -167,8 +167,8 @@ public class AdornmentTests Assert.Equal (new (1, 2, 10, 20), parent.Frame); Assert.Equal (new (0, 0, 10, 20), parent.Viewport); - Assert.Equal (new (0, 0, 10, 20), parent.Margin.Frame); - Assert.Equal (new (0, 0, 10, 20), parent.Margin.Viewport); + Assert.Equal (new (0, 0, 10, 20), parent.Margin!.Frame); + Assert.Equal (new (0, 0, 10, 20), parent.Margin!.Viewport); } [Fact] @@ -183,13 +183,13 @@ public class AdornmentTests }; var marginThickness = 1; - view.Margin.Thickness = new (marginThickness); + view.Margin!.Thickness = new (marginThickness); var borderThickness = 2; - view.Border.Thickness = new (borderThickness); + view.Border!.Thickness = new (borderThickness); var paddingThickness = 3; - view.Padding.Thickness = new (paddingThickness); + view.Padding!.Thickness = new (paddingThickness); view.BeginInit (); view.EndInit (); @@ -198,12 +198,12 @@ public class AdornmentTests Assert.Equal (new (0, 0, 8, 19), view.Viewport); // Margin.Frame is always the same as the view frame - Assert.Equal (new (0, 0, 20, 31), view.Margin.Frame); + Assert.Equal (new (0, 0, 20, 31), view.Margin!.Frame); // Border.Frame is View.Frame minus the Margin thickness Assert.Equal ( new (marginThickness, marginThickness, view.Frame.Width - marginThickness * 2, view.Frame.Height - marginThickness * 2), - view.Border.Frame); + view.Border!.Frame); // Padding.Frame is View.Frame minus the Border thickness plus Margin thickness Assert.Equal ( @@ -226,15 +226,15 @@ public class AdornmentTests public void FrameToScreen_Retains_Frame_Size (int marginThickness, int w, int h) { var parent = new View { X = 1, Y = 2, Width = w, Height = h }; - parent.Margin.Thickness = new (marginThickness); + parent.Margin!.Thickness = new (marginThickness); parent.BeginInit (); parent.EndInit (); Assert.Equal (new (1, 2, w, h), parent.Frame); - Assert.Equal (new (0, 0, w, h), parent.Margin.Frame); + Assert.Equal (new (0, 0, w, h), parent.Margin!.Frame); - Assert.Equal (parent.Frame, parent.Margin.FrameToScreen ()); + Assert.Equal (parent.Frame, parent.Margin!.FrameToScreen ()); } // Test that Adornment.FrameToScreen override returns Frame if Parent is null @@ -276,8 +276,8 @@ public class AdornmentTests Width = 20, Height = 20 }; - superView.Margin.Thickness = new (marginThickness); - superView.Border.Thickness = new (borderThickness); + superView.Margin!.Thickness = new (marginThickness); + superView.Border!.Thickness = new (borderThickness); var view = new View { X = x, Y = y, Width = 1, Height = 1 }; superView.Add (view); @@ -285,16 +285,16 @@ public class AdornmentTests superView.EndInit (); Assert.Equal (new (x, y, 1, 1), view.Frame); - Assert.Equal (new (0, 0, 20, 20), superView.Margin.Frame); + Assert.Equal (new (0, 0, 20, 20), superView.Margin!.Frame); Assert.Equal ( new (marginThickness, marginThickness, 20 - marginThickness * 2, 20 - marginThickness * 2), - superView.Border.Frame + superView.Border!.Frame ); Assert.Equal ( new (superView.Frame.X + marginThickness, superView.Frame.Y + marginThickness, 20 - marginThickness * 2, 20 - marginThickness * 2), - superView.Border.FrameToScreen () + superView.Border!.FrameToScreen () ); } @@ -309,11 +309,11 @@ public class AdornmentTests Assert.Equal (new (1, 2, 10, 10), parent.Frame); Assert.Equal (new (0, 0, 10, 10), parent.Viewport); - Assert.Equal (new (0, 0, 10, 10), parent.Margin.Frame); - Assert.Equal (new (0, 0, 10, 10), parent.Margin.Viewport); + Assert.Equal (new (0, 0, 10, 10), parent.Margin!.Frame); + Assert.Equal (new (0, 0, 10, 10), parent.Margin!.Viewport); - Assert.Null (parent.Margin.SuperView); - Assert.Equal (new (1, 2, 10, 10), parent.Margin.FrameToScreen ()); + Assert.Null (parent.Margin!.SuperView); + Assert.Equal (new (1, 2, 10, 10), parent.Margin!.FrameToScreen ()); } [Fact] @@ -322,13 +322,13 @@ public class AdornmentTests var view = new View (); Assert.Equal (Thickness.Empty, view.GetAdornmentsThickness ()); - view.Margin.Thickness = new (1); + view.Margin!.Thickness = new (1); Assert.Equal (new (1), view.GetAdornmentsThickness ()); - view.Border.Thickness = new (1); + view.Border!.Thickness = new (1); Assert.Equal (new (2), view.GetAdornmentsThickness ()); - view.Padding.Thickness = new (1); + view.Padding!.Thickness = new (1); Assert.Equal (new (3), view.GetAdornmentsThickness ()); view.Padding.Thickness = new (2); @@ -337,7 +337,7 @@ public class AdornmentTests view.Padding.Thickness = new (1, 2, 3, 4); Assert.Equal (new (3, 4, 5, 6), view.GetAdornmentsThickness ()); - view.Margin.Thickness = new (1, 2, 3, 4); + view.Margin!.Thickness = new (1, 2, 3, 4); Assert.Equal (new (3, 5, 7, 9), view.GetAdornmentsThickness ()); view.Dispose (); } @@ -345,14 +345,14 @@ public class AdornmentTests [Fact] public void Setting_Viewport_Throws () { - var adornment = new Adornment (null); + var adornment = new Adornment (null!); Assert.Throws (() => adornment.Viewport = new (1, 2, 3, 4)); } [Fact] public void Setting_SuperViewRendersLineCanvas_Throws () { - var adornment = new Adornment (null); + var adornment = new Adornment (null!); Assert.Throws (() => adornment.SuperViewRendersLineCanvas = true); } @@ -366,7 +366,7 @@ public class AdornmentTests Assert.Equal (new (0, 0, 10, 10), parent.Frame); Assert.Equal (new (0, 0, 10, 10), parent.Viewport); - parent.Margin.Thickness = new (1); + parent.Margin!.Thickness = new (1); Assert.Equal (new (0, 0, 10, 10), parent.Frame); Assert.Equal (new (0, 0, 8, 8), parent.Viewport); } @@ -374,7 +374,7 @@ public class AdornmentTests [Fact] public void Setting_Thickness_Raises_ThicknessChanged () { - var adornment = new Adornment (null); + var adornment = new Adornment (null!); var super = new View (); var raised = false; @@ -396,15 +396,15 @@ public class AdornmentTests parent.EndInit (); parent.SubViewLayout += LayoutStarted; - parent.Margin.Thickness = new (1, 2, 3, 4); + parent.Margin!.Thickness = new (1, 2, 3, 4); Assert.True (parent.NeedsLayout); - Assert.True (parent.Margin.NeedsLayout); + Assert.True (parent.Margin!.NeedsLayout); parent.Layout (); Assert.True (raised); return; - void LayoutStarted (object sender, LayoutEventArgs e) { raised = true; } + void LayoutStarted (object? sender, LayoutEventArgs e) { raised = true; } } [Fact] @@ -415,16 +415,16 @@ public class AdornmentTests parent.BeginInit (); parent.EndInit (); - parent.Margin.SubViewLayout += LayoutStarted; - parent.Margin.Thickness = new (1, 2, 3, 4); + parent.Margin!.SubViewLayout += LayoutStarted; + parent.Margin!.Thickness = new (1, 2, 3, 4); Assert.True (parent.NeedsLayout); - Assert.True (parent.Margin.NeedsLayout); + Assert.True (parent.Margin!.NeedsLayout); parent.Layout (); Assert.True (raised); return; - void LayoutStarted (object sender, LayoutEventArgs e) { raised = true; } + void LayoutStarted (object? sender, LayoutEventArgs e) { raised = true; } } [Fact] @@ -434,7 +434,7 @@ public class AdornmentTests view.BeginInit (); view.EndInit (); - view.Padding.Thickness = new (2, 2, 2, 2); + view.Padding!.Thickness = new (2, 2, 2, 2); Assert.Throws (() => view.Padding.Viewport = view.Padding.Viewport with { Location = new (1, 1) }); } diff --git a/Tests/UnitTestsParallelizable/View/Adornment/MarginTests.cs b/Tests/UnitTestsParallelizable/View/Adornment/MarginTests.cs index 6719c0ee5..d7cc2ed0d 100644 --- a/Tests/UnitTestsParallelizable/View/Adornment/MarginTests.cs +++ b/Tests/UnitTestsParallelizable/View/Adornment/MarginTests.cs @@ -28,7 +28,7 @@ public class MarginTests view.Margin!.Thickness = new Thickness (1, 1, 1, 1); // Give it Text - view.Margin.Text = "Test"; + view.Margin!.Text = "Test"; // Strip off ViewportSettings.Transparent view.Margin!.ViewportSettings &= ~ViewportSettingsFlags.Transparent; @@ -50,7 +50,7 @@ public class MarginTests { var view = new View { Height = 3, Width = 3, ShadowStyle = ShadowStyle.Transparent }; Assert.Equal (ShadowStyle.Transparent, view.Margin!.ShadowStyle); - Assert.True (view.Margin.ViewportSettings.HasFlag (ViewportSettingsFlags.TransparentMouse), "Margin should be transparent to mouse when ShadowStyle is Transparent."); + Assert.True (view.Margin!.ViewportSettings.HasFlag (ViewportSettingsFlags.TransparentMouse), "Margin should be transparent to mouse when ShadowStyle is Transparent."); Assert.True (view.Margin!.ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent), "Margin should be transparent when ShadowStyle is Transparent.."); } @@ -59,7 +59,7 @@ public class MarginTests { var view = new View { Height = 3, Width = 3, ShadowStyle = ShadowStyle.Opaque }; Assert.Equal (ShadowStyle.Opaque, view.Margin!.ShadowStyle); - Assert.True (view.Margin.ViewportSettings.HasFlag (ViewportSettingsFlags.TransparentMouse), "Margin should be transparent to mouse when ShadowStyle is Opaque."); + Assert.True (view.Margin!.ViewportSettings.HasFlag (ViewportSettingsFlags.TransparentMouse), "Margin should be transparent to mouse when ShadowStyle is Opaque."); Assert.True (view.Margin!.ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent), "Margin should be transparent when ShadowStyle is Opaque.."); } diff --git a/Tests/UnitTestsParallelizable/View/Layout/GetViewsUnderLocationTests.cs b/Tests/UnitTestsParallelizable/View/Layout/GetViewsUnderLocationTests.cs index 7e8a93564..4bb0506c1 100644 --- a/Tests/UnitTestsParallelizable/View/Layout/GetViewsUnderLocationTests.cs +++ b/Tests/UnitTestsParallelizable/View/Layout/GetViewsUnderLocationTests.cs @@ -128,14 +128,14 @@ public class GetViewsUnderLocationTests containedType = view.GetType (); } - if (view.Margin.Contains (new (testX, testY))) + if (view.Margin!.Contains (new (testX, testY))) { - containedType = view.Margin.GetType (); + containedType = view.Margin!.GetType (); } - if (view.Border.Contains (new (testX, testY))) + if (view.Border!.Contains (new (testX, testY))) { - containedType = view.Border.GetType (); + containedType = view.Border!.GetType (); } if (view.Padding.Contains (new (testX, testY))) diff --git a/Tests/UnitTestsParallelizable/View/Layout/LayoutTests.cs b/Tests/UnitTestsParallelizable/View/Layout/LayoutTests.cs index 27b34c2b9..1c45576f2 100644 --- a/Tests/UnitTestsParallelizable/View/Layout/LayoutTests.cs +++ b/Tests/UnitTestsParallelizable/View/Layout/LayoutTests.cs @@ -275,8 +275,8 @@ public class LayoutTests : GlobalTestSetup view.SubViewLayout += (sender, e) => layoutStartedCount++; view.SubViewsLaidOut += (sender, e) => layoutCompleteCount++; - view.Border.SubViewLayout += (sender, e) => borderLayoutStartedCount++; - view.Border.SubViewsLaidOut += (sender, e) => borderLayoutCompleteCount++; + view.Border!.SubViewLayout += (sender, e) => borderLayoutStartedCount++; + view.Border!.SubViewsLaidOut += (sender, e) => borderLayoutCompleteCount++; superView.Add (view); @@ -300,7 +300,7 @@ public class LayoutTests : GlobalTestSetup Assert.Equal (2, layoutCompleteCount); // With Border subview - view.Border.Add (new View ()); + view.Border!.Add (new View ()); superView.LayoutSubViews (); Assert.Equal (1, borderLayoutStartedCount); Assert.Equal (1, borderLayoutCompleteCount); @@ -338,8 +338,8 @@ public class LayoutTests : GlobalTestSetup view.SubViewLayout += (sender, e) => layoutStartedCount++; view.SubViewsLaidOut += (sender, e) => layoutCompleteCount++; - view.Border.SubViewLayout += (sender, e) => borderLayoutStartedCount++; - view.Border.SubViewsLaidOut += (sender, e) => borderLayoutCompleteCount++; + view.Border!.SubViewLayout += (sender, e) => borderLayoutStartedCount++; + view.Border!.SubViewsLaidOut += (sender, e) => borderLayoutCompleteCount++; superView.Add (view); Assert.Equal (0, borderLayoutStartedCount); diff --git a/Tests/UnitTestsParallelizable/View/Layout/ToScreenTests.cs b/Tests/UnitTestsParallelizable/View/Layout/ToScreenTests.cs index 1c2a12989..e005b7ca9 100644 --- a/Tests/UnitTestsParallelizable/View/Layout/ToScreenTests.cs +++ b/Tests/UnitTestsParallelizable/View/Layout/ToScreenTests.cs @@ -119,7 +119,7 @@ public class ToScreenTests () Height = 1 }; - view.Border.Add (subviewOfBorder); + view.Border!.Add (subviewOfBorder); view.BeginInit (); view.EndInit (); diff --git a/Tests/UnitTestsParallelizable/View/Layout/ViewportTests.cs b/Tests/UnitTestsParallelizable/View/Layout/ViewportTests.cs index 11c935283..123ff90c2 100644 --- a/Tests/UnitTestsParallelizable/View/Layout/ViewportTests.cs +++ b/Tests/UnitTestsParallelizable/View/Layout/ViewportTests.cs @@ -55,7 +55,7 @@ public class ViewportTests (ITestOutputHelper output) Height = 10, Width = 10, }; - superSuperView.Border.Thickness = new Thickness (borderThickness); + superSuperView.Border!.Thickness = new Thickness (borderThickness); var superView = new View () { @@ -64,7 +64,7 @@ public class ViewportTests (ITestOutputHelper output) Height = Dim.Fill (), Width = Dim.Fill () }; - superView.Border.Thickness = new Thickness (borderThickness); + superView.Border!.Thickness = new Thickness (borderThickness); superSuperView.Add (superView); @@ -113,7 +113,7 @@ public class ViewportTests (ITestOutputHelper output) Height = 10, Width = 10, }; - superSuperView.Border.Thickness = new Thickness (borderThickness); + superSuperView.Border!.Thickness = new Thickness (borderThickness); var superView = new View () { @@ -122,7 +122,7 @@ public class ViewportTests (ITestOutputHelper output) Height = Dim.Fill (), Width = Dim.Fill () }; - superView.Border.Thickness = new Thickness (borderThickness); + superView.Border!.Thickness = new Thickness (borderThickness); superSuperView.Add (superView); @@ -133,7 +133,7 @@ public class ViewportTests (ITestOutputHelper output) Height = Dim.Fill (), Width = Dim.Fill () }; - view.Border.Thickness = new Thickness (borderThickness); + view.Border!.Thickness = new Thickness (borderThickness); superView.Add (view); superSuperView.BeginInit (); @@ -328,7 +328,7 @@ public class ViewportTests (ITestOutputHelper output) }; view.BeginInit (); view.EndInit (); - view.Margin.Thickness = new (adornmentThickness); + view.Margin!.Thickness = new (adornmentThickness); Assert.Equal (expectedOffset, view.GetViewportOffsetFromFrame ().X); } diff --git a/Tests/UnitTestsParallelizable/View/Navigation/SetFocusTests.cs b/Tests/UnitTestsParallelizable/View/Navigation/SetFocusTests.cs index 09dc7edb4..1eb8c6383 100644 --- a/Tests/UnitTestsParallelizable/View/Navigation/SetFocusTests.cs +++ b/Tests/UnitTestsParallelizable/View/Navigation/SetFocusTests.cs @@ -236,14 +236,14 @@ public class SetFocusTests () : TestsAllViews }; borderSubView.Add (subViewSubView1, subViewSubView2, subViewSubView3); - view.Border.Add (borderSubView); + view.Border!.Add (borderSubView); view.SetFocus (); Assert.True (view.HasFocus); Assert.True (subView.HasFocus); Assert.False (borderSubView.HasFocus); - view.Border.CanFocus = true; + view.Border!.CanFocus = true; subViewSubView1.SetFocus (); Assert.True (view.HasFocus); Assert.False (subView.HasFocus); @@ -252,47 +252,47 @@ public class SetFocusTests () : TestsAllViews Assert.False (subViewSubView2.HasFocus); Assert.False (subViewSubView3.HasFocus); - view.Border.CanFocus = false; + view.Border!.CanFocus = false; Assert.True (view.HasFocus); Assert.True (subView.HasFocus); - Assert.False (view.Border.HasFocus); + Assert.False (view.Border!.HasFocus); Assert.False (borderSubView.HasFocus); Assert.False (subViewSubView1.HasFocus); Assert.False (subViewSubView2.HasFocus); Assert.False (subViewSubView3.HasFocus); - view.Border.CanFocus = true; + view.Border!.CanFocus = true; Assert.True (view.HasFocus); Assert.True (subView.HasFocus); - Assert.False (view.Border.HasFocus); + Assert.False (view.Border!.HasFocus); Assert.False (borderSubView.HasFocus); Assert.False (subViewSubView1.HasFocus); Assert.False (subViewSubView2.HasFocus); Assert.False (subViewSubView3.HasFocus); - view.Border.SetFocus (); + view.Border!.SetFocus (); Assert.True (view.HasFocus); - Assert.True (view.Border.HasFocus); + Assert.True (view.Border!.HasFocus); Assert.False (subView.HasFocus); Assert.True (borderSubView.HasFocus); Assert.True (subViewSubView1.HasFocus); Assert.False (subViewSubView2.HasFocus); Assert.False (subViewSubView3.HasFocus); - view.Border.CanFocus = false; + view.Border!.CanFocus = false; Assert.True (view.HasFocus); Assert.True (subView.HasFocus); - Assert.False (view.Border.HasFocus); + Assert.False (view.Border!.HasFocus); Assert.False (borderSubView.HasFocus); Assert.False (subViewSubView1.HasFocus); Assert.False (subViewSubView2.HasFocus); Assert.False (subViewSubView3.HasFocus); - view.Border.CanFocus = true; + view.Border!.CanFocus = true; subViewSubView1.SetFocus (); Assert.True (view.HasFocus); Assert.False (subView.HasFocus); - Assert.True (view.Border.HasFocus); + Assert.True (view.Border!.HasFocus); Assert.True (borderSubView.HasFocus); Assert.True (subViewSubView1.HasFocus); Assert.False (subViewSubView2.HasFocus); @@ -301,7 +301,7 @@ public class SetFocusTests () : TestsAllViews subView.SetFocus (); Assert.True (view.HasFocus); Assert.True (subView.HasFocus); - Assert.False (view.Border.HasFocus); + Assert.False (view.Border!.HasFocus); Assert.False (borderSubView.HasFocus); Assert.False (subViewSubView1.HasFocus); Assert.False (subViewSubView2.HasFocus); diff --git a/Tests/UnitTestsParallelizable/View/SchemeTests.cs b/Tests/UnitTestsParallelizable/View/SchemeTests.cs index 8ea8cec50..e38e88689 100644 --- a/Tests/UnitTestsParallelizable/View/SchemeTests.cs +++ b/Tests/UnitTestsParallelizable/View/SchemeTests.cs @@ -1,10 +1,11 @@ #nullable enable +using UnitTests.Parallelizable; using Xunit; namespace UnitTests_Parallelizable.ViewTests; [Trait ("Category", "View.Scheme")] -public class SchemeTests +public class SchemeTests : ParallelizableBase { [Fact] @@ -63,7 +64,7 @@ public class SchemeTests public void GetAttribute_ReturnsCorrectAttribute_Via_Mock () { var view = new View { SchemeName = "Base" }; - view.Driver = new MockConsoleDriver (); + view.Driver = CreateFakeDriver (); view.Driver.SetAttribute (new Attribute (Color.Red, Color.Green)); // Act @@ -103,7 +104,7 @@ public class SchemeTests public void SetAttributeForRole_SetsCorrectAttribute () { var view = new View { SchemeName = "Base" }; - view.Driver = new MockConsoleDriver (); + view.Driver = CreateFakeDriver (); view.Driver.SetAttribute (new Attribute (Color.Red, Color.Green)); var previousAttribute = view.SetAttributeForRole (VisualRole.Focus); @@ -316,16 +317,16 @@ public class SchemeTests // Border (an Adornment) doesn't have a SuperView but should use its Parent's scheme var view = new View { SchemeName = "Dialog" }; var border = view.Border!; - + Assert.NotNull (border); Assert.Null (border.SuperView); // Adornments don't have SuperView Assert.NotNull (border.Parent); - + var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"]; - + // Border should use its Parent's scheme, not Base Assert.Equal (dialogScheme!.Normal, border.GetAttributeForRole (VisualRole.Normal)); - + view.Dispose (); } @@ -365,11 +366,11 @@ public class SchemeTests { // Test: grandchild without explicit scheme defers through parent to grandparent // Would fail without the SuperView deferral fix (commit 154ac15) - + var grandparentView = new View { SchemeName = "Base" }; var parentView = new View (); // No scheme or SchemeName var childView = new View (); // No scheme or SchemeName - + grandparentView.Add (parentView); parentView.Add (childView); @@ -386,7 +387,7 @@ public class SchemeTests // Child should get attribute from grandparent through parent Assert.Equal (customAttribute, childView.GetAttributeForRole (VisualRole.Normal)); - + // Parent should also get attribute from grandparent Assert.Equal (customAttribute, parentView.GetAttributeForRole (VisualRole.Normal)); @@ -400,11 +401,11 @@ public class SchemeTests { // Test: parent with SchemeName stops deferral chain // Would fail without the SchemeName check (commit 866e002) - + var grandparentView = new View { SchemeName = "Base" }; var parentView = new View { SchemeName = "Dialog" }; // Sets SchemeName var childView = new View (); // No scheme or SchemeName - + grandparentView.Add (parentView); parentView.Add (childView); @@ -423,7 +424,7 @@ public class SchemeTests var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"]; Assert.NotEqual (customAttribute, parentView.GetAttributeForRole (VisualRole.Normal)); Assert.Equal (dialogScheme!.Normal, parentView.GetAttributeForRole (VisualRole.Normal)); - + // Child should get parent's Dialog scheme (defers to parent, parent uses Dialog scheme) Assert.Equal (dialogScheme!.Normal, childView.GetAttributeForRole (VisualRole.Normal)); @@ -437,7 +438,7 @@ public class SchemeTests { // Test: view's own OnGettingAttributeForRole takes precedence over parent // This should work with or without the fix, but validates precedence - + var parentView = new View { SchemeName = "Base" }; var childView = new TestViewWithAttributeOverride (); parentView.Add (childView); @@ -456,7 +457,7 @@ public class SchemeTests // Child's own override should take precedence var childOverrideAttribute = new Attribute (Color.BrightRed, Color.BrightCyan); childView.OverrideAttribute = childOverrideAttribute; - + Assert.Equal (childOverrideAttribute, childView.GetAttributeForRole (VisualRole.Normal)); childView.Dispose (); @@ -468,7 +469,7 @@ public class SchemeTests { // Test: multiple VisualRoles all defer correctly // Would fail without the SuperView deferral fix for any role - + var parentView = new View { SchemeName = "Base" }; var childView = new View (); parentView.Add (childView); diff --git a/Tests/UnitTestsParallelizable/Views/AllViewsTests.cs b/Tests/UnitTestsParallelizable/Views/AllViewsTests.cs index 4acedccda..42fb13502 100644 --- a/Tests/UnitTestsParallelizable/Views/AllViewsTests.cs +++ b/Tests/UnitTestsParallelizable/Views/AllViewsTests.cs @@ -146,37 +146,37 @@ public class AllViewsTests (ITestOutputHelper output) : TestsAllViews view?.Dispose (); } - [Theory] - [MemberData (nameof (AllViewTypes))] - public void AllViews_Disabled_Draws_Disabled_Or_Faint (Type viewType) - { - var view = CreateInstanceIfNotGeneric (viewType); + //[Theory] + //[MemberData (nameof (AllViewTypes))] + //public void AllViews_Disabled_Draws_Disabled_Or_Faint (Type viewType) + //{ + // var view = CreateInstanceIfNotGeneric (viewType); - if (view == null) - { - output.WriteLine ($"Ignoring {viewType} - It's a Generic"); + // if (view == null) + // { + // output.WriteLine ($"Ignoring {viewType} - It's a Generic"); - return; - } + // return; + // } - if (view is IDesignable designable) - { - designable.EnableForDesign (); - } + // if (view is IDesignable designable) + // { + // designable.EnableForDesign (); + // } - var mockDriver = new MockConsoleDriver (); - mockDriver.AttributeSet += (_, args) => - { - if (args != view.GetAttributeForRole (VisualRole.Disabled) && args.Style != TextStyle.Faint) - { - Assert.Fail($"{viewType} with `Enabled == false` tried to SetAttribute to {args}"); - } - }; - view.Driver = mockDriver; - view.Enabled = false; - view.SetNeedsDraw (); - view.Draw (); + // var driver = CreateFakeDriver (); + // driver.AttributeSet += (_, args) => + // { + // if (args != view.GetAttributeForRole (VisualRole.Disabled) && args.Style != TextStyle.Faint) + // { + // Assert.Fail($"{viewType} with `Enabled == false` tried to SetAttribute to {args}"); + // } + // }; + // view.Driver = driver; + // view.Enabled = false; + // view.SetNeedsDraw (); + // view.Draw (); - view?.Dispose (); - } + // view?.Dispose (); + //} } diff --git a/Tests/UnitTestsParallelizable/Views/CheckBoxTests.cs b/Tests/UnitTestsParallelizable/Views/CheckBoxTests.cs index 2bc1d9159..d88923151 100644 --- a/Tests/UnitTestsParallelizable/Views/CheckBoxTests.cs +++ b/Tests/UnitTestsParallelizable/Views/CheckBoxTests.cs @@ -3,7 +3,7 @@ using Xunit.Abstractions; namespace UnitTests_Parallelizable.ViewsTests; -public class CheckBoxTests (ITestOutputHelper output) +public class CheckBoxTests () { [Theory] [InlineData ("01234", 0, 0, 0, 0)] diff --git a/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs b/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs index 0955db87b..f192ae202 100644 --- a/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs +++ b/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs @@ -1,8 +1,11 @@ using System.Text; +using UnitTests; +using UnitTests.Parallelizable; +using Xunit.Abstractions; namespace UnitTests_Parallelizable.ViewsTests; -public class TextFieldTests +public class TextFieldTests (ITestOutputHelper output) : ParallelizableBase { [Fact] public void Cancel_TextChanging_ThenBackspace () @@ -554,4 +557,80 @@ public class TextFieldTests Assert.Equal (new (3, 0), tf.PositionCursor ()); Assert.Equal ("📄a", tf.Text); } + + [Fact] + public void Accented_Letter_With_Three_Combining_Unicode_Chars () + { + IConsoleDriver driver = CreateFakeDriver (); + + var tf = new TextField { Width = 3, Text = "ắ" }; + tf.Driver = driver; + tf.Layout (); + tf.Draw (); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +ắ", + output, + driver + ); + + tf.Text = "\u1eaf"; + tf.Layout (); + tf.Draw (); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +ắ", + output, + driver + ); + + tf.Text = "\u0103\u0301"; + tf.Layout (); + tf.Draw (); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +ắ", + output, + driver + ); + + tf.Text = "\u0061\u0306\u0301"; + tf.Layout (); + tf.Draw (); + + DriverAssert.AssertDriverContentsWithFrameAre ( + @" +ắ", + output, + driver + ); + } + + [Fact] + public void Adjust_First () + { + IConsoleDriver driver = CreateFakeDriver (); + + var tf = new TextField { Width = Dim.Fill (), Text = "This is a test." }; + tf.Driver = driver; + tf.SetRelativeLayout (new (20, 20)); + tf.Draw (); + + Assert.Equal ("This is a test. ", GetContents ()); + + string GetContents () + { + var item = ""; + + for (var i = 0; i < 16; i++) + { + item += driver.Contents [0, i]!.Rune; + } + + return item; + } + } }