diff --git a/Examples/CommunityToolkitExample/Program.cs b/Examples/CommunityToolkitExample/Program.cs index 265c979aac..74e45ce06d 100644 --- a/Examples/CommunityToolkitExample/Program.cs +++ b/Examples/CommunityToolkitExample/Program.cs @@ -16,7 +16,7 @@ private static void Main (string [] args) Services = ConfigureServices (); Application.Init (); Application.Run (Services.GetRequiredService ()); - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.Shutdown (); } diff --git a/Examples/ReactiveExample/Program.cs b/Examples/ReactiveExample/Program.cs index 910d1f4a44..f73aa807de 100644 --- a/Examples/ReactiveExample/Program.cs +++ b/Examples/ReactiveExample/Program.cs @@ -16,7 +16,7 @@ private static void Main (string [] args) RxApp.MainThreadScheduler = TerminalScheduler.Default; RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; Application.Run (new LoginView (new LoginViewModel ())); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.Shutdown (); } } diff --git a/Examples/ReactiveExample/TerminalScheduler.cs b/Examples/ReactiveExample/TerminalScheduler.cs index 9c82867225..3b24cc6d82 100644 --- a/Examples/ReactiveExample/TerminalScheduler.cs +++ b/Examples/ReactiveExample/TerminalScheduler.cs @@ -22,7 +22,7 @@ IDisposable PostOnMainLoop () var cancellation = new CancellationDisposable (); Application.Invoke ( - () => + (_) => { if (!cancellation.Token.IsCancellationRequested) { diff --git a/Examples/UICatalog/Resources/config.json b/Examples/UICatalog/Resources/config.json index 916743d239..e47ea567cc 100644 --- a/Examples/UICatalog/Resources/config.json +++ b/Examples/UICatalog/Resources/config.json @@ -86,7 +86,7 @@ "Menu": { "Normal": { "Foreground": "Black", - "Background": "WHite" + "Background": "White" }, "Focus": { "Foreground": "White", @@ -136,17 +136,16 @@ { "UI Catalog Theme": { "Window.DefaultShadow": "Transparent", + "Button.DefaultShadow": "None", "CheckBox.DefaultHighlightStates": "In, Pressed, PressedOutside", "MessageBox.DefaultButtonAlignment": "Start", "StatusBar.DefaultSeparatorLineStyle": "Single", "Dialog.DefaultMinimumWidth": 80, - "MessageBox.DefaultBorderStyle": "Dotted", "NerdFonts.Enable": false, "MessageBox.DefaultMinimumWidth": 0, "Window.DefaultBorderStyle": "Double", "Dialog.DefaultShadow": "Opaque", "Dialog.DefaultButtonAlignment": "Start", - "Button.DefaultShadow": "Transparent", "FrameView.DefaultBorderStyle": "Double", "MessageBox.DefaultMinimumHeight": 0, "Button.DefaultHighlightStates": "In, Pressed", diff --git a/Examples/UICatalog/Scenario.cs b/Examples/UICatalog/Scenario.cs index 76fc5dc2a9..0531d06c99 100644 --- a/Examples/UICatalog/Scenario.cs +++ b/Examples/UICatalog/Scenario.cs @@ -221,7 +221,7 @@ private void OnApplicationOnIteration (object? s, IterationEventArgs a) private void OnApplicationSessionBegun (object? sender, SessionTokenEventArgs e) { - SubscribeAllSubViews (Application.Top!); + SubscribeAllSubViews (Application.Current!); _demoKeys = GetDemoKeyStrokes (); @@ -241,7 +241,7 @@ private void OnApplicationSessionBegun (object? sender, SessionTokenEventArgs e) return; - // Get a list of all subviews under Application.Top (and their subviews, etc.) + // Get a list of all subviews under Application.Current (and their subviews, etc.) // and subscribe to their DrawComplete event void SubscribeAllSubViews (View view) { diff --git a/Examples/UICatalog/Scenarios/AllViewsTester.cs b/Examples/UICatalog/Scenarios/AllViewsTester.cs index c3695121e8..2f6e9e0164 100644 --- a/Examples/UICatalog/Scenarios/AllViewsTester.cs +++ b/Examples/UICatalog/Scenarios/AllViewsTester.cs @@ -28,7 +28,7 @@ public class AllViewsTester : Scenario public override void Main () { - // Don't create a sub-win (Scenario.Win); just use Application.Top + // Don't create a sub-win (Scenario.Win); just use Application.Current Application.Init (); var app = new Window diff --git a/Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs b/Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs index e058ea4bf3..7d1b360201 100644 --- a/Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs +++ b/Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs @@ -92,7 +92,7 @@ private void OnWinOnInitialized (object? sender, EventArgs args) { // When updating from a Thread/Task always use Invoke Application.Invoke ( - () => + (_) => { _imageView.NextFrame (); _imageView.SetNeedsDraw (); diff --git a/Examples/UICatalog/Scenarios/AnsiRequestsScenario.cs b/Examples/UICatalog/Scenarios/AnsiRequestsScenario.cs index ad4f881bad..a8d7c72ade 100644 --- a/Examples/UICatalog/Scenarios/AnsiRequestsScenario.cs +++ b/Examples/UICatalog/Scenarios/AnsiRequestsScenario.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +#nullable enable using System.Text; namespace UICatalog.Scenarios; @@ -9,16 +7,19 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Tests")] public sealed class AnsiEscapeSequenceRequests : Scenario { - private GraphView _graphView; + private GraphView? _graphView; - private ScatterSeries _sentSeries; - private ScatterSeries _answeredSeries; + private ScatterSeries? _sentSeries; + private ScatterSeries? _answeredSeries; private readonly List _sends = new (); private readonly object _lockAnswers = new object (); private readonly Dictionary _answers = new (); - private Label _lblSummary; + private Label? _lblSummary; + + private object? _updateTimeoutToken; + private object? _sendDarTimeoutToken; public override void Main () { @@ -32,7 +33,7 @@ public override void Main () CanFocus = true }; - Tab single = new Tab (); + Tab single = new (); single.DisplayText = "Single"; single.View = BuildSingleTab (); @@ -57,6 +58,8 @@ public override void Main () single.View.Dispose (); appWindow.Dispose (); + Application.RemoveTimeout (_updateTimeoutToken!); + Application.RemoveTimeout (_sendDarTimeoutToken!); // Shutdown - Calling Application.Shutdown is required. Application.Shutdown (); } @@ -70,7 +73,7 @@ private View BuildSingleTab () CanFocus = true }; - w.Padding.Thickness = new (1); + w!.Padding!.Thickness = new (1); var scrRequests = new List { @@ -103,7 +106,7 @@ private View BuildSingleTab () } var selAnsiEscapeSequenceRequestName = scrRequests [cbRequests.SelectedItem]; - AnsiEscapeSequence selAnsiEscapeSequenceRequest = null; + AnsiEscapeSequence? selAnsiEscapeSequenceRequest = null; switch (selAnsiEscapeSequenceRequestName) { @@ -163,12 +166,12 @@ private View BuildSingleTab () Value = string.IsNullOrEmpty (tfValue.Text) ? null : tfValue.Text }; - Application.Driver.QueueAnsiRequest ( + Application.Driver?.QueueAnsiRequest ( new () { Request = ansiEscapeSequenceRequest.Request, Terminator = ansiEscapeSequenceRequest.Terminator, - ResponseReceived = (s) => OnSuccess (s, tvResponse, tvError, tvValue, tvTerminator, lblSuccess), + ResponseReceived = (s) => OnSuccess (s!, tvResponse, tvError, tvValue, tvTerminator, lblSuccess), Abandoned = () => OnFail (tvResponse, tvError, tvValue, tvTerminator, lblSuccess) }); }; @@ -218,21 +221,21 @@ private View BuildBulkTab () Width = Dim.Fill () }; - Application.AddTimeout ( - TimeSpan.FromMilliseconds (1000), - () => - { - lock (_lockAnswers) - { - UpdateGraph (); + _updateTimeoutToken = Application.AddTimeout ( + TimeSpan.FromMilliseconds (1000), + () => + { + lock (_lockAnswers) + { + UpdateGraph (); - UpdateResponses (); - } + UpdateResponses (); + } - return true; - }); + return true; + }); var tv = new TextView () { @@ -266,28 +269,28 @@ private View BuildBulkTab () int lastSendTime = Environment.TickCount; object lockObj = new object (); - Application.AddTimeout ( - TimeSpan.FromMilliseconds (50), - () => - { - lock (lockObj) - { - if (cbDar.Value > 0) - { - int interval = 1000 / cbDar.Value; // Calculate the desired interval in milliseconds - int currentTime = Environment.TickCount; // Current system time in milliseconds - - // Check if the time elapsed since the last send is greater than the interval - if (currentTime - lastSendTime >= interval) - { - SendDar (); // Send the request - lastSendTime = currentTime; // Update the last send time - } - } - } - - return true; - }); + _sendDarTimeoutToken = Application.AddTimeout ( + TimeSpan.FromMilliseconds (50), + () => + { + lock (lockObj) + { + if (cbDar.Value > 0) + { + int interval = 1000 / cbDar.Value; // Calculate the desired interval in milliseconds + int currentTime = Environment.TickCount; // Current system time in milliseconds + + // Check if the time elapsed since the last send is greater than the interval + if (currentTime - lastSendTime >= interval) + { + SendDar (); // Send the request + lastSendTime = currentTime; // Update the last send time + } + } + } + + return true; + }); _graphView = new GraphView () @@ -318,7 +321,7 @@ private View BuildBulkTab () } private void UpdateResponses () { - _lblSummary.Text = GetSummary (); + _lblSummary!.Text = GetSummary (); _lblSummary.SetNeedsDraw (); } @@ -340,8 +343,8 @@ private string GetSummary () private void SetupGraph () { - _graphView.Series.Add (_sentSeries = new ScatterSeries ()); - _graphView.Series.Add (_answeredSeries = new ScatterSeries ()); + _graphView!.Series.Add (_sentSeries = new ScatterSeries ()); + _graphView!.Series.Add (_answeredSeries = new ScatterSeries ()); _sentSeries.Fill = new GraphCellToRender (new Rune ('.'), new Attribute (ColorName16.BrightGreen, ColorName16.Black)); _answeredSeries.Fill = new GraphCellToRender (new Rune ('.'), new Attribute (ColorName16.BrightRed, ColorName16.Black)); @@ -358,17 +361,17 @@ private void SetupGraph () private void UpdateGraph () { - _sentSeries.Points = _sends + _sentSeries!.Points = _sends .GroupBy (ToSeconds) .Select (g => new PointF (g.Key, g.Count ())) .ToList (); - _answeredSeries.Points = _answers.Keys + _answeredSeries!.Points = _answers.Keys .GroupBy (ToSeconds) .Select (g => new PointF (g.Key, g.Count ())) .ToList (); // _graphView.ScrollOffset = new PointF(,0); - _graphView.SetNeedsDraw (); + _graphView!.SetNeedsDraw (); } @@ -379,13 +382,13 @@ private int ToSeconds (DateTime t) private void SendDar () { - Application.Driver.QueueAnsiRequest ( - new () - { - Request = EscSeqUtils.CSI_SendDeviceAttributes.Request, - Terminator = EscSeqUtils.CSI_SendDeviceAttributes.Terminator, - ResponseReceived = HandleResponse - }); + Application.Driver?.QueueAnsiRequest ( + new () + { + Request = EscSeqUtils.CSI_SendDeviceAttributes.Request, + Terminator = EscSeqUtils.CSI_SendDeviceAttributes.Terminator, + ResponseReceived = HandleResponse! + }); _sends.Add (DateTime.Now); } diff --git a/Examples/UICatalog/Scenarios/Bars.cs b/Examples/UICatalog/Scenarios/Bars.cs index 50153f07d6..03f9086111 100644 --- a/Examples/UICatalog/Scenarios/Bars.cs +++ b/Examples/UICatalog/Scenarios/Bars.cs @@ -28,7 +28,7 @@ public override void Main () // QuitKey and it only sticks if changed after init private void App_Loaded (object sender, EventArgs e) { - Application.Top!.Title = GetQuitKeyAndName (); + Application.Current!.Title = GetQuitKeyAndName (); ObservableCollection eventSource = new (); ListView eventLog = new ListView () @@ -41,7 +41,7 @@ private void App_Loaded (object sender, EventArgs e) Source = new ListWrapper (eventSource) }; eventLog.Border!.Thickness = new (0, 1, 0, 0); - Application.Top.Add (eventLog); + Application.Current.Add (eventLog); FrameView menuBarLikeExamples = new () { @@ -51,7 +51,7 @@ private void App_Loaded (object sender, EventArgs e) Width = Dim.Fill () - Dim.Width (eventLog), Height = Dim.Percent(33), }; - Application.Top.Add (menuBarLikeExamples); + Application.Current.Add (menuBarLikeExamples); Label label = new Label () { @@ -98,7 +98,7 @@ private void App_Loaded (object sender, EventArgs e) Width = Dim.Fill () - Dim.Width (eventLog), Height = Dim.Percent (33), }; - Application.Top.Add (menuLikeExamples); + Application.Current.Add (menuLikeExamples); label = new Label () { @@ -212,7 +212,7 @@ void MenuLikeExamplesMouseEvent (object _, MouseEventArgs e) Width = Dim.Width (menuLikeExamples), Height = Dim.Percent (33), }; - Application.Top.Add (statusBarLikeExamples); + Application.Current.Add (statusBarLikeExamples); label = new Label () { @@ -249,7 +249,7 @@ void MenuLikeExamplesMouseEvent (object _, MouseEventArgs e) ConfigStatusBar (bar); statusBarLikeExamples.Add (bar); - foreach (FrameView frameView in Application.Top.SubViews.Where (f => f is FrameView)!) + foreach (FrameView frameView in Application.Current.SubViews.Where (f => f is FrameView)!) { foreach (Bar barView in frameView.SubViews.Where (b => b is Bar)!) { @@ -269,8 +269,8 @@ void MenuLikeExamplesMouseEvent (object _, MouseEventArgs e) //private void SetupContentMenu () //{ - // Application.Top.Add (new Label { Text = "Right Click for Context Menu", X = Pos.Center (), Y = 4 }); - // Application.Top.MouseClick += ShowContextMenu; + // Application.Current.Add (new Label { Text = "Right Click for Context Menu", X = Pos.Center (), Y = 4 }); + // Application.Current.MouseClick += ShowContextMenu; //} //private void ShowContextMenu (object s, MouseEventEventArgs e) diff --git a/Examples/UICatalog/Scenarios/CombiningMarks.cs b/Examples/UICatalog/Scenarios/CombiningMarks.cs index 7d8437a230..47b8dfbc70 100644 --- a/Examples/UICatalog/Scenarios/CombiningMarks.cs +++ b/Examples/UICatalog/Scenarios/CombiningMarks.cs @@ -13,7 +13,7 @@ public override void Main () top.DrawComplete += (s, e) => { // Forces reset _lineColsOffset because we're dealing with direct draw - Application.Top!.SetNeedsDraw (); + Application.Current!.SetNeedsDraw (); var i = -1; top.AddStr ("Terminal.Gui only supports combining marks that normalize. See Issue #2616."); @@ -58,9 +58,9 @@ public override void Main () top.Move (0, ++i); top.AddStr ("From now on we are using TextFormatter"); TextFormatter tf = new () { Text = "[e\u0301\u0301\u0328]<- \"[e\\u0301\\u0301\\u0328]\" using TextFormatter." }; - tf.Draw (new (0, ++i, tf.Text.Length, 1), top.GetAttributeForRole (VisualRole.Normal), top.GetAttributeForRole (VisualRole.Normal)); + tf.Draw (driver: Application.Driver, screen: new (0, ++i, tf.Text.Length, 1), normalColor: top.GetAttributeForRole (VisualRole.Normal), hotColor: top.GetAttributeForRole (VisualRole.Normal)); tf.Text = "[e\u0328\u0301]<- \"[e\\u0328\\u0301]\" using TextFormatter."; - tf.Draw (new (0, ++i, tf.Text.Length, 1), top.GetAttributeForRole (VisualRole.Normal), top.GetAttributeForRole (VisualRole.Normal)); + tf.Draw (driver: Application.Driver, screen: new (0, ++i, tf.Text.Length, 1), normalColor: top.GetAttributeForRole (VisualRole.Normal), hotColor: top.GetAttributeForRole (VisualRole.Normal)); i++; top.Move (0, ++i); top.AddStr ("From now on we are using Surrogate pairs with combining diacritics"); diff --git a/Examples/UICatalog/Scenarios/ConfigurationEditor.cs b/Examples/UICatalog/Scenarios/ConfigurationEditor.cs index a5beca9e98..36011a78c2 100644 --- a/Examples/UICatalog/Scenarios/ConfigurationEditor.cs +++ b/Examples/UICatalog/Scenarios/ConfigurationEditor.cs @@ -75,7 +75,7 @@ public override void Main () void ConfigurationManagerOnApplied (object? sender, ConfigurationManagerEventArgs e) { - Application.Top?.SetNeedsDraw (); + Application.Current?.SetNeedsDraw (); } } public void Save () diff --git a/Examples/UICatalog/Scenarios/ContextMenus.cs b/Examples/UICatalog/Scenarios/ContextMenus.cs index 141392f292..bb3a7b19ea 100644 --- a/Examples/UICatalog/Scenarios/ContextMenus.cs +++ b/Examples/UICatalog/Scenarios/ContextMenus.cs @@ -1,5 +1,7 @@ -using System.Globalization; +#nullable enable +using System.Globalization; using JetBrains.Annotations; +// ReSharper disable AccessToDisposedClosure namespace UICatalog.Scenarios; @@ -7,78 +9,85 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Menus")] public class ContextMenus : Scenario { - [CanBeNull] - private PopoverMenu _winContextMenu; - private TextField _tfTopLeft, _tfTopRight, _tfMiddle, _tfBottomLeft, _tfBottomRight; - private readonly List _cultureInfos = Application.SupportedCultures; + private PopoverMenu? _winContextMenu; + private TextField? _tfTopLeft, _tfTopRight, _tfMiddle, _tfBottomLeft, _tfBottomRight; + private readonly List? _cultureInfos = Application.SupportedCultures; private readonly Key _winContextMenuKey = Key.Space.WithCtrl; + private Window? _appWindow; + public override void Main () { // Init Application.Init (); // Setup - Create a top-level application window and configure it. - Window appWindow = new () + _appWindow = new () { Title = GetQuitKeyAndName (), Arrangement = ViewArrangement.Fixed, SchemeName = "Toplevel" }; - var text = "Context Menu"; - var width = 20; + _appWindow.Initialized += AppWindowOnInitialized; - CreateWinContextMenu (); + // Run - Start the application. + Application.Run (_appWindow); + _appWindow.Dispose (); + _appWindow.KeyDown -= OnAppWindowOnKeyDown; + _appWindow.MouseClick -= OnAppWindowOnMouseClick; + _winContextMenu?.Dispose (); - var label = new Label - { - X = Pos.Center (), Y = 1, Text = $"Press '{_winContextMenuKey}' to open the Window context menu." - }; - appWindow.Add (label); + // Shutdown - Calling Application.Shutdown is required. + Application.Shutdown (); + + return; - label = new () + void AppWindowOnInitialized (object? sender, EventArgs e) { - X = Pos.Center (), - Y = Pos.Bottom (label), - Text = $"Press '{PopoverMenu.DefaultKey}' to open the TextField context menu." - }; - appWindow.Add (label); - _tfTopLeft = new () { Id = "_tfTopLeft", Width = width, Text = text }; - appWindow.Add (_tfTopLeft); + var text = "Context Menu"; + var width = 20; + + CreateWinContextMenu (); - _tfTopRight = new () { Id = "_tfTopRight", X = Pos.AnchorEnd (width), Width = width, Text = text }; - appWindow.Add (_tfTopRight); + var label = new Label + { + X = Pos.Center (), Y = 1, Text = $"Press '{_winContextMenuKey}' to open the Window context menu." + }; + _appWindow.Add (label); - _tfMiddle = new () { Id = "_tfMiddle", X = Pos.Center (), Y = Pos.Center (), Width = width, Text = text }; - appWindow.Add (_tfMiddle); + label = new () + { + X = Pos.Center (), + Y = Pos.Bottom (label), + Text = $"Press '{PopoverMenu.DefaultKey}' to open the TextField context menu." + }; + _appWindow.Add (label); - _tfBottomLeft = new () { Id = "_tfBottomLeft", Y = Pos.AnchorEnd (1), Width = width, Text = text }; - appWindow.Add (_tfBottomLeft); + _tfTopLeft = new () { Id = "_tfTopLeft", Width = width, Text = text }; + _appWindow.Add (_tfTopLeft); - _tfBottomRight = new () { Id = "_tfBottomRight", X = Pos.AnchorEnd (width), Y = Pos.AnchorEnd (1), Width = width, Text = text }; - appWindow.Add (_tfBottomRight); + _tfTopRight = new () { Id = "_tfTopRight", X = Pos.AnchorEnd (width), Width = width, Text = text }; + _appWindow.Add (_tfTopRight); - appWindow.KeyDown += OnAppWindowOnKeyDown; - appWindow.MouseClick += OnAppWindowOnMouseClick; + _tfMiddle = new () { Id = "_tfMiddle", X = Pos.Center (), Y = Pos.Center (), Width = width, Text = text }; + _appWindow.Add (_tfMiddle); - CultureInfo originalCulture = Thread.CurrentThread.CurrentUICulture; - appWindow.Closed += (s, e) => { Thread.CurrentThread.CurrentUICulture = originalCulture; }; + _tfBottomLeft = new () { Id = "_tfBottomLeft", Y = Pos.AnchorEnd (1), Width = width, Text = text }; + _appWindow.Add (_tfBottomLeft); - // Run - Start the application. - Application.Run (appWindow); - appWindow.Dispose (); - appWindow.KeyDown -= OnAppWindowOnKeyDown; - appWindow.MouseClick -= OnAppWindowOnMouseClick; - _winContextMenu?.Dispose (); + _tfBottomRight = new () { Id = "_tfBottomRight", X = Pos.AnchorEnd (width), Y = Pos.AnchorEnd (1), Width = width, Text = text }; + _appWindow.Add (_tfBottomRight); - // Shutdown - Calling Application.Shutdown is required. - Application.Shutdown (); + _appWindow.KeyDown += OnAppWindowOnKeyDown; + _appWindow.MouseClick += OnAppWindowOnMouseClick; - return; + CultureInfo originalCulture = Thread.CurrentThread.CurrentUICulture; + _appWindow.Closed += (s, e) => { Thread.CurrentThread.CurrentUICulture = originalCulture; }; + } - void OnAppWindowOnMouseClick (object s, MouseEventArgs e) + void OnAppWindowOnMouseClick (object? s, MouseEventArgs e) { if (e.Flags == MouseFlags.Button3Clicked) { @@ -88,7 +97,7 @@ void OnAppWindowOnMouseClick (object s, MouseEventArgs e) } } - void OnAppWindowOnKeyDown (object s, Key e) + void OnAppWindowOnKeyDown (object? s, Key e) { if (e == _winContextMenuKey) { @@ -101,12 +110,6 @@ void OnAppWindowOnKeyDown (object s, Key e) private void CreateWinContextMenu () { - if (_winContextMenu is { }) - { - _winContextMenu.Dispose (); - _winContextMenu = null; - } - _winContextMenu = new ( [ new MenuItemv2 @@ -171,6 +174,7 @@ private void CreateWinContextMenu () { Key = _winContextMenuKey }; + Application.Popover?.Register (_winContextMenu); } private Menuv2 GetSupportedCultureMenu () @@ -178,7 +182,7 @@ private Menuv2 GetSupportedCultureMenu () List supportedCultures = []; int index = -1; - foreach (CultureInfo c in _cultureInfos) + foreach (CultureInfo c in _cultureInfos!) { MenuItemv2 culture = new (); diff --git a/Examples/UICatalog/Scenarios/CsvEditor.cs b/Examples/UICatalog/Scenarios/CsvEditor.cs index 609cb4e06b..aca10fdac2 100644 --- a/Examples/UICatalog/Scenarios/CsvEditor.cs +++ b/Examples/UICatalog/Scenarios/CsvEditor.cs @@ -502,7 +502,7 @@ private void Open (string filename) // Only set the current filename if we successfully loaded the entire file _currentFile = filename; _selectedCellTextField.SuperView.Enabled = true; - Application.Top.Title = $"{GetName ()} - {Path.GetFileName (_currentFile)}"; + Application.Current.Title = $"{GetName ()} - {Path.GetFileName (_currentFile)}"; } catch (Exception ex) { diff --git a/Examples/UICatalog/Scenarios/Images.cs b/Examples/UICatalog/Scenarios/Images.cs index 488f595a72..a9854092d9 100644 --- a/Examples/UICatalog/Scenarios/Images.cs +++ b/Examples/UICatalog/Scenarios/Images.cs @@ -151,7 +151,7 @@ public override void Main () _win.Add (_tabView); // Start trying to detect sixel support - var sixelSupportDetector = new SixelSupportDetector (); + var sixelSupportDetector = new SixelSupportDetector (Application.Driver); sixelSupportDetector.Detect (UpdateSixelSupportState); Application.Run (_win); diff --git a/Examples/UICatalog/Scenarios/Mazing.cs b/Examples/UICatalog/Scenarios/Mazing.cs index 01bbc41a4e..935c72ae56 100644 --- a/Examples/UICatalog/Scenarios/Mazing.cs +++ b/Examples/UICatalog/Scenarios/Mazing.cs @@ -171,7 +171,7 @@ private void TopCommandNotBound (object? sender, CommandEventArgs e) if (_m.PlayerHp <= 0) { _message = "You died!"; - Application.Top!.SetNeedsDraw (); // trigger redraw + Application.Current!.SetNeedsDraw (); // trigger redraw _dead = true; return; // Stop further action if dead @@ -190,7 +190,7 @@ private void TopCommandNotBound (object? sender, CommandEventArgs e) _message = string.Empty; } - Application.Top!.SetNeedsDraw (); // trigger redraw + Application.Current!.SetNeedsDraw (); // trigger redraw } // Optional win condition: @@ -200,7 +200,7 @@ private void TopCommandNotBound (object? sender, CommandEventArgs e) _m = new (); // Generate a new maze _m.PlayerHp = hp; GenerateNpcs (); - Application.Top!.SetNeedsDraw (); // trigger redraw + Application.Current!.SetNeedsDraw (); // trigger redraw } } } diff --git a/Examples/UICatalog/Scenarios/Menus.cs b/Examples/UICatalog/Scenarios/Menus.cs index 70f67f6e29..7558447154 100644 --- a/Examples/UICatalog/Scenarios/Menus.cs +++ b/Examples/UICatalog/Scenarios/Menus.cs @@ -121,7 +121,7 @@ public MenuHost () Command.Cancel, ctx => { - if (Application.Popover?.GetActivePopover () as PopoverMenu is { Visible: true } visiblePopover) + if (App?.Popover?.GetActivePopover () as PopoverMenu is { Visible: true } visiblePopover) { visiblePopover.Visible = false; } diff --git a/Examples/UICatalog/Scenarios/Navigation.cs b/Examples/UICatalog/Scenarios/Navigation.cs index 7ce1d3317f..94e7dad8ec 100644 --- a/Examples/UICatalog/Scenarios/Navigation.cs +++ b/Examples/UICatalog/Scenarios/Navigation.cs @@ -219,7 +219,7 @@ void OnApplicationIteration (object sender, IterationEventArgs args) progressBar.Fraction += 0.01f; - Application.Invoke (() => { }); + Application.Invoke ((_) => { }); } void ColorPicker_ColorChanged (object sender, ResultEventArgs e) diff --git a/Examples/UICatalog/Scenarios/Notepad.cs b/Examples/UICatalog/Scenarios/Notepad.cs index 996702298d..595604a7bc 100644 --- a/Examples/UICatalog/Scenarios/Notepad.cs +++ b/Examples/UICatalog/Scenarios/Notepad.cs @@ -294,7 +294,7 @@ private void TabView_TabClicked (object? sender, TabMouseEventArgs e) // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused // and the context menu is disposed when it is closed. - Application.Popover?.Register (contextMenu); + tv.App!.Popover?.Register (contextMenu); contextMenu?.MakeVisible (e.MouseEvent.ScreenPosition); e.MouseEvent.Handled = true; diff --git a/Examples/UICatalog/Scenarios/Progress.cs b/Examples/UICatalog/Scenarios/Progress.cs index 4696c160c7..0e3af66c43 100644 --- a/Examples/UICatalog/Scenarios/Progress.cs +++ b/Examples/UICatalog/Scenarios/Progress.cs @@ -43,7 +43,7 @@ public override void Main () { // Note the check for Mainloop being valid. System.Timers can run after they are Disposed. // This code must be defensive for that. - Application.Invoke (() => systemTimerDemo.Pulse ()); + Application.Invoke ((_) => systemTimerDemo.Pulse ()); }, null, 0, diff --git a/Examples/UICatalog/Scenarios/RegionScenario.cs b/Examples/UICatalog/Scenarios/RegionScenario.cs index dea5d5f0a3..495d90d6a2 100644 --- a/Examples/UICatalog/Scenarios/RegionScenario.cs +++ b/Examples/UICatalog/Scenarios/RegionScenario.cs @@ -24,31 +24,31 @@ public override void Main () { Application.Init (); - Window app = new () + Window appWindow = new () { Title = GetQuitKeyAndName (), TabStop = TabBehavior.TabGroup }; - app.Padding!.Thickness = new (1); + appWindow.Padding!.Thickness = new (1); var tools = new ToolsView { Title = "Tools", X = Pos.AnchorEnd (), Y = 2 }; - tools.CurrentAttribute = app.GetAttributeForRole (VisualRole.HotNormal); + tools.CurrentAttribute = appWindow.GetAttributeForRole (VisualRole.HotNormal); tools.SetStyle += b => { _drawStyle = b; - app.SetNeedsDraw (); + appWindow.SetNeedsDraw (); }; tools.RegionOpChanged += (s, e) => { _regionOp = e; }; //tools.AddLayer += () => canvas.AddLayer (); - app.Add (tools); + appWindow.Add (tools); // Add drag handling to window - app.MouseEvent += (s, e) => + appWindow.MouseEvent += (s, e) => { if (e.Flags.HasFlag (MouseFlags.Button1Pressed)) { @@ -62,7 +62,7 @@ public override void Main () // Drag if (_isDragging && _dragStart.HasValue) { - app.SetNeedsDraw (); + appWindow.SetNeedsDraw (); } } } @@ -77,31 +77,31 @@ public override void Main () _dragStart = null; } - app.SetNeedsDraw (); + appWindow.SetNeedsDraw (); } }; // Draw the regions - app.DrawingContent += (s, e) => + appWindow.DrawingContent += (s, e) => { // Draw all regions with single line style //_region.FillRectangles (_attribute.Value, _fillRune); switch (_drawStyle) { case RegionDrawStyles.FillOnly: - _region.FillRectangles (tools.CurrentAttribute!.Value, _previewFillRune); + _region.FillRectangles (appWindow.App?.Driver, tools.CurrentAttribute!.Value, _previewFillRune); break; case RegionDrawStyles.InnerBoundaries: - _region.DrawBoundaries (app.LineCanvas, LineStyle.Single, tools.CurrentAttribute); - _region.FillRectangles (tools.CurrentAttribute!.Value, (Rune)' '); + _region.DrawBoundaries (appWindow.LineCanvas, LineStyle.Single, tools.CurrentAttribute); + _region.FillRectangles (appWindow.App?.Driver, tools.CurrentAttribute!.Value, (Rune)' '); break; case RegionDrawStyles.OuterBoundary: - _region.DrawOuterBoundary (app.LineCanvas, LineStyle.Single, tools.CurrentAttribute); - _region.FillRectangles (tools.CurrentAttribute!.Value, (Rune)' '); + _region.DrawOuterBoundary (appWindow.LineCanvas, LineStyle.Single, tools.CurrentAttribute); + _region.FillRectangles (appWindow.App?.Driver, tools.CurrentAttribute!.Value, (Rune)' '); break; } @@ -109,14 +109,14 @@ public override void Main () // If currently dragging, draw preview rectangle if (_isDragging && _dragStart.HasValue) { - Point currentMousePos = Application.GetLastMousePosition ()!.Value; + Point currentMousePos = appWindow.App!.Mouse.LastMousePosition!.Value; Rectangle previewRect = GetRectFromPoints (_dragStart.Value, currentMousePos); var previewRegion = new Region (previewRect); - previewRegion.FillRectangles (tools.CurrentAttribute!.Value, (Rune)' '); + previewRegion.FillRectangles (appWindow.App.Driver, tools.CurrentAttribute!.Value, (Rune)' '); previewRegion.DrawBoundaries ( - app.LineCanvas, + appWindow.LineCanvas, LineStyle.Dashed, new ( tools.CurrentAttribute!.Value.Foreground.GetBrighterColor (), @@ -124,10 +124,10 @@ public override void Main () } }; - Application.Run (app); + Application.Run (appWindow); // Clean up - app.Dispose (); + appWindow.Dispose (); Application.Shutdown (); } diff --git a/Examples/UICatalog/Scenarios/Shortcuts.cs b/Examples/UICatalog/Scenarios/Shortcuts.cs index 712b69acf0..331ed03377 100644 --- a/Examples/UICatalog/Scenarios/Shortcuts.cs +++ b/Examples/UICatalog/Scenarios/Shortcuts.cs @@ -28,7 +28,7 @@ public override void Main () private void App_Loaded (object? sender, EventArgs e) { Application.QuitKey = Key.F4.WithCtrl; - Application.Top!.Title = GetQuitKeyAndName (); + Application.Current!.Title = GetQuitKeyAndName (); ObservableCollection eventSource = new (); @@ -46,14 +46,14 @@ private void App_Loaded (object? sender, EventArgs e) eventLog.Width = Dim.Func ( _ => Math.Min ( - Application.Top.Viewport.Width / 2, + Application.Current.Viewport.Width / 2, eventLog?.MaxLength + eventLog!.GetAdornmentsThickness ().Horizontal ?? 0)); eventLog.Width = Dim.Func ( _ => Math.Min ( eventLog.SuperView!.Viewport.Width / 2, eventLog?.MaxLength + eventLog!.GetAdornmentsThickness ().Horizontal ?? 0)); - Application.Top.Add (eventLog); + Application.Current.Add (eventLog); var alignKeysShortcut = new Shortcut { @@ -86,7 +86,7 @@ private void App_Loaded (object? sender, EventArgs e) }; - Application.Top.Add (alignKeysShortcut); + Application.Current.Add (alignKeysShortcut); var commandFirstShortcut = new Shortcut { @@ -115,7 +115,7 @@ private void App_Loaded (object? sender, EventArgs e) $"{commandFirstShortcut.Id}.CommandView.CheckedStateChanging: {cb.Text}"); eventLog.MoveDown (); - IEnumerable toAlign = Application.Top.SubViews.OfType (); + IEnumerable toAlign = Application.Current.SubViews.OfType (); IEnumerable enumerable = toAlign as View [] ?? toAlign.ToArray (); foreach (View view in enumerable) @@ -134,7 +134,7 @@ private void App_Loaded (object? sender, EventArgs e) } }; - Application.Top.Add (commandFirstShortcut); + Application.Current.Add (commandFirstShortcut); var canFocusShortcut = new Shortcut { @@ -159,7 +159,7 @@ private void App_Loaded (object? sender, EventArgs e) SetCanFocus (e.Result == CheckState.Checked); } }; - Application.Top.Add (canFocusShortcut); + Application.Current.Add (canFocusShortcut); var appShortcut = new Shortcut { @@ -173,7 +173,7 @@ private void App_Loaded (object? sender, EventArgs e) BindKeyToApplication = true }; - Application.Top.Add (appShortcut); + Application.Current.Add (appShortcut); var buttonShortcut = new Shortcut { @@ -193,7 +193,7 @@ private void App_Loaded (object? sender, EventArgs e) var button = (Button)buttonShortcut.CommandView; buttonShortcut.Accepting += Button_Clicked; - Application.Top.Add (buttonShortcut); + Application.Current.Add (buttonShortcut); var optionSelectorShortcut = new Shortcut { @@ -221,7 +221,7 @@ private void App_Loaded (object? sender, EventArgs e) } }; - Application.Top.Add (optionSelectorShortcut); + Application.Current.Add (optionSelectorShortcut); var sliderShortcut = new Shortcut { @@ -248,7 +248,7 @@ private void App_Loaded (object? sender, EventArgs e) eventLog.MoveDown (); }; - Application.Top.Add (sliderShortcut); + Application.Current.Add (sliderShortcut); ListView listView = new ListView () { @@ -270,7 +270,7 @@ private void App_Loaded (object? sender, EventArgs e) Key = Key.F5.WithCtrl, }; - Application.Top.Add (listViewShortcut); + Application.Current.Add (listViewShortcut); var noCommandShortcut = new Shortcut { @@ -282,7 +282,7 @@ private void App_Loaded (object? sender, EventArgs e) Key = Key.D0 }; - Application.Top.Add (noCommandShortcut); + Application.Current.Add (noCommandShortcut); var noKeyShortcut = new Shortcut { @@ -295,7 +295,7 @@ private void App_Loaded (object? sender, EventArgs e) HelpText = "Keyless" }; - Application.Top.Add (noKeyShortcut); + Application.Current.Add (noKeyShortcut); var noHelpShortcut = new Shortcut { @@ -308,7 +308,7 @@ private void App_Loaded (object? sender, EventArgs e) HelpText = "" }; - Application.Top.Add (noHelpShortcut); + Application.Current.Add (noHelpShortcut); noHelpShortcut.SetFocus (); var framedShortcut = new Shortcut @@ -340,7 +340,7 @@ private void App_Loaded (object? sender, EventArgs e) } framedShortcut.SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Toplevel); - Application.Top.Add (framedShortcut); + Application.Current.Add (framedShortcut); // Horizontal var progressShortcut = new Shortcut @@ -387,7 +387,7 @@ private void App_Loaded (object? sender, EventArgs e) }; timer.Start (); - Application.Top.Add (progressShortcut); + Application.Current.Add (progressShortcut); var textField = new TextField { @@ -408,7 +408,7 @@ private void App_Loaded (object? sender, EventArgs e) }; textField.CanFocus = true; - Application.Top.Add (textFieldShortcut); + Application.Current.Add (textFieldShortcut); var bgColorShortcut = new Shortcut { @@ -450,19 +450,19 @@ private void App_Loaded (object? sender, EventArgs e) eventSource.Add ($"ColorChanged: {o.GetType ().Name} - {args.Result}"); eventLog.MoveDown (); - Application.Top.SetScheme ( - new (Application.Top.GetScheme ()) + Application.Current.SetScheme ( + new (Application.Current.GetScheme ()) { Normal = new ( - Application.Top!.GetAttributeForRole (VisualRole.Normal).Foreground, + Application.Current!.GetAttributeForRole (VisualRole.Normal).Foreground, args.Result, - Application.Top!.GetAttributeForRole (VisualRole.Normal).Style) + Application.Current!.GetAttributeForRole (VisualRole.Normal).Style) }); } }; bgColorShortcut.CommandView = bgColor; - Application.Top.Add (bgColorShortcut); + Application.Current.Add (bgColorShortcut); var appQuitShortcut = new Shortcut { @@ -476,9 +476,9 @@ private void App_Loaded (object? sender, EventArgs e) }; appQuitShortcut.Accepting += (o, args) => { Application.RequestStop (); }; - Application.Top.Add (appQuitShortcut); + Application.Current.Add (appQuitShortcut); - foreach (Shortcut shortcut in Application.Top.SubViews.OfType ()) + foreach (Shortcut shortcut in Application.Current.SubViews.OfType ()) { shortcut.Selecting += (o, args) => { @@ -529,7 +529,7 @@ private void App_Loaded (object? sender, EventArgs e) void SetCanFocus (bool canFocus) { - foreach (Shortcut peer in Application.Top!.SubViews.OfType ()) + foreach (Shortcut peer in Application.Current!.SubViews.OfType ()) { if (peer.CanFocus) { @@ -542,7 +542,7 @@ void AlignKeys (bool align) { var max = 0; - IEnumerable toAlign = Application.Top!.SubViews.OfType ().Where(s => !s.Y.Has(out _)).Cast(); + IEnumerable toAlign = Application.Current!.SubViews.OfType ().Where(s => !s.Y.Has(out _)).Cast(); IEnumerable enumerable = toAlign as Shortcut [] ?? toAlign.ToArray (); if (align) diff --git a/Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs b/Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs index 62c35ac40e..571597f8c0 100644 --- a/Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs +++ b/Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs @@ -179,9 +179,9 @@ private void RunWorker () var builderUI = new StagingUIController (_startStaging, e.Result as ObservableCollection); - Toplevel top = Application.Top; + Toplevel top = Application.Current; top.Visible = false; - Application.Top.Visible = false; + Application.Current.Visible = false; builderUI.Load (); builderUI.Dispose (); top.Visible = true; diff --git a/Examples/UICatalog/Scenarios/TableEditor.cs b/Examples/UICatalog/Scenarios/TableEditor.cs index 84d7644b95..57b1f9a608 100644 --- a/Examples/UICatalog/Scenarios/TableEditor.cs +++ b/Examples/UICatalog/Scenarios/TableEditor.cs @@ -1363,7 +1363,7 @@ private void ShowHeaderContextMenu (int clickedCol, MouseEventArgs e) // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused // and the context menu is disposed when it is closed. - Application.Popover?.Register (contextMenu); + e.View?.App!.Popover?.Register (contextMenu); contextMenu?.MakeVisible (new (e.ScreenPosition.X + 1, e.ScreenPosition.Y + 1)); } diff --git a/Examples/UICatalog/Scenarios/Themes.cs b/Examples/UICatalog/Scenarios/Themes.cs index 6e4d676eae..d515b5ee0d 100644 --- a/Examples/UICatalog/Scenarios/Themes.cs +++ b/Examples/UICatalog/Scenarios/Themes.cs @@ -129,7 +129,7 @@ public override void Main () { if (_view is { }) { - Application.Top!.SchemeName = args.NewValue; + Application.Current!.SchemeName = args.NewValue; if (_view.HasScheme) { @@ -160,11 +160,11 @@ public override void Main () TabStop = TabBehavior.TabStop }; - allViewsView.FocusedChanged += (s, args) => + allViewsView.FocusedChanged += (s, a) => { allViewsView.Title = - $"All Views - Focused: {args.NewFocused.Title}"; - viewPropertiesEditor.ViewToEdit = args.NewFocused.SubViews.ElementAt(0); + $"All Views - Focused: {a.NewFocused?.Title}"; + viewPropertiesEditor.ViewToEdit = a.NewFocused?.SubViews.ElementAt(0); }; appWindow.Add (allViewsView); diff --git a/Examples/UICatalog/Scenarios/TreeUseCases.cs b/Examples/UICatalog/Scenarios/TreeUseCases.cs index 4603b9e54c..d9ee48d155 100644 --- a/Examples/UICatalog/Scenarios/TreeUseCases.cs +++ b/Examples/UICatalog/Scenarios/TreeUseCases.cs @@ -77,7 +77,7 @@ private void LoadArmies (bool useDelegate) if (_currentTree != null) { - Application.Top.Remove (_currentTree); + Application.Current.Remove (_currentTree); _currentTree.Dispose (); } @@ -97,7 +97,7 @@ o is Army a tree.TreeBuilder = new GameObjectTreeBuilder (); } - Application.Top.Add (tree); + Application.Current.Add (tree); tree.AddObject (army1); @@ -117,13 +117,13 @@ private void LoadRooms () if (_currentTree != null) { - Application.Top.Remove (_currentTree); + Application.Current.Remove (_currentTree); _currentTree.Dispose (); } var tree = new TreeView { X = 0, Y = 1, Width = Dim.Fill(), Height = Dim.Fill (1) }; - Application.Top.Add (tree); + Application.Current.Add (tree); tree.AddObject (myHouse); @@ -134,13 +134,13 @@ private void LoadSimpleNodes () { if (_currentTree != null) { - Application.Top.Remove (_currentTree); + Application.Current.Remove (_currentTree); _currentTree.Dispose (); } var tree = new TreeView { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) }; - Application.Top.Add (tree); + Application.Current.Add (tree); var root1 = new TreeNode ("Root1"); root1.Children.Add (new TreeNode ("Child1.1")); diff --git a/Examples/UICatalog/Scenarios/TreeViewFileSystem.cs b/Examples/UICatalog/Scenarios/TreeViewFileSystem.cs index 34540d5ccc..eeb4f8b786 100644 --- a/Examples/UICatalog/Scenarios/TreeViewFileSystem.cs +++ b/Examples/UICatalog/Scenarios/TreeViewFileSystem.cs @@ -418,7 +418,7 @@ private void ShowContextMenu (Point screenPoint, IFileSystemInfo forObject) // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused // and the context menu is disposed when it is closed. - Application.Popover?.Register (contextMenu); + _detailsFrame.App?.Popover?.Register (contextMenu); Application.Invoke (() => contextMenu?.MakeVisible (screenPoint)); } diff --git a/Examples/UICatalog/Scenarios/ViewExperiments.cs b/Examples/UICatalog/Scenarios/ViewExperiments.cs index b7abf8588f..c36fce3e7c 100644 --- a/Examples/UICatalog/Scenarios/ViewExperiments.cs +++ b/Examples/UICatalog/Scenarios/ViewExperiments.cs @@ -75,15 +75,15 @@ public override void Main () Y = Pos.Center (), Title = $"_Close", }; - //popoverButton.Accepting += (sender, e) => Application.Popover!.Visible = false; + //popoverButton.Accepting += (sender, e) => App?.Popover!.Visible = false; popoverView.Add (popoverButton); button.Accepting += ButtonAccepting; void ButtonAccepting (object sender, CommandEventArgs e) { - //Application.Popover = popoverView; - //Application.Popover!.Visible = true; + //App?.Popover = popoverView; + //App?.Popover!.Visible = true; } testFrame.MouseClick += TestFrameOnMouseClick; @@ -94,8 +94,8 @@ void TestFrameOnMouseClick (object sender, MouseEventArgs e) { popoverView.X = e.ScreenPosition.X; popoverView.Y = e.ScreenPosition.Y; - //Application.Popover = popoverView; - //Application.Popover!.Visible = true; + //App?.Popover = popoverView; + //App?.Popover!.Visible = true; } } diff --git a/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs b/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs index fcf57343a7..afe039f1c7 100644 --- a/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs +++ b/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs @@ -69,7 +69,7 @@ static int About () // add it to our list listWin.Add (win); - // create 3 more Windows in a loop, adding them Application.Top + // create 3 more Windows in a loop, adding them Application.Current // Each with a // button // sub Window with diff --git a/Examples/UICatalog/UICatalog.cs b/Examples/UICatalog/UICatalog.cs index 6f7a371390..4460572592 100644 --- a/Examples/UICatalog/UICatalog.cs +++ b/Examples/UICatalog/UICatalog.cs @@ -242,7 +242,7 @@ private static ILogger CreateLogger () /// /// Shows the UI Catalog selection UI. When the user selects a Scenario to run, the UI Catalog main app UI is - /// killed and the Scenario is run as though it were Application.Top. When the Scenario exits, this function exits. + /// killed and the Scenario is run as though it were Application.Current. When the Scenario exits, this function exits. /// /// private static Scenario RunUICatalogTopLevel () @@ -269,7 +269,7 @@ private static Scenario RunUICatalogTopLevel () [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] private static readonly FileSystemWatcher _homeDirWatcher = new (); - private static void StartConfigFileWatcher () + private static void StartConfigWatcher () { // Set up a file system watcher for `./.tui/` _currentDirWatcher.NotifyFilter = NotifyFilters.LastWrite; @@ -317,10 +317,19 @@ private static void StartConfigFileWatcher () //_homeDirWatcher.Created += ConfigFileChanged; _homeDirWatcher.EnableRaisingEvents = true; + + ThemeManager.ThemeChanged += ThemeManagerOnThemeChanged; } - private static void StopConfigFileWatcher () + private static void ThemeManagerOnThemeChanged (object? sender, EventArgs e) { + CM.Apply (); + } + + private static void StopConfigWatcher () + { + ThemeManager.ThemeChanged += ThemeManagerOnThemeChanged; + _currentDirWatcher.EnableRaisingEvents = false; _currentDirWatcher.Changed -= ConfigFileChanged; _currentDirWatcher.Created -= ConfigFileChanged; @@ -332,7 +341,7 @@ private static void StopConfigFileWatcher () private static void ConfigFileChanged (object sender, FileSystemEventArgs e) { - if (Application.Top == null) + if (Application.Current == null) { return; } @@ -398,7 +407,7 @@ private static void UICatalogMain (UICatalogCommandLineOptions options) if (!Options.DontEnableConfigurationManagement) { ConfigurationManager.Enable (ConfigLocations.All); - StartConfigFileWatcher (); + StartConfigWatcher (); } while (RunUICatalogTopLevel () is { } scenario) @@ -440,7 +449,7 @@ void ApplicationOnInitializedChanged (object? sender, EventArgs e) #endif } - StopConfigFileWatcher (); + StopConfigWatcher (); VerifyObjectsWereDisposed (); } diff --git a/Examples/UICatalog/UICatalog.csproj b/Examples/UICatalog/UICatalog.csproj index 0b529c4994..88e916f9cf 100644 --- a/Examples/UICatalog/UICatalog.csproj +++ b/Examples/UICatalog/UICatalog.csproj @@ -49,4 +49,5 @@ + \ No newline at end of file diff --git a/Examples/UICatalog/UICatalogTop.cs b/Examples/UICatalog/UICatalogTop.cs index afec138a80..88a5e41443 100644 --- a/Examples/UICatalog/UICatalogTop.cs +++ b/Examples/UICatalog/UICatalogTop.cs @@ -211,6 +211,7 @@ View [] CreateThemeMenuItems () return; } ThemeManager.Theme = ThemeManager.GetThemeNames () [(int)args.Value]; + }; var menuItem = new MenuItemv2 @@ -691,7 +692,7 @@ private void ConfigApplied () _disableMouseCb!.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked; _force16ColorsShortcutCb!.CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked; - Application.Top?.SetNeedsDraw (); + Application.Current?.SetNeedsDraw (); } private void ConfigAppliedHandler (object? sender, ConfigurationManagerEventArgs? a) { ConfigApplied (); } diff --git a/NULLABLE_VIEWS_REMAINING.md b/NULLABLE_VIEWS_REMAINING.md new file mode 100644 index 0000000000..220e6933f8 --- /dev/null +++ b/NULLABLE_VIEWS_REMAINING.md @@ -0,0 +1,163 @@ +# View Subclasses Still With `#nullable disable` + +This document lists all View-related files in the `/Views` directory that still have `#nullable disable` set. + +**Total**: 121 files + +## Breakdown by Subdirectory + +### Autocomplete (8 files) +- Autocomplete/AppendAutocomplete.cs +- Autocomplete/AutocompleteBase.cs +- Autocomplete/AutocompleteContext.cs +- Autocomplete/AutocompleteFilepathContext.cs +- Autocomplete/IAutocomplete.cs +- Autocomplete/ISuggestionGenerator.cs +- Autocomplete/SingleWordSuggestionGenerator.cs +- Autocomplete/Suggestion.cs + +### CollectionNavigation (7 files) +- CollectionNavigation/CollectionNavigator.cs +- CollectionNavigation/CollectionNavigatorBase.cs +- CollectionNavigation/DefaultCollectionNavigatorMatcher.cs +- CollectionNavigation/ICollectionNavigator.cs +- CollectionNavigation/ICollectionNavigatorMatcher.cs +- CollectionNavigation/IListCollectionNavigator.cs +- CollectionNavigation/TableCollectionNavigator.cs + +### Color/ColorPicker (13 files) +- Color/BBar.cs +- Color/ColorBar.cs +- Color/ColorModelStrategy.cs +- Color/ColorPicker.16.cs +- Color/ColorPicker.Prompt.cs +- Color/ColorPicker.Style.cs +- Color/ColorPicker.cs +- Color/GBar.cs +- Color/HueBar.cs +- Color/IColorBar.cs +- Color/LightnessBar.cs +- Color/RBar.cs +- Color/SaturationBar.cs +- Color/ValueBar.cs + +### FileDialogs (10 files) +- FileDialogs/AllowedType.cs +- FileDialogs/DefaultFileOperations.cs +- FileDialogs/FileDialogCollectionNavigator.cs +- FileDialogs/FileDialogHistory.cs +- FileDialogs/FileDialogState.cs +- FileDialogs/FileDialogStyle.cs +- FileDialogs/FileDialogTableSource.cs +- FileDialogs/FilesSelectedEventArgs.cs +- FileDialogs/OpenDialog.cs +- FileDialogs/OpenMode.cs +- FileDialogs/SaveDialog.cs + +### GraphView (9 files) +- GraphView/Axis.cs +- GraphView/BarSeriesBar.cs +- GraphView/GraphCellToRender.cs +- GraphView/GraphView.cs +- GraphView/IAnnotation.cs +- GraphView/LegendAnnotation.cs +- GraphView/LineF.cs +- GraphView/PathAnnotation.cs +- GraphView/TextAnnotation.cs + +### Menu (3 files) +- Menu/MenuBarv2.cs +- Menu/Menuv2.cs +- Menu/PopoverMenu.cs + +### Menuv1 (4 files) +- Menuv1/MenuClosingEventArgs.cs +- Menuv1/MenuItemCheckStyle.cs +- Menuv1/MenuOpenedEventArgs.cs +- Menuv1/MenuOpeningEventArgs.cs + +### ScrollBar (2 files) +- ScrollBar/ScrollBar.cs +- ScrollBar/ScrollSlider.cs + +### Selectors (2 files) +- Selectors/FlagSelector.cs +- Selectors/SelectorStyles.cs + +### Slider (9 files) +- Slider/Slider.cs +- Slider/SliderAttributes.cs +- Slider/SliderConfiguration.cs +- Slider/SliderEventArgs.cs +- Slider/SliderOption.cs +- Slider/SliderOptionEventArgs.cs +- Slider/SliderStyle.cs +- Slider/SliderType.cs + +### SpinnerView (2 files) +- SpinnerView/SpinnerStyle.cs +- SpinnerView/SpinnerView.cs + +### TabView (4 files) +- TabView/Tab.cs +- TabView/TabChangedEventArgs.cs +- TabView/TabMouseEventArgs.cs +- TabView/TabStyle.cs + +### TableView (18 files) +- TableView/CellActivatedEventArgs.cs +- TableView/CellColorGetterArgs.cs +- TableView/CellToggledEventArgs.cs +- TableView/CheckBoxTableSourceWrapper.cs +- TableView/CheckBoxTableSourceWrapperByIndex.cs +- TableView/CheckBoxTableSourceWrapperByObject.cs +- TableView/ColumnStyle.cs +- TableView/DataTableSource.cs +- TableView/EnumerableTableSource.cs +- TableView/IEnumerableTableSource.cs +- TableView/ITableSource.cs +- TableView/ListColumnStyle.cs +- TableView/ListTableSource.cs +- TableView/RowColorGetterArgs.cs +- TableView/SelectedCellChangedEventArgs.cs +- TableView/TableSelection.cs +- TableView/TableStyle.cs +- TableView/TableView.cs +- TableView/TreeTableSource.cs + +### TextInput (11 files) +- TextInput/ContentsChangedEventArgs.cs +- TextInput/DateField.cs +- TextInput/HistoryTextItemEventArgs.cs +- TextInput/ITextValidateProvider.cs +- TextInput/NetMaskedTextProvider.cs +- TextInput/TextEditingLineStatus.cs +- TextInput/TextField.cs +- TextInput/TextRegexProvider.cs +- TextInput/TextValidateField.cs +- TextInput/TimeField.cs + +### TreeView (14 files) +- TreeView/AspectGetterDelegate.cs +- TreeView/Branch.cs +- TreeView/DelegateTreeBuilder.cs +- TreeView/DrawTreeViewLineEventArgs.cs +- TreeView/ITreeBuilder.cs +- TreeView/ITreeViewFilter.cs +- TreeView/ObjectActivatedEventArgs.cs +- TreeView/SelectionChangedEventArgs.cs +- TreeView/TreeBuilder.cs +- TreeView/TreeNode.cs +- TreeView/TreeNodeBuilder.cs +- TreeView/TreeStyle.cs +- TreeView/TreeView.cs +- TreeView/TreeViewTextFilter.cs + +### Wizard (3 files) +- Wizard/Wizard.cs +- Wizard/WizardEventArgs.cs +- Wizard/WizardStep.cs + +## Summary + +These 121 View-related files still have `#nullable disable` as they require additional work to be fully nullable-compliant. All other files in the Terminal.Gui library (outside of the Views directory) have been updated to support nullable reference types. diff --git a/PR_DESCRIPTION_UPDATED.md b/PR_DESCRIPTION_UPDATED.md new file mode 100644 index 0000000000..8a653ba686 --- /dev/null +++ b/PR_DESCRIPTION_UPDATED.md @@ -0,0 +1,322 @@ +# Fixes #4329 - Major Architectural Improvements: API Rename, Nullable Types, and Application Decoupling + +## Overview + +This PR delivers **three major architectural improvements** to Terminal.Gui v2: + +1. **API Terminology Modernization** - Renamed confusing `Application.Top`/`TopLevels` to intuitive `Application.Current`/`Session Stack` +2. **Nullable Reference Types** - Enabled nullable for 143 non-View library files +3. **Application Decoupling** - Introduced `View.App` property to decouple View hierarchy from static Application class + +**Impact**: 561 files changed, 7,033 insertions(+), 2,736 deletions(-) across library, tests, and examples. + +--- + +## Part 1: API Terminology Modernization (Breaking Change) + +### Changes + +- **`Application.Top` → `Application.Current`** (684 occurrences across codebase) +- **`Application.TopLevels` → `Application.SessionStack`** (31 occurrences) +- Updated `IApplication` interface, `ApplicationImpl`, all tests, examples, and documentation + +### Rationale + +The old naming was ambiguous and inconsistent with .NET patterns: +- `Top` didn't clearly indicate "currently active/running view" +- `TopLevels` exposed implementation detail (it's a stack!) and didn't match `SessionToken` terminology + +New naming follows established patterns: +- `Current` matches `Thread.CurrentThread`, `HttpContext.Current`, `Synchronization Context.Current` +- `SessionStack` clearly describes both content (sessions) and structure (stack), aligning with `SessionToken` + +### Impact Statistics + +| Category | Files Changed | Occurrences Updated | +|----------|---------------|---------------------| +| Terminal.Gui library | 41 | 715 | +| Unit tests | 43 | 631 | +| Integration tests | 3 | 25 | +| Examples | 15 | 15 | +| Documentation | 3 | 14 | +| **Total** | **91** | **~800** | + +###Breaking Changes + +**All references must be updated:** +```csharp +// OLD (v1/early v2) +Application.Top?.SetNeedsDraw(); +foreach (var tl in Application.TopLevels) { } + +// NEW (v2 current) +Application.Current?.SetNeedsDraw(); +foreach (var tl in Application.SessionStack) { } +``` + +--- + +## Part 2: Nullable Reference Types Enabled + +### Changes + +**Phase 1** - Project Configuration (commit 439e161): +- Added `enable` to `Terminal.Gui.csproj` (project-wide default) +- Removed redundant `#nullable enable` from 37 files +- Added `#nullable disable` to 170 files not yet compliant + +**Phase 2** - Non-View Compliance (commit 06bd50d): +- **Removed `#nullable disable` from ALL 143 non-View library files** +- Build successful with 0 errors +- All core infrastructure now fully nullable-aware + +**Phase 3** - Cleanup (commits 97d9c7d, 49d4fb2): +- Fixed duplicate `#nullable` directives in 37 files +- All files now have clean, single nullable directive + +### Impact Statistics + +| Directory | Files Nullable-Enabled | +|-----------|------------------------| +| App/ | 25 ✅ | +| Configuration/ | 24 ✅ | +| ViewBase/ | 30 ✅ | +| Drivers/ | 25 ✅ | +| Drawing/ | 18 ✅ | +| FileServices/ | 7 ✅ | +| Input/ | 6 ✅ | +| Text/ | 5 ✅ | +| Resources/ | 3 ✅ | +| **Views/** | **121 ⏸️ (documented in NULLABLE_VIEWS_REMAINING.md)** | +| **Total Enabled** | **143 files** | + +### Remaining Work + +See [NULLABLE_VIEWS_REMAINING.md](./NULLABLE_VIEWS_REMAINING.md) for the 121 View subclass files still with `#nullable disable`. These require careful migration due to complex view hierarchies and will be addressed in a follow-up PR. + +--- + +## Part 3: Application Decoupling (MASSIVE Change) + +### Problem + +Prior to this PR, Views were tightly coupled to the **static** `Application` class: +- Direct static calls: `Application.Current`, `Application.Driver`, `Application.MainLoop` +- Made Views untestable in isolation +- Violated dependency inversion principle +- Prevented Views from working with different IApplication implementations + +### Solution: `View.App` Property + +Introduced `View.App` property that provides IApplication instance: + +```csharp +// Terminal.Gui/ViewBase/View.cs +public IApplication? App +{ + get => GetApp(); + internal set => _app = value; +} + +private IApplication? GetApp() +{ + // Walk up hierarchy to find IApplication + if (_app is { }) return _app; + if (SuperView is { }) return SuperView.App; + return Application.Instance; // Fallback to global +} +``` + +### Migration Pattern + +**Before** (tightly coupled): +```csharp +// Direct static dependency +Application.Driver.Move(x, y); +if (Application.Current == this) { } +Application.MainLoop.Invoke(() => { }); +``` + +**After** (decoupled via View.App): +```csharp +// Use injected IApplication instance +App?.Driver.Move(x, y); +if (App?.Current == this) { } +App?.MainLoop.Invoke(() => { }); +``` + +### Impact Statistics + +- **90 files changed** in decoupling commit (899fd76) +- **987 insertions, 728 deletions** +- Affects ViewBase, Views, Adornments, Input handling, Drawing + +### Benefits + +✅ **Testability**: Views can now be tested with mock IApplication +✅ **Flexibility**: Views work with any IApplication implementation +✅ **Cleaner Architecture**: Follows dependency injection pattern +✅ **Future-proof**: Enables multi-application scenarios +✅ **Maintainability**: Clearer dependencies, easier to refactor + +### Known Remaining Coupling + +After decoupling work, only **1 direct Application dependency** remains in ViewBase: +- `Border.Arrangement.cs`: Uses `Application.ArrangeKey` for hotkey binding + +Additional investigation areas for future work: +1. Some Views still reference Application for convenience (non-critical) +2. Test infrastructure may have residual static dependencies +3. Example applications use Application.Run (expected pattern) + +--- + +## Part 4: Test Infrastructure Improvements + +### New Test File: `ApplicationImplBeginEndTests.cs` + +Added **16 comprehensive tests** validating fragile Begin/End state management: + +**Critical Test Coverage:** +- `End_ThrowsArgumentException_WhenNotBalanced` - Ensures proper Begin/End pairing +- `End_RestoresCurrentToPreviousToplevel` - Validates Current property management +- `MultipleBeginEnd_MaintainsStackIntegrity` - Tests nested sessions (5 levels deep) + +**Additional Coverage:** +- Argument validation (null checks) +- SessionStack push/pop operations +- Current property state transitions +- Unique ID generation for toplevels +- SessionToken management +- ResetState cleanup behavior +- Toplevel activation/deactivation events + +### Test Quality Improvements + +All new tests follow best practices: +- Work directly with ApplicationImpl instances (no global Application pollution) +- Use try-finally blocks ensuring Shutdown() always called +- Properly dispose toplevels before Shutdown (satisfies DEBUG_IDISPOSABLE assertions) +- No redundant ResetState calls (Shutdown calls it internally) + +**Result**: All 16 new tests + all existing tests passing ✅ + +--- + +## Additional Changes + +### Merged from v2_develop + +- RunState → SessionToken terminology (precedent for this rename) +- Application.TopLevels visibility changed to public (made this rename more important) +- Legacy MainLoop infrastructure removed +- Driver architecture modernization +- Test infrastructure improvements + +### Documentation + +- Created 5 comprehensive terminology proposal documents in `docfx/docs/`: + - `terminology-index.md` - Navigation guide + - `terminology-proposal.md` - Complete analysis + - `terminology-proposal-summary.md` - Quick reference + - `terminology-diagrams.md` - 11 Mermaid diagrams + - `terminology-before-after.md` - Side-by-side examples +- Updated `navigation.md`, `config.md`, `migratingfromv1.md` +- Created `NULLABLE_VIEWS_REMAINING.md` - Tracks remaining nullable work + +--- + +## Testing + +- ✅ **Build**: Successful with 0 errors +- ✅ **Unit Tests**: All 16 new tests + all existing tests passing +- ✅ **Integration Tests**: Updated and passing +- ✅ **Examples**: UICatalog, ReactiveExample, CommunityToolkitExample all updated and functional +- ✅ **Documentation**: Builds successfully + +--- + +## Breaking Changes Summary + +### API Changes (Requires Code Updates) + +1. **`Application.Top` → `Application.Current`** + - All usages must be updated + - Affects any code accessing the currently running toplevel + +2. **`Application.TopLevels` → `Application.SessionStack`** + - All usages must be updated + - Affects code iterating over running sessions + +### Non-Breaking Changes + +- Nullable reference types: Improved type safety, no runtime changes +- View.App property: Additive, existing Application. * calls still work (for now) + +--- + +## Migration Guide + +### For Terminology Changes + +```bash +# Find and replace in your codebase +Application.Top → Application.Current +Application.TopLevels → Application.SessionStack +``` + +### For View.App Usage (Recommended, Not Required) + +When writing new View code or refactoring existing Views: + +```csharp +// Prefer (future-proof, testable) +App?.Driver.AddRune(rune); +if (App?.Current == this) { } + +// Over (works but tightly coupled) +Application.Driver.AddRune(rune); +if (Application.Current == this) { } +``` + +--- + +## Future Work + +### Nullable Types +- Enable nullable for remaining 121 View files +- Document nullable patterns for View subclass authors + +### Application Decoupling +- Remove last `Application.ArrangeKey` reference from Border +- Consider making View.App property public for advanced scenarios +- Add documentation on using View.App for testable Views + +### Tests +- Expand ApplicationImpl test coverage based on new patterns discovered +- Add tests for View.App hierarchy traversal + +--- + +## Pull Request Checklist + +- [x] I've named my PR in the form of "Fixes #issue. Terse description." +- [x] My code follows the style guidelines of Terminal.Gui +- [x] My code follows the Terminal.Gui library design guidelines +- [x] I ran `dotnet test` before commit +- [x] I have made corresponding changes to the API documentation +- [x] My changes generate no new warnings +- [x] I have checked my code and corrected any poor grammar or misspellings +- [x] I conducted basic QA to assure all features are working + +--- + +## Related Issues + +- Fixes #4329 - Rename/Clarify Application.Toplevels/Top Terminology +- Related to #2491 - Toplevel refactoring +- Fixes #4333 (duplicate/related issue) + +--- + +**Note**: This is a large, multi-faceted PR that delivers significant architectural improvements. The changes are well-tested and maintain backward compatibility except for the intentional breaking API rename. The work positions Terminal.Gui v2 for better testability, maintainability, and future enhancements. diff --git a/Terminal.Gui/App/Application.Current.cs b/Terminal.Gui/App/Application.Current.cs new file mode 100644 index 0000000000..e94d074e73 --- /dev/null +++ b/Terminal.Gui/App/Application.Current.cs @@ -0,0 +1,18 @@ +using System.Collections.Concurrent; + +namespace Terminal.Gui.App; + +public static partial class Application // Current handling +{ + /// + [Obsolete ("The legacy static Application object is going away.")] public static ConcurrentStack SessionStack => ApplicationImpl.Instance.SessionStack; + + /// The that is currently active. + /// The current toplevel. + [Obsolete ("The legacy static Application object is going away.")] + public static Toplevel? Current + { + get => ApplicationImpl.Instance.Current; + internal set => ApplicationImpl.Instance.Current = value; + } +} diff --git a/Terminal.Gui/App/Application.Driver.cs b/Terminal.Gui/App/Application.Driver.cs index c08fb879f3..741134d390 100644 --- a/Terminal.Gui/App/Application.Driver.cs +++ b/Terminal.Gui/App/Application.Driver.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics.CodeAnalysis; @@ -7,6 +6,7 @@ namespace Terminal.Gui.App; public static partial class Application // Driver abstractions { /// + [Obsolete ("The legacy static Application object is going away.")] public static IDriver? Driver { get => ApplicationImpl.Instance.Driver; @@ -15,6 +15,7 @@ public static IDriver? Driver /// [ConfigurationProperty (Scope = typeof (SettingsScope))] + [Obsolete ("The legacy static Application object is going away.")] public static bool Force16Colors { get => ApplicationImpl.Instance.Force16Colors; @@ -23,6 +24,7 @@ public static bool Force16Colors /// [ConfigurationProperty (Scope = typeof (SettingsScope))] + [Obsolete ("The legacy static Application object is going away.")] public static string ForceDriver { get => ApplicationImpl.Instance.ForceDriver; @@ -30,11 +32,13 @@ public static string ForceDriver } /// + [Obsolete ("The legacy static Application object is going away.")] public static List Sixel => ApplicationImpl.Instance.Sixel; /// Gets a list of types and type names that are available. /// [RequiresUnreferencedCode ("AOT")] + [Obsolete ("The legacy static Application object is going away.")] public static (List, List) GetDriverTypes () { // use reflection to get the list of drivers @@ -59,4 +63,4 @@ public static (List, List) GetDriverTypes () return (driverTypes, driverTypeNames); } -} \ No newline at end of file +} diff --git a/Terminal.Gui/App/Application.Keyboard.cs b/Terminal.Gui/App/Application.Keyboard.cs index 1fa176a7d3..9dbdde8547 100644 --- a/Terminal.Gui/App/Application.Keyboard.cs +++ b/Terminal.Gui/App/Application.Keyboard.cs @@ -1,10 +1,9 @@ -#nullable enable - -namespace Terminal.Gui.App; +namespace Terminal.Gui.App; public static partial class Application // Keyboard handling { /// + [Obsolete ("The legacy static Application object is going away.")] public static IKeyboard Keyboard { get => ApplicationImpl.Instance.Keyboard; @@ -13,14 +12,9 @@ public static IKeyboard Keyboard } /// + [Obsolete ("The legacy static Application object is going away.")] public static bool RaiseKeyDownEvent (Key key) => ApplicationImpl.Instance.Keyboard.RaiseKeyDownEvent (key); - /// - public static bool? InvokeCommandsBoundToKey (Key key) => ApplicationImpl.Instance.Keyboard.InvokeCommandsBoundToKey (key); - - /// - public static bool? InvokeCommand (Command command, Key key, KeyBinding binding) => ApplicationImpl.Instance.Keyboard.InvokeCommand (command, key, binding); - /// /// Raised when the user presses a key. /// @@ -33,15 +27,14 @@ public static IKeyboard Keyboard /// and events. /// Fired after and before . /// + [Obsolete ("The legacy static Application object is going away.")] public static event EventHandler? KeyDown { add => ApplicationImpl.Instance.Keyboard.KeyDown += value; remove => ApplicationImpl.Instance.Keyboard.KeyDown -= value; } - /// - public static bool RaiseKeyUpEvent (Key key) => ApplicationImpl.Instance.Keyboard.RaiseKeyUpEvent (key); - /// + [Obsolete ("The legacy static Application object is going away.")] public static KeyBindings KeyBindings => ApplicationImpl.Instance.Keyboard.KeyBindings; } diff --git a/Terminal.Gui/App/Application.Lifecycle.cs b/Terminal.Gui/App/Application.Lifecycle.cs index 051d6b5c8a..802ba7a539 100644 --- a/Terminal.Gui/App/Application.Lifecycle.cs +++ b/Terminal.Gui/App/Application.Lifecycle.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -11,68 +10,41 @@ namespace Terminal.Gui.App; public static partial class Application // Lifecycle (Init/Shutdown) { + /// + /// Creates a new instance. + /// + /// + /// The recommended pattern is for developers to call Application.Create() and then use the returned + /// instance for all subsequent application operations. + /// + /// A new instance. + public static IApplication Create () { return new ApplicationImpl (); } - /// Initializes a new instance of a Terminal.Gui Application. must be called when the application is closing. - /// Call this method once per instance (or after has been called). - /// - /// This function loads the right for the platform, Creates a . and - /// assigns it to - /// - /// - /// must be called when the application is closing (typically after - /// has returned) to ensure resources are cleaned up and - /// terminal settings - /// restored. - /// - /// - /// The function combines - /// and - /// into a single - /// call. An application can use without explicitly calling - /// . - /// - /// - /// The to use. If neither or - /// are specified the default driver for the platform will be used. - /// - /// - /// The short name (e.g. "dotnet", "windows", "unix", or "fake") of the - /// to use. If neither or are - /// specified the default driver for the platform will be used. - /// + /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public static void Init (IDriver? driver = null, string? driverName = null) + [Obsolete ("The legacy static Application object is going away.")] + public static void Init (string? driverName = null) { - ApplicationImpl.Instance.Init (driver, driverName ?? ForceDriver); + ApplicationImpl.Instance.Init (driverName ?? ForceDriver); } /// /// Gets or sets the main thread ID for the application. /// + [Obsolete ("The legacy static Application object is going away.")] public static int? MainThreadId { get => ((ApplicationImpl)ApplicationImpl.Instance).MainThreadId; set => ((ApplicationImpl)ApplicationImpl.Instance).MainThreadId = value; } - /// Shutdown an application initialized with . - /// - /// Shutdown must be called for every call to or - /// to ensure all resources are cleaned - /// up (Disposed) - /// and terminal settings are restored. - /// + /// + [Obsolete ("The legacy static Application object is going away.")] public static void Shutdown () => ApplicationImpl.Instance.Shutdown (); - /// - /// Gets whether the application has been initialized with and not yet shutdown with . - /// - /// - /// - /// The event is raised after the and methods have been called. - /// - /// + /// + [Obsolete ("The legacy static Application object is going away.")] public static bool Initialized { get => ApplicationImpl.Instance.Initialized; @@ -80,6 +52,7 @@ public static bool Initialized } /// + [Obsolete ("The legacy static Application object is going away.")] public static event EventHandler>? InitializedChanged { add => ApplicationImpl.Instance.InitializedChanged += value; @@ -91,5 +64,6 @@ public static event EventHandler>? InitializedChanged // this in a function like this ensures we don't make mistakes in // guaranteeing that the state of this singleton is deterministic when Init // starts running and after Shutdown returns. + [Obsolete ("The legacy static Application object is going away.")] internal static void ResetState (bool ignoreDisposed = false) => ApplicationImpl.Instance?.ResetState (ignoreDisposed); } diff --git a/Terminal.Gui/App/Application.Mouse.cs b/Terminal.Gui/App/Application.Mouse.cs index 659e07b1c8..5e6ec118f6 100644 --- a/Terminal.Gui/App/Application.Mouse.cs +++ b/Terminal.Gui/App/Application.Mouse.cs @@ -1,17 +1,12 @@ -#nullable enable using System.ComponentModel; namespace Terminal.Gui.App; public static partial class Application // Mouse handling { - /// - /// Gets the most recent position of the mouse. - /// - public static Point? GetLastMousePosition () { return Mouse.GetLastMousePosition (); } - /// Disable or enable the mouse. The mouse is enabled by default. [ConfigurationProperty (Scope = typeof (SettingsScope))] + [Obsolete ("The legacy static Application object is going away.")] public static bool IsMouseDisabled { get => Mouse.IsMouseDisabled; @@ -26,12 +21,8 @@ public static bool IsMouseDisabled /// This property provides access to mouse-related functionality in a way that supports /// parallel test execution by avoiding static state. /// - /// - /// New code should use Application.Mouse instead of the static properties and methods - /// for better testability. Legacy static properties like and - /// are retained for backward compatibility. - /// /// + [Obsolete ("The legacy static Application object is going away.")] public static IMouse Mouse => ApplicationImpl.Instance.Mouse; #pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved @@ -54,6 +45,7 @@ public static bool IsMouseDisabled /// Use this even to handle mouse events at the application level, before View-specific handling. /// /// + [Obsolete ("The legacy static Application object is going away.")] public static event EventHandler? MouseEvent { add => Mouse.MouseEvent += value; @@ -65,11 +57,13 @@ public static event EventHandler? MouseEvent /// INTERNAL: Holds the non- views that are currently under the /// mouse. /// + [Obsolete ("The legacy static Application object is going away.")] internal static List CachedViewsUnderMouse => Mouse.CachedViewsUnderMouse; /// /// INTERNAL API: Holds the last mouse position. /// + [Obsolete ("The legacy static Application object is going away.")] internal static Point? LastMousePosition { get => Mouse.LastMousePosition; @@ -81,6 +75,7 @@ internal static Point? LastMousePosition /// /// The position of the mouse. /// The most recent result from GetViewsUnderLocation(). + [Obsolete ("The legacy static Application object is going away.")] internal static void RaiseMouseEnterLeaveEvents (Point screenPosition, List currentViewsUnderMouse) { Mouse.RaiseMouseEnterLeaveEvents (screenPosition, currentViewsUnderMouse); @@ -92,6 +87,7 @@ internal static void RaiseMouseEnterLeaveEvents (Point screenPosition, List /// This method can be used to simulate a mouse event, e.g. in unit tests. /// The mouse event with coordinates relative to the screen. + [Obsolete ("The legacy static Application object is going away.")] internal static void RaiseMouseEvent (MouseEventArgs mouseEvent) { Mouse.RaiseMouseEvent (mouseEvent); diff --git a/Terminal.Gui/App/Application.Navigation.cs b/Terminal.Gui/App/Application.Navigation.cs index b822a60279..b1053ae427 100644 --- a/Terminal.Gui/App/Application.Navigation.cs +++ b/Terminal.Gui/App/Application.Navigation.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; @@ -7,6 +6,7 @@ public static partial class Application // Navigation stuff /// /// Gets the instance for the current . /// + [Obsolete ("The legacy static Application object is going away.")] public static ApplicationNavigation? Navigation { get => ApplicationImpl.Instance.Navigation; @@ -15,7 +15,7 @@ public static ApplicationNavigation? Navigation /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key. [ConfigurationProperty (Scope = typeof (SettingsScope))] - public static Key NextTabGroupKey + [Obsolete ("The legacy static Application object is going away.")]public static Key NextTabGroupKey { get => ApplicationImpl.Instance.Keyboard.NextTabGroupKey; set => ApplicationImpl.Instance.Keyboard.NextTabGroupKey = value; @@ -41,6 +41,7 @@ public static Key NextTabKey /// and events. /// Fired after . /// + [Obsolete ("The legacy static Application object is going away.")] public static event EventHandler? KeyUp { add => ApplicationImpl.Instance.Keyboard.KeyUp += value; diff --git a/Terminal.Gui/App/Application.Popover.cs b/Terminal.Gui/App/Application.Popover.cs index 31522f80fb..40eba8d4d2 100644 --- a/Terminal.Gui/App/Application.Popover.cs +++ b/Terminal.Gui/App/Application.Popover.cs @@ -1,13 +1,13 @@ -#nullable enable namespace Terminal.Gui.App; public static partial class Application // Popover handling { /// Gets the Application manager. + [Obsolete ("The legacy static Application object is going away.")] public static ApplicationPopover? Popover { get => ApplicationImpl.Instance.Popover; internal set => ApplicationImpl.Instance.Popover = value; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/App/Application.Run.cs b/Terminal.Gui/App/Application.Run.cs index 56206a997f..d218c370da 100644 --- a/Terminal.Gui/App/Application.Run.cs +++ b/Terminal.Gui/App/Application.Run.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.App; @@ -22,41 +21,57 @@ public static Key ArrangeKey } /// + [Obsolete ("The legacy static Application object is going away.")] public static SessionToken Begin (Toplevel toplevel) => ApplicationImpl.Instance.Begin (toplevel); /// + [Obsolete ("The legacy static Application object is going away.")] public static bool PositionCursor () => ApplicationImpl.Instance.PositionCursor (); /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public static Toplevel Run (Func? errorHandler = null, string? driver = null) => ApplicationImpl.Instance.Run (errorHandler, driver); + [Obsolete ("The legacy static Application object is going away.")] + public static Toplevel Run (Func? errorHandler = null, string? driverName = null) => ApplicationImpl.Instance.Run (errorHandler, driverName); /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public static TView Run (Func? errorHandler = null, string? driver = null) - where TView : Toplevel, new() => ApplicationImpl.Instance.Run (errorHandler, driver); + [Obsolete ("The legacy static Application object is going away.")] + public static TView Run (Func? errorHandler = null, string? driverName = null) + where TView : Toplevel, new() => ApplicationImpl.Instance.Run (errorHandler, driverName); /// + [Obsolete ("The legacy static Application object is going away.")] public static void Run (Toplevel view, Func? errorHandler = null) => ApplicationImpl.Instance.Run (view, errorHandler); /// + [Obsolete ("The legacy static Application object is going away.")] public static object? AddTimeout (TimeSpan time, Func callback) => ApplicationImpl.Instance.AddTimeout (time, callback); /// + [Obsolete ("The legacy static Application object is going away.")] public static bool RemoveTimeout (object token) => ApplicationImpl.Instance.RemoveTimeout (token); /// /// + [Obsolete ("The legacy static Application object is going away.")] public static ITimedEvents? TimedEvents => ApplicationImpl.Instance?.TimedEvents; - /// + + /// + [Obsolete ("The legacy static Application object is going away.")] + public static void Invoke (Action action) => ApplicationImpl.Instance.Invoke (action); + + /// + [Obsolete ("The legacy static Application object is going away.")] public static void Invoke (Action action) => ApplicationImpl.Instance.Invoke (action); /// + [Obsolete ("The legacy static Application object is going away.")] public static void LayoutAndDraw (bool forceRedraw = false) => ApplicationImpl.Instance.LayoutAndDraw (forceRedraw); /// + [Obsolete ("The legacy static Application object is going away.")] public static bool StopAfterFirstIteration { get => ApplicationImpl.Instance.StopAfterFirstIteration; @@ -64,15 +79,15 @@ public static bool StopAfterFirstIteration } /// + [Obsolete ("The legacy static Application object is going away.")] public static void RequestStop (Toplevel? top = null) => ApplicationImpl.Instance.RequestStop (top); /// + [Obsolete ("The legacy static Application object is going away.")] public static void End (SessionToken sessionToken) => ApplicationImpl.Instance.End (sessionToken); - /// - internal static void RaiseIteration () => ApplicationImpl.Instance.RaiseIteration (); - /// + [Obsolete ("The legacy static Application object is going away.")] public static event EventHandler? Iteration { add => ApplicationImpl.Instance.Iteration += value; @@ -80,6 +95,7 @@ public static event EventHandler? Iteration } /// + [Obsolete ("The legacy static Application object is going away.")] public static event EventHandler? SessionBegun { add => ApplicationImpl.Instance.SessionBegun += value; @@ -87,6 +103,7 @@ public static event EventHandler? SessionBegun } /// + [Obsolete ("The legacy static Application object is going away.")] public static event EventHandler? SessionEnded { add => ApplicationImpl.Instance.SessionEnded += value; diff --git a/Terminal.Gui/App/Application.Screen.cs b/Terminal.Gui/App/Application.Screen.cs index 2cc223cdb2..195c33ea62 100644 --- a/Terminal.Gui/App/Application.Screen.cs +++ b/Terminal.Gui/App/Application.Screen.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; @@ -6,6 +5,7 @@ public static partial class Application // Screen related stuff; intended to hid { /// + [Obsolete ("The legacy static Application object is going away.")] public static Rectangle Screen { get => ApplicationImpl.Instance.Screen; @@ -13,6 +13,7 @@ public static Rectangle Screen } /// + [Obsolete ("The legacy static Application object is going away.")] public static event EventHandler>? ScreenChanged { add => ApplicationImpl.Instance.ScreenChanged += value; @@ -21,6 +22,7 @@ public static event EventHandler>? ScreenChanged /// + [Obsolete ("The legacy static Application object is going away.")] internal static bool ClearScreenNextIteration { get => ApplicationImpl.Instance.ClearScreenNextIteration; diff --git a/Terminal.Gui/App/Application.Toplevel.cs b/Terminal.Gui/App/Application.Toplevel.cs deleted file mode 100644 index e7c05e437d..0000000000 --- a/Terminal.Gui/App/Application.Toplevel.cs +++ /dev/null @@ -1,18 +0,0 @@ -#nullable enable -using System.Collections.Concurrent; - -namespace Terminal.Gui.App; - -public static partial class Application // Toplevel handling -{ - /// - public static ConcurrentStack TopLevels => ApplicationImpl.Instance.TopLevels; - - /// The that is currently active. - /// The top. - public static Toplevel? Top - { - get => ApplicationImpl.Instance.Top; - internal set => ApplicationImpl.Instance.Top = value; - } -} diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs index bd3ccf7a3f..7213e16be2 100644 --- a/Terminal.Gui/App/Application.cs +++ b/Terminal.Gui/App/Application.cs @@ -1,8 +1,7 @@ -#nullable enable - // We use global using directives to simplify the code and avoid repetitive namespace declarations. // Put them here so they are available throughout the application. // Do not put them in AssemblyInfo.cs as it will break GitVersion's /updateassemblyinfo + global using Attribute = Terminal.Gui.Drawing.Attribute; global using Color = Terminal.Gui.Drawing.Color; global using CM = Terminal.Gui.Configuration.ConfigurationManager; @@ -16,7 +15,6 @@ global using Terminal.Gui.Text; global using Terminal.Gui.Resources; global using Terminal.Gui.FileServices; -using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Resources; @@ -40,95 +38,31 @@ namespace Terminal.Gui.App; public static partial class Application { /// - /// Maximum number of iterations of the main loop (and hence draws) - /// to allow to occur per second. Defaults to > which is a 40ms sleep - /// after iteration (factoring in how long iteration took to run). - /// Note that not every iteration draws (see ). - /// Only affects v2 drivers. + /// Maximum number of iterations of the main loop (and hence draws) + /// to allow to occur per second. Defaults to > which is a 40ms sleep + /// after iteration (factoring in how long iteration took to run). + /// + /// Note that not every iteration draws (see ). + /// Only affects v2 drivers. + /// /// public static ushort MaximumIterationsPerSecond = DefaultMaximumIterationsPerSecond; /// - /// Default value for + /// Default value for /// public const ushort DefaultMaximumIterationsPerSecond = 25; - /// - /// Gets a string representation of the Application as rendered by . - /// - /// A string representation of the Application - public new static string ToString () - { - IDriver? driver = Driver; - - if (driver is null) - { - return string.Empty; - } - - return ToString (driver); - } - - /// - /// Gets a string representation of the Application rendered by the provided . - /// - /// The driver to use to render the contents. - /// A string representation of the Application - public static string ToString (IDriver? driver) - { - if (driver is null) - { - return string.Empty; - } - - var sb = new StringBuilder (); - - Cell [,] contents = driver?.Contents!; - - for (var r = 0; r < driver!.Rows; r++) - { - for (var c = 0; c < driver.Cols; c++) - { - Rune rune = contents [r, c].Rune; - - if (rune.DecodeSurrogatePair (out char []? sp)) - { - sb.Append (sp); - } - else - { - sb.Append ((char)rune.Value); - } - - if (rune.GetColumns () > 1) - { - c++; - } - - // See Issue #2616 - //foreach (var combMark in contents [r, c].CombiningMarks) { - // sb.Append ((char)combMark.Value); - //} - } - - sb.AppendLine (); - } - - return sb.ToString (); - } - /// Gets all cultures supported by the application without the invariant language. public static List? SupportedCultures { get; private set; } = GetSupportedCultures (); - internal static List GetAvailableCulturesFromEmbeddedResources () { ResourceManager rm = new (typeof (Strings)); CultureInfo [] cultures = CultureInfo.GetCultures (CultureTypes.AllCultures); - return cultures.Where ( - cultureInfo => + return cultures.Where (cultureInfo => !cultureInfo.Equals (CultureInfo.InvariantCulture) && rm.GetResourceSet (cultureInfo, true, false) is { } ) @@ -152,8 +86,7 @@ internal static List GetSupportedCultures () if (cultures.Length > 1 && Directory.Exists (Path.Combine (assemblyLocation, "pt-PT"))) { // Return all culture for which satellite folder found with culture code. - return cultures.Where ( - cultureInfo => + return cultures.Where (cultureInfo => Directory.Exists (Path.Combine (assemblyLocation, cultureInfo.Name)) && File.Exists (Path.Combine (assemblyLocation, cultureInfo.Name, resourceFilename)) ) diff --git a/Terminal.Gui/App/ApplicationImpl.Driver.cs b/Terminal.Gui/App/ApplicationImpl.Driver.cs index 36679b2b07..837024b83c 100644 --- a/Terminal.Gui/App/ApplicationImpl.Driver.cs +++ b/Terminal.Gui/App/ApplicationImpl.Driver.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.App; @@ -80,7 +79,7 @@ private void CreateDriver (string? driverName) Logging.Trace ($"Created Subcomponents: {Coordinator}"); - Coordinator.StartInputTaskAsync ().Wait (); + Coordinator.StartInputTaskAsync (this).Wait (); if (Driver == null) { diff --git a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs index 0a77958338..73465bf8ac 100644 --- a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs +++ b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -15,7 +14,7 @@ public partial class ApplicationImpl /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public void Init (IDriver? driver = null, string? driverName = null) + public void Init (string? driverName = null) { if (Initialized) { @@ -34,11 +33,11 @@ public void Init (IDriver? driver = null, string? driverName = null) _driverName = ForceDriver; } - Debug.Assert (Navigation is null); - Navigation = new (); + // Debug.Assert (Navigation is null); + // Navigation = new (); - Debug.Assert (Popover is null); - Popover = new (); + //Debug.Assert (Popover is null); + //Popover = new (); // Preserve existing keyboard settings if they exist bool hasExistingKeyboard = _keyboard is { }; @@ -50,7 +49,7 @@ public void Init (IDriver? driver = null, string? driverName = null) Key existingPrevTabGroupKey = _keyboard?.PrevTabGroupKey ?? Key.F6.WithShift; // Reset keyboard to ensure fresh state with default bindings - _keyboard = new KeyboardImpl { Application = this }; + _keyboard = new KeyboardImpl { App = this }; // Restore previously set keys if they existed and were different from defaults if (hasExistingKeyboard) @@ -114,9 +113,6 @@ public void Shutdown () // Clear the event to prevent memory leaks InitializedChanged = null; - - // Create a new lazy instance for potential future Init - _lazyInstance = new (() => new ApplicationImpl ()); } #if DEBUG @@ -156,8 +152,11 @@ public void ResetState (bool ignoreDisposed = false) // Init created. Apps that do any threading will need to code defensively for this. // e.g. see Issue #537 + // === 0. Stop all timers === + TimedEvents?.StopAll (); + // === 1. Stop all running toplevels === - foreach (Toplevel? t in TopLevels) + foreach (Toplevel? t in SessionStack) { t!.Running = false; } @@ -170,29 +169,30 @@ public void ResetState (bool ignoreDisposed = false) popover.Visible = false; } + // Any popovers added to Popover have their lifetime controlled by Popover Popover?.Dispose (); Popover = null; // === 3. Clean up toplevels === - TopLevels.Clear (); + SessionStack.Clear (); #if DEBUG_IDISPOSABLE - // Don't dispose the Top. It's up to caller dispose it - if (View.EnableDebugIDisposableAsserts && !ignoreDisposed && Top is { }) + // Don't dispose the Current. It's up to caller dispose it + if (View.EnableDebugIDisposableAsserts && !ignoreDisposed && Current is { }) { - Debug.Assert (Top.WasDisposed, $"Title = {Top.Title}, Id = {Top.Id}"); + Debug.Assert (Current.WasDisposed, $"Title = {Current.Title}, Id = {Current.Id}"); // If End wasn't called _CachedSessionTokenToplevel may be null if (CachedSessionTokenToplevel is { }) { Debug.Assert (CachedSessionTokenToplevel.WasDisposed); - Debug.Assert (CachedSessionTokenToplevel == Top); + Debug.Assert (CachedSessionTokenToplevel == Current); } } #endif - Top = null; + Current = null; CachedSessionTokenToplevel = null; // === 4. Clean up driver === @@ -222,7 +222,7 @@ public void ResetState (bool ignoreDisposed = false) // === 7. Clear navigation and screen state === ScreenChanged = null; - Navigation = null; + //Navigation = null; // === 8. Reset initialization state === Initialized = false; diff --git a/Terminal.Gui/App/ApplicationImpl.Run.cs b/Terminal.Gui/App/ApplicationImpl.Run.cs index fd0564e05b..4e9f59645d 100644 --- a/Terminal.Gui/App/ApplicationImpl.Run.cs +++ b/Terminal.Gui/App/ApplicationImpl.Run.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -37,28 +36,28 @@ public SessionToken Begin (Toplevel toplevel) var rs = new SessionToken (toplevel); #if DEBUG_IDISPOSABLE - if (View.EnableDebugIDisposableAsserts && Top is { } && toplevel != Top && !TopLevels.Contains (Top)) + if (View.EnableDebugIDisposableAsserts && Current is { } && toplevel != Current && !SessionStack.Contains (Current)) { - // This assertion confirm if the Top was already disposed - Debug.Assert (Top.WasDisposed); - Debug.Assert (Top == CachedSessionTokenToplevel); + // This assertion confirm if the Current was already disposed + Debug.Assert (Current.WasDisposed); + Debug.Assert (Current == CachedSessionTokenToplevel); } #endif - lock (TopLevels) + lock (SessionStack) { - if (Top is { } && toplevel != Top && !TopLevels.Contains (Top)) + if (Current is { } && toplevel != Current && !SessionStack.Contains (Current)) { - // If Top was already disposed and isn't on the Toplevels Stack, + // If Current was already disposed and isn't on the Toplevels Stack, // clean it up here if is the same as _CachedSessionTokenToplevel - if (Top == CachedSessionTokenToplevel) + if (Current == CachedSessionTokenToplevel) { - Top = null; + Current = null; } else { // Probably this will never hit - throw new ObjectDisposedException (Top.GetType ().FullName); + throw new ObjectDisposedException (Current.GetType ().FullName); } } @@ -67,56 +66,58 @@ public SessionToken Begin (Toplevel toplevel) if (string.IsNullOrEmpty (toplevel.Id)) { var count = 1; - var id = (TopLevels.Count + count).ToString (); + var id = (SessionStack.Count + count).ToString (); - while (TopLevels.Count > 0 && TopLevels.FirstOrDefault (x => x.Id == id) is { }) + while (SessionStack.Count > 0 && SessionStack.FirstOrDefault (x => x.Id == id) is { }) { count++; - id = (TopLevels.Count + count).ToString (); + id = (SessionStack.Count + count).ToString (); } - toplevel.Id = (TopLevels.Count + count).ToString (); + toplevel.Id = (SessionStack.Count + count).ToString (); - TopLevels.Push (toplevel); + SessionStack.Push (toplevel); } else { - Toplevel? dup = TopLevels.FirstOrDefault (x => x.Id == toplevel.Id); + Toplevel? dup = SessionStack.FirstOrDefault (x => x.Id == toplevel.Id); if (dup is null) { - TopLevels.Push (toplevel); + SessionStack.Push (toplevel); } } } - if (Top is null) + if (Current is null) { - Top = toplevel; + toplevel.App = this; + Current = toplevel; } - if ((Top?.Modal == false && toplevel.Modal) - || (Top?.Modal == false && !toplevel.Modal) - || (Top?.Modal == true && toplevel.Modal)) + if ((Current?.Modal == false && toplevel.Modal) + || (Current?.Modal == false && !toplevel.Modal) + || (Current?.Modal == true && toplevel.Modal)) { if (toplevel.Visible) { - if (Top is { HasFocus: true }) + if (Current is { HasFocus: true }) { - Top.HasFocus = false; + Current.HasFocus = false; } - // Force leave events for any entered views in the old Top - if (Mouse.GetLastMousePosition () is { }) + // Force leave events for any entered views in the old Current + if (Mouse.LastMousePosition is { }) { - Mouse.RaiseMouseEnterLeaveEvents (Mouse.GetLastMousePosition ()!.Value, new ()); + Mouse.RaiseMouseEnterLeaveEvents (Mouse.LastMousePosition!.Value, new ()); } - Top?.OnDeactivate (toplevel); - Toplevel previousTop = Top!; + Current?.OnDeactivate (toplevel); + Toplevel previousTop = Current!; - Top = toplevel; - Top.OnActivate (previousTop); + Current = toplevel; + Current.App = this; + Current.OnActivate (previousTop); } } @@ -135,7 +136,7 @@ public SessionToken Begin (Toplevel toplevel) toplevel.OnLoaded (); - Instance.LayoutAndDraw (true); + LayoutAndDraw (true); if (PositionCursor ()) { @@ -156,18 +157,18 @@ public SessionToken Begin (Toplevel toplevel) /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public Toplevel Run (Func? errorHandler = null, string? driver = null) { return Run (errorHandler, driver); } + public Toplevel Run (Func? errorHandler = null, string? driverName = null) { return Run (errorHandler, driverName); } /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public TView Run (Func? errorHandler = null, string? driver = null) + public TView Run (Func? errorHandler = null, string? driverName = null) where TView : Toplevel, new () { if (!Initialized) { // Init() has NOT been called. Auto-initialize as per interface contract. - Init (null, driver); + Init (driverName); } TView top = new (); @@ -193,15 +194,15 @@ public void Run (Toplevel view, Func? errorHandler = null) throw new InvalidOperationException ("Driver was inexplicably null when trying to Run view"); } - Top = view; + Current = view; - SessionToken rs = Application.Begin (view); + SessionToken rs = Begin (view); - Top.Running = true; + Current.Running = true; var firstIteration = true; - while (TopLevels.TryPeek (out Toplevel? found) && found == view && view.Running) + while (SessionStack.TryPeek (out Toplevel? found) && found == view && view.Running) { if (Coordinator is null) { @@ -220,7 +221,7 @@ public void Run (Toplevel view, Func? errorHandler = null) } Logging.Information ("Run - Calling End"); - Application.End (rs); + End (rs); } /// @@ -233,11 +234,11 @@ public void End (SessionToken sessionToken) ApplicationPopover.HideWithQuitCommand (visiblePopover); } - sessionToken.Toplevel.OnUnloaded (); + sessionToken.Toplevel?.OnUnloaded (); // End the Session // First, take it off the Toplevel Stack - if (TopLevels.TryPop (out Toplevel? topOfStack)) + if (SessionStack.TryPop (out Toplevel? topOfStack)) { if (topOfStack != sessionToken.Toplevel) { @@ -250,10 +251,11 @@ public void End (SessionToken sessionToken) // Notify that it is closing sessionToken.Toplevel?.OnClosed (sessionToken.Toplevel); - if (TopLevels.TryPeek (out Toplevel? newTop)) + if (SessionStack.TryPeek (out Toplevel? newTop)) { - Top = newTop; - Top?.SetNeedsDraw (); + newTop.App = this; + Current = newTop; + Current?.SetNeedsDraw (); } if (sessionToken.Toplevel is { HasFocus: true }) @@ -261,9 +263,9 @@ public void End (SessionToken sessionToken) sessionToken.Toplevel.HasFocus = false; } - if (Top is { HasFocus: false }) + if (Current is { HasFocus: false }) { - Top.SetFocus (); + Current.SetFocus (); } CachedSessionTokenToplevel = sessionToken.Toplevel; @@ -283,9 +285,9 @@ public void End (SessionToken sessionToken) /// public void RequestStop (Toplevel? top) { - Logging.Trace ($"Top: '{(top is { } ? top : "null")}'"); + Logging.Trace ($"Current: '{(top is { } ? top : "null")}'"); - top ??= Top; + top ??= Current; if (top == null) { @@ -321,13 +323,36 @@ public void RequestStop (Toplevel? top) /// public bool RemoveTimeout (object token) { return _timedEvents.Remove (token); } + /// + public void Invoke (Action? action) + { + // If we are already on the main UI thread + if (Current is { Running: true } && MainThreadId == Thread.CurrentThread.ManagedThreadId) + { + action?.Invoke (this); + + return; + } + + _timedEvents.Add ( + TimeSpan.Zero, + () => + { + action?.Invoke (this); + + return false; + } + ); + } + + /// public void Invoke (Action action) { // If we are already on the main UI thread - if (Top is { Running: true } && MainThreadId == Thread.CurrentThread.ManagedThreadId) + if (Current is { Running: true } && MainThreadId == Thread.CurrentThread.ManagedThreadId) { - action (); + action?.Invoke (); return; } @@ -336,7 +361,7 @@ public void Invoke (Action action) TimeSpan.Zero, () => { - action (); + action?.Invoke (); return false; } diff --git a/Terminal.Gui/App/ApplicationImpl.Screen.cs b/Terminal.Gui/App/ApplicationImpl.Screen.cs index 3c2f32cb64..b5a9307945 100644 --- a/Terminal.Gui/App/ApplicationImpl.Screen.cs +++ b/Terminal.Gui/App/ApplicationImpl.Screen.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; @@ -45,6 +44,11 @@ public Rectangle Screen /// public bool PositionCursor () { + if (Driver is null) + { + return false; + } + // Find the most focused view and position the cursor there. View? mostFocused = Navigation?.GetFocused (); @@ -66,7 +70,7 @@ public bool PositionCursor () Rectangle mostFocusedViewport = mostFocused.ViewportToScreen (mostFocused.Viewport with { Location = Point.Empty }); Rectangle superViewViewport = - mostFocused.SuperView?.ViewportToScreen (mostFocused.SuperView.Viewport with { Location = Point.Empty }) ?? Driver!.Screen; + mostFocused.SuperView?.ViewportToScreen (mostFocused.SuperView.Viewport with { Location = Point.Empty }) ?? Driver.Screen; if (!superViewViewport.IntersectsWith (mostFocusedViewport)) { @@ -133,7 +137,7 @@ private void RaiseScreenChangedEvent (Rectangle screen) ScreenChanged?.Invoke (this, new (screen)); - foreach (Toplevel t in TopLevels) + foreach (Toplevel t in SessionStack) { t.OnSizeChanging (new (screen.Size)); t.SetNeedsLayout (); @@ -147,7 +151,7 @@ private void RaiseScreenChangedEvent (Rectangle screen) /// public void LayoutAndDraw (bool forceRedraw = false) { - List tops = [.. TopLevels]; + List tops = [.. SessionStack]; if (Popover?.GetActivePopover () as View is { Visible: true } visiblePopover) { @@ -169,9 +173,12 @@ public void LayoutAndDraw (bool forceRedraw = false) Driver?.ClearContents (); } - View.SetClipToScreen (); - View.Draw (tops, neededLayout || forceRedraw); - View.SetClipToScreen (); - Driver?.Refresh (); + if (Driver is { }) + { + Driver.Clip = new (Screen); + View.Draw (tops, neededLayout || forceRedraw); + Driver.Clip = new (Screen); + Driver?.Refresh (); + } } } diff --git a/Terminal.Gui/App/ApplicationImpl.cs b/Terminal.Gui/App/ApplicationImpl.cs index d3b0277bac..bcedf0b14c 100644 --- a/Terminal.Gui/App/ApplicationImpl.cs +++ b/Terminal.Gui/App/ApplicationImpl.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.App; @@ -10,9 +9,9 @@ namespace Terminal.Gui.App; public partial class ApplicationImpl : IApplication { /// - /// Creates a new instance of the Application backend. + /// INTERNAL: Creates a new instance of the Application backend. /// - public ApplicationImpl () { } + internal ApplicationImpl () { } /// /// INTERNAL: Creates a new instance of the Application backend. @@ -22,22 +21,22 @@ public ApplicationImpl () { } #region Singleton - // Private static readonly Lazy instance of Application - private static Lazy _lazyInstance = new (() => new ApplicationImpl ()); - /// - /// Change the singleton implementation, should not be called except before application - /// startup. This method lets you provide alternative implementations of core static gateway - /// methods of . + /// Configures the singleton instance of to use the specified backend implementation. /// - /// - public static void ChangeInstance (IApplication? newApplication) { _lazyInstance = new (newApplication!); } + /// + public static void SetInstance (IApplication? app) + { + _instance = app; + } + + // Private static readonly Lazy instance of Application + private static IApplication? _instance; /// /// Gets the currently configured backend implementation of gateway methods. - /// Change to your own implementation by using (before init). /// - public static IApplication Instance => _lazyInstance.Value; + public static IApplication Instance => _instance ??= new ApplicationImpl (); #endregion Singleton @@ -57,7 +56,7 @@ public IMouse Mouse { if (_mouse is null) { - _mouse = new MouseImpl { Application = this }; + _mouse = new MouseImpl { App = this }; } return _mouse; @@ -66,7 +65,6 @@ public IMouse Mouse } private IKeyboard? _keyboard; - private bool _stopAfterFirstIteration; /// /// Handles keyboard input and key bindings at the Application level @@ -77,7 +75,7 @@ public IKeyboard Keyboard { if (_keyboard is null) { - _keyboard = new KeyboardImpl { Application = this }; + _keyboard = new KeyboardImpl { App = this }; } return _keyboard; @@ -89,22 +87,67 @@ public IKeyboard Keyboard #region View Management + private ApplicationPopover? _popover; + /// - public ApplicationPopover? Popover { get; set; } + public ApplicationPopover? Popover + { + get + { + if (_popover is null) + { + _popover = new () { App = this }; + } + + return _popover; + } + set => _popover = value; + } + + private ApplicationNavigation? _navigation; /// - public ApplicationNavigation? Navigation { get; set; } + public ApplicationNavigation? Navigation + { + get + { + if (_navigation is null) + { + _navigation = new () { App = this }; + } + + return _navigation; + } + set => _navigation = value ?? throw new ArgumentNullException (nameof (value)); + } + + private Toplevel? _current; /// - public Toplevel? Top { get; set; } + public Toplevel? Current + { + get => _current; + set + { + _current = value; + + if (_current is { }) + { + _current.App = this; + } + } + } - // BUGBUG: Technically, this is not the full lst of TopLevels. There be dragons here, e.g. see how Toplevel.Id is used. What + // BUGBUG: Technically, this is not the full lst of sessions. There be dragons here, e.g. see how Toplevel.Id is used. What /// - public ConcurrentStack TopLevels { get; } = new (); + public ConcurrentStack SessionStack { get; } = new (); /// public Toplevel? CachedSessionTokenToplevel { get; set; } #endregion View Management + + /// + public new string ToString () => Driver?.ToString () ?? string.Empty; } diff --git a/Terminal.Gui/App/ApplicationNavigation.cs b/Terminal.Gui/App/ApplicationNavigation.cs index 4a64b037b3..d0ef40d9c2 100644 --- a/Terminal.Gui/App/ApplicationNavigation.cs +++ b/Terminal.Gui/App/ApplicationNavigation.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; @@ -17,6 +16,11 @@ public ApplicationNavigation () // TODO: Move navigation key bindings here from AddApplicationKeyBindings } + /// + /// The instance used by this instance. + /// + public IApplication? App { get; set; } + private View? _focused; /// @@ -105,10 +109,10 @@ internal void SetFocused (View? value) /// public bool AdvanceFocus (NavigationDirection direction, TabBehavior? behavior) { - if (Application.Popover?.GetActivePopover () as View is { Visible: true } visiblePopover) + if (App?.Popover?.GetActivePopover () as View is { Visible: true } visiblePopover) { return visiblePopover.AdvanceFocus (direction, behavior); } - return Application.Top is { } && Application.Top.AdvanceFocus (direction, behavior); + return App?.Current is { } && App.Current.AdvanceFocus (direction, behavior); } } diff --git a/Terminal.Gui/App/ApplicationPopover.cs b/Terminal.Gui/App/ApplicationPopover.cs index 69430dbab3..72f9957df8 100644 --- a/Terminal.Gui/App/ApplicationPopover.cs +++ b/Terminal.Gui/App/ApplicationPopover.cs @@ -1,12 +1,11 @@ -#nullable enable using System.Diagnostics; namespace Terminal.Gui.App; /// -/// Helper class for support of views for . Held by -/// +/// Helper class for support of views for . Held by +/// /// public sealed class ApplicationPopover : IDisposable { @@ -15,6 +14,11 @@ public sealed class ApplicationPopover : IDisposable /// public ApplicationPopover () { } + /// + /// The instance used by this instance. + /// + public IApplication? App { get; set; } + private readonly List _popovers = []; /// @@ -35,10 +39,15 @@ public ApplicationPopover () { } /// , after it has been registered. public IPopover? Register (IPopover? popover) { - if (popover is { } && !_popovers.Contains (popover)) + if (popover is { } && !IsRegistered (popover)) { - // When created, set IPopover.Toplevel to the current Application.Top - popover.Toplevel ??= Application.Top; + // When created, set IPopover.Toplevel to the current Application.Current + popover.Current ??= App?.Current; + + if (popover is View popoverView) + { + popoverView.App = App; + } _popovers.Add (popover); } @@ -46,6 +55,13 @@ public ApplicationPopover () { } return popover; } + /// + /// Indicates whether a popover has been registered or not. + /// + /// + /// + public bool IsRegistered (IPopover? popover) => popover is { } && _popovers.Contains (popover); + /// /// De-registers with the application. Use this to remove the popover and it's /// keyboard bindings from the application. @@ -59,7 +75,7 @@ public ApplicationPopover () { } /// public bool DeRegister (IPopover? popover) { - if (popover is null || !_popovers.Contains (popover)) + if (popover is null || !IsRegistered (popover)) { return false; } @@ -100,9 +116,14 @@ public bool DeRegister (IPopover? popover) /// public void Show (IPopover? popover) { + if (!IsRegistered (popover)) + { + throw new InvalidOperationException (@"Popovers must be registered before being shown."); + } // If there's an existing popover, hide it. if (_activePopover is View popoverView) { + popoverView.App = App; popoverView.Visible = false; _activePopover = null; } @@ -120,9 +141,6 @@ public void Show (IPopover? popover) throw new InvalidOperationException ("Popovers must have a key binding for Command.Quit."); } - - Register (popover); - if (!newPopover.IsInitialized) { newPopover.BeginInit (); @@ -148,7 +166,7 @@ public void Hide (IPopover? popover) { _activePopover = null; popoverView.Visible = false; - Application.Top?.SetNeedsDraw (); + popoverView.App?.Current?.SetNeedsDraw (); } } @@ -177,7 +195,7 @@ internal static void HideWithQuitCommand (View visiblePopover) internal bool DispatchKeyDown (Key key) { // Do active first - Active gets all key down events. - var activePopover = GetActivePopover () as View; + View? activePopover = GetActivePopover () as View; if (activePopover is { Visible: true }) { @@ -197,13 +215,14 @@ internal bool DispatchKeyDown (Key key) { if (popover == activePopover || popover is not View popoverView - || (popover.Toplevel is { } && popover.Toplevel != Application.Top)) + || (popover.Current is { } && popover.Current != App?.Current)) { continue; } // hotKeyHandled = popoverView.InvokeCommandsBoundToHotKey (key); //Logging.Debug ($"Inactive - Calling NewKeyDownEvent ({key}) on {popoverView.Title}"); + popoverView.App ??= App; hotKeyHandled = popoverView.NewKeyDownEvent (key); if (hotKeyHandled is true) diff --git a/Terminal.Gui/App/CWP/CWPEventHelper.cs b/Terminal.Gui/App/CWP/CWPEventHelper.cs index d85d184d53..4840a358c2 100644 --- a/Terminal.Gui/App/CWP/CWPEventHelper.cs +++ b/Terminal.Gui/App/CWP/CWPEventHelper.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; using System; @@ -53,4 +52,4 @@ public static bool Execute ( eventHandler.Invoke (null, args); return args.Handled; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/App/CWP/CWPPropertyHelper.cs b/Terminal.Gui/App/CWP/CWPPropertyHelper.cs index 2ea94ce975..09bcd7fa07 100644 --- a/Terminal.Gui/App/CWP/CWPPropertyHelper.cs +++ b/Terminal.Gui/App/CWP/CWPPropertyHelper.cs @@ -1,7 +1,5 @@ namespace Terminal.Gui.App; -#nullable enable - /// /// Provides helper methods for executing property change workflows in the Cancellable Work Pattern (CWP). /// diff --git a/Terminal.Gui/App/CWP/CWPWorkflowHelper.cs b/Terminal.Gui/App/CWP/CWPWorkflowHelper.cs index 401f17fb89..4c0328589c 100644 --- a/Terminal.Gui/App/CWP/CWPWorkflowHelper.cs +++ b/Terminal.Gui/App/CWP/CWPWorkflowHelper.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; using System; @@ -126,4 +125,4 @@ public static TResult ExecuteWithResult ( } return args.Result!; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/App/CWP/CancelEventArgs.cs b/Terminal.Gui/App/CWP/CancelEventArgs.cs index 7378b722aa..422fa53231 100644 --- a/Terminal.Gui/App/CWP/CancelEventArgs.cs +++ b/Terminal.Gui/App/CWP/CancelEventArgs.cs @@ -1,4 +1,3 @@ -#nullable enable using System.ComponentModel; namespace Terminal.Gui.App; diff --git a/Terminal.Gui/App/CWP/EventArgs.cs b/Terminal.Gui/App/CWP/EventArgs.cs index fe76442644..edd1cc450f 100644 --- a/Terminal.Gui/App/CWP/EventArgs.cs +++ b/Terminal.Gui/App/CWP/EventArgs.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; #pragma warning disable CS1711 diff --git a/Terminal.Gui/App/CWP/ResultEventArgs.cs b/Terminal.Gui/App/CWP/ResultEventArgs.cs index d75627c3b3..e8a3f67c01 100644 --- a/Terminal.Gui/App/CWP/ResultEventArgs.cs +++ b/Terminal.Gui/App/CWP/ResultEventArgs.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; using System; @@ -42,4 +41,4 @@ public ResultEventArgs (T? result) Result = result; } } -#pragma warning restore CS1711 \ No newline at end of file +#pragma warning restore CS1711 diff --git a/Terminal.Gui/App/CWP/ValueChangedEventArgs.cs b/Terminal.Gui/App/CWP/ValueChangedEventArgs.cs index d04c42825e..880c592457 100644 --- a/Terminal.Gui/App/CWP/ValueChangedEventArgs.cs +++ b/Terminal.Gui/App/CWP/ValueChangedEventArgs.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; /// diff --git a/Terminal.Gui/App/CWP/ValueChangingEventArgs.cs b/Terminal.Gui/App/CWP/ValueChangingEventArgs.cs index fed087b8c9..15d4688b26 100644 --- a/Terminal.Gui/App/CWP/ValueChangingEventArgs.cs +++ b/Terminal.Gui/App/CWP/ValueChangingEventArgs.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; /// @@ -41,4 +40,4 @@ public ValueChangingEventArgs (T currentValue, T newValue) CurrentValue = currentValue; NewValue = newValue; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/App/Clipboard/Clipboard.cs b/Terminal.Gui/App/Clipboard/Clipboard.cs index f8cf39892b..0db0133517 100644 --- a/Terminal.Gui/App/Clipboard/Clipboard.cs +++ b/Terminal.Gui/App/Clipboard/Clipboard.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; /// Provides cut, copy, and paste support for the OS clipboard. @@ -31,7 +30,7 @@ public static string? Contents if (IsSupported) { // throw new InvalidOperationException ($"{Application.Driver?.GetType ().Name}.GetClipboardData returned null instead of string.Empty"); - string? clipData = Application.Driver?.Clipboard?.GetClipboardData () ?? string.Empty; + string clipData = Application.Driver?.Clipboard?.GetClipboardData () ?? string.Empty; _contents = clipData; } @@ -66,4 +65,4 @@ public static string? Contents /// Returns true if the environmental dependencies are in place to interact with the OS clipboard. /// public static bool IsSupported => Application.Driver?.Clipboard?.IsSupported ?? false; -} \ No newline at end of file +} diff --git a/Terminal.Gui/App/Clipboard/ClipboardBase.cs b/Terminal.Gui/App/Clipboard/ClipboardBase.cs index 97cfec61e2..46a1f26bf3 100644 --- a/Terminal.Gui/App/Clipboard/ClipboardBase.cs +++ b/Terminal.Gui/App/Clipboard/ClipboardBase.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Diagnostics; namespace Terminal.Gui.App; diff --git a/Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs b/Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs index 214b5337de..70c471d744 100644 --- a/Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs +++ b/Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; namespace Terminal.Gui.App; @@ -45,7 +44,6 @@ public static (int exitCode, string result) Process ( CreateNoWindow = true }; - TaskCompletionSource eventHandled = new (); process.Start (); if (!string.IsNullOrEmpty (input)) diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs index 64620f09e9..c025631707 100644 --- a/Terminal.Gui/App/IApplication.cs +++ b/Terminal.Gui/App/IApplication.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.App; @@ -41,14 +40,9 @@ public interface IApplication #region Initialization and Shutdown /// Initializes a new instance of Application. - /// - /// The to use. If neither or - /// are specified the default driver for the platform will be used. - /// /// /// The short name (e.g. "dotnet", "windows", "unix", or "fake") of the - /// to use. If neither or are - /// specified the default driver for the platform will be used. + /// to use. If not specified the default driver for the platform will be used. /// /// /// Call this method once per instance (or after has been called). @@ -61,14 +55,14 @@ public interface IApplication /// has returned) to ensure resources are cleaned up and terminal settings restored. /// /// - /// The function combines and + /// The function combines and /// into a single call. An application can use - /// without explicitly calling . + /// without explicitly calling . /// /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public void Init (IDriver? driver = null, string? driverName = null); + public void Init (string? driverName = null); /// /// This event is raised after the and methods have been called. @@ -137,7 +131,7 @@ public interface IApplication /// stopped, will be called. /// /// Handler for any unhandled exceptions (resumes when returns true, rethrows when null). - /// + /// /// The driver name. If not specified the default driver for the platform will be used. Must be /// if has already been called. /// @@ -154,7 +148,7 @@ public interface IApplication /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public Toplevel Run (Func? errorHandler = null, string? driver = null); + public Toplevel Run (Func? errorHandler = null, string? driverName = null); /// /// Runs a new Session creating a -derived object of type @@ -163,7 +157,7 @@ public interface IApplication /// /// The type of to create and run. /// Handler for any unhandled exceptions (resumes when returns true, rethrows when null). - /// + /// /// The driver name. If not specified the default driver for the platform will be used. Must be /// if has already been called. /// @@ -206,7 +200,7 @@ public interface IApplication /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public TView Run (Func? errorHandler = null, string? driver = null) + public TView Run (Func? errorHandler = null, string? driverName = null) where TView : Toplevel, new (); /// @@ -266,6 +260,17 @@ public TView Run (Func? errorHandler = null, string? dri /// public event EventHandler? Iteration; + /// Runs on the main UI loop thread. + /// The action to be invoked on the main processing thread. + /// + /// + /// If called from the main thread, the action is executed immediately. Otherwise, it is queued via + /// with and will be executed on the next main loop + /// iteration. + /// + /// + void Invoke (Action? action); + /// Runs on the main UI loop thread. /// The action to be invoked on the main processing thread. /// @@ -296,14 +301,14 @@ public TView Run (Func? errorHandler = null, string? dri /// /// This will cause to return. /// - /// This is equivalent to calling with as the parameter. + /// This is equivalent to calling with as the parameter. /// /// void RequestStop (); /// Requests that the currently running Session stop. The Session will stop after the current iteration completes. /// - /// The to stop. If , stops the currently running . + /// The to stop. If , stops the currently running . /// /// /// This will cause to return. @@ -351,22 +356,22 @@ public TView Run (Func? errorHandler = null, string? dri #region Toplevel Management - /// Gets or sets the current Toplevel. + /// Gets or sets the currently active Toplevel. /// /// /// This is set by and cleared by . /// /// - Toplevel? Top { get; set; } + Toplevel? Current { get; set; } - /// Gets the stack of all Toplevels. + /// Gets the stack of all active Toplevel sessions. /// /// /// Toplevels are added to this stack by and removed by /// . /// /// - ConcurrentStack TopLevels { get; } + ConcurrentStack SessionStack { get; } /// /// Caches the Toplevel associated with the current Session. @@ -428,7 +433,7 @@ public TView Run (Func? errorHandler = null, string? dri /// /// /// This is typically set to when a View's changes and that view - /// has no SuperView (e.g. when is moved or resized). + /// has no SuperView (e.g. when is moved or resized). /// /// /// Automatically reset to after processes it. @@ -509,12 +514,15 @@ public TView Run (Func? errorHandler = null, string? dri /// returns , the timeout will stop and be removed. /// /// - /// A token that can be used to stop the timeout by calling . + /// Call with the returned value to stop the timeout. /// /// /// /// When the time specified passes, the callback will be invoked on the main UI thread. /// + /// + /// calls StopAll on to remove all timeouts. + /// /// object AddTimeout (TimeSpan time, Func callback); @@ -539,4 +547,10 @@ public TView Run (Func? errorHandler = null, string? dri ITimedEvents? TimedEvents { get; } #endregion Timeouts + + /// + /// Gets a string representation of the Application as rendered by . + /// + /// A string representation of the Application + public string ToString (); } diff --git a/Terminal.Gui/App/IPopover.cs b/Terminal.Gui/App/IPopover.cs index 7e86ffe774..87218d07ac 100644 --- a/Terminal.Gui/App/IPopover.cs +++ b/Terminal.Gui/App/IPopover.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; @@ -51,12 +50,12 @@ namespace Terminal.Gui.App; public interface IPopover { /// - /// Gets or sets the that this Popover is associated with. If null, it is not associated with + /// Gets or sets the that this Popover is associated with. If null, it is not associated with /// any Toplevel and will receive all keyboard - /// events from the . If set, it will only receive keyboard events the Toplevel would normally + /// events from the . If set, it will only receive keyboard events the Toplevel would normally /// receive. - /// When is called, the is set to the current - /// if not already set. + /// When is called, the is set to the current + /// if not already set. /// - Toplevel? Toplevel { get; set; } + Toplevel? Current { get; set; } } diff --git a/Terminal.Gui/App/Keyboard/IKeyboard.cs b/Terminal.Gui/App/Keyboard/IKeyboard.cs index d0fbd023eb..4042173085 100644 --- a/Terminal.Gui/App/Keyboard/IKeyboard.cs +++ b/Terminal.Gui/App/Keyboard/IKeyboard.cs @@ -1,10 +1,9 @@ -#nullable enable namespace Terminal.Gui.App; /// /// Defines a contract for managing keyboard input and key bindings at the Application level. /// -/// This interface decouples keyboard handling state from the static class, +/// This interface decouples keyboard handling state from the static class, /// enabling parallelizable unit tests and better testability. /// /// @@ -14,7 +13,7 @@ public interface IKeyboard /// Sets the application instance that this keyboard handler is associated with. /// This provides access to application state without coupling to static Application class. /// - IApplication? Application { get; set; } + IApplication? App { get; set; } /// /// Called when the user presses a key (by the ). Raises the cancelable diff --git a/Terminal.Gui/App/Keyboard/KeyboardImpl.cs b/Terminal.Gui/App/Keyboard/KeyboardImpl.cs index 0741f4c533..e1be672e50 100644 --- a/Terminal.Gui/App/Keyboard/KeyboardImpl.cs +++ b/Terminal.Gui/App/Keyboard/KeyboardImpl.cs @@ -1,12 +1,9 @@ -#nullable enable -using System.Diagnostics; - namespace Terminal.Gui.App; /// /// INTERNAL: Implements to manage keyboard input and key bindings at the Application level. /// -/// This implementation decouples keyboard handling state from the static class, +/// This implementation decouples keyboard handling state from the static class, /// enabling parallelizable unit tests and better testability. /// /// @@ -28,7 +25,7 @@ internal class KeyboardImpl : IKeyboard private readonly Dictionary _commandImplementations = new (); /// - public IApplication? Application { get; set; } + public IApplication? App { get; set; } /// public KeyBindings KeyBindings { get; internal set; } = new (null); @@ -136,16 +133,16 @@ public bool RaiseKeyDownEvent (Key key) return true; } - if (Application?.Popover?.DispatchKeyDown (key) is true) + if (App?.Popover?.DispatchKeyDown (key) is true) { return true; } - if (Application?.Top is null) + if (App?.Current is null) { - if (Application?.TopLevels is { }) + if (App?.SessionStack is { }) { - foreach (Toplevel topLevel in Application.TopLevels.ToList ()) + foreach (Toplevel topLevel in App.SessionStack.ToList ()) { if (topLevel.NewKeyDownEvent (key)) { @@ -161,7 +158,7 @@ public bool RaiseKeyDownEvent (Key key) } else { - if (Application.Top.NewKeyDownEvent (key)) + if (App.Current.NewKeyDownEvent (key)) { return true; } @@ -179,7 +176,7 @@ public bool RaiseKeyDownEvent (Key key) /// public bool RaiseKeyUpEvent (Key key) { - if (Application?.Initialized != true) + if (App?.Initialized != true) { return true; } @@ -194,9 +191,9 @@ public bool RaiseKeyUpEvent (Key key) // TODO: Add Popover support - if (Application?.TopLevels is { }) + if (App?.SessionStack is { }) { - foreach (Toplevel topLevel in Application.TopLevels.ToList ()) + foreach (Toplevel topLevel in App.SessionStack.ToList ()) { if (topLevel.NewKeyUpEvent (key)) { @@ -294,7 +291,7 @@ internal void AddKeyBindings () Command.Quit, () => { - Application?.RequestStop (); + App?.RequestStop (); return true; } @@ -303,32 +300,32 @@ internal void AddKeyBindings () Command.Suspend, () => { - Application?.Driver?.Suspend (); + App?.Driver?.Suspend (); return true; } ); AddCommand ( Command.NextTabStop, - () => Application?.Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop)); + () => App?.Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop)); AddCommand ( Command.PreviousTabStop, - () => Application?.Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop)); + () => App?.Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop)); AddCommand ( Command.NextTabGroup, - () => Application?.Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup)); + () => App?.Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup)); AddCommand ( Command.PreviousTabGroup, - () => Application?.Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup)); + () => App?.Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup)); AddCommand ( Command.Refresh, () => { - Application?.LayoutAndDraw (true); + App?.LayoutAndDraw (true); return true; } @@ -338,7 +335,7 @@ internal void AddKeyBindings () Command.Arrange, () => { - View? viewToArrange = Application?.Navigation?.GetFocused (); + View? viewToArrange = App?.Navigation?.GetFocused (); // Go up the superview hierarchy and find the first that is not ViewArrangement.Fixed while (viewToArrange is { SuperView: { }, Arrangement: ViewArrangement.Fixed }) diff --git a/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs b/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs index 69a16bd49f..df03c3187e 100644 --- a/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs +++ b/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs @@ -1,8 +1,5 @@ -#nullable enable -using System; using System.Collections.Concurrent; using System.Diagnostics; -using Terminal.Gui.Drivers; namespace Terminal.Gui.App; @@ -30,6 +27,9 @@ public class ApplicationMainLoop : IApplicationMainLoop + public IApplication? App { get; private set; } + /// public ITimedEvents TimedEvents { @@ -82,7 +82,7 @@ public ISizeMonitor SizeMonitor } /// - /// Handles raising events and setting required draw status etc when changes + /// Handles raising events and setting required draw status etc when changes /// public IToplevelTransitionManager ToplevelTransitionManager = new ToplevelTransitionManager (); @@ -94,14 +94,17 @@ public ISizeMonitor SizeMonitor /// /// /// + /// public void Initialize ( ITimedEvents timedEvents, ConcurrentQueue inputBuffer, IInputProcessor inputProcessor, IOutput consoleOutput, - IComponentFactory componentFactory + IComponentFactory componentFactory, + IApplication? app ) { + App = app; InputQueue = inputBuffer; Output = consoleOutput; InputProcessor = inputProcessor; @@ -116,10 +119,10 @@ IComponentFactory componentFactory /// public void Iteration () { - Application.RaiseIteration (); + App?.RaiseIteration (); DateTime dt = DateTime.Now; - int timeAllowed = 1000 / Math.Max(1,(int)Application.MaximumIterationsPerSecond); + int timeAllowed = 1000 / Math.Max (1, (int)Application.MaximumIterationsPerSecond); IterationImpl (); @@ -139,14 +142,14 @@ internal void IterationImpl () // Pull any input events from the input queue and process them InputProcessor.ProcessQueue (); - ToplevelTransitionManager.RaiseReadyEventIfNeeded (); - ToplevelTransitionManager.HandleTopMaybeChanging (); + ToplevelTransitionManager.RaiseReadyEventIfNeeded (App); + ToplevelTransitionManager.HandleTopMaybeChanging (App); - if (Application.Top != null) + if (App?.Current != null) { - bool needsDrawOrLayout = AnySubViewsNeedDrawn (Application.Popover?.GetActivePopover () as View) - || AnySubViewsNeedDrawn (Application.Top) - || (Application.Mouse.MouseGrabView != null && AnySubViewsNeedDrawn (Application.Mouse.MouseGrabView)); + bool needsDrawOrLayout = AnySubViewsNeedDrawn (App?.Popover?.GetActivePopover () as View) + || AnySubViewsNeedDrawn (App?.Current) + || (App?.Mouse.MouseGrabView != null && AnySubViewsNeedDrawn (App?.Mouse.MouseGrabView)); bool sizeChanged = SizeMonitor.Poll (); @@ -154,7 +157,7 @@ internal void IterationImpl () { Logging.Redraws.Add (1); - Application.LayoutAndDraw (true); + App?.LayoutAndDraw (true); Output.Write (OutputBuffer); @@ -173,7 +176,7 @@ internal void IterationImpl () private void SetCursor () { - View? mostFocused = Application.Top!.MostFocused; + View? mostFocused = App?.Current!.MostFocused; if (mostFocused == null) { @@ -205,7 +208,7 @@ private bool AnySubViewsNeedDrawn (View? v) if (v.NeedsDraw || v.NeedsLayout) { - // Logging.Trace ($"{v.GetType ().Name} triggered redraw (NeedsDraw={v.NeedsDraw} NeedsLayout={v.NeedsLayout}) "); + // Logging.Trace ($"{v.GetType ().Name} triggered redraw (NeedsDraw={v.NeedsDraw} NeedsLayout={v.NeedsLayout}) "); return true; } diff --git a/Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs b/Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs index 014e002ba1..24c997e2e3 100644 --- a/Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs +++ b/Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.App; @@ -18,6 +17,11 @@ namespace Terminal.Gui.App; /// Type of raw input events processed by the loop, e.g. for cross-platform .NET driver public interface IApplicationMainLoop : IDisposable where TInputRecord : struct { + /// + /// The Application this loop is associated with. + /// + public IApplication? App { get; } + /// /// Gets the implementation that manages user-defined timeouts and periodic events. /// @@ -73,6 +77,7 @@ public interface IApplicationMainLoop : IDisposable where TInputRe /// The factory for creating driver-specific components. Used here to create the /// that tracks terminal size changes. /// + /// /// /// /// This method is called by during application startup @@ -98,7 +103,8 @@ void Initialize ( ConcurrentQueue inputQueue, IInputProcessor inputProcessor, IOutput output, - IComponentFactory componentFactory + IComponentFactory componentFactory, + IApplication? app ); /// diff --git a/Terminal.Gui/App/MainLoop/IMainLoopCoordinator.cs b/Terminal.Gui/App/MainLoop/IMainLoopCoordinator.cs index e08b2a742d..c5321a2eae 100644 --- a/Terminal.Gui/App/MainLoop/IMainLoopCoordinator.cs +++ b/Terminal.Gui/App/MainLoop/IMainLoopCoordinator.cs @@ -16,6 +16,7 @@ public interface IMainLoopCoordinator /// /// Initializes all required subcomponents and starts the input thread. /// + /// /// /// This method: /// @@ -25,7 +26,7 @@ public interface IMainLoopCoordinator /// /// /// A task that completes when initialization is done - public Task StartInputTaskAsync (); + public Task StartInputTaskAsync (IApplication? app); /// /// Stops the input thread and performs cleanup. diff --git a/Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs b/Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs index 45fd2a7d36..58b54c94e5 100644 --- a/Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs +++ b/Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs @@ -46,24 +46,25 @@ IComponentFactory componentFactory private readonly ITimedEvents _timedEvents; private readonly SemaphoreSlim _startupSemaphore = new (0, 1); - private IInput _input; - private Task _inputTask; - private IOutput _output; - private DriverImpl _driver; + private IInput? _input; + private Task? _inputTask; + private IOutput? _output; + private DriverImpl? _driver; private bool _stopCalled; /// /// Starts the input loop thread in separate task (returning immediately). /// - public async Task StartInputTaskAsync () + /// The instance that is running the input loop. + public async Task StartInputTaskAsync (IApplication? app) { Logging.Trace ("Booting... ()"); - _inputTask = Task.Run (RunInput); + _inputTask = Task.Run (() => RunInput (app)); // Main loop is now booted on same thread as rest of users application - BootMainLoop (); + BootMainLoop (app); // Wait asynchronously for the semaphore or task failure. Task waitForSemaphore = _startupSemaphore.WaitAsync (); @@ -107,13 +108,13 @@ public void Stop () _stopCalled = true; _runCancellationTokenSource.Cancel (); - _output.Dispose (); + _output?.Dispose (); // Wait for input infinite loop to exit - _inputTask.Wait (); + _inputTask?.Wait (); } - private void BootMainLoop () + private void BootMainLoop (IApplication? app) { //Logging.Trace ($"_inputProcessor: {_inputProcessor}, _output: {_output}, _componentFactory: {_componentFactory}"); @@ -121,13 +122,13 @@ private void BootMainLoop () { // Instance must be constructed on the thread in which it is used. _output = _componentFactory.CreateOutput (); - _loop.Initialize (_timedEvents, _inputQueue, _inputProcessor, _output, _componentFactory); + _loop.Initialize (_timedEvents, _inputQueue, _inputProcessor, _output, _componentFactory, app); - BuildDriverIfPossible (); + BuildDriverIfPossible (app); } } - private void BuildDriverIfPossible () + private void BuildDriverIfPossible (IApplication? app) { if (_input != null && _output != null) @@ -139,7 +140,7 @@ private void BuildDriverIfPossible () _loop.AnsiRequestScheduler, _loop.SizeMonitor); - Application.Driver = _driver; + app!.Driver = _driver; _startupSemaphore.Release (); Logging.Trace ($"Driver: _input: {_input}, _output: {_output}"); @@ -149,7 +150,8 @@ private void BuildDriverIfPossible () /// /// INTERNAL: Runs the IInput read loop on a new thread called the "Input Thread". /// - private void RunInput () + /// + private void RunInput (IApplication? app) { try { @@ -165,7 +167,7 @@ private void RunInput () impl.InputImpl = _input; } - BuildDriverIfPossible (); + BuildDriverIfPossible (app); } try diff --git a/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs b/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs index f67f5ec813..b9375ccca4 100644 --- a/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs +++ b/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs @@ -1,42 +1,43 @@ +#nullable disable namespace Terminal.Gui.App; -/// -/// provides the sync context set while executing code in Terminal.Gui, to let -/// users use async/await on their code -/// -internal sealed class MainLoopSyncContext : SynchronizationContext -{ - public override SynchronizationContext CreateCopy () { return new MainLoopSyncContext (); } +///// +///// provides the sync context set while executing code in Terminal.Gui, to let +///// users use async/await on their code +///// +//internal sealed class MainLoopSyncContext : SynchronizationContext +//{ +// public override SynchronizationContext CreateCopy () { return new MainLoopSyncContext (); } - public override void Post (SendOrPostCallback d, object state) - { - // Queue the task using the modern architecture - ApplicationImpl.Instance.Invoke (() => { d (state); }); - } +// public override void Post (SendOrPostCallback d, object state) +// { +// // Queue the task using the modern architecture +// ApplicationImpl.Instance.Invoke (() => { d (state); }); +// } - //_mainLoop.Driver.Wakeup (); - public override void Send (SendOrPostCallback d, object state) - { - if (Thread.CurrentThread.ManagedThreadId == Application.MainThreadId) - { - d (state); - } - else - { - var wasExecuted = false; +// //_mainLoop.Driver.Wakeup (); +// public override void Send (SendOrPostCallback d, object state) +// { +// if (Thread.CurrentThread.ManagedThreadId == Application.MainThreadId) +// { +// d (state); +// } +// else +// { +// var wasExecuted = false; - Application.Invoke ( - () => - { - d (state); - wasExecuted = true; - } - ); +// ApplicationImpl.Instance.Invoke ( +// () => +// { +// d (state); +// wasExecuted = true; +// } +// ); - while (!wasExecuted) - { - Thread.Sleep (15); - } - } - } -} +// while (!wasExecuted) +// { +// Thread.Sleep (15); +// } +// } +// } +//} diff --git a/Terminal.Gui/App/Mouse/IMouse.cs b/Terminal.Gui/App/Mouse/IMouse.cs index a4bc2c2e8c..ed01dbf1bf 100644 --- a/Terminal.Gui/App/Mouse/IMouse.cs +++ b/Terminal.Gui/App/Mouse/IMouse.cs @@ -1,4 +1,3 @@ -#nullable enable using System.ComponentModel; namespace Terminal.Gui.App; @@ -6,7 +5,7 @@ namespace Terminal.Gui.App; /// /// Defines a contract for mouse event handling and state management in a Terminal.Gui application. /// -/// This interface allows for decoupling of mouse-related functionality from the static class, +/// This interface allows for decoupling of mouse-related functionality from the static class, /// enabling better testability and parallel test execution. /// /// @@ -16,18 +15,13 @@ public interface IMouse : IMouseGrabHandler /// Sets the application instance that this mouse handler is associated with. /// This provides access to application state without coupling to static Application class. /// - IApplication? Application { get; set; } + IApplication? App { get; set; } /// /// Gets or sets the last known position of the mouse. /// Point? LastMousePosition { get; set; } - /// - /// Gets the most recent position of the mouse. - /// - Point? GetLastMousePosition (); - /// /// Gets or sets whether the mouse is disabled. The mouse is enabled by default. /// diff --git a/Terminal.Gui/App/Mouse/IMouseGrabHandler.cs b/Terminal.Gui/App/Mouse/IMouseGrabHandler.cs index 06fd0e626c..31bdebeaf7 100644 --- a/Terminal.Gui/App/Mouse/IMouseGrabHandler.cs +++ b/Terminal.Gui/App/Mouse/IMouseGrabHandler.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; /// diff --git a/Terminal.Gui/App/Mouse/MouseGrabHandler.cs b/Terminal.Gui/App/Mouse/MouseGrabHandler.cs index 3fe7ab6890..175d371ed3 100644 --- a/Terminal.Gui/App/Mouse/MouseGrabHandler.cs +++ b/Terminal.Gui/App/Mouse/MouseGrabHandler.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; /// diff --git a/Terminal.Gui/App/Mouse/MouseImpl.cs b/Terminal.Gui/App/Mouse/MouseImpl.cs index 78d662ddeb..29116db0ad 100644 --- a/Terminal.Gui/App/Mouse/MouseImpl.cs +++ b/Terminal.Gui/App/Mouse/MouseImpl.cs @@ -1,13 +1,11 @@ -#nullable enable using System.ComponentModel; -using System.Diagnostics; namespace Terminal.Gui.App; /// /// INTERNAL: Implements to manage mouse event handling and state. /// -/// This class holds all mouse-related state that was previously in the static class, +/// This class holds all mouse-related state that was previously in the static class, /// enabling better testability and parallel test execution. /// /// @@ -19,14 +17,11 @@ internal class MouseImpl : IMouse public MouseImpl () { } /// - public IApplication? Application { get; set; } + public IApplication? App { get; set; } /// public Point? LastMousePosition { get; set; } - /// - public Point? GetLastMousePosition () { return LastMousePosition; } - /// public bool IsMouseDisabled { get; set; } @@ -57,7 +52,7 @@ public MouseImpl () { } public void RaiseMouseEvent (MouseEventArgs mouseEvent) { //Debug.Assert (App.Application.MainThreadId == Thread.CurrentThread.ManagedThreadId); - if (Application?.Initialized is true) + if (App?.Initialized is true) { // LastMousePosition is only set if the application is initialized. LastMousePosition = mouseEvent.ScreenPosition; @@ -72,9 +67,9 @@ public void RaiseMouseEvent (MouseEventArgs mouseEvent) //Debug.Assert (mouseEvent.Position == mouseEvent.ScreenPosition); mouseEvent.Position = mouseEvent.ScreenPosition; - List currentViewsUnderMouse = View.GetViewsUnderLocation (mouseEvent.ScreenPosition, ViewportSettingsFlags.TransparentMouse); + List? currentViewsUnderMouse = App?.Current?.GetViewsUnderLocation (mouseEvent.ScreenPosition, ViewportSettingsFlags.TransparentMouse); - View? deepestViewUnderMouse = currentViewsUnderMouse.LastOrDefault (); + View? deepestViewUnderMouse = currentViewsUnderMouse?.LastOrDefault (); if (deepestViewUnderMouse is { }) { @@ -96,7 +91,7 @@ public void RaiseMouseEvent (MouseEventArgs mouseEvent) // Dismiss the Popover if the user presses mouse outside of it if (mouseEvent.IsPressed - && Application?.Popover?.GetActivePopover () as View is { Visible: true } visiblePopover + && App?.Popover?.GetActivePopover () as View is { Visible: true } visiblePopover && View.IsInHierarchy (visiblePopover, deepestViewUnderMouse, includeAdornments: true) is false) { ApplicationPopover.HideWithQuitCommand (visiblePopover); @@ -119,9 +114,9 @@ public void RaiseMouseEvent (MouseEventArgs mouseEvent) return; } - // if the mouse is outside the Application.Top or Application.Popover hierarchy, we don't want to + // if the mouse is outside the Application.Current or Popover hierarchy, we don't want to // send the mouse event to the deepest view under the mouse. - if (!View.IsInHierarchy (Application?.Top, deepestViewUnderMouse, true) && !View.IsInHierarchy (Application?.Popover?.GetActivePopover () as View, deepestViewUnderMouse, true)) + if (!View.IsInHierarchy (App?.Current, deepestViewUnderMouse, true) && !View.IsInHierarchy (App?.Popover?.GetActivePopover () as View, deepestViewUnderMouse, true)) { return; } @@ -161,7 +156,10 @@ public void RaiseMouseEvent (MouseEventArgs mouseEvent) return; } - RaiseMouseEnterLeaveEvents (viewMouseEvent.ScreenPosition, currentViewsUnderMouse); + if (currentViewsUnderMouse is { }) + { + RaiseMouseEnterLeaveEvents (viewMouseEvent.ScreenPosition, currentViewsUnderMouse); + } while (deepestViewUnderMouse.NewMouseEvent (viewMouseEvent) is not true && MouseGrabView is not { }) { diff --git a/Terminal.Gui/App/PopoverBaseImpl.cs b/Terminal.Gui/App/PopoverBaseImpl.cs index 1e9aacb71b..41258401f1 100644 --- a/Terminal.Gui/App/PopoverBaseImpl.cs +++ b/Terminal.Gui/App/PopoverBaseImpl.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; @@ -74,8 +73,18 @@ protected PopoverBaseImpl () } } + private Toplevel? _current; + /// - public Toplevel? Toplevel { get; set; } + public Toplevel? Current + { + get => _current; + set + { + _current = value; + App ??= _current?.App; + } + } /// /// Called when the property is changing. @@ -100,14 +109,17 @@ protected override bool OnVisibleChanging () { // Whenever visible is changing to true, we need to resize; // it's our only chance because we don't get laid out until we're visible - Layout (Application.Screen.Size); + if (App is { }) + { + Layout (App.Screen.Size); + } } else { // Whenever visible is changing to false, we need to reset the focus - if (ApplicationNavigation.IsInHierarchy (this, Application.Navigation?.GetFocused ())) + if (ApplicationNavigation.IsInHierarchy (this, App?.Navigation?.GetFocused ())) { - Application.Navigation?.SetFocused (Application.Top?.MostFocused); + App?.Navigation?.SetFocused (App?.Current?.MostFocused); } } diff --git a/Terminal.Gui/App/SessionToken.cs b/Terminal.Gui/App/SessionToken.cs index d6466ed3f2..6505727bb7 100644 --- a/Terminal.Gui/App/SessionToken.cs +++ b/Terminal.Gui/App/SessionToken.cs @@ -10,7 +10,7 @@ public class SessionToken : IDisposable public SessionToken (Toplevel view) { Toplevel = view; } /// The belonging to this . - public Toplevel Toplevel { get; internal set; } + public Toplevel? Toplevel { get; internal set; } /// Releases all resource used by the object. /// Call when you are finished using the . diff --git a/Terminal.Gui/App/Timeout/ITimedEvents.cs b/Terminal.Gui/App/Timeout/ITimedEvents.cs index aa9499520c..376f71f516 100644 --- a/Terminal.Gui/App/Timeout/ITimedEvents.cs +++ b/Terminal.Gui/App/Timeout/ITimedEvents.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.App; /// @@ -49,4 +48,14 @@ public interface ITimedEvents /// for each timeout that is not actively executing. /// SortedList Timeouts { get; } + + /// + /// Gets the timeout for the specified event. + /// + /// The token of the event. + /// The for the event, or if the event is not found. + TimeSpan? GetTimeout (object token); + + /// Stops and removes all timed events. + void StopAll (); } diff --git a/Terminal.Gui/App/Timeout/LogarithmicTimeout.cs b/Terminal.Gui/App/Timeout/LogarithmicTimeout.cs index eacf09607e..25690eb244 100644 --- a/Terminal.Gui/App/Timeout/LogarithmicTimeout.cs +++ b/Terminal.Gui/App/Timeout/LogarithmicTimeout.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.App; /// Implements a logarithmic increasing timeout. diff --git a/Terminal.Gui/App/Timeout/SmoothAcceleratingTimeout.cs b/Terminal.Gui/App/Timeout/SmoothAcceleratingTimeout.cs index 962ce4b19f..7a11dcddcd 100644 --- a/Terminal.Gui/App/Timeout/SmoothAcceleratingTimeout.cs +++ b/Terminal.Gui/App/Timeout/SmoothAcceleratingTimeout.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.App; /// diff --git a/Terminal.Gui/App/Timeout/TimedEvents.cs b/Terminal.Gui/App/Timeout/TimedEvents.cs index da1dcc5c02..09e008b511 100644 --- a/Terminal.Gui/App/Timeout/TimedEvents.cs +++ b/Terminal.Gui/App/Timeout/TimedEvents.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; namespace Terminal.Gui.App; @@ -145,6 +144,22 @@ public bool CheckTimers (out int waitTimeout) return false; } + /// + public TimeSpan? GetTimeout (object token) + { + lock (_timeoutsLockToken) + { + int idx = _timeouts.IndexOfValue ((token as Timeout)!); + + if (idx == -1) + { + return null; + } + + return _timeouts.Values [idx].Span; + } + } + private void AddTimeout (TimeSpan time, Timeout timeout) { lock (_timeoutsLockToken) @@ -202,7 +217,7 @@ private void RunTimersImpl () { if (k < now) { - if (timeout.Callback ()) + if (timeout.Callback! ()) { AddTimeout (timeout.Span, timeout); } @@ -216,4 +231,13 @@ private void RunTimersImpl () } } } + + /// + public void StopAll () + { + lock (_timeoutsLockToken) + { + _timeouts.Clear (); + } + } } diff --git a/Terminal.Gui/App/Timeout/Timeout.cs b/Terminal.Gui/App/Timeout/Timeout.cs index c3054869f8..226dc9e2fb 100644 --- a/Terminal.Gui/App/Timeout/Timeout.cs +++ b/Terminal.Gui/App/Timeout/Timeout.cs @@ -20,7 +20,7 @@ public class Timeout /// rescheduled and invoked again after the same interval. /// If the callback returns , the timeout will be removed and not invoked again. /// - public Func Callback { get; set; } + public Func? Callback { get; set; } /// /// Gets or sets the time interval to wait before invoking the . diff --git a/Terminal.Gui/App/Toplevel/IToplevelTransitionManager.cs b/Terminal.Gui/App/Toplevel/IToplevelTransitionManager.cs index 7b69f8c9ba..26c44129b9 100644 --- a/Terminal.Gui/App/Toplevel/IToplevelTransitionManager.cs +++ b/Terminal.Gui/App/Toplevel/IToplevelTransitionManager.cs @@ -7,14 +7,16 @@ public interface IToplevelTransitionManager { /// - /// Raises the event on the current top level + /// Raises the event on tahe current top level /// if it has not been raised before now. /// - void RaiseReadyEventIfNeeded (); + /// + void RaiseReadyEventIfNeeded (IApplication? app); /// /// Handles any state change needed when the application top changes e.g. /// setting redraw flags /// - void HandleTopMaybeChanging (); + /// + void HandleTopMaybeChanging (IApplication? app); } diff --git a/Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs b/Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs index 282dde040b..10166b4502 100644 --- a/Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs +++ b/Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs @@ -1,6 +1,3 @@ -#nullable enable -using Terminal.Gui.Drivers; - namespace Terminal.Gui.App; /// @@ -12,10 +9,11 @@ public class ToplevelTransitionManager : IToplevelTransitionManager private View? _lastTop; + /// /// - public void RaiseReadyEventIfNeeded () + public void RaiseReadyEventIfNeeded (IApplication? app) { - Toplevel? top = Application.Top; + Toplevel? top = app?.Current; if (top != null && !_readiedTopLevels.Contains (top)) { @@ -27,16 +25,17 @@ public void RaiseReadyEventIfNeeded () } } + /// /// - public void HandleTopMaybeChanging () + public void HandleTopMaybeChanging (IApplication? app) { - Toplevel? newTop = Application.Top; + Toplevel? newTop = app?.Current; if (_lastTop != null && _lastTop != newTop && newTop != null) { newTop.SetNeedsDraw (); } - _lastTop = Application.Top; + _lastTop = app?.Current; } } diff --git a/Terminal.Gui/Configuration/AppSettingsScope.cs b/Terminal.Gui/Configuration/AppSettingsScope.cs index 35594cacbf..66c6af7f09 100644 --- a/Terminal.Gui/Configuration/AppSettingsScope.cs +++ b/Terminal.Gui/Configuration/AppSettingsScope.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace Terminal.Gui.Configuration; diff --git a/Terminal.Gui/Configuration/AttributeJsonConverter.cs b/Terminal.Gui/Configuration/AttributeJsonConverter.cs index 34ee281c51..2a00b556f6 100644 --- a/Terminal.Gui/Configuration/AttributeJsonConverter.cs +++ b/Terminal.Gui/Configuration/AttributeJsonConverter.cs @@ -9,7 +9,7 @@ namespace Terminal.Gui.Configuration; internal class AttributeJsonConverter : JsonConverter { - private static AttributeJsonConverter _instance; + private static AttributeJsonConverter? _instance; /// public static AttributeJsonConverter Instance @@ -63,7 +63,7 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J throw new JsonException ($"{propertyName}: Unexpected token when parsing Attribute: {reader.TokenType}."); } - propertyName = reader.GetString (); + propertyName = reader.GetString ()!; reader.Read (); var property = $"\"{reader.GetString ()}\""; diff --git a/Terminal.Gui/Configuration/ColorJsonConverter.cs b/Terminal.Gui/Configuration/ColorJsonConverter.cs index 70d6ca7e71..8959a9a109 100644 --- a/Terminal.Gui/Configuration/ColorJsonConverter.cs +++ b/Terminal.Gui/Configuration/ColorJsonConverter.cs @@ -15,7 +15,7 @@ namespace Terminal.Gui.Configuration; /// internal class ColorJsonConverter : JsonConverter { - private static ColorJsonConverter _instance; + private static ColorJsonConverter? _instance; /// Singleton public static ColorJsonConverter Instance diff --git a/Terminal.Gui/Configuration/ConcurrentDictionaryJsonConverter.cs b/Terminal.Gui/Configuration/ConcurrentDictionaryJsonConverter.cs index a33f9181a7..a5d1861841 100644 --- a/Terminal.Gui/Configuration/ConcurrentDictionaryJsonConverter.cs +++ b/Terminal.Gui/Configuration/ConcurrentDictionaryJsonConverter.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Text.Json; diff --git a/Terminal.Gui/Configuration/ConfigLocations.cs b/Terminal.Gui/Configuration/ConfigLocations.cs index 8f348fa8ca..c41aacde9a 100644 --- a/Terminal.Gui/Configuration/ConfigLocations.cs +++ b/Terminal.Gui/Configuration/ConfigLocations.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.Configuration; +namespace Terminal.Gui.Configuration; /// /// Describes the location of the configuration settings. The constants can be combined (bitwise) to specify multiple diff --git a/Terminal.Gui/Configuration/ConfigProperty.cs b/Terminal.Gui/Configuration/ConfigProperty.cs index 0442a3b6f0..0a5d7dc8f9 100644 --- a/Terminal.Gui/Configuration/ConfigProperty.cs +++ b/Terminal.Gui/Configuration/ConfigProperty.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index 6f0364ad58..b8d3cdf5d4 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -1,6 +1,4 @@ -#nullable enable - -using System.Collections.Frozen; +using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; diff --git a/Terminal.Gui/Configuration/ConfigurationManagerEventArgs.cs b/Terminal.Gui/Configuration/ConfigurationManagerEventArgs.cs index 24a325ab7d..ea03daff18 100644 --- a/Terminal.Gui/Configuration/ConfigurationManagerEventArgs.cs +++ b/Terminal.Gui/Configuration/ConfigurationManagerEventArgs.cs @@ -1,6 +1,4 @@ -#nullable enable - -namespace Terminal.Gui.Configuration; +namespace Terminal.Gui.Configuration; /// Event arguments for the events. public class ConfigurationManagerEventArgs : EventArgs diff --git a/Terminal.Gui/Configuration/ConfigurationManagerNotEnabledException.cs b/Terminal.Gui/Configuration/ConfigurationManagerNotEnabledException.cs index 45206c876d..f9fc6b62ee 100644 --- a/Terminal.Gui/Configuration/ConfigurationManagerNotEnabledException.cs +++ b/Terminal.Gui/Configuration/ConfigurationManagerNotEnabledException.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.Configuration; +namespace Terminal.Gui.Configuration; /// /// The exception that is thrown when a API is called but the configuration manager is not enabled. diff --git a/Terminal.Gui/Configuration/ConfigurationPropertyAttribute.cs b/Terminal.Gui/Configuration/ConfigurationPropertyAttribute.cs index 2f1218aa99..3911e9f881 100644 --- a/Terminal.Gui/Configuration/ConfigurationPropertyAttribute.cs +++ b/Terminal.Gui/Configuration/ConfigurationPropertyAttribute.cs @@ -1,6 +1,4 @@ -#nullable enable - -namespace Terminal.Gui.Configuration; +namespace Terminal.Gui.Configuration; /// An attribute indicating a property is managed by . /// diff --git a/Terminal.Gui/Configuration/DeepCloner.cs b/Terminal.Gui/Configuration/DeepCloner.cs index 0d918625c8..3a6caec52b 100644 --- a/Terminal.Gui/Configuration/DeepCloner.cs +++ b/Terminal.Gui/Configuration/DeepCloner.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.Collections; using System.Collections.Concurrent; diff --git a/Terminal.Gui/Configuration/DictionaryJsonConverter.cs b/Terminal.Gui/Configuration/DictionaryJsonConverter.cs index bfd940d336..2cdbbfd48f 100644 --- a/Terminal.Gui/Configuration/DictionaryJsonConverter.cs +++ b/Terminal.Gui/Configuration/DictionaryJsonConverter.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.CodeAnalysis; +#nullable disable +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/Terminal.Gui/Configuration/KeyCodeJsonConverter.cs b/Terminal.Gui/Configuration/KeyCodeJsonConverter.cs index 04d8d9765f..d69ea94796 100644 --- a/Terminal.Gui/Configuration/KeyCodeJsonConverter.cs +++ b/Terminal.Gui/Configuration/KeyCodeJsonConverter.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +#nullable disable +using System.Text.Json; using System.Text.Json.Serialization; namespace Terminal.Gui.Configuration; diff --git a/Terminal.Gui/Configuration/KeyJsonConverter.cs b/Terminal.Gui/Configuration/KeyJsonConverter.cs index 01413c432f..1e723c8b72 100644 --- a/Terminal.Gui/Configuration/KeyJsonConverter.cs +++ b/Terminal.Gui/Configuration/KeyJsonConverter.cs @@ -9,7 +9,7 @@ public class KeyJsonConverter : JsonConverter /// public override Key Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return Key.TryParse (reader.GetString (), out Key key) ? key : Key.Empty; + return Key.TryParse (reader.GetString ()!, out Key key) ? key : Key.Empty; } /// diff --git a/Terminal.Gui/Configuration/RuneJsonConverter.cs b/Terminal.Gui/Configuration/RuneJsonConverter.cs index 8fc7f9f7bc..48ec575180 100644 --- a/Terminal.Gui/Configuration/RuneJsonConverter.cs +++ b/Terminal.Gui/Configuration/RuneJsonConverter.cs @@ -26,7 +26,7 @@ public override Rune Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSe { case JsonTokenType.String: { - string value = reader.GetString (); + string? value = reader.GetString (); int first = RuneExtensions.MaxUnicodeCodePoint + 1; int second = RuneExtensions.MaxUnicodeCodePoint + 1; diff --git a/Terminal.Gui/Configuration/SchemeJsonConverter.cs b/Terminal.Gui/Configuration/SchemeJsonConverter.cs index cabeefacf2..fe363dc54d 100644 --- a/Terminal.Gui/Configuration/SchemeJsonConverter.cs +++ b/Terminal.Gui/Configuration/SchemeJsonConverter.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/Terminal.Gui/Configuration/SchemeManager.cs b/Terminal.Gui/Configuration/SchemeManager.cs index 4fa1fd8098..0023a0824a 100644 --- a/Terminal.Gui/Configuration/SchemeManager.cs +++ b/Terminal.Gui/Configuration/SchemeManager.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; diff --git a/Terminal.Gui/Configuration/Scope.cs b/Terminal.Gui/Configuration/Scope.cs index 88d6372640..a3fe4f069d 100644 --- a/Terminal.Gui/Configuration/Scope.cs +++ b/Terminal.Gui/Configuration/Scope.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; diff --git a/Terminal.Gui/Configuration/ScopeJsonConverter.cs b/Terminal.Gui/Configuration/ScopeJsonConverter.cs index 034e904ae3..94ed04e8fa 100644 --- a/Terminal.Gui/Configuration/ScopeJsonConverter.cs +++ b/Terminal.Gui/Configuration/ScopeJsonConverter.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/Terminal.Gui/Configuration/SettingsScope.cs b/Terminal.Gui/Configuration/SettingsScope.cs index 5feeb11313..de9dbdeb8a 100644 --- a/Terminal.Gui/Configuration/SettingsScope.cs +++ b/Terminal.Gui/Configuration/SettingsScope.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace Terminal.Gui.Configuration; diff --git a/Terminal.Gui/Configuration/SourcesManager.cs b/Terminal.Gui/Configuration/SourcesManager.cs index 7290865419..541d452a29 100644 --- a/Terminal.Gui/Configuration/SourcesManager.cs +++ b/Terminal.Gui/Configuration/SourcesManager.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Diagnostics; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json; diff --git a/Terminal.Gui/Configuration/ThemeManager.cs b/Terminal.Gui/Configuration/ThemeManager.cs index b184ba9ba3..a234ac8699 100644 --- a/Terminal.Gui/Configuration/ThemeManager.cs +++ b/Terminal.Gui/Configuration/ThemeManager.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; diff --git a/Terminal.Gui/Configuration/ThemeScope.cs b/Terminal.Gui/Configuration/ThemeScope.cs index 541cb80f66..29bef03d84 100644 --- a/Terminal.Gui/Configuration/ThemeScope.cs +++ b/Terminal.Gui/Configuration/ThemeScope.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace Terminal.Gui.Configuration; diff --git a/Terminal.Gui/Drawing/Attribute.cs b/Terminal.Gui/Drawing/Attribute.cs index bc7005c411..0b794f20ce 100644 --- a/Terminal.Gui/Drawing/Attribute.cs +++ b/Terminal.Gui/Drawing/Attribute.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Numerics; +using System.Numerics; using System.Text.Json.Serialization; namespace Terminal.Gui.Drawing; diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index e72a7837ee..2fa98bef1e 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drawing; diff --git a/Terminal.Gui/Drawing/Color/AnsiColorCode.cs b/Terminal.Gui/Drawing/Color/AnsiColorCode.cs index 56bc857a80..28d1fafb7b 100644 --- a/Terminal.Gui/Drawing/Color/AnsiColorCode.cs +++ b/Terminal.Gui/Drawing/Color/AnsiColorCode.cs @@ -1,3 +1,4 @@ +// ReSharper disable InconsistentNaming namespace Terminal.Gui.Drawing; /// diff --git a/Terminal.Gui/Drawing/Color/AnsiColorNameResolver.cs b/Terminal.Gui/Drawing/Color/AnsiColorNameResolver.cs index aae4a6da55..5a0ebc8278 100644 --- a/Terminal.Gui/Drawing/Color/AnsiColorNameResolver.cs +++ b/Terminal.Gui/Drawing/Color/AnsiColorNameResolver.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; diff --git a/Terminal.Gui/Drawing/Color/Color.ColorExtensions.cs b/Terminal.Gui/Drawing/Color/Color.ColorExtensions.cs index 84e5f089aa..a0b23b5454 100644 --- a/Terminal.Gui/Drawing/Color/Color.ColorExtensions.cs +++ b/Terminal.Gui/Drawing/Color/Color.ColorExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Frozen; using ColorHelper; diff --git a/Terminal.Gui/Drawing/Color/Color.ColorParseException.cs b/Terminal.Gui/Drawing/Color/Color.ColorParseException.cs index 97595db040..ac1da5d5f1 100644 --- a/Terminal.Gui/Drawing/Color/Color.ColorParseException.cs +++ b/Terminal.Gui/Drawing/Color/Color.ColorParseException.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.Drawing; diff --git a/Terminal.Gui/Drawing/Color/Color.Formatting.cs b/Terminal.Gui/Drawing/Color/Color.Formatting.cs index 15e0dccb99..89082867bb 100644 --- a/Terminal.Gui/Drawing/Color/Color.Formatting.cs +++ b/Terminal.Gui/Drawing/Color/Color.Formatting.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; diff --git a/Terminal.Gui/Drawing/Color/Color.Operators.cs b/Terminal.Gui/Drawing/Color/Color.Operators.cs index 831f32babc..b8d33bd4ab 100644 --- a/Terminal.Gui/Drawing/Color/Color.Operators.cs +++ b/Terminal.Gui/Drawing/Color/Color.Operators.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics.Contracts; using System.Numerics; diff --git a/Terminal.Gui/Drawing/Color/Color.cs b/Terminal.Gui/Drawing/Color/Color.cs index 995249c133..e83a05a73b 100644 --- a/Terminal.Gui/Drawing/Color/Color.cs +++ b/Terminal.Gui/Drawing/Color/Color.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Frozen; using System.Globalization; using System.Numerics; diff --git a/Terminal.Gui/Drawing/Color/ColorModel.cs b/Terminal.Gui/Drawing/Color/ColorModel.cs index 6af865a9ca..158c03236f 100644 --- a/Terminal.Gui/Drawing/Color/ColorModel.cs +++ b/Terminal.Gui/Drawing/Color/ColorModel.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Drawing; /// diff --git a/Terminal.Gui/Drawing/Color/ColorStrings.cs b/Terminal.Gui/Drawing/Color/ColorStrings.cs index 705ea13e4c..8f70c6a9e6 100644 --- a/Terminal.Gui/Drawing/Color/ColorStrings.cs +++ b/Terminal.Gui/Drawing/Color/ColorStrings.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Globalization; namespace Terminal.Gui.Drawing; diff --git a/Terminal.Gui/Drawing/Color/IColorNameResolver.cs b/Terminal.Gui/Drawing/Color/IColorNameResolver.cs index 36881adb39..14e44718fd 100644 --- a/Terminal.Gui/Drawing/Color/IColorNameResolver.cs +++ b/Terminal.Gui/Drawing/Color/IColorNameResolver.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.Drawing; diff --git a/Terminal.Gui/Drawing/Color/ICustomColorFormatter.cs b/Terminal.Gui/Drawing/Color/ICustomColorFormatter.cs index 425a024415..3bcd149198 100644 --- a/Terminal.Gui/Drawing/Color/ICustomColorFormatter.cs +++ b/Terminal.Gui/Drawing/Color/ICustomColorFormatter.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drawing; /// An interface to support custom formatting and parsing of values. diff --git a/Terminal.Gui/Drawing/Color/MultiStandardColorNameResolver.cs b/Terminal.Gui/Drawing/Color/MultiStandardColorNameResolver.cs index d409950eb3..36ed509103 100644 --- a/Terminal.Gui/Drawing/Color/MultiStandardColorNameResolver.cs +++ b/Terminal.Gui/Drawing/Color/MultiStandardColorNameResolver.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; diff --git a/Terminal.Gui/Drawing/Color/StandardColors.cs b/Terminal.Gui/Drawing/Color/StandardColors.cs index 05adfdb304..7e5b83831d 100644 --- a/Terminal.Gui/Drawing/Color/StandardColors.cs +++ b/Terminal.Gui/Drawing/Color/StandardColors.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; diff --git a/Terminal.Gui/Drawing/Color/StandardColorsNameResolver.cs b/Terminal.Gui/Drawing/Color/StandardColorsNameResolver.cs index daae80b9e4..0c8a4257a4 100644 --- a/Terminal.Gui/Drawing/Color/StandardColorsNameResolver.cs +++ b/Terminal.Gui/Drawing/Color/StandardColorsNameResolver.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.Drawing; @@ -17,4 +15,4 @@ public class StandardColorsNameResolver : IColorNameResolver /// public bool TryNameColor (Color color, [NotNullWhen (true)] out string? name) => StandardColors.TryNameColor (color, out name); -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drawing/Glyphs.cs b/Terminal.Gui/Drawing/Glyphs.cs index 28eb3a5465..71336009d8 100644 --- a/Terminal.Gui/Drawing/Glyphs.cs +++ b/Terminal.Gui/Drawing/Glyphs.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drawing; /// Defines the standard set of glyphs used to draw checkboxes, lines, borders, etc... diff --git a/Terminal.Gui/Drawing/LineCanvas/IntersectionDefinition.cs b/Terminal.Gui/Drawing/LineCanvas/IntersectionDefinition.cs index a03f8bca84..9f2c77df7e 100644 --- a/Terminal.Gui/Drawing/LineCanvas/IntersectionDefinition.cs +++ b/Terminal.Gui/Drawing/LineCanvas/IntersectionDefinition.cs @@ -17,4 +17,4 @@ internal IntersectionDefinition (Point point, IntersectionType type, StraightLin /// Defines how position relates to . internal IntersectionType Type { get; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drawing/LineCanvas/IntersectionRuneType.cs b/Terminal.Gui/Drawing/LineCanvas/IntersectionRuneType.cs index b32310fa76..c3a0043f7d 100644 --- a/Terminal.Gui/Drawing/LineCanvas/IntersectionRuneType.cs +++ b/Terminal.Gui/Drawing/LineCanvas/IntersectionRuneType.cs @@ -16,4 +16,4 @@ internal enum IntersectionRuneType Cross, HLine, VLine -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drawing/LineCanvas/IntersectionType.cs b/Terminal.Gui/Drawing/LineCanvas/IntersectionType.cs index 87a051e559..e0cecf8152 100644 --- a/Terminal.Gui/Drawing/LineCanvas/IntersectionType.cs +++ b/Terminal.Gui/Drawing/LineCanvas/IntersectionType.cs @@ -25,4 +25,4 @@ internal enum IntersectionType /// A line exists at this point who has 0 length Dot -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs index 637b61861e..c426b68968 100644 --- a/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Runtime.InteropServices; @@ -181,7 +180,7 @@ public void Clear () } // Safe as long as the list is not modified while the span is in use. ReadOnlySpan intersects = CollectionsMarshal.AsSpan(intersectionsBufferList); - Cell? cell = GetCellForIntersects (Application.Driver, intersects); + Cell? cell = GetCellForIntersects (intersects); // TODO: Can we skip the whole nested looping if _exclusionRegion is null? if (cell is { } && _exclusionRegion?.Contains (x, y) is null or false) { @@ -223,7 +222,7 @@ public Dictionary GetMap (Rectangle inArea) .Where (i => i is not null) .ToArray (); - Rune? rune = GetRuneForIntersects (Application.Driver, intersects); + Rune? rune = GetRuneForIntersects (intersects); if (rune is { } && _exclusionRegion?.Contains (x, y) is null or false) { @@ -402,7 +401,7 @@ private void ConfigurationManager_Applied (object? sender, ConfigurationManagerE // TODO: Add other resolvers }; - private Cell? GetCellForIntersects (IDriver? driver, ReadOnlySpan intersects) + private Cell? GetCellForIntersects (ReadOnlySpan intersects) { if (intersects.IsEmpty) { @@ -410,7 +409,7 @@ private void ConfigurationManager_Applied (object? sender, ConfigurationManagerE } var cell = new Cell (); - Rune? rune = GetRuneForIntersects (driver, intersects); + Rune? rune = GetRuneForIntersects (intersects); if (rune.HasValue) { @@ -422,7 +421,7 @@ private void ConfigurationManager_Applied (object? sender, ConfigurationManagerE return cell; } - private Rune? GetRuneForIntersects (IDriver? driver, ReadOnlySpan intersects) + private Rune? GetRuneForIntersects (ReadOnlySpan intersects) { if (intersects.IsEmpty) { @@ -432,7 +431,7 @@ private void ConfigurationManager_Applied (object? sender, ConfigurationManagerE IntersectionRuneType runeType = GetRuneTypeForIntersects (intersects); if (_runeResolvers.TryGetValue (runeType, out IntersectionRuneResolver? resolver)) { - return resolver.GetRuneForIntersects (driver, intersects); + return resolver.GetRuneForIntersects (intersects); } // TODO: Remove these once we have all of the below ported to IntersectionRuneResolvers @@ -769,7 +768,7 @@ private abstract class IntersectionRuneResolver internal Rune _thickV; protected IntersectionRuneResolver () { SetGlyphs (); } - public Rune? GetRuneForIntersects (IDriver? driver, ReadOnlySpan intersects) + public Rune? GetRuneForIntersects (ReadOnlySpan intersects) { // Note that there aren't any glyphs for intersections of double lines with heavy lines diff --git a/Terminal.Gui/Drawing/LineCanvas/LineStyle.cs b/Terminal.Gui/Drawing/LineCanvas/LineStyle.cs index 9c8f234fed..9b93708dba 100644 --- a/Terminal.Gui/Drawing/LineCanvas/LineStyle.cs +++ b/Terminal.Gui/Drawing/LineCanvas/LineStyle.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Text.Json.Serialization; namespace Terminal.Gui.Drawing; diff --git a/Terminal.Gui/Drawing/LineCanvas/StraightLine.cs b/Terminal.Gui/Drawing/LineCanvas/StraightLine.cs index 5e64c0b7a5..b4f86665df 100644 --- a/Terminal.Gui/Drawing/LineCanvas/StraightLine.cs +++ b/Terminal.Gui/Drawing/LineCanvas/StraightLine.cs @@ -1,6 +1,5 @@  namespace Terminal.Gui.Drawing; -#nullable enable // TODO: Add events that notify when StraightLine changes to enable dynamic layout /// A line between two points on a horizontal or vertical and a given style/color. diff --git a/Terminal.Gui/Drawing/LineCanvas/StraightLineExtensions.cs b/Terminal.Gui/Drawing/LineCanvas/StraightLineExtensions.cs index 1cff548ae0..f8089952f3 100644 --- a/Terminal.Gui/Drawing/LineCanvas/StraightLineExtensions.cs +++ b/Terminal.Gui/Drawing/LineCanvas/StraightLineExtensions.cs @@ -1,4 +1,4 @@ - + namespace Terminal.Gui.Drawing; /// Extension methods for (including collections). @@ -187,7 +187,7 @@ private static int GetLineEndOnDiffAxis (Point start, int length, Orientation or { if (length == 0) { - throw new ArgumentException ("0 length lines are not supported", nameof (length)); + throw new ArgumentException (@"0 length lines are not supported", nameof (length)); } int sub = length > 0 ? 1 : -1; @@ -220,7 +220,7 @@ private static int GetLineStartOnDiffAxis (Point start, int length, Orientation { if (length == 0) { - throw new ArgumentException ("0 length lines are not supported", nameof (length)); + throw new ArgumentException (@"0 length lines are not supported", nameof (length)); } int sub = length > 0 ? 1 : -1; diff --git a/Terminal.Gui/Drawing/Quant/PopularityPaletteWithThreshold.cs b/Terminal.Gui/Drawing/Quant/PopularityPaletteWithThreshold.cs index 0e7f535967..c98050941a 100644 --- a/Terminal.Gui/Drawing/Quant/PopularityPaletteWithThreshold.cs +++ b/Terminal.Gui/Drawing/Quant/PopularityPaletteWithThreshold.cs @@ -1,8 +1,9 @@ - +namespace Terminal.Gui.Drawing; + /// /// Simple fast palette building algorithm which uses the frequency that a color is seen -/// to determine whether it will appear in the final palette. Includes a threshold where -/// by colors will be considered 'the same'. This reduces the chance of under represented +/// to determine whether it will appear in the final palette. Includes a threshold whereby +/// colors will be considered 'the same'. This reduces the chance of underrepresented /// colors being missed completely. /// public class PopularityPaletteWithThreshold : IPaletteBuilder @@ -22,11 +23,11 @@ public PopularityPaletteWithThreshold (IColorDistance colorDistance, double merg } /// - public List BuildPalette (List colors, int maxColors) + public List BuildPalette (List? colors, int maxColors) { - if (colors == null || colors.Count == 0 || maxColors <= 0) + if (colors is null || colors.Count == 0 || maxColors <= 0) { - return new (); + return []; } // Step 1: Build the histogram of colors (count occurrences) @@ -34,14 +35,10 @@ public List BuildPalette (List colors, int maxColors) foreach (Color color in colors) { - if (colorHistogram.ContainsKey (color)) + if (!colorHistogram.TryAdd (color, 1)) { colorHistogram [color]++; } - else - { - colorHistogram [color] = 1; - } } // If we already have fewer or equal colors than the limit, no need to merge diff --git a/Terminal.Gui/Drawing/Region.cs b/Terminal.Gui/Drawing/Region.cs index f3abb8e072..597801a6af 100644 --- a/Terminal.Gui/Drawing/Region.cs +++ b/Terminal.Gui/Drawing/Region.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drawing; @@ -51,10 +50,10 @@ public class Region private readonly List _rectangles = []; // Add a single reusable list for temp operations - private readonly List _tempRectangles = new(); + private readonly List _tempRectangles = new (); // Object used for synchronization - private readonly object _syncLock = new object(); + private readonly object _syncLock = new object (); /// /// Initializes a new instance of the class. @@ -121,12 +120,12 @@ public void Combine (Region? region, RegionOp operation) { lock (_syncLock) { - CombineInternal(region, operation); + CombineInternal (region, operation); } } // Private method to implement the combine logic within a lock - private void CombineInternal(Region? region, RegionOp operation) + private void CombineInternal (Region? region, RegionOp operation) { if (region is null || region._rectangles.Count == 0) { @@ -189,36 +188,36 @@ private void CombineInternal(Region? region, RegionOp operation) case RegionOp.Union: // Avoid collection initialization with spread operator - _tempRectangles.Clear(); - _tempRectangles.AddRange(_rectangles); + _tempRectangles.Clear (); + _tempRectangles.AddRange (_rectangles); if (region != null) { // Get the region's rectangles safely lock (region._syncLock) { - _tempRectangles.AddRange(region._rectangles); + _tempRectangles.AddRange (region._rectangles); } } - List mergedUnion = MergeRectangles(_tempRectangles, false); - _rectangles.Clear(); - _rectangles.AddRange(mergedUnion); + List mergedUnion = MergeRectangles (_tempRectangles, false); + _rectangles.Clear (); + _rectangles.AddRange (mergedUnion); break; case RegionOp.MinimalUnion: // Avoid collection initialization with spread operator - _tempRectangles.Clear(); - _tempRectangles.AddRange(_rectangles); + _tempRectangles.Clear (); + _tempRectangles.AddRange (_rectangles); if (region != null) { // Get the region's rectangles safely lock (region._syncLock) { - _tempRectangles.AddRange(region._rectangles); + _tempRectangles.AddRange (region._rectangles); } } - List mergedMinimalUnion = MergeRectangles(_tempRectangles, true); - _rectangles.Clear(); - _rectangles.AddRange(mergedMinimalUnion); + List mergedMinimalUnion = MergeRectangles (_tempRectangles, true); + _rectangles.Clear (); + _rectangles.AddRange (mergedMinimalUnion); break; case RegionOp.XOR: @@ -588,17 +587,26 @@ internal static List MergeRectangles (List rectangles, boo { // 1. Sort by X int cmp = a.x.CompareTo (b.x); - if (cmp != 0) return cmp; + if (cmp != 0) + { + return cmp; + } // 2. Sort End events before Start events bool aIsEnd = !a.isStart; bool bIsEnd = !b.isStart; cmp = aIsEnd.CompareTo (bIsEnd); // True (End) comes after False (Start) - if (cmp != 0) return -cmp; // Reverse: End (true) should come before Start (false) + if (cmp != 0) + { + return -cmp; // Reverse: End (true) should come before Start (false) + } // 3. Tie-breaker: Sort by yTop cmp = a.yTop.CompareTo (b.yTop); - if (cmp != 0) return cmp; + if (cmp != 0) + { + return cmp; + } // 4. Final Tie-breaker: Sort by yBottom return a.yBottom.CompareTo (b.yBottom); @@ -901,13 +909,16 @@ internal static IEnumerable SubtractRectangle (Rectangle original, Re /// /// Fills the interior of all rectangles in the region with the specified attribute and fill rune. /// + /// /// The attribute (color/style) to use. /// /// The rune to fill the interior of the rectangles with. If space will be /// used. /// - public void FillRectangles (Attribute attribute, Rune? fillRune = null) + public void FillRectangles (IDriver? driver, Attribute? attribute, Rune? fillRune = null) { + ArgumentNullException.ThrowIfNull (driver); + if (_rectangles.Count == 0) { return; @@ -920,14 +931,14 @@ public void FillRectangles (Attribute attribute, Rune? fillRune = null) continue; } - Application.Driver?.SetAttribute (attribute); + driver?.SetAttribute (attribute!.Value); for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { - Application.Driver?.Move (x, y); - Application.Driver?.AddRune (fillRune ?? (Rune)' '); + driver?.Move (x, y); + driver?.AddRune (fillRune ?? (Rune)' '); } } } @@ -1046,7 +1057,7 @@ public void DrawOuterBoundary (LineCanvas lineCanvas, LineStyle style, Attribute if (bounds.Width > 1000 || bounds.Height > 1000) { // Fall back to drawing each rectangle's boundary - DrawBoundaries(lineCanvas, style, attribute); + DrawBoundaries (lineCanvas, style, attribute); return; } diff --git a/Terminal.Gui/Drawing/RegionOp.cs b/Terminal.Gui/Drawing/RegionOp.cs index e40de16632..0b4689a37d 100644 --- a/Terminal.Gui/Drawing/RegionOp.cs +++ b/Terminal.Gui/Drawing/RegionOp.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drawing; /// diff --git a/Terminal.Gui/Drawing/Ruler.cs b/Terminal.Gui/Drawing/Ruler.cs index 89ef6b6d14..4c457976f2 100644 --- a/Terminal.Gui/Drawing/Ruler.cs +++ b/Terminal.Gui/Drawing/Ruler.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drawing; /// Draws a ruler on the screen. @@ -21,11 +20,13 @@ internal class Ruler private string _vTemplate { get; } = "-123456789"; /// Draws the . + /// Optional Driver. If not provided, driver will be used. /// The location to start drawing the ruler, in screen-relative coordinates. /// The start value of the ruler. - /// Optional Driver. If not provided, driver will be used. - public void Draw (Point location, int start = 0, IDriver? driver = null) + public void Draw (IDriver? driver, Point location, int start = 0) { + ArgumentNullException.ThrowIfNull (driver); + if (start < 0) { throw new ArgumentException ("start must be greater than or equal to 0"); @@ -36,8 +37,6 @@ public void Draw (Point location, int start = 0, IDriver? driver = null) return; } - driver ??= driver; - if (Orientation == Orientation.Horizontal) { string hrule = diff --git a/Terminal.Gui/Drawing/Scheme.cs b/Terminal.Gui/Drawing/Scheme.cs index ff0933aac7..9bd6125375 100644 --- a/Terminal.Gui/Drawing/Scheme.cs +++ b/Terminal.Gui/Drawing/Scheme.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Immutable; using System.Numerics; using System.Text.Json.Serialization; diff --git a/Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs b/Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs index 01df783a49..a9e1ae8aa0 100644 --- a/Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs +++ b/Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs @@ -6,8 +6,21 @@ namespace Terminal.Gui.Drawing; /// Uses Ansi escape sequences to detect whether sixel is supported /// by the terminal. /// -public class SixelSupportDetector +public class SixelSupportDetector () { + private readonly IDriver? _driver; + + /// + /// Creates a new instance of the class. + /// + /// + public SixelSupportDetector (IDriver? driver) : this () + { + ArgumentNullException.ThrowIfNull (driver); + + _driver = driver; + } + /// /// Sends Ansi escape sequences to the console to determine whether /// sixel is supported (and @@ -127,17 +140,17 @@ private void IsSixelSupportedByDar (SixelSupportResult result, Action resultCallback (result)); } - private static void QueueRequest (AnsiEscapeSequence req, Action responseCallback, Action abandoned) + private void QueueRequest (AnsiEscapeSequence req, Action responseCallback, Action abandoned) { var newRequest = new AnsiEscapeSequenceRequest { Request = req.Request, Terminator = req.Terminator, - ResponseReceived = responseCallback, + ResponseReceived = responseCallback!, Abandoned = abandoned }; - Application.Driver?.QueueAnsiRequest (newRequest); + _driver?.QueueAnsiRequest (newRequest); } private static bool ResponseIndicatesSupport (string response) { return response.Split (';').Contains ("4"); } @@ -145,14 +158,12 @@ private static void QueueRequest (AnsiEscapeSequence req, Action respons private static bool IsVirtualTerminal () { return !string.IsNullOrWhiteSpace (Environment.GetEnvironmentVariable ("WT_SESSION")); - - ; } private static bool IsXtermWithTransparency () { // Check if running in real xterm (XTERM_VERSION is more reliable than TERM) - string xtermVersionStr = Environment.GetEnvironmentVariable ("XTERM_VERSION"); + string xtermVersionStr = Environment.GetEnvironmentVariable (@"XTERM_VERSION")!; // If XTERM_VERSION exists, we are in a real xterm if (!string.IsNullOrWhiteSpace (xtermVersionStr) && int.TryParse (xtermVersionStr, out int xtermVersion) && xtermVersion >= 370) diff --git a/Terminal.Gui/Drawing/Sixel/SixelToRender.cs b/Terminal.Gui/Drawing/Sixel/SixelToRender.cs index c66d4bdaf8..89c3b26b52 100644 --- a/Terminal.Gui/Drawing/Sixel/SixelToRender.cs +++ b/Terminal.Gui/Drawing/Sixel/SixelToRender.cs @@ -10,7 +10,7 @@ public class SixelToRender /// gets or sets the encoded sixel data. Use to convert bitmaps /// into encoded sixel data. /// - public string SixelData { get; set; } + public string? SixelData { get; set; } /// /// gets or sets where to move the cursor to before outputting the . diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs index c89773f1c7..a15b49fd47 100644 --- a/Terminal.Gui/Drawing/Thickness.cs +++ b/Terminal.Gui/Drawing/Thickness.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Numerics; +using System.Numerics; using System.Text.Json.Serialization; namespace Terminal.Gui.Drawing; @@ -90,15 +89,15 @@ public bool Contains (in Rectangle outside, in Point location) /// 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, IDriver? driver = null) + public Rectangle Draw (IDriver? driver, Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string? label = null) { + ArgumentNullException.ThrowIfNull (driver); + if (rect.Size.Width < 1 || rect.Size.Height < 1) { return Rectangle.Empty; } - driver ??= Application.Driver; - var clearChar = (Rune)' '; Rune leftChar = clearChar; Rune rightChar = clearChar; @@ -165,7 +164,7 @@ rect with if (Top > 0) { - hRuler.Draw (rect.Location, driver: driver); + hRuler.Draw (driver: driver, location: rect.Location); } //Left @@ -173,19 +172,19 @@ rect with if (Left > 0) { - vRuler.Draw (rect.Location with { Y = rect.Y + 1 }, 1, driver); + vRuler.Draw (driver, rect.Location with { Y = rect.Y + 1 }, 1); } // Bottom if (Bottom > 0) { - hRuler.Draw (rect.Location with { Y = rect.Y + rect.Height - 1 }, driver: driver); + hRuler.Draw (driver: driver, location: rect.Location with { Y = rect.Y + rect.Height - 1 }); } // Right if (Right > 0) { - vRuler.Draw (new (rect.X + rect.Width - 1, rect.Y + 1), 1, driver); + vRuler.Draw (driver, new (rect.X + rect.Width - 1, rect.Y + 1), 1); } } @@ -205,7 +204,7 @@ rect with if (driver?.CurrentAttribute is { }) { - tf.Draw (rect, driver!.CurrentAttribute, driver!.CurrentAttribute, rect, driver); + tf.Draw (driver, rect, driver!.CurrentAttribute, driver!.CurrentAttribute, rect); } } diff --git a/Terminal.Gui/Drawing/VisualRoleEventArgs.cs b/Terminal.Gui/Drawing/VisualRoleEventArgs.cs index 5f03899677..bdc54af8f9 100644 --- a/Terminal.Gui/Drawing/VisualRoleEventArgs.cs +++ b/Terminal.Gui/Drawing/VisualRoleEventArgs.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.Drawing; +namespace Terminal.Gui.Drawing; using System; @@ -60,4 +59,4 @@ public VisualRoleEventArgs (in VisualRole role, Attribute? result) } } -#pragma warning restore CS1711 \ No newline at end of file +#pragma warning restore CS1711 diff --git a/Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequence.cs b/Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequence.cs index 5b04717764..b90bf3702d 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequence.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequence.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequenceRequest.cs b/Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequenceRequest.cs index de61ae9205..cce053a1a5 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequenceRequest.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequenceRequest.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Drivers; /// @@ -20,12 +18,12 @@ public class AnsiEscapeSequenceRequest : AnsiEscapeSequence /// public Action? Abandoned { get; init; } - /// /// Sends the to the raw output stream of the current . /// Only call this method from the main UI thread. You should use if /// sending many requests. /// - public void Send () { Application.Driver?.WriteRaw (Request); } + /// + public void Send (IDriver? driver) { driver?.WriteRaw (Request); } } diff --git a/Terminal.Gui/Drivers/AnsiHandling/AnsiMouseParser.cs b/Terminal.Gui/Drivers/AnsiHandling/AnsiMouseParser.cs index 7f3f827096..882863a6a5 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/AnsiMouseParser.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/AnsiMouseParser.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Text.RegularExpressions; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/AnsiHandling/AnsiRequestScheduler.cs b/Terminal.Gui/Drivers/AnsiHandling/AnsiRequestScheduler.cs index 3f20af735f..8fd50a4cc8 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/AnsiRequestScheduler.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/AnsiRequestScheduler.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; @@ -70,15 +69,16 @@ public AnsiRequestScheduler (IAnsiResponseParser parser, Func? now = n /// Sends the immediately or queues it if there is already /// an outstanding request for the given . /// + /// /// /// if request was sent immediately. if it was queued. - public bool SendOrSchedule (AnsiEscapeSequenceRequest request) { return SendOrSchedule (request, true); } + public bool SendOrSchedule (IDriver? driver, AnsiEscapeSequenceRequest request) { return SendOrSchedule (driver, request, true); } - private bool SendOrSchedule (AnsiEscapeSequenceRequest request, bool addToQueue) + private bool SendOrSchedule (IDriver? driver, AnsiEscapeSequenceRequest request, bool addToQueue) { if (CanSend (request, out ReasonCannotSend reason)) { - Send (request); + Send (driver, request); return true; } @@ -91,7 +91,7 @@ private bool SendOrSchedule (AnsiEscapeSequenceRequest request, bool addToQueue) // Try again after evicting if (CanSend (request, out _)) { - Send (request); + Send (driver, request); return true; } @@ -142,6 +142,7 @@ private bool EvictStaleRequests (string? withTerminator) /// Identifies and runs any that can be sent based on the /// current outstanding requests of the parser. /// + /// /// /// Repeated requests to run the schedule over short period of time will be ignored. /// Pass to override this behaviour and force evaluation of outstanding requests. @@ -150,7 +151,7 @@ private bool EvictStaleRequests (string? withTerminator) /// if a request was found and run. /// if no outstanding requests or all have existing outstanding requests underway in parser. /// - public bool RunSchedule (bool force = false) + public bool RunSchedule (IDriver? driver, bool force = false) { if (!force && Now () - _lastRun < _runScheduleThrottle) { @@ -163,7 +164,7 @@ public bool RunSchedule (bool force = false) if (opportunity != null) { // Give it another go - if (SendOrSchedule (opportunity.Item1, false)) + if (SendOrSchedule (driver, opportunity.Item1, false)) { _queuedRequests.Remove (opportunity); @@ -176,11 +177,11 @@ public bool RunSchedule (bool force = false) return false; } - private void Send (AnsiEscapeSequenceRequest r) + private void Send (IDriver? driver, AnsiEscapeSequenceRequest r) { _lastSend.AddOrUpdate (r.Terminator!, _ => Now (), (_, _) => Now ()); _parser.ExpectResponse (r.Terminator, r.ResponseReceived, r.Abandoned, false); - r.Send (); + r.Send (driver); } private bool CanSend (AnsiEscapeSequenceRequest r, out ReasonCannotSend reason) diff --git a/Terminal.Gui/Drivers/AnsiHandling/AnsiResponseExpectation.cs b/Terminal.Gui/Drivers/AnsiHandling/AnsiResponseExpectation.cs index fd087de75a..1c65393168 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/AnsiResponseExpectation.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/AnsiResponseExpectation.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; internal record AnsiResponseExpectation (string? Terminator, Action Response, Action? Abandoned) diff --git a/Terminal.Gui/Drivers/AnsiHandling/AnsiResponseParser.cs b/Terminal.Gui/Drivers/AnsiHandling/AnsiResponseParser.cs index 5bafbfe55e..60f6d87f59 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/AnsiResponseParser.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/AnsiResponseParser.cs @@ -1,5 +1,3 @@ -#nullable enable - using Microsoft.Extensions.Logging; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqReqStatus.cs b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqReqStatus.cs index ab062e2536..8943ab1bc0 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqReqStatus.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqReqStatus.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqRequests.cs b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqRequests.cs index 6e69f6a120..7732bd319c 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqRequests.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqRequests.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs index 6257d1d66a..3f9d27d0fa 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; using System.Globalization; diff --git a/Terminal.Gui/Drivers/AnsiHandling/GenericHeld.cs b/Terminal.Gui/Drivers/AnsiHandling/GenericHeld.cs index 6373003fa9..6339b4ee22 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/GenericHeld.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/GenericHeld.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/AnsiHandling/IAnsiResponseParser.cs b/Terminal.Gui/Drivers/AnsiHandling/IAnsiResponseParser.cs index cfdb775f0b..29892a2dac 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/IAnsiResponseParser.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/IAnsiResponseParser.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/AnsiHandling/IHeld.cs b/Terminal.Gui/Drivers/AnsiHandling/IHeld.cs index 369ef47324..e6d5a3a06a 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/IHeld.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/IHeld.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParser.cs b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParser.cs index ee41d6cca9..89ef61a58e 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParser.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParser.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParserPattern.cs b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParserPattern.cs index 62b0acb645..51011124a7 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParserPattern.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParserPattern.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiCursorPattern.cs b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiCursorPattern.cs index 744658a767..61adedd62d 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiCursorPattern.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiCursorPattern.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Text.RegularExpressions; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiKeyPattern.cs b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiKeyPattern.cs index f0fb3b20be..ed2bcecbe1 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiKeyPattern.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiKeyPattern.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Text.RegularExpressions; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/EscAsAltPattern.cs b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/EscAsAltPattern.cs index a9b16e90a8..fd6b543219 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/EscAsAltPattern.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/EscAsAltPattern.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Text.RegularExpressions; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/Ss3Pattern.cs b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/Ss3Pattern.cs index 988b584f14..cf2804072c 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/Keyboard/Ss3Pattern.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/Keyboard/Ss3Pattern.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Text.RegularExpressions; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/AnsiHandling/Osc8UrlLinker.cs b/Terminal.Gui/Drivers/AnsiHandling/Osc8UrlLinker.cs index 7a3e41a6dd..d4cd43065b 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/Osc8UrlLinker.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/Osc8UrlLinker.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/AnsiHandling/ReasonCannotSend.cs b/Terminal.Gui/Drivers/AnsiHandling/ReasonCannotSend.cs index 675c9ff643..ba0aa399c0 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/ReasonCannotSend.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/ReasonCannotSend.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; internal enum ReasonCannotSend diff --git a/Terminal.Gui/Drivers/AnsiHandling/StringHeld.cs b/Terminal.Gui/Drivers/AnsiHandling/StringHeld.cs index 3202410abd..79e922098a 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/StringHeld.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/StringHeld.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/ComponentFactoryImpl.cs b/Terminal.Gui/Drivers/ComponentFactoryImpl.cs index d09099954f..04f79a88e9 100644 --- a/Terminal.Gui/Drivers/ComponentFactoryImpl.cs +++ b/Terminal.Gui/Drivers/ComponentFactoryImpl.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/CursorVisibility.cs b/Terminal.Gui/Drivers/CursorVisibility.cs index ca86e9a0a6..7d0c5d9f4d 100644 --- a/Terminal.Gui/Drivers/CursorVisibility.cs +++ b/Terminal.Gui/Drivers/CursorVisibility.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// Terminal Cursor Visibility settings. diff --git a/Terminal.Gui/Drivers/DotNetDriver/NetComponentFactory.cs b/Terminal.Gui/Drivers/DotNetDriver/NetComponentFactory.cs index 669c6efcee..46b8b9efb9 100644 --- a/Terminal.Gui/Drivers/DotNetDriver/NetComponentFactory.cs +++ b/Terminal.Gui/Drivers/DotNetDriver/NetComponentFactory.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/DotNetDriver/NetInput.cs b/Terminal.Gui/Drivers/DotNetDriver/NetInput.cs index b44c59522b..026689a454 100644 --- a/Terminal.Gui/Drivers/DotNetDriver/NetInput.cs +++ b/Terminal.Gui/Drivers/DotNetDriver/NetInput.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.Logging; - +#nullable disable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs b/Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs index 8fbd11ba12..4f8ab1fc09 100644 --- a/Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs +++ b/Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs @@ -1,5 +1,3 @@ -using Microsoft.Extensions.Logging; - namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/DotNetDriver/NetWinVTConsole.cs b/Terminal.Gui/Drivers/DotNetDriver/NetWinVTConsole.cs index d6730e044a..36cbf0e2a5 100644 --- a/Terminal.Gui/Drivers/DotNetDriver/NetWinVTConsole.cs +++ b/Terminal.Gui/Drivers/DotNetDriver/NetWinVTConsole.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Runtime.InteropServices; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/DriverImpl.cs b/Terminal.Gui/Drivers/DriverImpl.cs index db19184590..ee125c1438 100644 --- a/Terminal.Gui/Drivers/DriverImpl.cs +++ b/Terminal.Gui/Drivers/DriverImpl.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace Terminal.Gui.Drivers; @@ -75,9 +74,7 @@ ISizeMonitor sizeMonitor CreateClipboard (); } - /// - /// The event fired when the screen changes (size, position, etc.). - /// + /// public event EventHandler? SizeChanged; /// @@ -89,7 +86,6 @@ ISizeMonitor sizeMonitor /// public ISizeMonitor SizeMonitor { get; } - private void CreateClipboard () { if (InputProcessor.DriverName is { } && InputProcessor.DriverName.Contains ("fake")) @@ -120,27 +116,18 @@ private void CreateClipboard () // Clipboard is set to FakeClipboard at initialization } - /// Gets the location and size of the terminal screen. - public Rectangle Screen - { - get - { - //if (Application.RunningUnitTests && _output is WindowsConsoleOutput or NetOutput) - //{ - // // In unit tests, we don't have a real output, so we return an empty rectangle. - // return Rectangle.Empty; - //} + /// - return new (0, 0, OutputBuffer.Cols, OutputBuffer.Rows); - } - } + public Rectangle Screen => - /// - /// 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. + //if (Application.RunningUnitTests && _output is WindowsConsoleOutput or NetOutput) + //{ + // // In unit tests, we don't have a real output, so we return an empty rectangle. + // return Rectangle.Empty; + //} + new (0, 0, OutputBuffer.Cols, OutputBuffer.Rows); + + /// public virtual void SetScreenSize (int width, int height) { OutputBuffer.SetSize (width, height); @@ -148,64 +135,60 @@ public virtual void SetScreenSize (int width, int height) SizeChanged?.Invoke (this, new (new (width, height))); } - /// - /// Gets or sets the clip rectangle that and are subject - /// to. - /// - /// The rectangle describing the of region. + /// + public Region? Clip { get => OutputBuffer.Clip; set => OutputBuffer.Clip = value; } - /// Get the operating system clipboard. + /// + public IClipboard? Clipboard { get; private set; } = new FakeClipboard (); - /// - /// Gets the column last set by . and are used by - /// and to determine where to add content. - /// + /// + public int Col => OutputBuffer.Col; - /// The number of columns visible in the terminal. + /// + public int Cols { get => OutputBuffer.Cols; set => OutputBuffer.Cols = value; } - /// - /// The contents of the application output. The driver outputs this buffer to the terminal. - /// The format of the array is rows, columns. The first index is the row, the second index is the column. - /// + /// + public Cell [,]? Contents { get => OutputBuffer.Contents; set => OutputBuffer.Contents = value; } - /// The leftmost column in the terminal. + /// + public int Left { get => OutputBuffer.Left; set => OutputBuffer.Left = value; } - /// - /// Gets the row last set by . and are used by - /// and to determine where to add content. - /// + /// + public int Row => OutputBuffer.Row; - /// The number of rows visible in the terminal. + /// + public int Rows { get => OutputBuffer.Rows; set => OutputBuffer.Rows = value; } - /// The topmost row in the terminal. + /// + public int Top { get => OutputBuffer.Top; @@ -214,75 +197,33 @@ public int Top // TODO: Probably not everyone right? - /// Gets whether the supports TrueColor output. + /// + public bool SupportsTrueColor => true; - // TODO: Currently ignored - /// - /// Gets or sets whether the should use 16 colors instead of the default TrueColors. - /// See to change this setting via . - /// - /// - /// - /// Will be forced to if is - /// , indicating that the cannot support TrueColor. - /// - /// + /// + public bool Force16Colors { get => Application.Force16Colors || !SupportsTrueColor; set => Application.Force16Colors = value || !SupportsTrueColor; } - /// - /// The that will be used for the next or - /// - /// call. - /// + /// + public Attribute CurrentAttribute { get => OutputBuffer.CurrentAttribute; set => OutputBuffer.CurrentAttribute = value; } - /// Adds the specified rune to the display at the current cursor position. - /// - /// - /// When the method returns, will be incremented by the number of columns - /// required, even if the new column value is outside of the - /// or screen - /// dimensions defined by . - /// - /// - /// If requires more than one column, and plus the number - /// of columns - /// needed exceeds the or screen dimensions, the default Unicode replacement - /// character (U+FFFD) - /// will be added instead. - /// - /// - /// Rune to add. + /// public void AddRune (Rune rune) { OutputBuffer.AddRune (rune); } - /// - /// Adds the specified to the display at the current cursor position. This method is a - /// convenience method that calls with the - /// constructor. - /// - /// Character to add. + /// public void AddRune (char c) { OutputBuffer.AddRune (c); } - /// Adds the to the display at the cursor position. - /// - /// - /// When the method returns, will be incremented by the number of columns - /// required, unless the new column value is outside of the - /// or screen - /// dimensions defined by . - /// - /// If requires more columns than are available, the output will be clipped. - /// - /// String. + /// public void AddStr (string str) { OutputBuffer.AddStr (str); } /// Clears the of the driver. @@ -292,28 +233,13 @@ public void ClearContents () ClearedContents?.Invoke (this, new MouseEventArgs ()); } - /// - /// Raised each time is called. For benchmarking. - /// + /// public event EventHandler? ClearedContents; - /// - /// Fills the specified rectangle with the specified rune, using - /// - /// - /// The value of is honored. Any parts of the rectangle not in the clip will not be - /// drawn. - /// - /// The Screen-relative rectangle. - /// The Rune used to fill the rectangle + /// public void FillRect (Rectangle rect, Rune rune = default) { OutputBuffer.FillRect (rect, rune); } - /// - /// Fills the specified rectangle with the specified . This method is a convenience method - /// that calls . - /// - /// - /// + /// public void FillRect (Rectangle rect, char c) { OutputBuffer.FillRect (rect, c); } /// @@ -324,48 +250,18 @@ public virtual string GetVersionInfo () return type; } - /// Tests if the specified rune is supported by the driver. - /// - /// - /// if the rune can be properly presented; if the driver does not - /// support displaying this rune. - /// - public bool IsRuneSupported (Rune rune) { return Rune.IsValid (rune.Value); } - - /// Tests whether the specified coordinate are valid for drawing the specified Rune. - /// Used to determine if one or two columns are required. - /// The column. - /// The row. - /// - /// if the coordinate is outside the screen bounds or outside of - /// . - /// otherwise. - /// - public bool IsValidLocation (Rune rune, int col, int row) { return OutputBuffer.IsValidLocation (rune, col, row); } + /// + public bool IsRuneSupported (Rune rune) => Rune.IsValid (rune.Value); - /// - /// Updates and to the specified column and row in - /// . - /// Used by and to determine - /// where to add content. - /// - /// - /// This does not move the cursor on the screen, it only updates the internal state of the driver. - /// - /// If or are negative or beyond - /// and - /// , the method still sets those properties. - /// - /// - /// Column to move to. - /// Row to move to. + /// + public bool IsValidLocation (Rune rune, int col, int row) => OutputBuffer.IsValidLocation (rune, col, row); + + /// public void Move (int col, int row) { OutputBuffer.Move (col, row); } // TODO: Probably part of output - /// Sets the terminal cursor visibility. - /// The wished - /// upon success + /// public bool SetCursorVisibility (CursorVisibility visibility) { _lastCursor = visibility; @@ -415,31 +311,22 @@ public void Suspend () Logging.Error ($"Error suspending terminal: {ex.Message}"); } - Application.LayoutAndDraw (); - - Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents); } - /// - /// Sets the position of the terminal cursor to and - /// . - /// + /// public void UpdateCursor () { _output.SetCursorPosition (Col, Row); } - /// Initializes the driver + /// public void Init () { throw new NotSupportedException (); } - /// Ends the execution of the console driver. + /// public void End () { // TODO: Nope } - /// Selects the specified attribute as the attribute to use for future calls to AddRune and AddString. - /// Implementations should call base.SetAttribute(c). - /// C. - /// The previously set Attribute. + /// public Attribute SetAttribute (Attribute newAttribute) { Attribute currentAttribute = OutputBuffer.CurrentAttribute; @@ -448,51 +335,29 @@ public Attribute SetAttribute (Attribute newAttribute) return currentAttribute; } - /// Gets the current . - /// The current attribute. - public Attribute GetAttribute () { return OutputBuffer.CurrentAttribute; } + /// + public Attribute GetAttribute () => OutputBuffer.CurrentAttribute; /// Event fired when a key is pressed down. This is a precursor to . public event EventHandler? KeyDown; - /// Event fired when a key is released. - /// - /// Drivers that do not support key release events will fire this event after - /// processing is - /// complete. - /// + /// public event EventHandler? KeyUp; /// Event fired when a mouse event occurs. public event EventHandler? MouseEvent; - /// - /// Provide proper writing to send escape sequence recognized by the . - /// - /// + /// public void WriteRaw (string ansi) { _output.Write (ansi); } /// - public void EnqueueKeyEvent (Key key) - { - InputProcessor.EnqueueKeyDownEvent (key); - } + public void EnqueueKeyEvent (Key key) { InputProcessor.EnqueueKeyDownEvent (key); } - /// - /// Queues the specified ANSI escape sequence request for execution. - /// - /// The ANSI request to queue. - /// - /// The request is sent immediately if possible, or queued for later execution - /// by the to prevent overwhelming the console. - /// - public void QueueAnsiRequest (AnsiEscapeSequenceRequest request) { _ansiRequestScheduler.SendOrSchedule (request); } + /// + public void QueueAnsiRequest (AnsiEscapeSequenceRequest request) { _ansiRequestScheduler.SendOrSchedule (this, request); } - /// - /// Gets the instance used by this driver. - /// - /// The ANSI request scheduler. - public AnsiRequestScheduler GetRequestScheduler () { return _ansiRequestScheduler; } + /// + public AnsiRequestScheduler GetRequestScheduler () => _ansiRequestScheduler; /// public void Refresh () @@ -500,8 +365,51 @@ public void Refresh () // No need we will always draw when dirty } - public string? GetName () + /// + public string? GetName () => InputProcessor.DriverName?.ToLowerInvariant (); + + /// + public new string ToString () + { + StringBuilder sb = new (); + + Cell [,] contents = Contents!; + + for (var r = 0; r < Rows; r++) + { + for (var c = 0; c < Cols; c++) + { + Rune rune = contents [r, c].Rune; + + if (rune.DecodeSurrogatePair (out char []? sp)) + { + sb.Append (sp); + } + else + { + sb.Append ((char)rune.Value); + } + + if (rune.GetColumns () > 1) + { + c++; + } + + // See Issue #2616 + //foreach (var combMark in contents [r, c].CombiningMarks) { + // sb.Append ((char)combMark.Value); + //} + } + + sb.AppendLine (); + } + + return sb.ToString (); + } + + /// + public string ToAnsi () { - return InputProcessor.DriverName?.ToLowerInvariant (); + return _output.ToAnsi (OutputBuffer); } } diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeClipboard.cs b/Terminal.Gui/Drivers/FakeDriver/FakeClipboard.cs index 6951b7923b..9a7bc4d571 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeClipboard.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeClipboard.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs b/Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs index e7ce72f3dd..5f4284bdc5 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeInput.cs b/Terminal.Gui/Drivers/FakeDriver/FakeInput.cs index 76093e785a..b8a0a5240c 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeInput.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeInput.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; @@ -44,4 +43,4 @@ public void AddInput (ConsoleKeyInfo input) // Will be called on the main loop thread. _testInput.Enqueue (input); } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeInputProcessor.cs b/Terminal.Gui/Drivers/FakeDriver/FakeInputProcessor.cs index 7c07f0a16a..83b0739a76 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeInputProcessor.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeInputProcessor.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; @@ -36,7 +37,7 @@ public override void EnqueueMouseEvent (MouseEventArgs mouseEvent) if (Application.MainThreadId is { }) { // Application is running - use Invoke to defer to next iteration - Application.Invoke (() => RaiseMouseEvent (mouseEvent)); + ApplicationImpl.Instance.Invoke ((_) => RaiseMouseEvent (mouseEvent)); } else { diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeOutput.cs b/Terminal.Gui/Drivers/FakeDriver/FakeOutput.cs index 0722079fea..8fd790f197 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeOutput.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeOutput.cs @@ -1,4 +1,3 @@ -#nullable enable using System; namespace Terminal.Gui.Drivers; @@ -87,8 +86,29 @@ public void Dispose () /// protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle) { - // For testing, we can skip the actual color/style output - // or capture it if needed for verification + if (Application.Force16Colors) + { + output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ())); + output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ())); + } + else + { + EscSeqUtils.CSI_AppendForegroundColorRGB ( + output, + attr.Foreground.R, + attr.Foreground.G, + attr.Foreground.B + ); + + EscSeqUtils.CSI_AppendBackgroundColorRGB ( + output, + attr.Background.R, + attr.Background.G, + attr.Background.B + ); + } + + EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style); } /// diff --git a/Terminal.Gui/Drivers/IComponentFactory.cs b/Terminal.Gui/Drivers/IComponentFactory.cs index 7122c4af3f..d58a95f68a 100644 --- a/Terminal.Gui/Drivers/IComponentFactory.cs +++ b/Terminal.Gui/Drivers/IComponentFactory.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/IDriver.cs b/Terminal.Gui/Drivers/IDriver.cs index 32af99b05e..090c192a44 100644 --- a/Terminal.Gui/Drivers/IDriver.cs +++ b/Terminal.Gui/Drivers/IDriver.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; @@ -295,4 +294,18 @@ public interface IDriver /// /// public AnsiRequestScheduler GetRequestScheduler (); + + + /// + /// Gets a string representation of . + /// + /// + public string ToString (); + + /// + /// Gets an ANSI escape sequence representation of . This is the + /// same output as would be written to the terminal to recreate the current screen contents. + /// + /// + public string ToAnsi (); } diff --git a/Terminal.Gui/Drivers/IInput.cs b/Terminal.Gui/Drivers/IInput.cs index 0b2ec7d41b..c4a0af1593 100644 --- a/Terminal.Gui/Drivers/IInput.cs +++ b/Terminal.Gui/Drivers/IInput.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/IInputProcessor.cs b/Terminal.Gui/Drivers/IInputProcessor.cs index d0c0284b8a..9c800946ce 100644 --- a/Terminal.Gui/Drivers/IInputProcessor.cs +++ b/Terminal.Gui/Drivers/IInputProcessor.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/IOutput.cs b/Terminal.Gui/Drivers/IOutput.cs index bc04dfaa60..0eb647dec8 100644 --- a/Terminal.Gui/Drivers/IOutput.cs +++ b/Terminal.Gui/Drivers/IOutput.cs @@ -53,4 +53,12 @@ public interface IOutput : IDisposable /// /// void Write (IOutputBuffer buffer); + + /// + /// Generates an ANSI escape sequence string representation of the given contents. + /// This is the same output that would be written to the terminal to recreate the current screen contents. + /// + /// The output buffer to convert to ANSI. + /// A string containing ANSI escape sequences representing the buffer contents. + string ToAnsi (IOutputBuffer buffer); } diff --git a/Terminal.Gui/Drivers/IOutputBuffer.cs b/Terminal.Gui/Drivers/IOutputBuffer.cs index 2b8991593f..387fde46c7 100644 --- a/Terminal.Gui/Drivers/IOutputBuffer.cs +++ b/Terminal.Gui/Drivers/IOutputBuffer.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/ISizeMonitor.cs b/Terminal.Gui/Drivers/ISizeMonitor.cs index 602d5e4b8e..df8273bfd3 100644 --- a/Terminal.Gui/Drivers/ISizeMonitor.cs +++ b/Terminal.Gui/Drivers/ISizeMonitor.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/InputImpl.cs b/Terminal.Gui/Drivers/InputImpl.cs index d340b3d85c..b70df2b448 100644 --- a/Terminal.Gui/Drivers/InputImpl.cs +++ b/Terminal.Gui/Drivers/InputImpl.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; @@ -86,4 +85,4 @@ public void Run (CancellationToken runCancellationToken) /// public virtual void Dispose () { } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drivers/InputProcessorImpl.cs b/Terminal.Gui/Drivers/InputProcessorImpl.cs index f79a963640..24c249a84a 100644 --- a/Terminal.Gui/Drivers/InputProcessorImpl.cs +++ b/Terminal.Gui/Drivers/InputProcessorImpl.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; using Microsoft.Extensions.Logging; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/KeyCode.cs b/Terminal.Gui/Drivers/KeyCode.cs index eb87a7e926..431508e5fb 100644 --- a/Terminal.Gui/Drivers/KeyCode.cs +++ b/Terminal.Gui/Drivers/KeyCode.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/MouseButtonStateEx.cs b/Terminal.Gui/Drivers/MouseButtonStateEx.cs index e6e2a1e964..1aba69677f 100644 --- a/Terminal.Gui/Drivers/MouseButtonStateEx.cs +++ b/Terminal.Gui/Drivers/MouseButtonStateEx.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/MouseInterpreter.cs b/Terminal.Gui/Drivers/MouseInterpreter.cs index cb585ed9f1..f5848ab659 100644 --- a/Terminal.Gui/Drivers/MouseInterpreter.cs +++ b/Terminal.Gui/Drivers/MouseInterpreter.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/OutputBase.cs b/Terminal.Gui/Drivers/OutputBase.cs index ac43dc93d3..ca8b92c015 100644 --- a/Terminal.Gui/Drivers/OutputBase.cs +++ b/Terminal.Gui/Drivers/OutputBase.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Drivers; /// @@ -32,9 +31,6 @@ public virtual void Write (IOutputBuffer buffer) CursorVisibility? savedVisibility = _cachedCursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); - const int MAX_CHARS_PER_RUNE = 2; - Span runeBuffer = stackalloc char [MAX_CHARS_PER_RUNE]; - for (int row = top; row < rows; row++) { if (!SetCursorPositionImpl (0, row)) @@ -75,32 +71,14 @@ public virtual void Write (IOutputBuffer buffer) lastCol = col; } - Attribute? attribute = buffer.Contents [row, col].Attribute; - - if (attribute is { }) - { - Attribute attr = attribute.Value; - - // Performance: Only send the escape sequence if the attribute has changed. - if (attr != redrawAttr) - { - redrawAttr = attr; - - AppendOrWriteAttribute (output, attr, _redrawTextStyle); - - _redrawTextStyle = attr.Style; - } - } + Cell cell = buffer.Contents [row, col]; + AppendCellAnsi (cell, output, ref redrawAttr, ref _redrawTextStyle, cols, ref col); outputWidth++; - // Avoid Rune.ToString() by appending the rune chars. - Rune rune = buffer.Contents [row, col].Rune; - int runeCharsWritten = rune.EncodeToUtf16 (runeBuffer); - ReadOnlySpan runeChars = runeBuffer [..runeCharsWritten]; - output.Append (runeChars); - - if (buffer.Contents [row, col].CombiningMarks.Count > 0) + // Handle special cases that AppendCellAnsi doesn't cover + Rune rune = cell.Rune; + if (cell.CombiningMarks.Count > 0) { // AtlasEngine does not support NON-NORMALIZED combining marks in a way // compatible with the driver architecture. Any CMs (except in the first col) @@ -170,6 +148,105 @@ public virtual void Write (IOutputBuffer buffer) /// protected abstract void Write (StringBuilder output); + /// + /// Builds ANSI escape sequences for the specified rectangular region of the buffer. + /// + /// The output buffer to build ANSI for. + /// The starting row (inclusive). + /// The ending row (exclusive). + /// The starting column (inclusive). + /// The ending column (exclusive). + /// The StringBuilder to append ANSI sequences to. + /// The last attribute used, for optimization. + /// Predicate to determine which cells to include. If null, includes all cells. + /// Whether to add newlines between rows. + protected void BuildAnsiForRegion ( + IOutputBuffer buffer, + int startRow, + int endRow, + int startCol, + int endCol, + StringBuilder output, + ref Attribute? lastAttr, + Func? includeCellPredicate = null, + bool addNewlines = true + ) + { + TextStyle redrawTextStyle = TextStyle.None; + + for (int row = startRow; row < endRow; row++) + { + for (int col = startCol; col < endCol; col++) + { + if (includeCellPredicate != null && !includeCellPredicate (row, col)) + { + continue; + } + + Cell cell = buffer.Contents![row, col]; + AppendCellAnsi (cell, output, ref lastAttr, ref redrawTextStyle, endCol, ref col); + } + + // Add newline at end of row if requested + if (addNewlines) + { + output.AppendLine (); + } + } + } + + /// + /// Appends ANSI sequences for a single cell to the output. + /// + /// The cell to append ANSI for. + /// The StringBuilder to append to. + /// The last attribute used, updated if the cell's attribute is different. + /// The current text style for optimization. + /// The maximum column, used for wide character handling. + /// The current column, updated for wide characters. + protected void AppendCellAnsi (Cell cell, StringBuilder output, ref Attribute? lastAttr, ref TextStyle redrawTextStyle, int maxCol, ref int currentCol) + { + Attribute? attribute = cell.Attribute; + + // Add ANSI escape sequence for attribute change + if (attribute.HasValue && attribute.Value != lastAttr) + { + lastAttr = attribute.Value; + AppendOrWriteAttribute (output, attribute.Value, redrawTextStyle); + redrawTextStyle = attribute.Value.Style; + } + + // Add the character + const int MAX_CHARS_PER_RUNE = 2; + Span runeBuffer = stackalloc char [MAX_CHARS_PER_RUNE]; + Rune rune = cell.Rune; + int runeCharsWritten = rune.EncodeToUtf16 (runeBuffer); + ReadOnlySpan runeChars = runeBuffer [..runeCharsWritten]; + output.Append (runeChars); + + // Handle wide characters + if (rune.GetColumns () > 1 && currentCol + 1 < maxCol) + { + currentCol++; // Skip next cell for wide character + } + } + + /// + /// Generates an ANSI escape sequence string representation of the given contents. + /// This is the same output that would be written to the terminal to recreate the current screen contents. + /// + /// The output buffer to convert to ANSI. + /// A string containing ANSI escape sequences representing the buffer contents. + public string ToAnsi (IOutputBuffer buffer) + { + var output = new StringBuilder (); + Attribute? lastAttr = null; + + BuildAnsiForRegion (buffer, 0, buffer.Rows, 0, buffer.Cols, output, ref lastAttr); + + return output.ToString (); + } + private void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth) { SetCursorPositionImpl (lastCol, row); diff --git a/Terminal.Gui/Drivers/OutputBufferImpl.cs b/Terminal.Gui/Drivers/OutputBufferImpl.cs index ee2493d602..d12eb79914 100644 --- a/Terminal.Gui/Drivers/OutputBufferImpl.cs +++ b/Terminal.Gui/Drivers/OutputBufferImpl.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Diagnostics; +using System.Diagnostics; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/Platform.cs b/Terminal.Gui/Drivers/Platform.cs index 4b14ba0536..4b7b20a18c 100644 --- a/Terminal.Gui/Drivers/Platform.cs +++ b/Terminal.Gui/Drivers/Platform.cs @@ -80,4 +80,4 @@ private static int GetSuspendSignal () [DllImport ("libc")] private static extern int uname (nint buf); -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drivers/SizeMonitorImpl.cs b/Terminal.Gui/Drivers/SizeMonitorImpl.cs index 409f276175..f41b50d7e4 100644 --- a/Terminal.Gui/Drivers/SizeMonitorImpl.cs +++ b/Terminal.Gui/Drivers/SizeMonitorImpl.cs @@ -1,5 +1,4 @@ -#nullable enable -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs b/Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs index c562ee66dd..6562f3b2dc 100644 --- a/Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs +++ b/Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Runtime.InteropServices; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/UnixDriver/UnixComponentFactory.cs b/Terminal.Gui/Drivers/UnixDriver/UnixComponentFactory.cs index f53b88f85b..5067832d73 100644 --- a/Terminal.Gui/Drivers/UnixDriver/UnixComponentFactory.cs +++ b/Terminal.Gui/Drivers/UnixDriver/UnixComponentFactory.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/UnixDriver/UnixInput.cs b/Terminal.Gui/Drivers/UnixDriver/UnixInput.cs index 0337e0affb..60807d5c47 100644 --- a/Terminal.Gui/Drivers/UnixDriver/UnixInput.cs +++ b/Terminal.Gui/Drivers/UnixDriver/UnixInput.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Runtime.InteropServices; // ReSharper disable IdentifierTypo diff --git a/Terminal.Gui/Drivers/UnixDriver/UnixKeyConverter.cs b/Terminal.Gui/Drivers/UnixDriver/UnixKeyConverter.cs index 62c05a0eda..22f95d4d39 100644 --- a/Terminal.Gui/Drivers/UnixDriver/UnixKeyConverter.cs +++ b/Terminal.Gui/Drivers/UnixDriver/UnixKeyConverter.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs b/Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs index 17dbe2bb44..dfbf63ead4 100644 --- a/Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs +++ b/Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; diff --git a/Terminal.Gui/Drivers/WindowsDriver/ClipboardImpl.cs b/Terminal.Gui/Drivers/WindowsDriver/ClipboardImpl.cs index 6dc7081103..cab68fc747 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/ClipboardImpl.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/ClipboardImpl.cs @@ -1,4 +1,3 @@ -#nullable enable using System.ComponentModel; using System.Runtime.InteropServices; diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsComponentFactory.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsComponentFactory.cs index 1f16fd66a4..4c361b00dc 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsComponentFactory.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsComponentFactory.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsConsole.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsConsole.cs index 744c325886..fa191262e7 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsConsole.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsConsole.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Runtime.InteropServices; // ReSharper disable InconsistentNaming diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsInput.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsInput.cs index 026e1f61b4..dc3c982055 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsInput.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsInput.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; using static Terminal.Gui.Drivers.WindowsConsole; diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsInputProcessor.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsInputProcessor.cs index 028eb4dc15..739a393fb1 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsInputProcessor.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsInputProcessor.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyConverter.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyConverter.cs index 4458ad6ff9..0a8f2427f7 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyConverter.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyConverter.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Drivers; /// diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyHelper.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyHelper.cs index 56193db21d..cd424136e4 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyHelper.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyHelper.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; // ReSharper disable InconsistentNaming diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyboardLayout.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyboardLayout.cs index e368dfa8d5..38fc85bfe6 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyboardLayout.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsKeyboardLayout.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Runtime.InteropServices; namespace Terminal.Gui.Drivers; diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs index a0bea436c0..5a81ae0ab0 100644 --- a/Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs +++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs @@ -1,4 +1,3 @@ -#nullable enable using System.ComponentModel; using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; @@ -148,7 +147,9 @@ public WindowsOutput () } // Force 16 colors if not in virtual terminal mode. - Application.Force16Colors = true; + // BUGBUG: This is bad. It does not work if the app was crated without + // BUGBUG: Apis. + ApplicationImpl.Instance.Force16Colors = true; } @@ -263,7 +264,10 @@ private void SetConsoleOutputWindow (WindowsConsole.CONSOLE_SCREEN_BUFFER_INFOEX public override void Write (IOutputBuffer outputBuffer) { - _force16Colors = Application.Driver!.Force16Colors; + // BUGBUG: This is bad. It does not work if the app was crated without + // BUGBUG: Apis. + //_force16Colors = ApplicationImpl.Instance.Driver!.Force16Colors; + _force16Colors = false; _everythingStringBuilder.Clear (); // for 16 color mode we will write to a backing buffer then flip it to the active one at the end to avoid jitter. @@ -303,6 +307,12 @@ public override void Write (IOutputBuffer outputBuffer) { int err = Marshal.GetLastWin32Error (); + if (err == 1) + { + Logging.Logger.LogError ($"Error: {Marshal.GetLastWin32Error ()} in {nameof (WindowsOutput)}"); + + return; + } if (err != 0) { throw new Win32Exception (err); @@ -345,7 +355,9 @@ protected override void Write (StringBuilder output) /// protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle) { - bool force16Colors = Application.Force16Colors; + // BUGBUG: This is bad. It does not work if the app was crated without + // BUGBUG: Apis. + bool force16Colors = ApplicationImpl.Instance.Force16Colors; if (force16Colors) { diff --git a/Terminal.Gui/FileServices/DefaultSearchMatcher.cs b/Terminal.Gui/FileServices/DefaultSearchMatcher.cs index 3c10e2a1c6..3d9ce80460 100644 --- a/Terminal.Gui/FileServices/DefaultSearchMatcher.cs +++ b/Terminal.Gui/FileServices/DefaultSearchMatcher.cs @@ -4,8 +4,8 @@ namespace Terminal.Gui.FileServices; internal class DefaultSearchMatcher : ISearchMatcher { - private string [] terms; - public void Initialize (string terms) { this.terms = terms.Split (new [] { " " }, StringSplitOptions.RemoveEmptyEntries); } + private string []? _terms; + public void Initialize (string terms) { _terms = terms.Split ([" "], StringSplitOptions.RemoveEmptyEntries); } public bool IsMatch (IFileSystemInfo f) { @@ -15,10 +15,10 @@ public bool IsMatch (IFileSystemInfo f) return // At least one term must match the file name only e.g. "my" in "myfile.csv" - terms.Any (t => f.Name.IndexOf (t, StringComparison.OrdinalIgnoreCase) >= 0) + _terms!.Any (t => f.Name.IndexOf (t, StringComparison.OrdinalIgnoreCase) >= 0) && // All terms must exist in full path e.g. "dos my" can match "c:\documents\myfile.csv" - terms.All (t => f.FullName.IndexOf (t, StringComparison.OrdinalIgnoreCase) >= 0); + _terms!.All (t => f.FullName.IndexOf (t, StringComparison.OrdinalIgnoreCase) >= 0); } } diff --git a/Terminal.Gui/FileServices/FileSystemIconProvider.cs b/Terminal.Gui/FileServices/FileSystemIconProvider.cs index 6075825766..de76d91c6c 100644 --- a/Terminal.Gui/FileServices/FileSystemIconProvider.cs +++ b/Terminal.Gui/FileServices/FileSystemIconProvider.cs @@ -1,4 +1,3 @@ -#nullable enable using System.IO.Abstractions; namespace Terminal.Gui.FileServices; diff --git a/Terminal.Gui/FileServices/FileSystemInfoStats.cs b/Terminal.Gui/FileServices/FileSystemInfoStats.cs index 72f8ded2d3..5444aa26fd 100644 --- a/Terminal.Gui/FileServices/FileSystemInfoStats.cs +++ b/Terminal.Gui/FileServices/FileSystemInfoStats.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Globalization; +using System.Globalization; using System.IO.Abstractions; namespace Terminal.Gui.FileServices; diff --git a/Terminal.Gui/FileServices/FileSystemTreeBuilder.cs b/Terminal.Gui/FileServices/FileSystemTreeBuilder.cs index 87aba39af5..a44ddd049d 100644 --- a/Terminal.Gui/FileServices/FileSystemTreeBuilder.cs +++ b/Terminal.Gui/FileServices/FileSystemTreeBuilder.cs @@ -1,4 +1,5 @@ -using System.IO.Abstractions; +#nullable disable +using System.IO.Abstractions; namespace Terminal.Gui.FileServices; diff --git a/Terminal.Gui/Input/Command.cs b/Terminal.Gui/Input/Command.cs index 88d99e639d..4d1b6d0086 100644 --- a/Terminal.Gui/Input/Command.cs +++ b/Terminal.Gui/Input/Command.cs @@ -336,4 +336,4 @@ public enum Command Edit, #endregion -} \ No newline at end of file +} diff --git a/Terminal.Gui/Input/CommandContext.cs b/Terminal.Gui/Input/CommandContext.cs index fe6c9c194d..dd6c529ba6 100644 --- a/Terminal.Gui/Input/CommandContext.cs +++ b/Terminal.Gui/Input/CommandContext.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Input; #pragma warning disable CS1574, CS0419 // XML comment has cref attribute that could not be resolved @@ -33,4 +32,4 @@ public CommandContext (Command command, View? source, TBinding? binding) /// The keyboard or mouse minding that was used to invoke the , if any. /// public TBinding? Binding { get; set; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Input/CommandEventArgs.cs b/Terminal.Gui/Input/CommandEventArgs.cs index 05d6624378..659d0db643 100644 --- a/Terminal.Gui/Input/CommandEventArgs.cs +++ b/Terminal.Gui/Input/CommandEventArgs.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.ComponentModel; namespace Terminal.Gui.Input; diff --git a/Terminal.Gui/Input/ICommandContext.cs b/Terminal.Gui/Input/ICommandContext.cs index a9d4e641c1..c4e57de222 100644 --- a/Terminal.Gui/Input/ICommandContext.cs +++ b/Terminal.Gui/Input/ICommandContext.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.Input; #pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved diff --git a/Terminal.Gui/Input/IInputBinding.cs b/Terminal.Gui/Input/IInputBinding.cs index f0ae3a25a4..34170c73c4 100644 --- a/Terminal.Gui/Input/IInputBinding.cs +++ b/Terminal.Gui/Input/IInputBinding.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.Input; +namespace Terminal.Gui.Input; /// /// Describes an input binding. Used to bind a set of objects to a specific input event. diff --git a/Terminal.Gui/Input/InputBindings.cs b/Terminal.Gui/Input/InputBindings.cs index 75a4711e54..8711cf87cd 100644 --- a/Terminal.Gui/Input/InputBindings.cs +++ b/Terminal.Gui/Input/InputBindings.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.Input; +namespace Terminal.Gui.Input; /// /// Abstract class for and . diff --git a/Terminal.Gui/Input/Keyboard/Key.cs b/Terminal.Gui/Input/Keyboard/Key.cs index cd99240473..7a60b6cb36 100644 --- a/Terminal.Gui/Input/Keyboard/Key.cs +++ b/Terminal.Gui/Input/Keyboard/Key.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics.CodeAnalysis; using System.Globalization; diff --git a/Terminal.Gui/Input/Keyboard/KeyBinding.cs b/Terminal.Gui/Input/Keyboard/KeyBinding.cs index b55a66842b..6d97a6b6e8 100644 --- a/Terminal.Gui/Input/Keyboard/KeyBinding.cs +++ b/Terminal.Gui/Input/Keyboard/KeyBinding.cs @@ -1,4 +1,4 @@ -#nullable enable + // These classes use a key binding system based on the design implemented in Scintilla.Net which is an diff --git a/Terminal.Gui/Input/Keyboard/KeyBindings.cs b/Terminal.Gui/Input/Keyboard/KeyBindings.cs index c4a2952eb4..46a7081de1 100644 --- a/Terminal.Gui/Input/Keyboard/KeyBindings.cs +++ b/Terminal.Gui/Input/Keyboard/KeyBindings.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Input; diff --git a/Terminal.Gui/Input/Keyboard/KeyEqualityComparer.cs b/Terminal.Gui/Input/Keyboard/KeyEqualityComparer.cs index 287dd9cfaf..25841107dd 100644 --- a/Terminal.Gui/Input/Keyboard/KeyEqualityComparer.cs +++ b/Terminal.Gui/Input/Keyboard/KeyEqualityComparer.cs @@ -1,4 +1,3 @@ -#nullable enable /// /// diff --git a/Terminal.Gui/Input/Mouse/GrabMouseEventArgs.cs b/Terminal.Gui/Input/Mouse/GrabMouseEventArgs.cs index c05fd74712..fd3ae56541 100644 --- a/Terminal.Gui/Input/Mouse/GrabMouseEventArgs.cs +++ b/Terminal.Gui/Input/Mouse/GrabMouseEventArgs.cs @@ -1,4 +1,3 @@ - namespace Terminal.Gui.Input; /// Args GrabMouse related events. diff --git a/Terminal.Gui/Input/Mouse/MouseBinding.cs b/Terminal.Gui/Input/Mouse/MouseBinding.cs index 1c6ebf3861..40f32ea8f8 100644 --- a/Terminal.Gui/Input/Mouse/MouseBinding.cs +++ b/Terminal.Gui/Input/Mouse/MouseBinding.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Input; diff --git a/Terminal.Gui/Input/Mouse/MouseBindings.cs b/Terminal.Gui/Input/Mouse/MouseBindings.cs index 55d0bf61df..255be246ba 100644 --- a/Terminal.Gui/Input/Mouse/MouseBindings.cs +++ b/Terminal.Gui/Input/Mouse/MouseBindings.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Input; /// diff --git a/Terminal.Gui/Input/Mouse/MouseEventArgs.cs b/Terminal.Gui/Input/Mouse/MouseEventArgs.cs index db0008ef32..3e10c68a45 100644 --- a/Terminal.Gui/Input/Mouse/MouseEventArgs.cs +++ b/Terminal.Gui/Input/Mouse/MouseEventArgs.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.ComponentModel; namespace Terminal.Gui.Input; diff --git a/Terminal.Gui/Resources/GlobalResources.cs b/Terminal.Gui/Resources/GlobalResources.cs index 8a9ac5a0c8..4c05e34156 100644 --- a/Terminal.Gui/Resources/GlobalResources.cs +++ b/Terminal.Gui/Resources/GlobalResources.cs @@ -1,5 +1,4 @@ -#nullable enable - + using System.Collections; using System.Globalization; using System.Resources; diff --git a/Terminal.Gui/Resources/ResourceManagerWrapper.cs b/Terminal.Gui/Resources/ResourceManagerWrapper.cs index 8bcc9271f8..dc3651454f 100644 --- a/Terminal.Gui/Resources/ResourceManagerWrapper.cs +++ b/Terminal.Gui/Resources/ResourceManagerWrapper.cs @@ -1,5 +1,4 @@ -#nullable enable - + using System.Collections; using System.Globalization; using System.Resources; diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 15d5869758..6f1e53802c 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -24,6 +24,7 @@ net8.0 12 enable + enable $(AssemblyName) true @@ -163,6 +164,16 @@ + + + + + + + + + + diff --git a/Terminal.Gui/Text/NerdFonts.cs b/Terminal.Gui/Text/NerdFonts.cs index 2ceaf4fa7d..c6da806976 100644 --- a/Terminal.Gui/Text/NerdFonts.cs +++ b/Terminal.Gui/Text/NerdFonts.cs @@ -741,8 +741,13 @@ internal class NerdFonts { "nf-seti-typescript", '' } }; - public char GetNerdIcon (IFileSystemInfo file, bool isOpen) + public char GetNerdIcon (IFileSystemInfo? file, bool isOpen) { + if (file == null) + { + throw new ArgumentNullException (nameof (file)); + } + if (FilenameToIcon.ContainsKey (file.Name)) { return Glyphs [FilenameToIcon [file.Name]]; diff --git a/Terminal.Gui/Text/RuneExtensions.cs b/Terminal.Gui/Text/RuneExtensions.cs index 4dfb4bb3f4..c8a273c834 100644 --- a/Terminal.Gui/Text/RuneExtensions.cs +++ b/Terminal.Gui/Text/RuneExtensions.cs @@ -1,5 +1,4 @@ -#nullable enable - + using System.Globalization; using Wcwidth; diff --git a/Terminal.Gui/Text/StringExtensions.cs b/Terminal.Gui/Text/StringExtensions.cs index e379cf3da6..eb053a3d8a 100644 --- a/Terminal.Gui/Text/StringExtensions.cs +++ b/Terminal.Gui/Text/StringExtensions.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Buffers; +using System.Buffers; using System.Globalization; namespace Terminal.Gui.Text; diff --git a/Terminal.Gui/Text/TextDirection.cs b/Terminal.Gui/Text/TextDirection.cs index 2ea3ba3a41..b989c7337b 100644 --- a/Terminal.Gui/Text/TextDirection.cs +++ b/Terminal.Gui/Text/TextDirection.cs @@ -58,4 +58,4 @@ public enum TextDirection /// This is a vertical direction. D O
L L
R L
O E
W H
BottomTop_RightLeft -} \ No newline at end of file +} diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs index 70636c018b..a11289a2b2 100644 --- a/Terminal.Gui/Text/TextFormatter.cs +++ b/Terminal.Gui/Text/TextFormatter.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Buffers; using System.Diagnostics; @@ -11,7 +10,7 @@ namespace Terminal.Gui.Text; public class TextFormatter { // Utilized in CRLF related helper methods for faster newline char index search. - private static readonly SearchValues NewlineSearchValues = SearchValues.Create(['\r', '\n']); + private static readonly SearchValues NewlineSearchValues = SearchValues.Create (['\r', '\n']); private Key _hotKey = new (); private int _hotKeyPos = -1; @@ -52,31 +51,28 @@ public TextDirection Direction /// Causes the text to be formatted (references ). Sets to /// false. /// + /// The console driver currently used by the application. /// Specifies the screen-relative location and maximum size for drawing the text. /// The color to use for all text except the hotkey /// The color to use to draw the hotkey /// Specifies the screen-relative location and maximum container size. - /// The console driver currently used by the application. /// public void Draw ( + IDriver? driver, Rectangle screen, Attribute normalColor, Attribute hotColor, - Rectangle maximum = default, - IDriver? driver = null + Rectangle maximum = default ) { + ArgumentNullException.ThrowIfNull (driver); + // With this check, we protect against subclasses with overrides of Text (like Button) if (string.IsNullOrEmpty (Text)) { return; } - if (driver is null) - { - driver = Application.Driver; - } - driver?.SetAttribute (normalColor); List linesFormatted = GetLines (); @@ -1199,8 +1195,8 @@ internal static string StripCRLF (string str, bool keepNewLine = false) return str; } - StringBuilder stringBuilder = new(); - ReadOnlySpan firstSegment = remaining[..firstNewlineCharIndex]; + StringBuilder stringBuilder = new (); + ReadOnlySpan firstSegment = remaining [..firstNewlineCharIndex]; stringBuilder.Append (firstSegment); // The first newline is not yet skipped because the "keepNewLine" condition has not been evaluated. @@ -1215,7 +1211,7 @@ internal static string StripCRLF (string str, bool keepNewLine = false) break; } - ReadOnlySpan segment = remaining[..newlineCharIndex]; + ReadOnlySpan segment = remaining [..newlineCharIndex]; stringBuilder.Append (segment); int stride = segment.Length; @@ -1267,8 +1263,8 @@ internal static string ReplaceCRLFWithSpace (string str) return str; } - StringBuilder stringBuilder = new(); - ReadOnlySpan firstSegment = remaining[..firstNewlineCharIndex]; + StringBuilder stringBuilder = new (); + ReadOnlySpan firstSegment = remaining [..firstNewlineCharIndex]; stringBuilder.Append (firstSegment); // The first newline is not yet skipped because the newline type has not been evaluated. @@ -1283,7 +1279,7 @@ internal static string ReplaceCRLFWithSpace (string str) break; } - ReadOnlySpan segment = remaining[..newlineCharIndex]; + ReadOnlySpan segment = remaining [..newlineCharIndex]; stringBuilder.Append (segment); int stride = segment.Length; @@ -2445,12 +2441,12 @@ public static string RemoveHotKeySpecifier (string text, int hotPos, Rune hotKey } const int maxStackallocCharBufferSize = 512; // ~1 kB - char[]? rentedBufferArray = null; + char []? rentedBufferArray = null; try { Span buffer = text.Length <= maxStackallocCharBufferSize - ? stackalloc char[text.Length] - : (rentedBufferArray = ArrayPool.Shared.Rent(text.Length)); + ? stackalloc char [text.Length] + : (rentedBufferArray = ArrayPool.Shared.Rent (text.Length)); int i = 0; var remainingBuffer = buffer; @@ -2468,7 +2464,7 @@ public static string RemoveHotKeySpecifier (string text, int hotPos, Rune hotKey ReadOnlySpan newText = buffer [..^remainingBuffer.Length]; // If the resulting string would be the same as original then just return the original. - if (newText.Equals(text, StringComparison.Ordinal)) + if (newText.Equals (text, StringComparison.Ordinal)) { return text; } diff --git a/Terminal.Gui/ViewBase/Adornment/Adornment.cs b/Terminal.Gui/ViewBase/Adornment/Adornment.cs index 9d5675b6bc..6852efea47 100644 --- a/Terminal.Gui/ViewBase/Adornment/Adornment.cs +++ b/Terminal.Gui/ViewBase/Adornment/Adornment.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.ViewBase; @@ -84,6 +84,12 @@ public Thickness Thickness #region View Overrides + /// + protected override IApplication? GetApp () => Parent?.App; + + /// + protected override IDriver? GetDriver () => Parent?.Driver ?? base.GetDriver(); + // If a scheme is explicitly set, use that. Otherwise, use the scheme of the parent view. private Scheme? _scheme; @@ -176,7 +182,10 @@ protected override bool OnClearingViewport () } // This just draws/clears the thickness, not the insides. - Thickness.Draw (ViewportToScreen (Viewport), Diagnostics, ToString ()); + if (Driver is { }) + { + Thickness.Draw (Driver, ViewportToScreen (Viewport), Diagnostics, ToString ()); + } NeedsDraw = true; diff --git a/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs b/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs index b55fec0279..f2e52fab8b 100644 --- a/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs +++ b/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs @@ -1,4 +1,3 @@ -#nullable enable using System.ComponentModel; using System.Diagnostics; @@ -40,7 +39,10 @@ public partial class Border // Add Commands and KeyBindings - Note it's ok these get added each time. KeyBindings are cleared in EndArrange() AddArrangeModeKeyBindings (); - Application.MouseEvent += ApplicationOnMouseEvent; + if (App is { }) + { + App.Mouse.MouseEvent += ApplicationOnMouseEvent; + } // Create all necessary arrangement buttons CreateArrangementButtons (); @@ -429,11 +431,14 @@ private void ApplicationOnMouseEvent (object? sender, MouseEventArgs e) MouseState &= ~MouseState.Pressed; - Application.MouseEvent -= ApplicationOnMouseEvent; - - if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue) + if (App is { }) { - Application.Mouse.UngrabMouse (); + App.Mouse.MouseEvent -= ApplicationOnMouseEvent; + + if (App.Mouse.MouseGrabView == this && _dragPosition.HasValue) + { + App.Mouse.UngrabMouse (); + } } // Clean up all arrangement buttons @@ -498,7 +503,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent) // Set the start grab point to the Frame coords _startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y); _dragPosition = mouseEvent.Position; - Application.Mouse.GrabMouse (this); + App?.Mouse.GrabMouse (this); // Determine the mode based on where the click occurred ViewArrangement arrangeMode = DetermineArrangeModeFromClick (); @@ -511,7 +516,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent) return true; } - if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && Application.Mouse.MouseGrabView == this) + if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && App?.Mouse.MouseGrabView == this) { if (_dragPosition.HasValue) { @@ -523,7 +528,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent) if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && _dragPosition.HasValue) { _dragPosition = null; - Application.Mouse.UngrabMouse (); + App?.Mouse.UngrabMouse (); EndArrangeMode (); @@ -652,7 +657,7 @@ internal void HandleDragOperation (MouseEventArgs mouseEvent) if (Parent!.SuperView is null) { // Redraw the entire app window. - Application.Top!.SetNeedsDraw (); + App?.Current?.SetNeedsDraw (); } else { @@ -763,7 +768,7 @@ internal void HandleDragOperation (MouseEventArgs mouseEvent) private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e) { - if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue) + if (App?.Mouse.MouseGrabView == this && _dragPosition.HasValue) { e.Cancel = true; } @@ -771,7 +776,7 @@ private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e) private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e) { - if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue) + if (App?.Mouse.MouseGrabView == this && _dragPosition.HasValue) { e.Cancel = true; } @@ -784,8 +789,11 @@ private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e) /// protected override void Dispose (bool disposing) { - Application.Mouse.GrabbingMouse -= Application_GrabbingMouse; - Application.Mouse.UnGrabbingMouse -= Application_UnGrabbingMouse; + if (App is { }) + { + App.Mouse.GrabbingMouse -= Application_GrabbingMouse; + App.Mouse.UnGrabbingMouse -= Application_UnGrabbingMouse; + } _dragPosition = null; base.Dispose (disposing); diff --git a/Terminal.Gui/ViewBase/Adornment/Border.cs b/Terminal.Gui/ViewBase/Adornment/Border.cs index 18a99b1c10..bfbe92a081 100644 --- a/Terminal.Gui/ViewBase/Adornment/Border.cs +++ b/Terminal.Gui/ViewBase/Adornment/Border.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; namespace Terminal.Gui.ViewBase; @@ -49,10 +48,6 @@ public Border (View parent) : base (parent) Parent = parent; CanFocus = false; TabStop = TabBehavior.TabGroup; - - Application.Mouse.GrabbingMouse += Application_GrabbingMouse; - Application.Mouse.UnGrabbingMouse += Application_UnGrabbingMouse; - ThicknessChanged += OnThicknessChanged; } @@ -113,6 +108,12 @@ public override void BeginInit () { base.BeginInit (); + if (App is { }) + { + App.Mouse.GrabbingMouse += Application_GrabbingMouse; + App.Mouse.UnGrabbingMouse += Application_UnGrabbingMouse; + } + if (Parent is null) { return; @@ -312,7 +313,8 @@ protected override bool OnDrawingContent () } } - if (Parent is { } + if (Driver is { } + && Parent is { } && canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 @@ -321,10 +323,7 @@ protected override bool OnDrawingContent () { Rectangle titleRect = new (borderBounds.X + 2, titleY, maxTitleWidth, 1); - Parent.TitleTextFormatter.Draw ( - titleRect, - GetAttributeForRole (Parent.HasFocus ? VisualRole.Focus : VisualRole.Normal), - GetAttributeForRole (Parent.HasFocus ? VisualRole.HotFocus : VisualRole.HotNormal)); + Parent.TitleTextFormatter.Draw (driver: Driver, screen: titleRect, normalColor: GetAttributeForRole (Parent.HasFocus ? VisualRole.Focus : VisualRole.Normal), hotColor: GetAttributeForRole (Parent.HasFocus ? VisualRole.HotFocus : VisualRole.HotNormal)); Parent?.LineCanvas.Exclude (new (titleRect)); } @@ -498,16 +497,13 @@ protected override bool OnDrawingContent () if (drawTop) { - hruler.Draw (new (screenBounds.X, screenBounds.Y)); + hruler.Draw (driver: Driver, location: new (screenBounds.X, screenBounds.Y)); } // Redraw title if (drawTop && maxTitleWidth > 0 && Settings.FastHasFlags (BorderSettings.Title)) { - Parent!.TitleTextFormatter.Draw ( - new (borderBounds.X + 2, titleY, maxTitleWidth, 1), - Parent.HasFocus ? Parent.GetAttributeForRole (VisualRole.Focus) : Parent.GetAttributeForRole (VisualRole.Normal), - Parent.HasFocus ? Parent.GetAttributeForRole (VisualRole.Focus) : Parent.GetAttributeForRole (VisualRole.Normal)); + Parent!.TitleTextFormatter.Draw (driver: Driver, screen: new (borderBounds.X + 2, titleY, maxTitleWidth, 1), normalColor: Parent.HasFocus ? Parent.GetAttributeForRole (VisualRole.Focus) : Parent.GetAttributeForRole (VisualRole.Normal), hotColor: Parent.HasFocus ? Parent.GetAttributeForRole (VisualRole.Focus) : Parent.GetAttributeForRole (VisualRole.Normal)); } //Left @@ -515,19 +511,19 @@ protected override bool OnDrawingContent () if (drawLeft) { - vruler.Draw (new (screenBounds.X, screenBounds.Y + 1), 1); + vruler.Draw (driver: Driver, location: new (screenBounds.X, screenBounds.Y + 1), start: 1); } // Bottom if (drawBottom) { - hruler.Draw (new (screenBounds.X, screenBounds.Y + screenBounds.Height - 1)); + hruler.Draw (driver: Driver, location: new (screenBounds.X, screenBounds.Y + screenBounds.Height - 1)); } // Right if (drawRight) { - vruler.Draw (new (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1); + vruler.Draw (driver: Driver, location: new (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), start: 1); } } diff --git a/Terminal.Gui/ViewBase/Adornment/BorderSettings.cs b/Terminal.Gui/ViewBase/Adornment/BorderSettings.cs index 5168420006..4921446aec 100644 --- a/Terminal.Gui/ViewBase/Adornment/BorderSettings.cs +++ b/Terminal.Gui/ViewBase/Adornment/BorderSettings.cs @@ -1,12 +1,9 @@ - - namespace Terminal.Gui.ViewBase; /// /// Determines the settings for . /// [Flags] - public enum BorderSettings { /// diff --git a/Terminal.Gui/ViewBase/Adornment/Margin.cs b/Terminal.Gui/ViewBase/Adornment/Margin.cs index 59b39930fa..0ce7740ba2 100644 --- a/Terminal.Gui/ViewBase/Adornment/Margin.cs +++ b/Terminal.Gui/ViewBase/Adornment/Margin.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.Runtime.InteropServices; @@ -80,10 +80,10 @@ internal static bool DrawMargins (IEnumerable views) if (view.Margin?.GetCachedClip () != null) { view.Margin!.NeedsDraw = true; - Region? saved = GetClip (); - View.SetClip (view.Margin!.GetCachedClip ()); - view.Margin!.Draw (); - View.SetClip (saved); + Region? saved = view.GetClip (); + view.SetClip (view.Margin!.GetCachedClip ()); + view.Margin!.Draw (); + view.SetClip (saved); view.Margin!.ClearCachedClip (); } @@ -128,7 +128,7 @@ protected override bool OnClearingViewport () // This just draws/clears the thickness, not the insides. // TODO: This is a hack. See https://github.com/gui-cs/Terminal.Gui/issues/4016 //SetAttribute (GetAttributeForRole (VisualRole.Normal)); - Thickness.Draw (screen, Diagnostics, ToString ()); + Thickness.Draw (Driver, screen, Diagnostics, ToString ()); } if (ShadowStyle != ShadowStyle.None) diff --git a/Terminal.Gui/ViewBase/Adornment/Padding.cs b/Terminal.Gui/ViewBase/Adornment/Padding.cs index 508670504f..0f19073a4e 100644 --- a/Terminal.Gui/ViewBase/Adornment/Padding.cs +++ b/Terminal.Gui/ViewBase/Adornment/Padding.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/Adornment/ShadowView.cs b/Terminal.Gui/ViewBase/Adornment/ShadowView.cs index 151aa149ca..9212e7271e 100644 --- a/Terminal.Gui/ViewBase/Adornment/ShadowView.cs +++ b/Terminal.Gui/ViewBase/Adornment/ShadowView.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.ViewBase; @@ -144,9 +144,9 @@ private Attribute GetAttributeUnderLocation (Point location) { if (SuperView is not Adornment adornment || location.X < 0 - || location.X >= Application.Screen.Width + || location.X >= App?.Screen.Width || location.Y < 0 - || location.Y >= Application.Screen.Height) + || location.Y >= App?.Screen.Height) { return Attribute.Default; } @@ -170,7 +170,7 @@ private Attribute GetAttributeUnderLocation (Point location) // use the Normal attribute from the View under the shadow. if (newAttribute.Background == Color.DarkGray) { - List currentViewsUnderMouse = View.GetViewsUnderLocation (location, ViewportSettingsFlags.Transparent); + List currentViewsUnderMouse = GetViewsUnderLocation (location, ViewportSettingsFlags.Transparent); View? underView = currentViewsUnderMouse!.LastOrDefault (); attr = underView?.GetAttributeForRole (VisualRole.Normal) ?? Attribute.Default; diff --git a/Terminal.Gui/ViewBase/DrawAdornmentsEventArgs.cs b/Terminal.Gui/ViewBase/DrawAdornmentsEventArgs.cs index 97092cf910..4fc8b826bd 100644 --- a/Terminal.Gui/ViewBase/DrawAdornmentsEventArgs.cs +++ b/Terminal.Gui/ViewBase/DrawAdornmentsEventArgs.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.ViewBase; +namespace Terminal.Gui.ViewBase; /// /// Provides data for events that allow cancellation of adornment drawing in the Cancellable Work Pattern (CWP). diff --git a/Terminal.Gui/ViewBase/DrawContext.cs b/Terminal.Gui/ViewBase/DrawContext.cs index 95eec9a2e4..e6df3033b9 100644 --- a/Terminal.Gui/ViewBase/DrawContext.cs +++ b/Terminal.Gui/ViewBase/DrawContext.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/DrawEventArgs.cs b/Terminal.Gui/ViewBase/DrawEventArgs.cs index f00bdb6186..68be3544cc 100644 --- a/Terminal.Gui/ViewBase/DrawEventArgs.cs +++ b/Terminal.Gui/ViewBase/DrawEventArgs.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.ComponentModel; namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/AddOrSubtractExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/AddOrSubtractExtensions.cs index eef2b43727..65ee7fd9ea 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/AddOrSubtractExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/AddOrSubtractExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/AlignmentExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/AlignmentExtensions.cs index e94e6b93e9..b6c2b05d47 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/AlignmentExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/AlignmentExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/AlignmentModesExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/AlignmentModesExtensions.cs index da91e93dfa..b129642f0a 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/AlignmentModesExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/AlignmentModesExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/BorderSettingsExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/BorderSettingsExtensions.cs index b908950d6f..c01b5b9b5d 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/BorderSettingsExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/BorderSettingsExtensions.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/DimAutoStyleExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/DimAutoStyleExtensions.cs index edccb473e0..3633cc5e86 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/DimAutoStyleExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/DimAutoStyleExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/DimPercentModeExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/DimPercentModeExtensions.cs index f6a62db756..0eac308905 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/DimPercentModeExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/DimPercentModeExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/DimensionExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/DimensionExtensions.cs index 0a217e1a89..7d34f36ad1 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/DimensionExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/DimensionExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/SideExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/SideExtensions.cs index f7a2a548f4..cbf67f18dd 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/SideExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/SideExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/EnumExtensions/ViewDiagnosticFlagsExtensions.cs b/Terminal.Gui/ViewBase/EnumExtensions/ViewDiagnosticFlagsExtensions.cs index e0d0891847..400d2d0810 100644 --- a/Terminal.Gui/ViewBase/EnumExtensions/ViewDiagnosticFlagsExtensions.cs +++ b/Terminal.Gui/ViewBase/EnumExtensions/ViewDiagnosticFlagsExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/Terminal.Gui/ViewBase/Helpers/StackExtensions.cs b/Terminal.Gui/ViewBase/Helpers/StackExtensions.cs index 96201df73a..e76788029a 100644 --- a/Terminal.Gui/ViewBase/Helpers/StackExtensions.cs +++ b/Terminal.Gui/ViewBase/Helpers/StackExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.ViewBase; /// Extension of helper to work with specific diff --git a/Terminal.Gui/ViewBase/IDesignable.cs b/Terminal.Gui/ViewBase/IDesignable.cs index 382bdd91b8..c7625d0794 100644 --- a/Terminal.Gui/ViewBase/IDesignable.cs +++ b/Terminal.Gui/ViewBase/IDesignable.cs @@ -9,10 +9,10 @@ public interface IDesignable /// Causes the View to enable design-time mode. This typically means that the view will load demo data and /// be configured to allow for design-time manipulation. /// - /// Optional arbitrary, View-specific, context. - /// A non-null type for . + /// + /// A non-null type for . /// if the view successfully loaded demo data. - public bool EnableForDesign (ref TContext context) where TContext : notnull => EnableForDesign (); + public bool EnableForDesign (ref TContext targetView) where TContext : notnull => EnableForDesign (); /// /// Causes the View to enable design-time mode. This typically means that the view will load demo data and diff --git a/Terminal.Gui/ViewBase/IMouseHeldDown.cs b/Terminal.Gui/ViewBase/IMouseHeldDown.cs index 5f7435793b..d0233fcd30 100644 --- a/Terminal.Gui/ViewBase/IMouseHeldDown.cs +++ b/Terminal.Gui/ViewBase/IMouseHeldDown.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.ComponentModel; namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/Layout/Aligner.cs b/Terminal.Gui/ViewBase/Layout/Aligner.cs index 72aae7ad5d..c3907bf92c 100644 --- a/Terminal.Gui/ViewBase/Layout/Aligner.cs +++ b/Terminal.Gui/ViewBase/Layout/Aligner.cs @@ -1,3 +1,4 @@ +#nullable disable using System.ComponentModel; namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/Layout/Dim.cs b/Terminal.Gui/ViewBase/Layout/Dim.cs index a3326e80ac..8952b980a9 100644 --- a/Terminal.Gui/ViewBase/Layout/Dim.cs +++ b/Terminal.Gui/ViewBase/Layout/Dim.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; using System.Numerics; @@ -188,18 +187,18 @@ public static Dim Percent (int percent, DimPercentMode mode = DimPercentMode.Con /// - /// Indicates whether the specified type is in the hierarchy of this Dim object. + /// Indicates whether the specified type is in the hierarchy of this Dim object. /// /// A reference to this instance. /// - public bool Has (out T dim) where T : Dim + public bool Has (out TDim dim) where TDim : Dim { - dim = (this as T)!; + dim = (this as TDim)!; return this switch { - DimCombine combine => combine.Left.Has (out dim) || combine.Right.Has (out dim), - T => true, + DimCombine combine => combine.Left.Has (out dim) || combine.Right.Has (out dim), + TDim => true, _ => false }; } diff --git a/Terminal.Gui/ViewBase/Layout/DimAbsolute.cs b/Terminal.Gui/ViewBase/Layout/DimAbsolute.cs index 6fd9e8072e..f630b34702 100644 --- a/Terminal.Gui/ViewBase/Layout/DimAbsolute.cs +++ b/Terminal.Gui/ViewBase/Layout/DimAbsolute.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/DimAuto.cs b/Terminal.Gui/ViewBase/Layout/DimAuto.cs index b70aca542b..1436eb9bc8 100644 --- a/Terminal.Gui/ViewBase/Layout/DimAuto.cs +++ b/Terminal.Gui/ViewBase/Layout/DimAuto.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; namespace Terminal.Gui.ViewBase; @@ -31,8 +30,10 @@ internal override int Calculate (int location, int superviewContentSize, View us var textSize = 0; var maxCalculatedSize = 0; + // 2048 x 2048 supports unit testing where no App is running. + Size screenSize = us.App?.Screen.Size ?? new (2048, 2048); int autoMin = MinimumContentDim?.GetAnchor (superviewContentSize) ?? 0; - int screenX4 = dimension == Dimension.Width ? Application.Screen.Width * 4 : Application.Screen.Height * 4; + int screenX4 = dimension == Dimension.Width ? screenSize.Width * 4 : screenSize.Height * 4; int autoMax = MaximumContentDim?.GetAnchor (superviewContentSize) ?? screenX4; //Debug.WriteLineIf (autoMin > autoMax, "MinimumContentDim must be less than or equal to MaximumContentDim."); diff --git a/Terminal.Gui/ViewBase/Layout/DimAutoStyle.cs b/Terminal.Gui/ViewBase/Layout/DimAutoStyle.cs index d3080dd942..ddd6e0ee5b 100644 --- a/Terminal.Gui/ViewBase/Layout/DimAutoStyle.cs +++ b/Terminal.Gui/ViewBase/Layout/DimAutoStyle.cs @@ -1,5 +1,3 @@ - - namespace Terminal.Gui.ViewBase; /// @@ -45,4 +43,4 @@ public enum DimAutoStyle /// corresponding dimension /// Auto = Content | Text, -} \ No newline at end of file +} diff --git a/Terminal.Gui/ViewBase/Layout/DimCombine.cs b/Terminal.Gui/ViewBase/Layout/DimCombine.cs index 3bf3b35343..b246d88bba 100644 --- a/Terminal.Gui/ViewBase/Layout/DimCombine.cs +++ b/Terminal.Gui/ViewBase/Layout/DimCombine.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/DimFill.cs b/Terminal.Gui/ViewBase/Layout/DimFill.cs index 0e278cbc54..feda107a0d 100644 --- a/Terminal.Gui/ViewBase/Layout/DimFill.cs +++ b/Terminal.Gui/ViewBase/Layout/DimFill.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/DimFunc.cs b/Terminal.Gui/ViewBase/Layout/DimFunc.cs index 0773dc316f..678406a9ba 100644 --- a/Terminal.Gui/ViewBase/Layout/DimFunc.cs +++ b/Terminal.Gui/ViewBase/Layout/DimFunc.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/DimPercent.cs b/Terminal.Gui/ViewBase/Layout/DimPercent.cs index 499ffb6fd6..2b9ade3b36 100644 --- a/Terminal.Gui/ViewBase/Layout/DimPercent.cs +++ b/Terminal.Gui/ViewBase/Layout/DimPercent.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/DimPercentMode.cs b/Terminal.Gui/ViewBase/Layout/DimPercentMode.cs index 3f9aed8367..bdb458d389 100644 --- a/Terminal.Gui/ViewBase/Layout/DimPercentMode.cs +++ b/Terminal.Gui/ViewBase/Layout/DimPercentMode.cs @@ -1,5 +1,3 @@ - - namespace Terminal.Gui.ViewBase; /// @@ -16,4 +14,4 @@ public enum DimPercentMode /// The dimension is computed using the View's . /// ContentSize = 1 -} \ No newline at end of file +} diff --git a/Terminal.Gui/ViewBase/Layout/DimView.cs b/Terminal.Gui/ViewBase/Layout/DimView.cs index 0a25e1983a..fec551d671 100644 --- a/Terminal.Gui/ViewBase/Layout/DimView.cs +++ b/Terminal.Gui/ViewBase/Layout/DimView.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/Dimension.cs b/Terminal.Gui/ViewBase/Layout/Dimension.cs index 5fae943605..60fbcaea63 100644 --- a/Terminal.Gui/ViewBase/Layout/Dimension.cs +++ b/Terminal.Gui/ViewBase/Layout/Dimension.cs @@ -1,5 +1,3 @@ - - namespace Terminal.Gui.ViewBase; /// @@ -21,4 +19,4 @@ public enum Dimension /// The width dimension. /// Width = 2 -} \ No newline at end of file +} diff --git a/Terminal.Gui/ViewBase/Layout/LayoutException.cs b/Terminal.Gui/ViewBase/Layout/LayoutException.cs index e21dc51f80..176f269822 100644 --- a/Terminal.Gui/ViewBase/Layout/LayoutException.cs +++ b/Terminal.Gui/ViewBase/Layout/LayoutException.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/Pos.cs b/Terminal.Gui/ViewBase/Layout/Pos.cs index 09694adc39..1e6e00688a 100644 --- a/Terminal.Gui/ViewBase/Layout/Pos.cs +++ b/Terminal.Gui/ViewBase/Layout/Pos.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; @@ -334,18 +333,18 @@ public static Pos Percent (int percent) internal virtual bool ReferencesOtherViews () { return false; } /// - /// Indicates whether the specified type is in the hierarchy of this Pos object. + /// Indicates whether the specified type is in the hierarchy of this Pos object. /// /// A reference to this instance. /// - public bool Has (out T pos) where T : Pos + public bool Has (out TPos pos) where TPos : Pos { - pos = (this as T)!; + pos = (this as TPos)!; return this switch { - PosCombine combine => combine.Left.Has (out pos) || combine.Right.Has (out pos), - T => true, + PosCombine combine => combine.Left.Has (out pos) || combine.Right.Has (out pos), + TPos => true, _ => false }; } diff --git a/Terminal.Gui/ViewBase/Layout/PosAbsolute.cs b/Terminal.Gui/ViewBase/Layout/PosAbsolute.cs index b217113552..494c63a50b 100644 --- a/Terminal.Gui/ViewBase/Layout/PosAbsolute.cs +++ b/Terminal.Gui/ViewBase/Layout/PosAbsolute.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/PosAlign.cs b/Terminal.Gui/ViewBase/Layout/PosAlign.cs index 4d72cc9afb..d6289e5102 100644 --- a/Terminal.Gui/ViewBase/Layout/PosAlign.cs +++ b/Terminal.Gui/ViewBase/Layout/PosAlign.cs @@ -1,4 +1,3 @@ -#nullable enable using System.ComponentModel; diff --git a/Terminal.Gui/ViewBase/Layout/PosAnchorEnd.cs b/Terminal.Gui/ViewBase/Layout/PosAnchorEnd.cs index 3cf2195aca..d7c6d30a78 100644 --- a/Terminal.Gui/ViewBase/Layout/PosAnchorEnd.cs +++ b/Terminal.Gui/ViewBase/Layout/PosAnchorEnd.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/PosCenter.cs b/Terminal.Gui/ViewBase/Layout/PosCenter.cs index 4a7945cd28..d1584f8e5f 100644 --- a/Terminal.Gui/ViewBase/Layout/PosCenter.cs +++ b/Terminal.Gui/ViewBase/Layout/PosCenter.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/PosCombine.cs b/Terminal.Gui/ViewBase/Layout/PosCombine.cs index 833b499f06..1be5325c25 100644 --- a/Terminal.Gui/ViewBase/Layout/PosCombine.cs +++ b/Terminal.Gui/ViewBase/Layout/PosCombine.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/PosFunc.cs b/Terminal.Gui/ViewBase/Layout/PosFunc.cs index 3900beb463..078459fd99 100644 --- a/Terminal.Gui/ViewBase/Layout/PosFunc.cs +++ b/Terminal.Gui/ViewBase/Layout/PosFunc.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/PosPercent.cs b/Terminal.Gui/ViewBase/Layout/PosPercent.cs index 98505dd3b2..08d9682100 100644 --- a/Terminal.Gui/ViewBase/Layout/PosPercent.cs +++ b/Terminal.Gui/ViewBase/Layout/PosPercent.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; /// diff --git a/Terminal.Gui/ViewBase/Layout/PosView.cs b/Terminal.Gui/ViewBase/Layout/PosView.cs index fb7f7266ab..d42e8cb104 100644 --- a/Terminal.Gui/ViewBase/Layout/PosView.cs +++ b/Terminal.Gui/ViewBase/Layout/PosView.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/Layout/Side.cs b/Terminal.Gui/ViewBase/Layout/Side.cs index b2256faac3..0cc52f0648 100644 --- a/Terminal.Gui/ViewBase/Layout/Side.cs +++ b/Terminal.Gui/ViewBase/Layout/Side.cs @@ -1,5 +1,3 @@ - - namespace Terminal.Gui.ViewBase; /// @@ -27,4 +25,4 @@ public enum Side /// The bottom (Y + Height) side of the view. /// Bottom = 3 -} \ No newline at end of file +} diff --git a/Terminal.Gui/ViewBase/Layout/SuperViewChangedEventArgs.cs b/Terminal.Gui/ViewBase/Layout/SuperViewChangedEventArgs.cs index f1e79536d6..611949a944 100644 --- a/Terminal.Gui/ViewBase/Layout/SuperViewChangedEventArgs.cs +++ b/Terminal.Gui/ViewBase/Layout/SuperViewChangedEventArgs.cs @@ -9,17 +9,17 @@ public class SuperViewChangedEventArgs : EventArgs /// Creates a new instance of the class. /// /// - public SuperViewChangedEventArgs (View superView, View subView) + public SuperViewChangedEventArgs (View? superView, View? subView) { SuperView = superView; SubView = subView; } /// The view that is having it's changed - public View SubView { get; } + public View? SubView { get; } /// /// The parent. For this is the old parent (new parent now being null). /// - public View SuperView { get; } + public View? SuperView { get; } } diff --git a/Terminal.Gui/ViewBase/MouseHeldDown.cs b/Terminal.Gui/ViewBase/MouseHeldDown.cs index ff0764733a..f902980a32 100644 --- a/Terminal.Gui/ViewBase/MouseHeldDown.cs +++ b/Terminal.Gui/ViewBase/MouseHeldDown.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.ComponentModel; namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/Navigation/FocusEventArgs.cs b/Terminal.Gui/ViewBase/Navigation/FocusEventArgs.cs index 55cc41c6e4..d58c68ee3b 100644 --- a/Terminal.Gui/ViewBase/Navigation/FocusEventArgs.cs +++ b/Terminal.Gui/ViewBase/Navigation/FocusEventArgs.cs @@ -10,16 +10,15 @@ public class HasFocusEventArgs : CancelEventArgs /// The value will have if the event is not cancelled. /// The view that is losing focus. /// The view that is gaining focus. - public HasFocusEventArgs (bool currentHasFocus, bool newHasFocus, View currentFocused, View newFocused) : base (ref currentHasFocus, ref newHasFocus) + public HasFocusEventArgs (bool currentHasFocus, bool newHasFocus, View? currentFocused, View? newFocused) : base (ref currentHasFocus, ref newHasFocus) { CurrentFocused = currentFocused; NewFocused = newFocused; } /// Gets or sets the view that is losing focus. - public View CurrentFocused { get; set; } + public View? CurrentFocused { get; set; } /// Gets or sets the view that is gaining focus. - public View NewFocused { get; set; } - -} \ No newline at end of file + public View? NewFocused { get; set; } +} diff --git a/Terminal.Gui/ViewBase/Orientation/IOrientation.cs b/Terminal.Gui/ViewBase/Orientation/IOrientation.cs index 4f86d21ddc..c56a0cfe51 100644 --- a/Terminal.Gui/ViewBase/Orientation/IOrientation.cs +++ b/Terminal.Gui/ViewBase/Orientation/IOrientation.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.ViewBase; using System; @@ -40,4 +40,4 @@ public interface IOrientation /// /// public void OnOrientationChanged (Orientation newOrientation) { return; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/ViewBase/Orientation/Orientation.cs b/Terminal.Gui/ViewBase/Orientation/Orientation.cs index fa4c766eef..5c847f57a7 100644 --- a/Terminal.Gui/ViewBase/Orientation/Orientation.cs +++ b/Terminal.Gui/ViewBase/Orientation/Orientation.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.ViewBase; /// Direction of an element (horizontal or vertical) diff --git a/Terminal.Gui/ViewBase/Orientation/OrientationHelper.cs b/Terminal.Gui/ViewBase/Orientation/OrientationHelper.cs index a7079128c0..dfd8a3d44e 100644 --- a/Terminal.Gui/ViewBase/Orientation/OrientationHelper.cs +++ b/Terminal.Gui/ViewBase/Orientation/OrientationHelper.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/View.Adornments.cs b/Terminal.Gui/ViewBase/View.Adornments.cs index de0ca20a1c..97d2da40c8 100644 --- a/Terminal.Gui/ViewBase/View.Adornments.cs +++ b/Terminal.Gui/ViewBase/View.Adornments.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.ViewBase; public partial class View // Adornments diff --git a/Terminal.Gui/ViewBase/View.Arrangement.cs b/Terminal.Gui/ViewBase/View.Arrangement.cs index dba4f6c83d..58323f11a8 100644 --- a/Terminal.Gui/ViewBase/View.Arrangement.cs +++ b/Terminal.Gui/ViewBase/View.Arrangement.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.ViewBase; +namespace Terminal.Gui.ViewBase; public partial class View { diff --git a/Terminal.Gui/ViewBase/View.Command.cs b/Terminal.Gui/ViewBase/View.Command.cs index 0c3a741ac6..ca8de67a16 100644 --- a/Terminal.Gui/ViewBase/View.Command.cs +++ b/Terminal.Gui/ViewBase/View.Command.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.ViewBase; public partial class View // Command APIs diff --git a/Terminal.Gui/ViewBase/View.Content.cs b/Terminal.Gui/ViewBase/View.Content.cs index cbb29308a2..8d6345a650 100644 --- a/Terminal.Gui/ViewBase/View.Content.cs +++ b/Terminal.Gui/ViewBase/View.Content.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.ViewBase; public partial class View diff --git a/Terminal.Gui/ViewBase/View.Cursor.cs b/Terminal.Gui/ViewBase/View.Cursor.cs index daaf75d69f..d710a273ef 100644 --- a/Terminal.Gui/ViewBase/View.Cursor.cs +++ b/Terminal.Gui/ViewBase/View.Cursor.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/View.Diagnostics.cs b/Terminal.Gui/ViewBase/View.Diagnostics.cs index d920ef4bfe..19f77eac7d 100644 --- a/Terminal.Gui/ViewBase/View.Diagnostics.cs +++ b/Terminal.Gui/ViewBase/View.Diagnostics.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.ViewBase; +namespace Terminal.Gui.ViewBase; public partial class View { diff --git a/Terminal.Gui/ViewBase/View.Drawing.Attribute.cs b/Terminal.Gui/ViewBase/View.Drawing.Attribute.cs index b43a426d2c..81167c439e 100644 --- a/Terminal.Gui/ViewBase/View.Drawing.Attribute.cs +++ b/Terminal.Gui/ViewBase/View.Drawing.Attribute.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.ComponentModel; namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/View.Drawing.Clipping.cs b/Terminal.Gui/ViewBase/View.Drawing.Clipping.cs index 49a0e1fe35..6a5bbeb67c 100644 --- a/Terminal.Gui/ViewBase/View.Drawing.Clipping.cs +++ b/Terminal.Gui/ViewBase/View.Drawing.Clipping.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.ViewBase; public partial class View @@ -16,7 +15,7 @@ public partial class View /// /// /// The current Clip. - public static Region? GetClip () { return Application.Driver?.Clip; } + public Region? GetClip () => Driver?.Clip; /// /// Sets the Clip to the specified region. @@ -28,11 +27,13 @@ public partial class View /// /// /// - public static void SetClip (Region? region) + public void SetClip (Region? region) { - if (Application.Driver is { } && region is { }) + // BUGBUG: If region is null we should set the clip to null. + // BUGBUG: Fixing this probably breaks other things. + if (Driver is { } && region is { }) { - Application.Driver.Clip = region; + Driver.Clip = region; } } @@ -51,13 +52,13 @@ public static void SetClip (Region? region) /// /// The current Clip, which can be then re-applied /// - public static Region? SetClipToScreen () + public Region? SetClipToScreen () { Region? previous = GetClip (); - if (Application.Driver is { }) + if (Driver is { }) { - Application.Driver.Clip = new (Application.Screen); + Driver.Clip = new (Driver!.Screen); } return previous; @@ -72,7 +73,7 @@ public static void SetClip (Region? region) /// /// /// - public static void ExcludeFromClip (Rectangle rectangle) { Application.Driver?.Clip?.Exclude (rectangle); } + public void ExcludeFromClip (Rectangle rectangle) { Driver?.Clip?.Exclude (rectangle); } /// /// Removes the specified rectangle from the Clip. @@ -83,7 +84,7 @@ public static void SetClip (Region? region) /// /// /// - public static void ExcludeFromClip (Region? region) { Application.Driver?.Clip?.Exclude (region); } + public void ExcludeFromClip (Region? region) { Driver?.Clip?.Exclude (region); } /// /// Changes the Clip to the intersection of the current Clip and the of this View. @@ -104,7 +105,7 @@ public static void SetClip (Region? region) return null; } - Region previous = GetClip () ?? new (Application.Screen); + Region previous = GetClip () ?? new (Driver.Screen); Region frameRegion = previous.Clone (); @@ -151,7 +152,7 @@ public static void SetClip (Region? region) return null; } - Region previous = GetClip () ?? new (Application.Screen); + Region previous = GetClip () ?? new (App!.Screen); Region viewportRegion = previous.Clone (); diff --git a/Terminal.Gui/ViewBase/View.Drawing.Primitives.cs b/Terminal.Gui/ViewBase/View.Drawing.Primitives.cs index d9d9333ee2..6b169718b6 100644 --- a/Terminal.Gui/ViewBase/View.Drawing.Primitives.cs +++ b/Terminal.Gui/ViewBase/View.Drawing.Primitives.cs @@ -63,7 +63,7 @@ public void AddRune (int col, int row, Rune rune) /// /// /// When the method returns, the draw position will be incremented by the number of columns - /// required, unless the new column value is outside the or . + /// required, unless the new column value is outside the or . /// /// If requires more columns than are available, the output will be clipped. /// @@ -121,8 +121,8 @@ public void DrawHotString (string text, bool focused) { DrawHotString ( text, - Enabled ? GetAttributeForRole (VisualRole.HotNormal) : GetScheme ()!.Disabled, - Enabled ? GetAttributeForRole (VisualRole.Normal) : GetScheme ()!.Disabled + Enabled ? GetAttributeForRole (VisualRole.HotNormal) : GetScheme ().Disabled, + Enabled ? GetAttributeForRole (VisualRole.Normal) : GetScheme ().Disabled ); } } @@ -137,7 +137,7 @@ public void FillRect (Rectangle rect, Color? color = null) return; } - Region prevClip = AddViewportToClip (); + Region? prevClip = AddViewportToClip (); Rectangle toClear = ViewportToScreen (rect); Attribute prev = SetAttribute (new (color ?? GetAttributeForRole (VisualRole.Normal).Background)); Driver.FillRect (toClear); @@ -155,7 +155,7 @@ public void FillRect (Rectangle rect, Rune rune) return; } - Region prevClip = AddViewportToClip (); + Region? prevClip = AddViewportToClip (); Rectangle toClear = ViewportToScreen (rect); Driver.FillRect (toClear, rune); SetClip (prevClip); diff --git a/Terminal.Gui/ViewBase/View.Drawing.Scheme.cs b/Terminal.Gui/ViewBase/View.Drawing.Scheme.cs index dcd28794be..5ad8c01018 100644 --- a/Terminal.Gui/ViewBase/View.Drawing.Scheme.cs +++ b/Terminal.Gui/ViewBase/View.Drawing.Scheme.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.ViewBase; +namespace Terminal.Gui.ViewBase; public partial class View { diff --git a/Terminal.Gui/ViewBase/View.Drawing.cs b/Terminal.Gui/ViewBase/View.Drawing.cs index 1f27561238..ed300ee5d9 100644 --- a/Terminal.Gui/ViewBase/View.Drawing.cs +++ b/Terminal.Gui/ViewBase/View.Drawing.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.ComponentModel; using System.Diagnostics; namespace Terminal.Gui.ViewBase; @@ -206,7 +205,7 @@ internal void DoDrawAdornments (Region? originalClip) if (Margin?.NeedsLayout == true) { Margin.NeedsLayout = false; - Margin?.Thickness.Draw (FrameToScreen ()); + Margin?.Thickness.Draw (Driver, FrameToScreen ()); Margin?.Parent?.SetSubViewNeedsDraw (); } @@ -446,12 +445,15 @@ public void DrawText (DrawContext? context = null) // Report the drawn area to the context context?.AddDrawnRegion (textRegion); - TextFormatter?.Draw ( - drawRect, - HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal), - HasFocus ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal), - Rectangle.Empty - ); + if (Driver is { }) + { + TextFormatter?.Draw ( + Driver, + drawRect, + HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal), + HasFocus ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal), + Rectangle.Empty); + } // We assume that the text has been drawn over the entire area; ensure that the subviews are redrawn. SetSubViewNeedsDraw (); diff --git a/Terminal.Gui/ViewBase/View.Hierarchy.cs b/Terminal.Gui/ViewBase/View.Hierarchy.cs index a80193c385..b88a071992 100644 --- a/Terminal.Gui/ViewBase/View.Hierarchy.cs +++ b/Terminal.Gui/ViewBase/View.Hierarchy.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -363,12 +362,12 @@ public virtual IReadOnlyCollection RemoveAll () where TView : View #endregion AddRemove - // TODO: This drives a weird coupling of Application.Top and View. It's not clear why this is needed. + // TODO: This drives a weird coupling of Application.Current and View. It's not clear why this is needed. /// Get the top superview of a given . /// The superview view. internal View? GetTopSuperView (View? view = null, View? superview = null) { - View? top = superview ?? Application.Top; + View? top = superview ?? App?.Current; for (View? v = view?.SuperView ?? this?.SuperView; v != null; v = v.SuperView) { diff --git a/Terminal.Gui/ViewBase/View.Keyboard.cs b/Terminal.Gui/ViewBase/View.Keyboard.cs index 51350dca73..8f9a5127ae 100644 --- a/Terminal.Gui/ViewBase/View.Keyboard.cs +++ b/Terminal.Gui/ViewBase/View.Keyboard.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.ViewBase; public partial class View // Keyboard APIs diff --git a/Terminal.Gui/ViewBase/View.Layout.cs b/Terminal.Gui/ViewBase/View.Layout.cs index 0c73aacbb4..ecd58e7377 100644 --- a/Terminal.Gui/ViewBase/View.Layout.cs +++ b/Terminal.Gui/ViewBase/View.Layout.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; namespace Terminal.Gui.ViewBase; @@ -438,10 +437,10 @@ public Dim Width private void NeedsClearScreenNextIteration () { - if (Application.Top is { } && Application.Top == this && Application.TopLevels.Count == 1) + if (App is { Current: { } } && App.Current == this && App.SessionStack.Count == 1) { // If this is the only TopLevel, we need to redraw the screen - Application.ClearScreenNextIteration = true; + App.ClearScreenNextIteration = true; } } @@ -532,7 +531,7 @@ public bool Layout (Size contentSize) /// /// Performs layout of the view and its subviews using the content size of either the or - /// . + /// . /// /// /// @@ -545,7 +544,7 @@ public bool Layout (Size contentSize) /// /// /// If the view could not be laid out (typically because dependency was not ready). - public bool Layout () { return Layout (GetContainerSize ()); } + public bool Layout () => Layout (GetContainerSize ()); /// /// Sets the position and size of this view, relative to the SuperView's ContentSize (nominally the same as @@ -1114,11 +1113,12 @@ private Size GetContainerSize () { // TODO: Get rid of refs to Top Size superViewContentSize = SuperView?.GetContentSize () - ?? (Application.Top is { } && Application.Top != this && Application.Top.IsInitialized - ? Application.Top.GetContentSize () - : Application.Screen.Size); + ?? (App?.Current is { } && App?.Current != this && App!.Current.IsInitialized + ? App.Current.GetContentSize () + : App?.Screen.Size ?? new (2048, 2048)); return superViewContentSize; + } // BUGBUG: This method interferes with Dialog/MessageBox default min/max size. @@ -1130,7 +1130,7 @@ private Size GetContainerSize () /// /// /// If does not have a or it's SuperView is not - /// the position will be bound by . + /// the position will be bound by . /// /// The View that is to be moved. /// The target x location. @@ -1138,7 +1138,7 @@ private Size GetContainerSize () /// The new x location that will ensure will be fully visible. /// The new y location that will ensure will be fully visible. /// - /// Either (if does not have a Super View) or + /// Either (if does not have a Super View) or /// 's SuperView. This can be used to ensure LayoutSubViews is called on the correct View. /// internal static View? GetLocationEnsuringFullVisibility ( @@ -1152,10 +1152,12 @@ out int ny int maxDimension; View? superView; - if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top) + IApplication? app = viewToMove.App; + + if (viewToMove?.SuperView is null || viewToMove == app?.Current || viewToMove?.SuperView == app?.Current) { - maxDimension = Application.Screen.Width; - superView = Application.Top; + maxDimension = app?.Screen.Width ?? 0; + superView = app?.Current; } else { @@ -1188,9 +1190,9 @@ out int ny var menuVisible = false; var statusVisible = false; - if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top) + if (viewToMove?.SuperView is null || viewToMove == app?.Current || viewToMove?.SuperView == app?.Current) { - menuVisible = Application.Top?.MenuBar?.Visible == true; + menuVisible = app?.Current?.MenuBar?.Visible == true; } else { @@ -1207,7 +1209,7 @@ out int ny } } - if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top) + if (viewToMove?.SuperView is null || viewToMove == app?.Current || viewToMove?.SuperView == app?.Current) { maxDimension = menuVisible ? 1 : 0; } @@ -1218,9 +1220,16 @@ out int ny ny = Math.Max (targetY, maxDimension); - if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top) + if (viewToMove?.SuperView is null || viewToMove == app?.Current || viewToMove?.SuperView == app?.Current) { - maxDimension = statusVisible ? Application.Screen.Height - 1 : Application.Screen.Height; + if (app is { }) + { + maxDimension = statusVisible ? app.Screen.Height - 1 : app.Screen.Height; + } + else + { + maxDimension = 0; + } } else { @@ -1267,10 +1276,10 @@ out int ny /// /// flags set in their ViewportSettings. /// - public static List GetViewsUnderLocation (in Point screenLocation, ViewportSettingsFlags excludeViewportSettingsFlags) + public List GetViewsUnderLocation (in Point screenLocation, ViewportSettingsFlags excludeViewportSettingsFlags) { // PopoverHost - If visible, start with it instead of Top - if (Application.Popover?.GetActivePopover () is View { Visible: true } visiblePopover) + if (App?.Popover?.GetActivePopover () is View { Visible: true } visiblePopover) { // BUGBUG: We do not traverse all visible toplevels if there's an active popover. This may be a bug. List result = []; @@ -1286,9 +1295,9 @@ out int ny var checkedTop = false; // Traverse all visible toplevels, topmost first (reverse stack order) - if (Application.TopLevels.Count > 0) + if (App?.SessionStack.Count > 0) { - foreach (Toplevel toplevel in Application.TopLevels) + foreach (Toplevel toplevel in App.SessionStack) { if (toplevel.Visible && toplevel.Contains (screenLocation)) { @@ -1301,7 +1310,7 @@ out int ny } } - if (toplevel == Application.Top) + if (toplevel == App.Current) { checkedTop = true; } @@ -1309,7 +1318,7 @@ out int ny } // Fallback: If TopLevels is empty or Top is not in TopLevels, check Top directly (for test compatibility) - if (!checkedTop && Application.Top is { Visible: true } top) + if (!checkedTop && App?.Current is { Visible: true } top) { // For root toplevels, allow hit-testing even if location is outside bounds (for drag/move) List result = GetViewsUnderLocation (top, screenLocation, excludeViewportSettingsFlags); diff --git a/Terminal.Gui/ViewBase/View.Mouse.cs b/Terminal.Gui/ViewBase/View.Mouse.cs index 546017059c..c899c2d2e6 100644 --- a/Terminal.Gui/ViewBase/View.Mouse.cs +++ b/Terminal.Gui/ViewBase/View.Mouse.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.ComponentModel; namespace Terminal.Gui.ViewBase; @@ -16,7 +15,7 @@ public partial class View // Mouse APIs private void SetupMouse () { - MouseHeldDown = new MouseHeldDown (this, Application.TimedEvents,Application.Mouse); + MouseHeldDown = new MouseHeldDown (this, App?.TimedEvents, App?.Mouse); MouseBindings = new (); // TODO: Should the default really work with any button or just button1? @@ -54,7 +53,7 @@ private void SetupMouse () #region MouseEnterLeave /// - /// INTERNAL Called by when the mouse moves over the View's + /// INTERNAL Called by when the mouse moves over the View's /// . /// will /// be raised when the mouse is no longer over the . If another View occludes this View, the @@ -151,7 +150,7 @@ private void SetupMouse () public event EventHandler? MouseEnter; /// - /// INTERNAL Called by when the mouse leaves , or is + /// INTERNAL Called by when the mouse leaves , or is /// occluded /// by another non-SubView. /// @@ -228,7 +227,7 @@ protected virtual void OnMouseLeave () { } public bool WantMousePositionReports { get; set; } /// - /// Processes a new . This method is called by when a + /// Processes a new . This method is called by when a /// mouse /// event occurs. /// @@ -375,7 +374,7 @@ internal bool WhenGrabbedHandleReleased (MouseEventArgs mouseEvent) if (mouseEvent.IsReleased) { - if (Application.Mouse.MouseGrabView == this) + if (App?.Mouse.MouseGrabView == this) { //Logging.Debug ($"{Id} - {MouseState}"); MouseState &= ~MouseState.Pressed; @@ -407,9 +406,9 @@ private bool WhenGrabbedHandlePressed (MouseEventArgs mouseEvent) if (mouseEvent.IsPressed) { // The first time we get pressed event, grab the mouse and set focus - if (Application.Mouse.MouseGrabView != this) + if (App?.Mouse.MouseGrabView != this) { - Application.Mouse.GrabMouse (this); + App?.Mouse.GrabMouse (this); if (!HasFocus && CanFocus) { @@ -541,10 +540,10 @@ internal bool WhenGrabbedHandleClicked (MouseEventArgs mouseEvent) { mouseEvent.Handled = false; - if (Application.Mouse.MouseGrabView == this && mouseEvent.IsSingleClicked) + if (App?.Mouse.MouseGrabView == this && mouseEvent.IsSingleClicked) { // We're grabbed. Clicked event comes after the last Release. This is our signal to ungrab - Application.Mouse.UngrabMouse (); + App?.Mouse.UngrabMouse (); // TODO: Prove we need to unset MouseState.Pressed and MouseState.PressedOutside here // TODO: There may be perf gains if we don't unset these flags here @@ -695,4 +694,4 @@ protected virtual void OnMouseStateChanged (EventArgs args) { } #endregion MouseState Handling private void DisposeMouse () { } -} \ No newline at end of file +} diff --git a/Terminal.Gui/ViewBase/View.Navigation.cs b/Terminal.Gui/ViewBase/View.Navigation.cs index 516aef3c07..5ff60aa7e5 100644 --- a/Terminal.Gui/ViewBase/View.Navigation.cs +++ b/Terminal.Gui/ViewBase/View.Navigation.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; @@ -396,7 +395,7 @@ protected virtual void OnFocusedChanged (View? previousFocused, View? focused) { public event EventHandler? FocusedChanged; /// Returns a value indicating if this View is currently on Top (Active) - public bool IsCurrentTop => Application.Top == this; + public bool IsCurrentTop => App?.Current == this; /// /// Returns the most focused SubView down the subview-hierarchy. @@ -520,7 +519,7 @@ public bool HasFocus if (value) { // NOTE: If Application.Navigation is null, we pass null to FocusChanging. For unit tests. - (bool focusSet, bool _) = SetHasFocusTrue (Application.Navigation?.GetFocused ()); + (bool focusSet, bool _) = SetHasFocusTrue (App?.Navigation?.GetFocused ()); if (focusSet) { @@ -557,7 +556,7 @@ public bool HasFocus /// if the focus changed; false otherwise. public bool SetFocus () { - (bool focusSet, bool _) = SetHasFocusTrue (Application.Navigation?.GetFocused ()); + (bool focusSet, bool _) = SetHasFocusTrue (App?.Navigation?.GetFocused ()); return focusSet; } @@ -722,17 +721,17 @@ private bool RaiseFocusChanging (bool currentHasFocus, bool newHasFocus, View? c return true; } - View? appFocused = Application.Navigation?.GetFocused (); + View? appFocused = App?.Navigation?.GetFocused (); if (appFocused == currentFocused) { if (newFocused is { HasFocus: true }) { - Application.Navigation?.SetFocused (newFocused); + App?.Navigation?.SetFocused (newFocused); } else { - Application.Navigation?.SetFocused (null); + App?.Navigation?.SetFocused (null); } } @@ -835,7 +834,7 @@ private void SetHasFocusFalse (View? newFocusedView, bool traversingDown = false } // Application.Navigation.GetFocused? - View? applicationFocused = Application.Navigation?.GetFocused (); + View? applicationFocused = App?.Navigation?.GetFocused (); if (newFocusedView is null && applicationFocused != this && applicationFocused is { CanFocus: true }) { @@ -854,18 +853,18 @@ private void SetHasFocusFalse (View? newFocusedView, bool traversingDown = false } } - // Application.Top? - if (newFocusedView is null && Application.Top is { CanFocus: true, HasFocus: false }) + // Application.Current? + if (newFocusedView is null && App?.Current is { CanFocus: true, HasFocus: false }) { // Temporarily ensure this view can't get focus bool prevCanFocus = _canFocus; _canFocus = false; - bool restoredFocus = Application.Top.RestoreFocus (); + bool restoredFocus = App?.Current.RestoreFocus () ?? false; _canFocus = prevCanFocus; - if (Application.Top is { CanFocus: true, HasFocus: true }) + if (App?.Current is { CanFocus: true, HasFocus: true }) { - newFocusedView = Application.Top; + newFocusedView = App?.Current; } else if (restoredFocus) { @@ -952,7 +951,7 @@ private void RaiseFocusChanged (bool newHasFocus, View? previousFocusedView, Vie // If we are the most focused view, we need to set the focused view in Application.Navigation if (newHasFocus && focusedView?.Focused is null) { - Application.Navigation?.SetFocused (focusedView); + App?.Navigation?.SetFocused (focusedView); } // Call the virtual method diff --git a/Terminal.Gui/ViewBase/View.ScrollBars.cs b/Terminal.Gui/ViewBase/View.ScrollBars.cs index 4463299f93..fd5f6a3273 100644 --- a/Terminal.Gui/ViewBase/View.ScrollBars.cs +++ b/Terminal.Gui/ViewBase/View.ScrollBars.cs @@ -1,5 +1,4 @@ -#nullable enable - + namespace Terminal.Gui.ViewBase; public partial class View diff --git a/Terminal.Gui/ViewBase/View.Text.cs b/Terminal.Gui/ViewBase/View.Text.cs index 32694d33fa..b5b401f81f 100644 --- a/Terminal.Gui/ViewBase/View.Text.cs +++ b/Terminal.Gui/ViewBase/View.Text.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.ViewBase; diff --git a/Terminal.Gui/ViewBase/View.cs b/Terminal.Gui/ViewBase/View.cs index 5aa8a6e1dd..68325dfe9b 100644 --- a/Terminal.Gui/ViewBase/View.cs +++ b/Terminal.Gui/ViewBase/View.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.ComponentModel; using System.Diagnostics; @@ -52,7 +51,7 @@ public void Dispose () /// Pretty prints the View /// - public override string ToString () { return $"{GetType ().Name}({Id}){Frame}"; } + public override string ToString () => $"{GetType ().Name}({Id}){Frame}"; /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -72,9 +71,9 @@ protected virtual void Dispose (bool disposing) DisposeAdornments (); DisposeScrollBars (); - if (Application.Mouse.MouseGrabView == this) + if (App?.Mouse.MouseGrabView == this) { - Application.Mouse.UngrabMouse (); + App.Mouse.UngrabMouse (); } for (int i = InternalSubViews.Count - 1; i >= 0; i--) @@ -109,6 +108,34 @@ protected virtual void Dispose (bool disposing) /// The id should be unique across all Views that share a SuperView. public string Id { get; set; } = ""; + private IApplication? _app; + + /// + /// Gets the instance this view is running in. If this view is at the top of the view + /// hierarchy, returns . + /// + /// + /// + /// If not explicitly set on an instance, this property will retrieve the value from the view at the top + /// of the View hierarchy (the top-most SuperView). + /// + /// + public IApplication? App + { + get => GetApp (); + internal set => _app = value; + } + + /// + /// Gets the instance this view is running in. Used internally to allow overrides by + /// . + /// + /// + /// If this view is at the top of the view hierarchy, and was not explicitly set, + /// returns . + /// + protected virtual IApplication? GetApp () => _app ?? SuperView?.App ?? null; + private IDriver? _driver; /// @@ -118,19 +145,21 @@ protected virtual void Dispose (bool disposing) /// internal IDriver? Driver { - get - { - if (_driver is { }) - { - return _driver; - } - - return Application.Driver; - } + get => GetDriver (); set => _driver = value; } - /// Gets the screen buffer contents. This is a convenience property for Views that need direct access to the screen buffer. + /// + /// Gets the instance for this view. Used internally to allow overrides by + /// . + /// + /// If this view is at the top of the view hierarchy, returns . + protected virtual IDriver? GetDriver () => _driver ?? App?.Driver ?? SuperView?.Driver /*?? ApplicationImpl.Instance.Driver*/; + + /// + /// Gets the screen buffer contents. This is a convenience property for Views that need direct access to the + /// screen buffer. + /// protected Cell [,]? ScreenContents => Driver?.Contents; /// Initializes a new instance of . @@ -387,7 +416,7 @@ public virtual bool Visible } /// Called when is changing. Can be cancelled by returning . - protected virtual bool OnVisibleChanging () { return false; } + protected virtual bool OnVisibleChanging () => false; /// /// Raised when the value is being changed. Can be cancelled by setting Cancel to diff --git a/Terminal.Gui/ViewBase/ViewDiagnosticFlags.cs b/Terminal.Gui/ViewBase/ViewDiagnosticFlags.cs index 345ec3c788..94959a874c 100644 --- a/Terminal.Gui/ViewBase/ViewDiagnosticFlags.cs +++ b/Terminal.Gui/ViewBase/ViewDiagnosticFlags.cs @@ -1,5 +1,4 @@ -#nullable enable -namespace Terminal.Gui.ViewBase; +namespace Terminal.Gui.ViewBase; /// Enables diagnostic functions for . [Flags] diff --git a/Terminal.Gui/ViewBase/ViewEventArgs.cs b/Terminal.Gui/ViewBase/ViewEventArgs.cs index d2de59ec05..e5cc5e36c3 100644 --- a/Terminal.Gui/ViewBase/ViewEventArgs.cs +++ b/Terminal.Gui/ViewBase/ViewEventArgs.cs @@ -13,4 +13,4 @@ public class ViewEventArgs : EventArgs /// child then sender may be the parent while is the child being added. /// public View View { get; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Autocomplete/AppendAutocomplete.cs b/Terminal.Gui/Views/Autocomplete/AppendAutocomplete.cs index b539716247..f8452862e7 100644 --- a/Terminal.Gui/Views/Autocomplete/AppendAutocomplete.cs +++ b/Terminal.Gui/Views/Autocomplete/AppendAutocomplete.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; @@ -8,12 +9,12 @@ namespace Terminal.Gui.Views; public class AppendAutocomplete : AutocompleteBase { private bool _suspendSuggestions; - private TextField textField; + private TextField _textField; /// Creates a new instance of the class. public AppendAutocomplete (TextField textField) { - this.textField = textField; + _textField = textField; base.SelectionKey = KeyCode.Tab; Scheme = new Scheme @@ -35,15 +36,15 @@ public AppendAutocomplete (TextField textField) /// public override View HostControl { - get => textField; - set => textField = (TextField)value; + get => _textField; + set => _textField = (TextField)value; } /// public override void ClearSuggestions () { base.ClearSuggestions (); - textField.SetNeedsDraw (); + _textField.SetNeedsDraw (); } /// @@ -107,19 +108,19 @@ public override void RenderOverlay (Point renderAt) } // draw it like it's selected, even though it's not - textField.SetAttribute ( + _textField.SetAttribute ( new Attribute ( Scheme.Normal.Foreground, - textField.GetAttributeForRole(VisualRole.Focus).Background, + _textField.GetAttributeForRole(VisualRole.Focus).Background, Scheme.Normal.Style ) ); - textField.Move (textField.Text.Length, 0); + _textField.Move (_textField.Text.Length, 0); Suggestion suggestion = Suggestions.ElementAt (SelectedIdx); string fragment = suggestion.Replacement.Substring (suggestion.Remove); - int spaceAvailable = textField.Viewport.Width - textField.Text.GetColumns (); + int spaceAvailable = _textField.Viewport.Width - _textField.Text.GetColumns (); int spaceRequired = fragment.EnumerateRunes ().Sum (c => c.GetColumns ()); if (spaceAvailable < spaceRequired) @@ -130,7 +131,7 @@ public override void RenderOverlay (Point renderAt) ); } - Application.Driver?.AddStr (fragment); + _textField.Driver?.AddStr (fragment); } /// @@ -143,12 +144,12 @@ internal bool AcceptSelectionIfAny () if (MakingSuggestion ()) { Suggestion insert = Suggestions.ElementAt (SelectedIdx); - string newText = textField.Text; + string newText = _textField.Text; newText = newText.Substring (0, newText.Length - insert.Remove); newText += insert.Replacement; - textField.Text = newText; + _textField.Text = newText; - textField.MoveEnd (); + _textField.MoveEnd (); ClearSuggestions (); @@ -167,8 +168,8 @@ internal void SetTextTo (FileSystemInfo fileSystemInfo) newText += Path.DirectorySeparatorChar; } - textField.Text = newText; - textField.MoveEnd (); + _textField.Text = newText; + _textField.MoveEnd (); } private bool CycleSuggestion (int direction) @@ -185,7 +186,7 @@ private bool CycleSuggestion (int direction) SelectedIdx = Suggestions.Count () - 1; } - textField.SetNeedsDraw (); + _textField.SetNeedsDraw (); return true; } @@ -195,5 +196,5 @@ private bool CycleSuggestion (int direction) /// to see auto-complete (i.e. focused and cursor in right place). /// /// - private bool MakingSuggestion () { return Suggestions.Any () && SelectedIdx != -1 && textField.HasFocus && textField.CursorIsAtEnd (); } + private bool MakingSuggestion () { return Suggestions.Any () && SelectedIdx != -1 && _textField.HasFocus && _textField.CursorIsAtEnd (); } } diff --git a/Terminal.Gui/Views/Autocomplete/AutocompleteBase.cs b/Terminal.Gui/Views/Autocomplete/AutocompleteBase.cs index f2122fa4b6..d8abc050fe 100644 --- a/Terminal.Gui/Views/Autocomplete/AutocompleteBase.cs +++ b/Terminal.Gui/Views/Autocomplete/AutocompleteBase.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Collections.ObjectModel; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Autocomplete/AutocompleteContext.cs b/Terminal.Gui/Views/Autocomplete/AutocompleteContext.cs index d2e7e4d597..bd9f327129 100644 --- a/Terminal.Gui/Views/Autocomplete/AutocompleteContext.cs +++ b/Terminal.Gui/Views/Autocomplete/AutocompleteContext.cs @@ -1,4 +1,5 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs b/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs index 6c3e12c34d..80481a787d 100644 --- a/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs +++ b/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs @@ -1,3 +1,4 @@ +#nullable disable using System.IO.Abstractions; using System.Runtime.InteropServices; diff --git a/Terminal.Gui/Views/Autocomplete/IAutocomplete.cs b/Terminal.Gui/Views/Autocomplete/IAutocomplete.cs index 717122e9de..ce15a641ee 100644 --- a/Terminal.Gui/Views/Autocomplete/IAutocomplete.cs +++ b/Terminal.Gui/Views/Autocomplete/IAutocomplete.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Collections.ObjectModel; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Autocomplete/ISuggestionGenerator.cs b/Terminal.Gui/Views/Autocomplete/ISuggestionGenerator.cs index 08fd17c9a2..e8eedd2ee1 100644 --- a/Terminal.Gui/Views/Autocomplete/ISuggestionGenerator.cs +++ b/Terminal.Gui/Views/Autocomplete/ISuggestionGenerator.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Generates autocomplete based on a given cursor location within a string diff --git a/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.PopUp.cs b/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.PopUp.cs index b7a67726e7..18bd89b521 100644 --- a/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.PopUp.cs +++ b/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.PopUp.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs b/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs index 602a849d7f..87f39fc2b0 100644 --- a/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs +++ b/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; - +#nullable disable namespace Terminal.Gui.Views; /// @@ -125,7 +124,7 @@ public override bool OnMouseEvent (MouseEventArgs me, bool fromHost = false) { Visible = true; HostControl?.SetNeedsDraw (); - Application.Mouse.UngrabMouse (); + HostControl?.App?.Mouse.UngrabMouse (); return false; } @@ -137,7 +136,7 @@ public override bool OnMouseEvent (MouseEventArgs me, bool fromHost = false) _closed = false; } - HostControl?.SetNeedsDraw (); + HostControl.SetNeedsDraw (); return false; } @@ -406,7 +405,7 @@ public override void RenderOverlay (Point renderAt) string text = TextFormatter.ClipOrPad (toRender [i].Title, width); - Application.Driver?.AddStr (text); + _popup.App?.Driver?.AddStr (text); } } @@ -544,7 +543,6 @@ protected bool Select () /// protected abstract void SetCursorPosition (int column); -#nullable enable private Point? LastPopupPos { get; set; } #nullable restore diff --git a/Terminal.Gui/Views/Autocomplete/SingleWordSuggestionGenerator.cs b/Terminal.Gui/Views/Autocomplete/SingleWordSuggestionGenerator.cs index 1accda9222..59bde624b3 100644 --- a/Terminal.Gui/Views/Autocomplete/SingleWordSuggestionGenerator.cs +++ b/Terminal.Gui/Views/Autocomplete/SingleWordSuggestionGenerator.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/Autocomplete/Suggestion.cs b/Terminal.Gui/Views/Autocomplete/Suggestion.cs index 7b7bbd12a4..a1facdc38b 100644 --- a/Terminal.Gui/Views/Autocomplete/Suggestion.cs +++ b/Terminal.Gui/Views/Autocomplete/Suggestion.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// A replacement suggestion made by diff --git a/Terminal.Gui/Views/Bar.cs b/Terminal.Gui/Views/Bar.cs index 55e4a79142..3c04a16dc4 100644 --- a/Terminal.Gui/Views/Bar.cs +++ b/Terminal.Gui/Views/Bar.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Views; /// @@ -33,13 +31,14 @@ public Bar (IEnumerable? shortcuts) // Initialized += Bar_Initialized; MouseEvent += OnMouseEvent; + if (shortcuts is null) + { + return; + } - if (shortcuts is { }) + foreach (View shortcut in shortcuts) { - foreach (View shortcut in shortcuts) - { - Add (shortcut); - } + base.Add (shortcut); } } @@ -107,7 +106,7 @@ public Orientation Orientation public void OnOrientationChanged (Orientation newOrientation) { // BUGBUG: this should not be SuperView.GetContentSize - LayoutBarItems (SuperView?.GetContentSize () ?? Application.Screen.Size); + LayoutBarItems (SuperView?.GetContentSize () ?? App?.Screen.Size ?? Size.Empty); } #endregion @@ -245,7 +244,7 @@ private void LayoutBarItems (Size contentSize) barItem.X = 0; scBarItem.MinimumKeyTextSize = minKeyWidth; scBarItem.Width = scBarItem.GetWidthDimAuto (); - barItem.Layout (Application.Screen.Size); + barItem.Layout (App?.Screen.Size ?? Size.Empty); maxBarItemWidth = Math.Max (maxBarItemWidth, barItem.Frame.Width); } diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 942e97b118..626af1852a 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -1,4 +1,5 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs index 02308ac1e0..bf54252cf8 100644 --- a/Terminal.Gui/Views/CharMap/CharMap.cs +++ b/Terminal.Gui/Views/CharMap/CharMap.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -384,12 +383,12 @@ private void ShowDetails () try { decResponse = await client.GetCodepointDec (SelectedCodePoint).ConfigureAwait (false); - Application.Invoke (() => waitIndicator.RequestStop ()); + Application.Invoke ((_) => waitIndicator.RequestStop ()); } catch (HttpRequestException e) { getCodePointError = errorLabel.Text = e.Message; - Application.Invoke (() => waitIndicator.RequestStop ()); + Application.Invoke ((_) => waitIndicator.RequestStop ()); } }; Application.Run (waitIndicator); @@ -972,7 +971,7 @@ public static string ToCamelCase (string str) // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused // and the context menu is disposed when it is closed. - Application.Popover?.Register (contextMenu); + App!.Popover?.Register (contextMenu); contextMenu?.MakeVisible (ViewportToScreen (GetCursor (SelectedCodePoint))); diff --git a/Terminal.Gui/Views/CharMap/UcdApiClient.cs b/Terminal.Gui/Views/CharMap/UcdApiClient.cs index ae0af4838f..a5abaf9945 100644 --- a/Terminal.Gui/Views/CharMap/UcdApiClient.cs +++ b/Terminal.Gui/Views/CharMap/UcdApiClient.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/CharMap/UnicodeRange.cs b/Terminal.Gui/Views/CharMap/UnicodeRange.cs index 24f7378b25..4904d1751a 100644 --- a/Terminal.Gui/Views/CharMap/UnicodeRange.cs +++ b/Terminal.Gui/Views/CharMap/UnicodeRange.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Reflection; using System.Text.Unicode; diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs index d5801f9f8f..2a05354694 100644 --- a/Terminal.Gui/Views/CheckBox.cs +++ b/Terminal.Gui/Views/CheckBox.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/CheckState.cs b/Terminal.Gui/Views/CheckState.cs index 2b001ddeae..494df666b8 100644 --- a/Terminal.Gui/Views/CheckState.cs +++ b/Terminal.Gui/Views/CheckState.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/CollectionNavigation/CollectionNavigator.cs b/Terminal.Gui/Views/CollectionNavigation/CollectionNavigator.cs index a12d769c5c..c0eb7a3127 100644 --- a/Terminal.Gui/Views/CollectionNavigation/CollectionNavigator.cs +++ b/Terminal.Gui/Views/CollectionNavigation/CollectionNavigator.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Collections; namespace Terminal.Gui.Views; @@ -21,4 +22,4 @@ public CollectionNavigator () { } /// protected override int GetCollectionLength () { return Collection.Count; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/CollectionNavigation/CollectionNavigatorBase.cs b/Terminal.Gui/Views/CollectionNavigation/CollectionNavigatorBase.cs index 274d326223..39234b10be 100644 --- a/Terminal.Gui/Views/CollectionNavigation/CollectionNavigatorBase.cs +++ b/Terminal.Gui/Views/CollectionNavigation/CollectionNavigatorBase.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; @@ -202,4 +202,4 @@ private void ClearSearchString () SearchString = ""; _lastKeystroke = DateTime.Now; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/CollectionNavigation/DefaultCollectionNavigatorMatcher.cs b/Terminal.Gui/Views/CollectionNavigation/DefaultCollectionNavigatorMatcher.cs index 20bee68094..ed910a828b 100644 --- a/Terminal.Gui/Views/CollectionNavigation/DefaultCollectionNavigatorMatcher.cs +++ b/Terminal.Gui/Views/CollectionNavigation/DefaultCollectionNavigatorMatcher.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/CollectionNavigation/ICollectionNavigator.cs b/Terminal.Gui/Views/CollectionNavigation/ICollectionNavigator.cs index 85a68d3000..5ea6216463 100644 --- a/Terminal.Gui/Views/CollectionNavigation/ICollectionNavigator.cs +++ b/Terminal.Gui/Views/CollectionNavigation/ICollectionNavigator.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/CollectionNavigation/ICollectionNavigatorMatcher.cs b/Terminal.Gui/Views/CollectionNavigation/ICollectionNavigatorMatcher.cs index f45b59c0f5..420c496745 100644 --- a/Terminal.Gui/Views/CollectionNavigation/ICollectionNavigatorMatcher.cs +++ b/Terminal.Gui/Views/CollectionNavigation/ICollectionNavigatorMatcher.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/CollectionNavigation/IListCollectionNavigator.cs b/Terminal.Gui/Views/CollectionNavigation/IListCollectionNavigator.cs index f89e3f7c47..b382bc6273 100644 --- a/Terminal.Gui/Views/CollectionNavigation/IListCollectionNavigator.cs +++ b/Terminal.Gui/Views/CollectionNavigation/IListCollectionNavigator.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Collections; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/CollectionNavigation/TableCollectionNavigator.cs b/Terminal.Gui/Views/CollectionNavigation/TableCollectionNavigator.cs index 69a817e50b..21e3ce7d10 100644 --- a/Terminal.Gui/Views/CollectionNavigation/TableCollectionNavigator.cs +++ b/Terminal.Gui/Views/CollectionNavigation/TableCollectionNavigator.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Collection navigator for cycling selections in a . diff --git a/Terminal.Gui/Views/Color/BBar.cs b/Terminal.Gui/Views/Color/BBar.cs index b59b5eecb6..11c5cc9df9 100644 --- a/Terminal.Gui/Views/Color/BBar.cs +++ b/Terminal.Gui/Views/Color/BBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; diff --git a/Terminal.Gui/Views/Color/ColorBar.cs b/Terminal.Gui/Views/Color/ColorBar.cs index ad15e231f0..2be624be55 100644 --- a/Terminal.Gui/Views/Color/ColorBar.cs +++ b/Terminal.Gui/Views/Color/ColorBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; diff --git a/Terminal.Gui/Views/Color/ColorModelStrategy.cs b/Terminal.Gui/Views/Color/ColorModelStrategy.cs index eb3f156083..0f803be184 100644 --- a/Terminal.Gui/Views/Color/ColorModelStrategy.cs +++ b/Terminal.Gui/Views/Color/ColorModelStrategy.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; using ColorConverter = ColorHelper.ColorConverter; diff --git a/Terminal.Gui/Views/Color/ColorPicker.16.cs b/Terminal.Gui/Views/Color/ColorPicker.16.cs index 8c76f26486..6c1eda4cb2 100644 --- a/Terminal.Gui/Views/Color/ColorPicker.16.cs +++ b/Terminal.Gui/Views/Color/ColorPicker.16.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Color/ColorPicker.Prompt.cs b/Terminal.Gui/Views/Color/ColorPicker.Prompt.cs index e6c29172c9..400040e63c 100644 --- a/Terminal.Gui/Views/Color/ColorPicker.Prompt.cs +++ b/Terminal.Gui/Views/Color/ColorPicker.Prompt.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Color/ColorPicker.Style.cs b/Terminal.Gui/Views/Color/ColorPicker.Style.cs index 6166871564..afab89d5a9 100644 --- a/Terminal.Gui/Views/Color/ColorPicker.Style.cs +++ b/Terminal.Gui/Views/Color/ColorPicker.Style.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Color/ColorPicker.cs b/Terminal.Gui/Views/Color/ColorPicker.cs index 2a60e536b2..f0cdc76b9d 100644 --- a/Terminal.Gui/Views/Color/ColorPicker.cs +++ b/Terminal.Gui/Views/Color/ColorPicker.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Color/GBar.cs b/Terminal.Gui/Views/Color/GBar.cs index ac9a7227fe..b9dd5b4355 100644 --- a/Terminal.Gui/Views/Color/GBar.cs +++ b/Terminal.Gui/Views/Color/GBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; diff --git a/Terminal.Gui/Views/Color/HueBar.cs b/Terminal.Gui/Views/Color/HueBar.cs index 9f7d29e450..a0b0b554de 100644 --- a/Terminal.Gui/Views/Color/HueBar.cs +++ b/Terminal.Gui/Views/Color/HueBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; using ColorConverter = ColorHelper.ColorConverter; diff --git a/Terminal.Gui/Views/Color/IColorBar.cs b/Terminal.Gui/Views/Color/IColorBar.cs index b8139b8d54..176edead9d 100644 --- a/Terminal.Gui/Views/Color/IColorBar.cs +++ b/Terminal.Gui/Views/Color/IColorBar.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; internal interface IColorBar diff --git a/Terminal.Gui/Views/Color/LightnessBar.cs b/Terminal.Gui/Views/Color/LightnessBar.cs index 0f176a3f69..f3d1e3942c 100644 --- a/Terminal.Gui/Views/Color/LightnessBar.cs +++ b/Terminal.Gui/Views/Color/LightnessBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; using ColorConverter = ColorHelper.ColorConverter; diff --git a/Terminal.Gui/Views/Color/RBar.cs b/Terminal.Gui/Views/Color/RBar.cs index 2610c66bb7..e71dd92461 100644 --- a/Terminal.Gui/Views/Color/RBar.cs +++ b/Terminal.Gui/Views/Color/RBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; diff --git a/Terminal.Gui/Views/Color/SaturationBar.cs b/Terminal.Gui/Views/Color/SaturationBar.cs index 76fcd20297..9be7ab7f72 100644 --- a/Terminal.Gui/Views/Color/SaturationBar.cs +++ b/Terminal.Gui/Views/Color/SaturationBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; using ColorConverter = ColorHelper.ColorConverter; diff --git a/Terminal.Gui/Views/Color/ValueBar.cs b/Terminal.Gui/Views/Color/ValueBar.cs index 6352c7caba..5673ce8db9 100644 --- a/Terminal.Gui/Views/Color/ValueBar.cs +++ b/Terminal.Gui/Views/Color/ValueBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using ColorHelper; using ColorConverter = ColorHelper.ColorConverter; diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs index 6da8acaeac..86c4f4f796 100644 --- a/Terminal.Gui/Views/ComboBox.cs +++ b/Terminal.Gui/Views/ComboBox.cs @@ -1,3 +1,4 @@ +#nullable disable // // ComboBox.cs: ComboBox control // diff --git a/Terminal.Gui/Views/DatePicker.cs b/Terminal.Gui/Views/DatePicker.cs index a4f2791afc..579607d3d8 100644 --- a/Terminal.Gui/Views/DatePicker.cs +++ b/Terminal.Gui/Views/DatePicker.cs @@ -1,4 +1,4 @@ -#nullable enable + // // DatePicker.cs: DatePicker control diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs index bf5379f36c..4037d73129 100644 --- a/Terminal.Gui/Views/Dialog.cs +++ b/Terminal.Gui/Views/Dialog.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; /// @@ -8,7 +8,7 @@ namespace Terminal.Gui.Views; /// /// /// To run the modally, create the , and pass it to -/// . This will execute the dialog until +/// . This will execute the dialog until /// it terminates via the (`Esc` by default), /// or when one of the views or buttons added to the dialog calls /// . @@ -21,7 +21,7 @@ public class Dialog : Window /// /// By default, , , , and are /// set - /// such that the will be centered in, and no larger than 90% of , if + /// such that the will be centered in, and no larger than 90% of , if /// there is one. Otherwise, /// it will be bound by the screen dimensions. /// diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs index 0519ecba6e..644cc55d97 100644 --- a/Terminal.Gui/Views/FileDialog.cs +++ b/Terminal.Gui/Views/FileDialog.cs @@ -1 +1,2 @@ - \ No newline at end of file +#nullable disable + diff --git a/Terminal.Gui/Views/FileDialogs/AllowedType.cs b/Terminal.Gui/Views/FileDialogs/AllowedType.cs index 8460edadf4..2bcdfbe4a0 100644 --- a/Terminal.Gui/Views/FileDialogs/AllowedType.cs +++ b/Terminal.Gui/Views/FileDialogs/AllowedType.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs b/Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs index 5f0aefcad2..467e8d74c7 100644 --- a/Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs +++ b/Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs @@ -1,3 +1,4 @@ +#nullable disable using System.IO.Abstractions; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.cs index 1d2a3533ff..4650730aab 100644 --- a/Terminal.Gui/Views/FileDialogs/FileDialog.cs +++ b/Terminal.Gui/Views/FileDialogs/FileDialog.cs @@ -1,4 +1,3 @@ -#nullable enable using System.IO.Abstractions; using System.Text.RegularExpressions; @@ -827,7 +826,7 @@ private Scheme ColorGetter (CellColorGetterArgs args) return _tableView.GetScheme (); } - Color color = Style.ColorProvider.GetColor (stats.FileSystemInfo) ?? new Color (Color.White); + Color color = Style.ColorProvider.GetColor (stats.FileSystemInfo!) ?? new Color (Color.White); var black = new Color (Color.Black); // TODO: Add some kind of cache for this @@ -1034,7 +1033,7 @@ private IEnumerable MultiRowToStats () private void New () { { - IFileSystemInfo created = FileOperationsHandler.New (_fileSystem, State!.Directory); + IFileSystemInfo created = FileOperationsHandler.New (_fileSystem!, State!.Directory); if (created is { }) { @@ -1175,7 +1174,7 @@ private void Rename () if (toRename?.Length == 1) { - IFileSystemInfo newNamed = FileOperationsHandler.Rename (_fileSystem, toRename.Single ()); + IFileSystemInfo newNamed = FileOperationsHandler.Rename (_fileSystem!, toRename.Single ()); if (newNamed is { }) { @@ -1233,7 +1232,7 @@ private void ShowCellContextMenu (Point? clickedCell, MouseEventArgs e) // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused // and the context menu is disposed when it is closed. - Application.Popover?.Register (contextMenu); + App!.Popover?.Register (contextMenu); contextMenu?.MakeVisible (e.ScreenPosition); } @@ -1261,7 +1260,7 @@ private void ShowHeaderContextMenu (int clickedCol, MouseEventArgs e) // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused // and the context menu is disposed when it is closed. - Application.Popover?.Register (contextMenu); + App!.Popover?.Register (contextMenu); contextMenu?.MakeVisible (e.ScreenPosition); } @@ -1569,7 +1568,7 @@ private void RecursiveFind (IDirectoryInfo directory) } } - if (Parent.SearchMatcher.IsMatch (f.FileSystemInfo)) + if (Parent.SearchMatcher.IsMatch (f.FileSystemInfo!)) { lock (_oLockFound) { @@ -1612,7 +1611,7 @@ private void UpdateChildren () UpdateChildrenToFound (); } - Application.Invoke (() => { Parent._spinnerView.Visible = false; }); + Application.Invoke ((_) => { Parent._spinnerView.Visible = false; }); } } @@ -1624,7 +1623,7 @@ private void UpdateChildrenToFound () } Application.Invoke ( - () => + (_) => { Parent._tbPath.Autocomplete.GenerateSuggestions ( new AutocompleteFilepathContext ( diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogCollectionNavigator.cs b/Terminal.Gui/Views/FileDialogs/FileDialogCollectionNavigator.cs index d482c0a890..9e0590b833 100644 --- a/Terminal.Gui/Views/FileDialogs/FileDialogCollectionNavigator.cs +++ b/Terminal.Gui/Views/FileDialogs/FileDialogCollectionNavigator.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; internal class FileDialogCollectionNavigator (FileDialog fileDialog, TableView tableView) : CollectionNavigatorBase diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogHistory.cs b/Terminal.Gui/Views/FileDialogs/FileDialogHistory.cs index 6558d40d9f..535a19777e 100644 --- a/Terminal.Gui/Views/FileDialogs/FileDialogHistory.cs +++ b/Terminal.Gui/Views/FileDialogs/FileDialogHistory.cs @@ -1,3 +1,4 @@ +#nullable disable using System.IO.Abstractions; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogState.cs b/Terminal.Gui/Views/FileDialogs/FileDialogState.cs index 26cde6a62c..aec6818175 100644 --- a/Terminal.Gui/Views/FileDialogs/FileDialogState.cs +++ b/Terminal.Gui/Views/FileDialogs/FileDialogState.cs @@ -1,3 +1,4 @@ +#nullable disable using System.IO.Abstractions; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogStyle.cs b/Terminal.Gui/Views/FileDialogs/FileDialogStyle.cs index 0ee7ac5aa8..c72f24d8ac 100644 --- a/Terminal.Gui/Views/FileDialogs/FileDialogStyle.cs +++ b/Terminal.Gui/Views/FileDialogs/FileDialogStyle.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO.Abstractions; diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogTableSource.cs b/Terminal.Gui/Views/FileDialogs/FileDialogTableSource.cs index 14d37f0976..bf15d3bac0 100644 --- a/Terminal.Gui/Views/FileDialogs/FileDialogTableSource.cs +++ b/Terminal.Gui/Views/FileDialogs/FileDialogTableSource.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; internal class FileDialogTableSource ( diff --git a/Terminal.Gui/Views/FileDialogs/FilesSelectedEventArgs.cs b/Terminal.Gui/Views/FileDialogs/FilesSelectedEventArgs.cs index 56ba5f1fe2..f8629e31a4 100644 --- a/Terminal.Gui/Views/FileDialogs/FilesSelectedEventArgs.cs +++ b/Terminal.Gui/Views/FileDialogs/FilesSelectedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/FileDialogs/OpenDialog.cs b/Terminal.Gui/Views/FileDialogs/OpenDialog.cs index 5f916b1de7..0940436d26 100644 --- a/Terminal.Gui/Views/FileDialogs/OpenDialog.cs +++ b/Terminal.Gui/Views/FileDialogs/OpenDialog.cs @@ -1,3 +1,4 @@ +#nullable disable // // FileDialog.cs: File system dialogs for open and save // @@ -22,7 +23,7 @@ namespace Terminal.Gui.Views; /// /// /// To use, create an instance of , and pass it to -/// . This will run the dialog modally, and when this returns, +/// . This will run the dialog modally, and when this returns, /// the list of files will be available on the property. /// /// To select more than one file, users can use the spacebar, or control-t. diff --git a/Terminal.Gui/Views/FileDialogs/OpenMode.cs b/Terminal.Gui/Views/FileDialogs/OpenMode.cs index e50370ea29..e3e62d60f4 100644 --- a/Terminal.Gui/Views/FileDialogs/OpenMode.cs +++ b/Terminal.Gui/Views/FileDialogs/OpenMode.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Determine which type to open. diff --git a/Terminal.Gui/Views/FileDialogs/SaveDialog.cs b/Terminal.Gui/Views/FileDialogs/SaveDialog.cs index 7646817f89..6e0da27e25 100644 --- a/Terminal.Gui/Views/FileDialogs/SaveDialog.cs +++ b/Terminal.Gui/Views/FileDialogs/SaveDialog.cs @@ -1,3 +1,4 @@ +#nullable disable // // FileDialog.cs: File system dialogs for open and save // @@ -17,7 +18,7 @@ namespace Terminal.Gui.Views; /// /// /// To use, create an instance of , and pass it to -/// . This will run the dialog modally, and when this returns, +/// . This will run the dialog modally, and when this returns, /// the property will contain the selected file name or null if the user canceled. /// /// diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs index 4f8713c8b7..cd99731b92 100644 --- a/Terminal.Gui/Views/FrameView.cs +++ b/Terminal.Gui/Views/FrameView.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/GraphView/Axis.cs b/Terminal.Gui/Views/GraphView/Axis.cs index 3160f3ee32..177500f9f2 100644 --- a/Terminal.Gui/Views/GraphView/Axis.cs +++ b/Terminal.Gui/Views/GraphView/Axis.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/GraphView/BarSeriesBar.cs b/Terminal.Gui/Views/GraphView/BarSeriesBar.cs index efe17b59cd..29f401e85a 100644 --- a/Terminal.Gui/Views/GraphView/BarSeriesBar.cs +++ b/Terminal.Gui/Views/GraphView/BarSeriesBar.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// A single bar in a diff --git a/Terminal.Gui/Views/GraphView/GraphCellToRender.cs b/Terminal.Gui/Views/GraphView/GraphCellToRender.cs index 8bf4d99e9d..db1b56e5ce 100644 --- a/Terminal.Gui/Views/GraphView/GraphCellToRender.cs +++ b/Terminal.Gui/Views/GraphView/GraphCellToRender.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/GraphView/GraphView.cs b/Terminal.Gui/Views/GraphView/GraphView.cs index 92b31a60ff..69f708da8e 100644 --- a/Terminal.Gui/Views/GraphView/GraphView.cs +++ b/Terminal.Gui/Views/GraphView/GraphView.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; @@ -8,6 +8,7 @@ public class GraphView : View, IDesignable /// Creates a new graph with a 1 to 1 graph space with absolute layout. public GraphView () { + App = ApplicationImpl.Instance; CanFocus = true; AxisX = new (); @@ -344,7 +345,7 @@ public void Scroll (float offsetX, float offsetY) } /// - /// Sets the color attribute of to the (if defined) or + /// Sets the color attribute of to the (if defined) or /// otherwise. /// public void SetDriverColorToGraphColor () { SetAttribute (GraphColor ?? GetAttributeForRole (VisualRole.Normal)); } diff --git a/Terminal.Gui/Views/GraphView/IAnnotation.cs b/Terminal.Gui/Views/GraphView/IAnnotation.cs index 0043398757..c396df59d4 100644 --- a/Terminal.Gui/Views/GraphView/IAnnotation.cs +++ b/Terminal.Gui/Views/GraphView/IAnnotation.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/GraphView/LegendAnnotation.cs b/Terminal.Gui/Views/GraphView/LegendAnnotation.cs index 813228f7db..d8ac4be8bc 100644 --- a/Terminal.Gui/Views/GraphView/LegendAnnotation.cs +++ b/Terminal.Gui/Views/GraphView/LegendAnnotation.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/GraphView/LineF.cs b/Terminal.Gui/Views/GraphView/LineF.cs index 67ef51cd10..462b032244 100644 --- a/Terminal.Gui/Views/GraphView/LineF.cs +++ b/Terminal.Gui/Views/GraphView/LineF.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Describes two points in graph space and a line between them diff --git a/Terminal.Gui/Views/GraphView/PathAnnotation.cs b/Terminal.Gui/Views/GraphView/PathAnnotation.cs index 4a623f7dfb..622ea83a83 100644 --- a/Terminal.Gui/Views/GraphView/PathAnnotation.cs +++ b/Terminal.Gui/Views/GraphView/PathAnnotation.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Sequence of lines to connect points e.g. of a diff --git a/Terminal.Gui/Views/GraphView/Series.cs b/Terminal.Gui/Views/GraphView/Series.cs index 187d6e6beb..d417404ba7 100644 --- a/Terminal.Gui/Views/GraphView/Series.cs +++ b/Terminal.Gui/Views/GraphView/Series.cs @@ -1,7 +1,6 @@ using System.Collections.ObjectModel; namespace Terminal.Gui.Views; -#nullable enable /// Describes a series of data that can be rendered into a > public interface ISeries { diff --git a/Terminal.Gui/Views/GraphView/TextAnnotation.cs b/Terminal.Gui/Views/GraphView/TextAnnotation.cs index 98f1fec3e9..d1309fe9e3 100644 --- a/Terminal.Gui/Views/GraphView/TextAnnotation.cs +++ b/Terminal.Gui/Views/GraphView/TextAnnotation.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Displays text at a given position (in screen space or graph space) diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs index cf645b03d7..ee81eb8326 100644 --- a/Terminal.Gui/Views/HexView.cs +++ b/Terminal.Gui/Views/HexView.cs @@ -1,4 +1,4 @@ -#nullable enable + // // HexView.cs: A hexadecimal viewer diff --git a/Terminal.Gui/Views/HexViewEventArgs.cs b/Terminal.Gui/Views/HexViewEventArgs.cs index 11e3721151..2ee496b173 100644 --- a/Terminal.Gui/Views/HexViewEventArgs.cs +++ b/Terminal.Gui/Views/HexViewEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable // // HexView.cs: A hexadecimal viewer // diff --git a/Terminal.Gui/Views/IListDataSource.cs b/Terminal.Gui/Views/IListDataSource.cs index 37f1a63d60..76ab7e9568 100644 --- a/Terminal.Gui/Views/IListDataSource.cs +++ b/Terminal.Gui/Views/IListDataSource.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable disable using System.Collections; using System.Collections.Specialized; diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs index 5ea22dba9d..c11347f9fc 100644 --- a/Terminal.Gui/Views/Label.cs +++ b/Terminal.Gui/Views/Label.cs @@ -24,13 +24,13 @@ public Label () Width = Dim.Auto (DimAutoStyle.Text); // On HoKey, pass it to the next view - AddCommand (Command.HotKey, InvokeHotKeyOnNextPeer); + AddCommand (Command.HotKey, InvokeHotKeyOnNextPeer!); TitleChanged += Label_TitleChanged; MouseClick += Label_MouseClick; } - private void Label_MouseClick (object sender, MouseEventArgs e) + private void Label_MouseClick (object? sender, MouseEventArgs e) { if (!CanFocus) { @@ -38,7 +38,7 @@ private void Label_MouseClick (object sender, MouseEventArgs e) } } - private void Label_TitleChanged (object sender, EventArgs e) + private void Label_TitleChanged (object? sender, EventArgs e) { base.Text = e.Value; TextFormatter.HotKeySpecifier = HotKeySpecifier; diff --git a/Terminal.Gui/Views/Line.cs b/Terminal.Gui/Views/Line.cs index 5b5a9c9a8b..8da81b99d2 100644 --- a/Terminal.Gui/Views/Line.cs +++ b/Terminal.Gui/Views/Line.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index b933af9e03..729e8066bc 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Collections; using System.Collections.ObjectModel; using System.Collections.Specialized; diff --git a/Terminal.Gui/Views/ListViewEventArgs.cs b/Terminal.Gui/Views/ListViewEventArgs.cs index fe83de5808..7a718b536b 100644 --- a/Terminal.Gui/Views/ListViewEventArgs.cs +++ b/Terminal.Gui/Views/ListViewEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// for events. diff --git a/Terminal.Gui/Views/Menu/MenuBarItemv2.cs b/Terminal.Gui/Views/Menu/MenuBarItemv2.cs index ca82d7fc35..8d04c3e2b2 100644 --- a/Terminal.Gui/Views/Menu/MenuBarItemv2.cs +++ b/Terminal.Gui/Views/Menu/MenuBarItemv2.cs @@ -1,6 +1,7 @@ -#nullable enable +using System.Diagnostics; + namespace Terminal.Gui.Views; /// @@ -113,6 +114,8 @@ public PopoverMenu? PopoverMenu if (_popoverMenu is { }) { + _popoverMenu.App = App; + PopoverMenuOpen = _popoverMenu.Visible; _popoverMenu.VisibleChanged += OnPopoverVisibleChanged; _popoverMenu.Accepted += OnPopoverMenuOnAccepted; diff --git a/Terminal.Gui/Views/Menu/MenuBarv2.cs b/Terminal.Gui/Views/Menu/MenuBarv2.cs index fdf1333a2a..746cdb44dc 100644 --- a/Terminal.Gui/Views/Menu/MenuBarv2.cs +++ b/Terminal.Gui/Views/Menu/MenuBarv2.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.ComponentModel; using System.Diagnostics; @@ -321,7 +321,7 @@ public override void EndInit () // TODO: This needs to be done whenever a menuitem in any MenuBarItem changes foreach (MenuBarItemv2? mbi in SubViews.Select (s => s as MenuBarItemv2)) { - Application.Popover?.Register (mbi?.PopoverMenu); + App?.Popover?.Register (mbi?.PopoverMenu); } } @@ -396,11 +396,11 @@ private void ShowItem (MenuBarItemv2? menuBarItem) } // If the active Application Popover is part of this MenuBar, hide it. - if (Application.Popover?.GetActivePopover () is PopoverMenu popoverMenu + if (App?.Popover?.GetActivePopover () is PopoverMenu popoverMenu && popoverMenu.Root?.SuperMenuItem?.SuperView == this) { - // Logging.Debug ($"{Title} - Calling Application.Popover?.Hide ({popoverMenu.Title})"); - Application.Popover.Hide (popoverMenu); + // Logging.Debug ($"{Title} - Calling App?.Popover?.Hide ({popoverMenu.Title})"); + App?.Popover.Hide (popoverMenu); } if (menuBarItem is null) @@ -420,7 +420,11 @@ private void ShowItem (MenuBarItemv2? menuBarItem) } // Logging.Debug ($"{Title} - \"{menuBarItem.PopoverMenu?.Title}\".MakeVisible"); - menuBarItem.PopoverMenu?.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom)); + if (menuBarItem.PopoverMenu is { }) + { + menuBarItem.PopoverMenu.App ??= App; + menuBarItem.PopoverMenu.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom)); + } menuBarItem.Accepting += OnMenuItemAccepted; @@ -501,11 +505,16 @@ public IEnumerable GetMenuItemsWithTitle (string title) } /// - public bool EnableForDesign (ref TContext context) where TContext : notnull + public bool EnableForDesign (ref TContext targetView) where TContext : notnull { // Note: This menu is used by unit tests. If you modify it, you'll likely have to update // unit tests. + if (targetView is View target) + { + App ??= target.App; + } + Id = "DemoBar"; var bordersCb = new CheckBox @@ -552,10 +561,10 @@ public bool EnableForDesign (ref TContext context) where TContext : no new MenuBarItemv2 ( "_File", [ - new MenuItemv2 (context as View, Command.New), - new MenuItemv2 (context as View, Command.Open), - new MenuItemv2 (context as View, Command.Save), - new MenuItemv2 (context as View, Command.SaveAs), + new MenuItemv2 (targetView as View, Command.New), + new MenuItemv2 (targetView as View, Command.Open), + new MenuItemv2 (targetView as View, Command.Save), + new MenuItemv2 (targetView as View, Command.SaveAs), new Line (), new MenuItemv2 { @@ -576,7 +585,7 @@ public bool EnableForDesign (ref TContext context) where TContext : no Key = Key.W.WithCtrl, CommandView = enableOverwriteCb, Command = Command.EnableOverwrite, - TargetView = context as View + TargetView = targetView as View }, new () { @@ -622,7 +631,7 @@ public bool EnableForDesign (ref TContext context) where TContext : no new Line (), new MenuItemv2 { - TargetView = context as View, + TargetView = targetView as View, Key = Application.QuitKey, Command = Command.Quit } @@ -634,11 +643,11 @@ public bool EnableForDesign (ref TContext context) where TContext : no new MenuBarItemv2 ( "_Edit", [ - new MenuItemv2 (context as View, Command.Cut), - new MenuItemv2 (context as View, Command.Copy), - new MenuItemv2 (context as View, Command.Paste), + new MenuItemv2 (targetView as View, Command.Cut), + new MenuItemv2 (targetView as View, Command.Copy), + new MenuItemv2 (targetView as View, Command.Paste), new Line (), - new MenuItemv2 (context as View, Command.SelectAll), + new MenuItemv2 (targetView as View, Command.SelectAll), new Line (), new MenuItemv2 { diff --git a/Terminal.Gui/Views/Menu/MenuItemv2.cs b/Terminal.Gui/Views/Menu/MenuItemv2.cs index c5f472307d..7533881cd7 100644 --- a/Terminal.Gui/Views/Menu/MenuItemv2.cs +++ b/Terminal.Gui/Views/Menu/MenuItemv2.cs @@ -1,4 +1,3 @@ -#nullable enable using System.ComponentModel; @@ -136,7 +135,7 @@ public Command Command { // Is this an Application-bound command? // Logging.Debug ($"{Title} - Application.InvokeCommandsBoundToKey ({Key})..."); - ret = Application.InvokeCommandsBoundToKey (Key); + ret = App?.Keyboard.InvokeCommandsBoundToKey (Key); } } @@ -186,6 +185,7 @@ public Menuv2? SubMenu if (_subMenu is { }) { + SubMenu!.App ??= App; SubMenu!.Visible = false; // TODO: This is a temporary hack - add a flag or something instead KeyView.Text = $"{Glyphs.RightArrow}"; diff --git a/Terminal.Gui/Views/Menu/Menuv2.cs b/Terminal.Gui/Views/Menu/Menuv2.cs index 1eb5f6cc49..2cd610f41a 100644 --- a/Terminal.Gui/Views/Menu/Menuv2.cs +++ b/Terminal.Gui/Views/Menu/Menuv2.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; @@ -229,4 +229,4 @@ protected override void Dispose (bool disposing) ConfigurationManager.Applied -= OnConfigurationManagerApplied; } } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Menu/PopoverMenu.cs b/Terminal.Gui/Views/Menu/PopoverMenu.cs index dcaef66ea6..c04041bf5d 100644 --- a/Terminal.Gui/Views/Menu/PopoverMenu.cs +++ b/Terminal.Gui/Views/Menu/PopoverMenu.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; @@ -9,7 +9,7 @@ namespace Terminal.Gui.Views; /// /// /// -/// To use as a context menu, register the popover menu with and call +/// To use as a context menu, register the popover menu with and call /// . /// /// @@ -176,7 +176,7 @@ public void MakeVisible (Point? idealScreenPosition = null) UpdateKeyBindings (); SetPosition (idealScreenPosition); - Application.Popover?.Show (this); + App!.Popover?.Show (this); } /// @@ -188,7 +188,7 @@ public void MakeVisible (Point? idealScreenPosition = null) /// If , the current mouse position will be used. public void SetPosition (Point? idealScreenPosition = null) { - idealScreenPosition ??= Application.GetLastMousePosition (); + idealScreenPosition ??= App?.Mouse.LastMousePosition; if (idealScreenPosition is null || Root is null) { @@ -199,6 +199,7 @@ public void SetPosition (Point? idealScreenPosition = null) if (!Root.IsInitialized) { + Root.App ??= App; Root.BeginInit (); Root.EndInit (); Root.Layout (); @@ -223,7 +224,7 @@ protected override void OnVisibleChanged () else { HideAndRemoveSubMenu (_root); - Application.Popover?.Hide (this); + App?.Popover?.Hide (this); } } @@ -246,6 +247,11 @@ public Menuv2? Root _root = value; + if (_root is { }) + { + _root.App = App; + } + // TODO: This needs to be done whenever any MenuItem in the menu tree changes to support dynamic menus // TODO: And it needs to clear the old bindings first UpdateKeyBindings (); @@ -255,6 +261,7 @@ public Menuv2? Root foreach (Menuv2 menu in allMenus) { + menu.App = App; menu.Visible = false; menu.Accepting += MenuOnAccepting; menu.Accepted += MenuAccepted; @@ -279,7 +286,7 @@ private void UpdateKeyBindings () else { // No TargetView implies Application HotKey - key = Application.KeyBindings.GetFirstFromCommands (menuItem.Command); + key = App?.Keyboard.KeyBindings.GetFirstFromCommands (menuItem.Command); } if (key is not { IsValid: true }) @@ -439,6 +446,7 @@ private void AddAndShowSubMenu (Menuv2? menu) if (!menu!.IsInitialized) { + menu.App ??= App; menu.BeginInit (); menu.EndInit (); } @@ -626,27 +634,27 @@ protected override void Dispose (bool disposing) } /// - public bool EnableForDesign (ref TContext context) where TContext : notnull + public bool EnableForDesign (ref TContext targetView) where TContext : notnull { // Note: This menu is used by unit tests. If you modify it, you'll likely have to update // unit tests. Root = new ( [ - new MenuItemv2 (context as View, Command.Cut), - new MenuItemv2 (context as View, Command.Copy), - new MenuItemv2 (context as View, Command.Paste), + new MenuItemv2 (targetView as View, Command.Cut), + new MenuItemv2 (targetView as View, Command.Copy), + new MenuItemv2 (targetView as View, Command.Paste), new Line (), - new MenuItemv2 (context as View, Command.SelectAll), + new MenuItemv2 (targetView as View, Command.SelectAll), new Line (), - new MenuItemv2 (context as View, Command.Quit) + new MenuItemv2 (targetView as View, Command.Quit) ]) { Title = "Popover Demo Root" }; // NOTE: This is a workaround for the fact that the PopoverMenu is not visible in the designer - // NOTE: without being activated via Application.Popover. But we want it to be visible. + // NOTE: without being activated via App?.Popover. But we want it to be visible. // NOTE: If you use PopoverView.EnableForDesign for real Popover scenarios, change back to false // NOTE: after calling EnableForDesign. //Visible = true; diff --git a/Terminal.Gui/Views/Menuv1/Menu.cs b/Terminal.Gui/Views/Menuv1/Menu.cs index 7833e41963..f538af7dd5 100644 --- a/Terminal.Gui/Views/Menuv1/Menu.cs +++ b/Terminal.Gui/Views/Menuv1/Menu.cs @@ -1,5 +1,4 @@ #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. -#nullable enable namespace Terminal.Gui.Views; @@ -13,10 +12,10 @@ internal sealed class Menu : View { public Menu () { - if (Application.Top is { }) + if (Application.Current is { }) { - Application.Top.DrawComplete += Top_DrawComplete; - Application.Top.SizeChanging += Current_TerminalResized; + Application.Current.DrawComplete += Top_DrawComplete; + Application.Current.SizeChanging += Current_TerminalResized; } Application.MouseEvent += Application_RootMouseEvent; @@ -232,10 +231,10 @@ protected override void Dispose (bool disposing) { RemoveKeyBindingsHotKey (_barItems); - if (Application.Top is { }) + if (Application.Current is { }) { - Application.Top.DrawComplete -= Top_DrawComplete; - Application.Top.SizeChanging -= Current_TerminalResized; + Application.Current.DrawComplete -= Top_DrawComplete; + Application.Current.SizeChanging -= Current_TerminalResized; } Application.MouseEvent -= Application_RootMouseEvent; @@ -968,11 +967,11 @@ private void Top_DrawComplete (object? sender, DrawEventArgs e) // The -3 is left/right border + one space (not sure what for) tf.Draw ( - ViewportToScreen (new Rectangle (1, i, Frame.Width - 3, 1)), - i == _currentChild ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal), - i == _currentChild ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal), - SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty - ); + driver: Driver, + screen: ViewportToScreen (new Rectangle (1, i, Frame.Width - 3, 1)), + normalColor: i == _currentChild ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal), + hotColor: i == _currentChild ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal), + maximum: SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty); } else { diff --git a/Terminal.Gui/Views/Menuv1/MenuBar.cs b/Terminal.Gui/Views/Menuv1/MenuBar.cs index d2ab03dc51..78d1c3a29d 100644 --- a/Terminal.Gui/Views/Menuv1/MenuBar.cs +++ b/Terminal.Gui/Views/Menuv1/MenuBar.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; @@ -422,7 +421,7 @@ public void OpenMenu () _selected = 0; SetNeedsDraw (); - _previousFocused = (SuperView is null ? Application.Top?.Focused : SuperView.Focused)!; + _previousFocused = (SuperView is null ? Application.Current?.Focused : SuperView.Focused)!; OpenMenu (_selected); if (!SelectEnabledItem ( @@ -491,7 +490,7 @@ internal void Activate (int idx, int sIdx = -1, MenuBarItem? subMenu = null!) if (_openMenu is null) { - _previousFocused = (SuperView is null ? Application.Top?.Focused ?? null : SuperView.Focused)!; + _previousFocused = (SuperView is null ? Application.Current?.Focused ?? null : SuperView.Focused)!; } OpenMenu (idx, sIdx, subMenu); @@ -703,7 +702,7 @@ internal Point GetScreenOffset () } Rectangle superViewFrame = SuperView?.Frame ?? Application.Screen; - View? sv = SuperView ?? Application.Top; + View? sv = SuperView ?? Application.Current; if (sv is null) { @@ -835,7 +834,7 @@ internal void OpenMenu (int index, int sIndex = -1, MenuBarItem? subMenu = null! { case null: // Open a submenu below a MenuBar - _lastFocused ??= SuperView is null ? Application.Top?.MostFocused : SuperView.MostFocused; + _lastFocused ??= SuperView is null ? Application.Current?.MostFocused : SuperView.MostFocused; if (_openSubMenu is { } && !CloseMenu (false, true)) { @@ -1680,9 +1679,9 @@ internal bool HandleGrabView (MouseEventArgs me, View current) /// - public bool EnableForDesign (ref TContext context) where TContext : notnull + public bool EnableForDesign (ref TContext targetView) where TContext : notnull { - if (context is not Func actionFn) + if (targetView is not Func actionFn) { actionFn = (_) => true; } diff --git a/Terminal.Gui/Views/Menuv1/MenuBarItem.cs b/Terminal.Gui/Views/Menuv1/MenuBarItem.cs index 8b3be21161..fe96c15892 100644 --- a/Terminal.Gui/Views/Menuv1/MenuBarItem.cs +++ b/Terminal.Gui/Views/Menuv1/MenuBarItem.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs index 0a830c2b84..e223893c8e 100644 --- a/Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs +++ b/Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; #pragma warning disable CS0618 // Type or member is obsolete @@ -30,4 +31,4 @@ public MenuClosingEventArgs (MenuBarItem currentMenu, bool reopen, bool isSubMen /// Indicates whether the current menu will reopen. public bool Reopen { get; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Menuv1/MenuItem.cs b/Terminal.Gui/Views/Menuv1/MenuItem.cs index 002ea0a385..9d66c1c459 100644 --- a/Terminal.Gui/Views/Menuv1/MenuItem.cs +++ b/Terminal.Gui/Views/Menuv1/MenuItem.cs @@ -1,5 +1,4 @@ #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. -#nullable enable namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs b/Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs index 87bc5168e0..ad189e3738 100644 --- a/Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs +++ b/Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Specifies how a shows selection state. @@ -12,4 +13,4 @@ public enum MenuItemCheckStyle /// The menu item is part of a menu radio group (see ) and will indicate selected state. Radio = 0b_0000_0010 -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs index ba6c4adcf7..581b9b04c9 100644 --- a/Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs +++ b/Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; #pragma warning disable CS0618 // Type or member is obsolete @@ -18,4 +19,4 @@ public MenuOpenedEventArgs (MenuBarItem parent, MenuItem menuItem) /// The parent of . Will be null if menu opening is the root. public MenuBarItem Parent { get; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs index e19421caa3..c2e10d6d4f 100644 --- a/Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs +++ b/Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; #pragma warning disable CS0618 // Type or member is obsolete @@ -23,4 +24,4 @@ public class MenuOpeningEventArgs : EventArgs /// The new to be replaced. public MenuBarItem NewMenuBarItem { get; set; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/MessageBox.cs b/Terminal.Gui/Views/MessageBox.cs index 478d795654..bdbf323a0f 100644 --- a/Terminal.Gui/Views/MessageBox.cs +++ b/Terminal.Gui/Views/MessageBox.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/NumericUpDown.cs b/Terminal.Gui/Views/NumericUpDown.cs index b0066c3637..adb94d5549 100644 --- a/Terminal.Gui/Views/NumericUpDown.cs +++ b/Terminal.Gui/Views/NumericUpDown.cs @@ -1,4 +1,3 @@ -#nullable enable using System.ComponentModel; using System.Numerics; diff --git a/Terminal.Gui/Views/ProgressBar.cs b/Terminal.Gui/Views/ProgressBar.cs index 3e27bd556c..b7cf3a2e0f 100644 --- a/Terminal.Gui/Views/ProgressBar.cs +++ b/Terminal.Gui/Views/ProgressBar.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; @@ -185,12 +185,12 @@ protected override bool OnDrawingContent () GetAttributeForRole (VisualRole.Active).Style); } - tf.Draw ( - ViewportToScreen (Viewport), - attr, - GetAttributeForRole (VisualRole.Normal), - SuperView?.ViewportToScreen (SuperView.Viewport) ?? default (Rectangle) - ); + tf.Draw ( + driver: Driver, + screen: ViewportToScreen (Viewport), + normalColor: attr, + hotColor: GetAttributeForRole (VisualRole.Normal), + maximum: SuperView?.ViewportToScreen (SuperView.Viewport) ?? default (Rectangle)); } return true; diff --git a/Terminal.Gui/Views/ReadOnlyCollectionExtensions.cs b/Terminal.Gui/Views/ReadOnlyCollectionExtensions.cs index 6eb572e294..0a615c92f2 100644 --- a/Terminal.Gui/Views/ReadOnlyCollectionExtensions.cs +++ b/Terminal.Gui/Views/ReadOnlyCollectionExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/ScrollBar/ScrollBar.cs b/Terminal.Gui/Views/ScrollBar/ScrollBar.cs index e6b1eb5908..4d74684341 100644 --- a/Terminal.Gui/Views/ScrollBar/ScrollBar.cs +++ b/Terminal.Gui/Views/ScrollBar/ScrollBar.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.ComponentModel; diff --git a/Terminal.Gui/Views/ScrollBar/ScrollSlider.cs b/Terminal.Gui/Views/ScrollBar/ScrollSlider.cs index 920f88ccee..d3d861ac26 100644 --- a/Terminal.Gui/Views/ScrollBar/ScrollSlider.cs +++ b/Terminal.Gui/Views/ScrollBar/ScrollSlider.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.ComponentModel; diff --git a/Terminal.Gui/Views/SelectedItemChangedArgs.cs b/Terminal.Gui/Views/SelectedItemChangedArgs.cs index 691dea2f62..6d09f0d8ff 100644 --- a/Terminal.Gui/Views/SelectedItemChangedArgs.cs +++ b/Terminal.Gui/Views/SelectedItemChangedArgs.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; /// Event arguments for the SelectedItemChanged event. diff --git a/Terminal.Gui/Views/Selectors/FlagSelector.cs b/Terminal.Gui/Views/Selectors/FlagSelector.cs index dfc45456a9..c2c8c15f98 100644 --- a/Terminal.Gui/Views/Selectors/FlagSelector.cs +++ b/Terminal.Gui/Views/Selectors/FlagSelector.cs @@ -1,7 +1,3 @@ -#nullable enable - -using System.Collections.Immutable; - namespace Terminal.Gui.Views; // DoubleClick - Focus, Select (Toggle), and Accept the item under the mouse. diff --git a/Terminal.Gui/Views/Selectors/FlagSelectorTEnum.cs b/Terminal.Gui/Views/Selectors/FlagSelectorTEnum.cs index 9ec066e023..86aa5c9a86 100644 --- a/Terminal.Gui/Views/Selectors/FlagSelectorTEnum.cs +++ b/Terminal.Gui/Views/Selectors/FlagSelectorTEnum.cs @@ -1,4 +1,3 @@ -#nullable enable using System; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Selectors/OptionSelector.cs b/Terminal.Gui/Views/Selectors/OptionSelector.cs index 48a9d35034..ab4159cc3d 100644 --- a/Terminal.Gui/Views/Selectors/OptionSelector.cs +++ b/Terminal.Gui/Views/Selectors/OptionSelector.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Immutable; using System.Diagnostics; diff --git a/Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs b/Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs index 604a6cf825..188d1fcc79 100644 --- a/Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs +++ b/Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/Selectors/SelectorBase.cs b/Terminal.Gui/Views/Selectors/SelectorBase.cs index 7bef55dd21..5747bf4b1b 100644 --- a/Terminal.Gui/Views/Selectors/SelectorBase.cs +++ b/Terminal.Gui/Views/Selectors/SelectorBase.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Collections.Immutable; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Selectors/SelectorStyles.cs b/Terminal.Gui/Views/Selectors/SelectorStyles.cs index c1087001d2..cf0fad51c0 100644 --- a/Terminal.Gui/Views/Selectors/SelectorStyles.cs +++ b/Terminal.Gui/Views/Selectors/SelectorStyles.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index ccccc01bb8..a0b7c5683b 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.Diagnostics; namespace Terminal.Gui.Views; @@ -25,7 +24,8 @@ namespace Terminal.Gui.Views; /// /// By default, a Shortcut displays the command text on the left side, the help text in the middle, and the key /// binding on the -/// right side. Set to to reverse the order. +/// right side. Set to to reverse the +/// order. /// /// /// The command text can be set by setting the 's Text property or by setting @@ -44,7 +44,7 @@ public class Shortcut : View, IOrientation, IDesignable /// /// Creates a new instance of . /// - public Shortcut () : this (Key.Empty, null, null, null) { } + public Shortcut () : this (Key.Empty, null, null) { } /// /// Creates a new instance of . @@ -60,7 +60,7 @@ public Shortcut () : this (Key.Empty, null, null, null) { } /// The help text to display. public Shortcut (Key key, string? commandText, Action? action, string? helpText = null) { - HighlightStates = ViewBase.MouseState.None; + HighlightStates = MouseState.None; CanFocus = true; if (Border is { }) @@ -88,10 +88,12 @@ public Shortcut (Key key, string? commandText, Action? action, string? helpText Title = commandText ?? string.Empty; HelpView.Id = "_helpView"; + //HelpView.CanFocus = false; HelpView.Text = helpText ?? string.Empty; KeyView.Id = "_keyView"; + //KeyView.CanFocus = false; key ??= Key.Empty; Key = key; @@ -101,13 +103,22 @@ public Shortcut (Key key, string? commandText, Action? action, string? helpText ShowHide (); } + /// + public override void EndInit () + { + base.EndInit (); + App ??= SuperView?.App; + Debug.Assert (App is { }); + UpdateKeyBindings (Key.Empty); + } + // Helper to set Width consistently internal Dim GetWidthDimAuto () { return Dim.Auto ( DimAutoStyle.Content, - minimumContentDim: Dim.Func (_ => _minimumNaturalWidth ?? 0), - maximumContentDim: Dim.Func (_ => _minimumNaturalWidth ?? 0))!; + Dim.Func (_ => _minimumNaturalWidth ?? 0), + Dim.Func (_ => _minimumNaturalWidth ?? 0))!; } private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast; @@ -168,24 +179,22 @@ internal void ShowHide () private void ForceCalculateNaturalWidth () { // Get the natural size of each subview - CommandView.SetRelativeLayout (Application.Screen.Size); - HelpView.SetRelativeLayout (Application.Screen.Size); - KeyView.SetRelativeLayout (Application.Screen.Size); + Size screenSize = App?.Screen.Size ?? new (2048, 2048); + CommandView.SetRelativeLayout (screenSize); + HelpView.SetRelativeLayout (screenSize); + KeyView.SetRelativeLayout (screenSize); _minimumNaturalWidth = PosAlign.CalculateMinDimension (0, SubViews, Dimension.Width); // Reset our relative layout - SetRelativeLayout (SuperView?.GetContentSize () ?? Application.Screen.Size); + SetRelativeLayout (SuperView?.GetContentSize () ?? screenSize); } // TODO: Enable setting of the margin thickness - private Thickness GetMarginThickness () - { - return new (1, 0, 1, 0); - } + private Thickness GetMarginThickness () => new (1, 0, 1, 0); // When layout starts, we need to adjust the layout of the HelpView and KeyView - /// + /// protected override void OnSubViewLayout (LayoutEventArgs e) { base.OnSubViewLayout (e); @@ -229,27 +238,30 @@ protected override void OnSubViewLayout (LayoutEventArgs e) } } - #region Accept/Select/HotKey Command Handling private void AddCommands () { // Accept (Enter key) - AddCommand (Command.Accept, DispatchCommand); + // Hotkey - AddCommand (Command.HotKey, DispatchCommand); + // Select (Space key or click) - AddCommand (Command.Select, DispatchCommand); } /// - /// Dispatches the Command in the (Raises Selected, then Accepting, then invoke the Action, if any). + /// Dispatches the Command in the (Raises Selected, then Accepting, then invoke the + /// Action, if any). /// Called when Command.Select, Accept, or HotKey has been invoked on this Shortcut. /// /// /// /// if no event was raised; input processing should continue. - /// if the event was raised and was not handled (or cancelled); input processing should continue. + /// if the event was raised and was not handled (or cancelled); input processing should + /// continue. /// if the event was raised and handled (or cancelled); input processing should stop. /// internal virtual bool? DispatchCommand (ICommandContext? commandContext) @@ -290,6 +302,7 @@ private void AddCommands () { commandContext.Source = this; } + Logging.Debug ($"{Title} ({commandContext?.Source?.Title}) - Calling RaiseAccepting..."); cancel = RaiseAccepting (commandContext) is true; @@ -378,7 +391,7 @@ public void OnOrientationChanged (Orientation newOrientation) /// /// /// This example illustrates how to add a to a that toggles the - /// property. + /// property. /// /// /// var force16ColorsShortcut = new Shortcut @@ -455,8 +468,8 @@ void CommandViewOnAccepted (object? sender, CommandEventArgs e) void CommandViewOnSelecting (object? sender, CommandEventArgs e) { - if ((e.Context is CommandContext keyCommandContext && keyCommandContext.Binding.Data != this) || - e.Context is CommandContext) + if ((e.Context is CommandContext keyCommandContext && keyCommandContext.Binding.Data != this) + || e.Context is CommandContext) { // Forward command to ourselves InvokeCommand (Command.Select, new ([Command.Select], null, this)); @@ -472,6 +485,7 @@ private void SetCommandViewDefaultLayout () if (CommandView.Margin is { }) { CommandView.Margin!.Thickness = GetMarginThickness (); + // strip off ViewportSettings.TransparentMouse CommandView.Margin!.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; } @@ -481,6 +495,7 @@ private void SetCommandViewDefaultLayout () CommandView.VerticalTextAlignment = Alignment.Center; CommandView.TextAlignment = Alignment.Start; CommandView.TextFormatter.WordWrap = false; + //CommandView.HighlightStates = HighlightStates.None; CommandView.GettingAttributeForRole += SubViewOnGettingAttributeForRole; } @@ -495,6 +510,7 @@ private void SubViewOnGettingAttributeForRole (object? sender, VisualRoleEventAr e.Handled = true; e.Result = GetAttributeForRole (VisualRole.Focus); } + break; case VisualRole.HotNormal: @@ -503,17 +519,18 @@ private void SubViewOnGettingAttributeForRole (object? sender, VisualRoleEventAr e.Handled = true; e.Result = GetAttributeForRole (VisualRole.HotFocus); } + break; } } - private void Shortcut_TitleChanged (object? sender, EventArgs e) { // If the Title changes, update the CommandView text. // This is a helper to make it easier to set the CommandView text. // CommandView is public and replaceable, but this is a convenience. _commandView.Text = Title; + //_commandView.Title = Title; } @@ -522,7 +539,7 @@ private void Shortcut_TitleChanged (object? sender, EventArgs e) #region Help // The maximum width of the HelpView. Calculated in OnLayoutStarted and used in HelpView.Width (Dim.Auto/Func). - private int _maxHelpWidth = 0; + private int _maxHelpWidth; /// /// The subview that displays the help text for the command. Internal for unit testing. @@ -534,20 +551,21 @@ private void SetHelpViewDefaultLayout () if (HelpView.Margin is { }) { HelpView.Margin!.Thickness = GetMarginThickness (); + // strip off ViewportSettings.TransparentMouse HelpView.Margin!.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; } HelpView.X = Pos.Align (Alignment.End, AlignmentModes); _maxHelpWidth = HelpView.Text.GetColumns (); - HelpView.Width = Dim.Auto (DimAutoStyle.Text, maximumContentDim: Dim.Func ((_ => _maxHelpWidth))); + HelpView.Width = Dim.Auto (DimAutoStyle.Text, maximumContentDim: Dim.Func (_ => _maxHelpWidth)); HelpView.Height = Dim.Fill (); HelpView.Visible = true; HelpView.VerticalTextAlignment = Alignment.Center; HelpView.TextAlignment = Alignment.Start; HelpView.TextFormatter.WordWrap = false; - HelpView.HighlightStates = ViewBase.MouseState.None; + HelpView.HighlightStates = MouseState.None; HelpView.GettingAttributeForRole += SubViewOnGettingAttributeForRole; } @@ -605,16 +623,19 @@ public Key Key } } - private bool _bindKeyToApplication = false; + private bool _bindKeyToApplication; /// - /// Gets or sets whether is bound to via or . + /// Gets or sets whether is bound to via or + /// . /// public bool BindKeyToApplication { get => _bindKeyToApplication; set { + App ??= SuperView?.App; + if (value == _bindKeyToApplication) { return; @@ -622,7 +643,7 @@ public bool BindKeyToApplication if (_bindKeyToApplication) { - Application.KeyBindings.Remove (Key); + App?.Keyboard.KeyBindings.Remove (Key); } else { @@ -661,18 +682,18 @@ public int MinimumKeyTextSize } } - private void SetKeyViewDefaultLayout () { if (KeyView.Margin is { }) { KeyView.Margin!.Thickness = GetMarginThickness (); + // strip off ViewportSettings.TransparentMouse KeyView.Margin!.ViewportSettings &= ~ViewportSettingsFlags.TransparentMouse; } KeyView.X = Pos.Align (Alignment.End, AlignmentModes); - KeyView.Width = Dim.Auto (DimAutoStyle.Text, minimumContentDim: Dim.Func (_ => MinimumKeyTextSize)); + KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (_ => MinimumKeyTextSize)); KeyView.Height = Dim.Fill (); KeyView.Visible = true; @@ -681,21 +702,23 @@ private void SetKeyViewDefaultLayout () KeyView.TextAlignment = Alignment.End; KeyView.VerticalTextAlignment = Alignment.Center; KeyView.KeyBindings.Clear (); - KeyView.HighlightStates = ViewBase.MouseState.None; + KeyView.HighlightStates = MouseState.None; KeyView.GettingAttributeForRole += (sender, args) => { if (args.Role == VisualRole.Normal) { - args.Result = SuperView?.GetAttributeForRole (HasFocus ? VisualRole.HotFocus : VisualRole.HotNormal) ?? Attribute.Default; + args.Result = SuperView?.GetAttributeForRole (HasFocus ? VisualRole.HotFocus : VisualRole.HotNormal) + ?? Attribute.Default; args.Handled = true; } }; + KeyView.ClearingViewport += (sender, args) => - { - // Do not clear; otherwise spaces will be printed with underlines - args.Cancel = true; - }; + { + // Do not clear; otherwise spaces will be printed with underlines + args.Cancel = true; + }; } private void UpdateKeyBindings (Key oldKey) @@ -709,11 +732,11 @@ private void UpdateKeyBindings (Key oldKey) { if (oldKey != Key.Empty) { - Application.KeyBindings.Remove (oldKey); + App?.Keyboard.KeyBindings.Remove (oldKey); } - Application.KeyBindings.Remove (Key); - Application.KeyBindings.Add (Key, this, Command.HotKey); + App?.Keyboard.KeyBindings.Remove (Key); + App?.Keyboard.KeyBindings.Add (Key, this, Command.HotKey); } else { @@ -746,7 +769,7 @@ public bool ForceFocusColors } } - /// + /// protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attribute currentAttribute) { if (!HasFocus) @@ -760,6 +783,7 @@ protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attri return true; } + if (role == VisualRole.HotNormal) { currentAttribute = GetAttributeForRole (VisualRole.HotFocus); @@ -807,4 +831,4 @@ protected override void Dispose (bool disposing) base.Dispose (disposing); } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Slider/Slider.cs b/Terminal.Gui/Views/Slider/Slider.cs index ff0308a560..3f379790a3 100644 --- a/Terminal.Gui/Views/Slider/Slider.cs +++ b/Terminal.Gui/Views/Slider/Slider.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Transactions; namespace Terminal.Gui.Views; @@ -1311,7 +1312,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent) { _dragPosition = mouseEvent.Position; _moveRenderPosition = ClampMovePosition ((Point)_dragPosition); - Application.Mouse.GrabMouse (this); + App?.Mouse.GrabMouse (this); } SetNeedsDraw (); @@ -1357,7 +1358,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent) || mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)) { // End Drag - Application.Mouse.UngrabMouse (); + App?.Mouse.UngrabMouse (); _dragPosition = null; _moveRenderPosition = null; diff --git a/Terminal.Gui/Views/Slider/SliderAttributes.cs b/Terminal.Gui/Views/Slider/SliderAttributes.cs index db7f199dc0..c57c8ab5ac 100644 --- a/Terminal.Gui/Views/Slider/SliderAttributes.cs +++ b/Terminal.Gui/Views/Slider/SliderAttributes.cs @@ -11,4 +11,4 @@ public class SliderAttributes /// Attribute for when the respective Option is Set. public Attribute? SetAttribute { get; set; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Slider/SliderConfiguration.cs b/Terminal.Gui/Views/Slider/SliderConfiguration.cs index 2e75ada0c5..1b9354a482 100644 --- a/Terminal.Gui/Views/Slider/SliderConfiguration.cs +++ b/Terminal.Gui/Views/Slider/SliderConfiguration.cs @@ -1,4 +1,4 @@ - + namespace Terminal.Gui.Views; /// All configuration are grouped in this class. @@ -17,4 +17,4 @@ internal class SliderConfiguration internal int _startSpacing; internal SliderType _type = SliderType.Single; internal bool _useMinimumSize; -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Slider/SliderEventArgs.cs b/Terminal.Gui/Views/Slider/SliderEventArgs.cs index b4f93ca120..0b55b40129 100644 --- a/Terminal.Gui/Views/Slider/SliderEventArgs.cs +++ b/Terminal.Gui/Views/Slider/SliderEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// for events. @@ -21,4 +22,4 @@ public SliderEventArgs (Dictionary> options, int focused = /// Gets/sets whether the option is set or not. public Dictionary> Options { get; set; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Slider/SliderOption.cs b/Terminal.Gui/Views/Slider/SliderOption.cs index 95a929d6aa..d5207c3015 100644 --- a/Terminal.Gui/Views/Slider/SliderOption.cs +++ b/Terminal.Gui/Views/Slider/SliderOption.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Represents an option in a . @@ -47,4 +48,4 @@ public SliderOption (string legend, Rune legendAbbr, T data) /// To Raise the event from the Slider. internal void OnUnSet () { UnSet?.Invoke (this, new (false)); } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Slider/SliderOptionEventArgs.cs b/Terminal.Gui/Views/Slider/SliderOptionEventArgs.cs index 8014c2a779..5176f96015 100644 --- a/Terminal.Gui/Views/Slider/SliderOptionEventArgs.cs +++ b/Terminal.Gui/Views/Slider/SliderOptionEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// for events. @@ -9,4 +10,4 @@ public class SliderOptionEventArgs : EventArgs /// Gets whether the option is set or not. public bool IsSet { get; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Slider/SliderStyle.cs b/Terminal.Gui/Views/Slider/SliderStyle.cs index 38c6570c36..aa5557116c 100644 --- a/Terminal.Gui/Views/Slider/SliderStyle.cs +++ b/Terminal.Gui/Views/Slider/SliderStyle.cs @@ -33,4 +33,4 @@ public class SliderStyle /// The glyph and the attribute used for the start of ranges on the slider. public Cell StartRangeChar { get; set; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/Slider/SliderType.cs b/Terminal.Gui/Views/Slider/SliderType.cs index 7ad287a976..7715aa3c3e 100644 --- a/Terminal.Gui/Views/Slider/SliderType.cs +++ b/Terminal.Gui/Views/Slider/SliderType.cs @@ -37,4 +37,4 @@ public enum SliderType /// /// Range -} \ No newline at end of file +} diff --git a/Terminal.Gui/Views/SpinnerView/SpinnerView.cs b/Terminal.Gui/Views/SpinnerView/SpinnerView.cs index 00f723fe69..be1337591c 100644 --- a/Terminal.Gui/Views/SpinnerView/SpinnerView.cs +++ b/Terminal.Gui/Views/SpinnerView/SpinnerView.cs @@ -1,4 +1,4 @@ -#nullable enable + //------------------------------------------------------------------------------ // Windows Terminal supports Unicode and Emoji characters, but by default @@ -114,7 +114,7 @@ public SpinnerStyle Style /// Advances the animation frame and notifies main loop that repainting needs to happen. Repeated calls are /// ignored based on . /// - /// Ensure this method is called on the main UI thread e.g. via + /// Ensure this method is called on the main UI thread e.g. via public void AdvanceAnimation (bool setNeedsDraw = true) { if (DateTime.Now - _lastRender > TimeSpan.FromMilliseconds (SpinDelay)) @@ -202,16 +202,16 @@ protected override void Dispose (bool disposing) private void AddAutoSpinTimeout () { // Only add timeout if we are initialized and not already spinning - if (_timeout is { } || !Application.Initialized) + if (App is { } && (_timeout is { } || !App.Initialized)) { return; } - _timeout = Application.AddTimeout ( + _timeout = App?.AddTimeout ( TimeSpan.FromMilliseconds (SpinDelay), () => { - Application.Invoke (() => AdvanceAnimation ()); + App.Invoke ((_) => AdvanceAnimation ()); return true; } @@ -266,7 +266,7 @@ private void RemoveAutoSpinTimeout () { if (_timeout is { }) { - Application.RemoveTimeout (_timeout); + App?.RemoveTimeout (_timeout); _timeout = null; } } diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs index 3d58a7afc6..cd97abd060 100644 --- a/Terminal.Gui/Views/StatusBar.cs +++ b/Terminal.Gui/Views/StatusBar.cs @@ -1,6 +1,3 @@ -#nullable enable - - namespace Terminal.Gui.Views; /// @@ -137,12 +134,14 @@ bool IDesignable.EnableForDesign () button1.Accepting += OnButtonClicked; Add (button1); - shortcut.Accepting += (s, e) => - { - button1.Visible = !button1.Visible; - button1.Enabled = button1.Visible; - e.Handled = false; - }; +#pragma warning disable TGUI001 + shortcut.Accepting += (_, e) => + { + button1.Visible = !button1.Visible; + button1.Enabled = button1.Visible; + e.Handled = false; + }; +#pragma warning restore TGUI001 Add (new Label { @@ -155,7 +154,7 @@ bool IDesignable.EnableForDesign () { Text = "Or me!", }; - button2.Accepting += (s, e) => Application.RequestStop (); + button2.Accepting += (s, e) => App?.RequestStop (); Add (button2); diff --git a/Terminal.Gui/Views/TabView/Tab.cs b/Terminal.Gui/Views/TabView/Tab.cs index f0025b9831..30562810d7 100644 --- a/Terminal.Gui/Views/TabView/Tab.cs +++ b/Terminal.Gui/Views/TabView/Tab.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TabView/TabChangedEventArgs.cs b/Terminal.Gui/Views/TabView/TabChangedEventArgs.cs index acaa90fd6f..4a769c6880 100644 --- a/Terminal.Gui/Views/TabView/TabChangedEventArgs.cs +++ b/Terminal.Gui/Views/TabView/TabChangedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Describes a change in diff --git a/Terminal.Gui/Views/TabView/TabMouseEventArgs.cs b/Terminal.Gui/Views/TabView/TabMouseEventArgs.cs index f52792549a..94c55803a4 100644 --- a/Terminal.Gui/Views/TabView/TabMouseEventArgs.cs +++ b/Terminal.Gui/Views/TabView/TabMouseEventArgs.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.ComponentModel; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index f85db683a4..5534ac5be5 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TabView/TabStyle.cs b/Terminal.Gui/Views/TabView/TabStyle.cs index 07ac128844..caf059ba04 100644 --- a/Terminal.Gui/Views/TabView/TabStyle.cs +++ b/Terminal.Gui/Views/TabView/TabStyle.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Describes render stylistic selections of a diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index 6c222d9f3b..f401c500b6 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TableView/CellActivatedEventArgs.cs b/Terminal.Gui/Views/TableView/CellActivatedEventArgs.cs index d5dc4eb3e7..cb27a1b43f 100644 --- a/Terminal.Gui/Views/TableView/CellActivatedEventArgs.cs +++ b/Terminal.Gui/Views/TableView/CellActivatedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; // TOOD: SHould support Handled diff --git a/Terminal.Gui/Views/TableView/CellColorGetterArgs.cs b/Terminal.Gui/Views/TableView/CellColorGetterArgs.cs index 2dfcc069ed..48a0b492e0 100644 --- a/Terminal.Gui/Views/TableView/CellColorGetterArgs.cs +++ b/Terminal.Gui/Views/TableView/CellColorGetterArgs.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TableView/CellToggledEventArgs.cs b/Terminal.Gui/Views/TableView/CellToggledEventArgs.cs index 281dbb6f94..f0a88aaae8 100644 --- a/Terminal.Gui/Views/TableView/CellToggledEventArgs.cs +++ b/Terminal.Gui/Views/TableView/CellToggledEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Event args for the event. diff --git a/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapper.cs b/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapper.cs index c058b25571..5d0b41ed95 100644 --- a/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapper.cs +++ b/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapper.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapperByIndex.cs b/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapperByIndex.cs index b82bebb13a..7e1c4ab58f 100644 --- a/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapperByIndex.cs +++ b/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapperByIndex.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Implementation of which records toggled rows by their row number. diff --git a/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapperByObject.cs b/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapperByObject.cs index faa746ced2..87028346f5 100644 --- a/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapperByObject.cs +++ b/Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapperByObject.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/TableView/ColumnStyle.cs b/Terminal.Gui/Views/TableView/ColumnStyle.cs index e9afecaa72..8a1ca6526d 100644 --- a/Terminal.Gui/Views/TableView/ColumnStyle.cs +++ b/Terminal.Gui/Views/TableView/ColumnStyle.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TableView/DataTableSource.cs b/Terminal.Gui/Views/TableView/DataTableSource.cs index 551b38b05c..e8ac64b2ee 100644 --- a/Terminal.Gui/Views/TableView/DataTableSource.cs +++ b/Terminal.Gui/Views/TableView/DataTableSource.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Data; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TableView/EnumerableTableSource.cs b/Terminal.Gui/Views/TableView/EnumerableTableSource.cs index a771ca6fd5..3499a63e1e 100644 --- a/Terminal.Gui/Views/TableView/EnumerableTableSource.cs +++ b/Terminal.Gui/Views/TableView/EnumerableTableSource.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// implementation that wraps arbitrary data. diff --git a/Terminal.Gui/Views/TableView/IEnumerableTableSource.cs b/Terminal.Gui/Views/TableView/IEnumerableTableSource.cs index c217008362..7827b9c77d 100644 --- a/Terminal.Gui/Views/TableView/IEnumerableTableSource.cs +++ b/Terminal.Gui/Views/TableView/IEnumerableTableSource.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/TableView/ITableSource.cs b/Terminal.Gui/Views/TableView/ITableSource.cs index 4848642da6..c9728efc8a 100644 --- a/Terminal.Gui/Views/TableView/ITableSource.cs +++ b/Terminal.Gui/Views/TableView/ITableSource.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Tabular matrix of data to be displayed in a . diff --git a/Terminal.Gui/Views/TableView/ListColumnStyle.cs b/Terminal.Gui/Views/TableView/ListColumnStyle.cs index cce3376b01..e805e6338a 100644 --- a/Terminal.Gui/Views/TableView/ListColumnStyle.cs +++ b/Terminal.Gui/Views/TableView/ListColumnStyle.cs @@ -1,4 +1,4 @@ - + namespace Terminal.Gui.Views; /// Defines rendering options that affect how the view is displayed. diff --git a/Terminal.Gui/Views/TableView/ListTableSource.cs b/Terminal.Gui/Views/TableView/ListTableSource.cs index bc07d404ff..7f535c3001 100644 --- a/Terminal.Gui/Views/TableView/ListTableSource.cs +++ b/Terminal.Gui/Views/TableView/ListTableSource.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Collections; using System.Data; diff --git a/Terminal.Gui/Views/TableView/RowColorGetterArgs.cs b/Terminal.Gui/Views/TableView/RowColorGetterArgs.cs index 6335a484d7..c17e0c958e 100644 --- a/Terminal.Gui/Views/TableView/RowColorGetterArgs.cs +++ b/Terminal.Gui/Views/TableView/RowColorGetterArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/TableView/SelectedCellChangedEventArgs.cs b/Terminal.Gui/Views/TableView/SelectedCellChangedEventArgs.cs index 4aad9ccae6..733f32b3d6 100644 --- a/Terminal.Gui/Views/TableView/SelectedCellChangedEventArgs.cs +++ b/Terminal.Gui/Views/TableView/SelectedCellChangedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Defines the event arguments for diff --git a/Terminal.Gui/Views/TableView/TableSelection.cs b/Terminal.Gui/Views/TableView/TableSelection.cs index f6217ef0b1..2fe21f9cd4 100644 --- a/Terminal.Gui/Views/TableView/TableSelection.cs +++ b/Terminal.Gui/Views/TableView/TableSelection.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Describes a selected region of the table diff --git a/Terminal.Gui/Views/TableView/TableStyle.cs b/Terminal.Gui/Views/TableView/TableStyle.cs index e52cddf1f1..8640fa6943 100644 --- a/Terminal.Gui/Views/TableView/TableStyle.cs +++ b/Terminal.Gui/Views/TableView/TableStyle.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index 272e7495d3..f09532ed2f 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Data; using System.Globalization; diff --git a/Terminal.Gui/Views/TableView/TreeTableSource.cs b/Terminal.Gui/Views/TableView/TreeTableSource.cs index 6cbb5b0a73..8c69253995 100644 --- a/Terminal.Gui/Views/TableView/TreeTableSource.cs +++ b/Terminal.Gui/Views/TableView/TreeTableSource.cs @@ -1,4 +1,5 @@ +#nullable disable namespace Terminal.Gui.Views; /// An with expandable rows. diff --git a/Terminal.Gui/Views/TextInput/ContentsChangedEventArgs.cs b/Terminal.Gui/Views/TextInput/ContentsChangedEventArgs.cs index 23e1a5daba..58eac91603 100644 --- a/Terminal.Gui/Views/TextInput/ContentsChangedEventArgs.cs +++ b/Terminal.Gui/Views/TextInput/ContentsChangedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable // TextView.cs: multi-line text editing namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/DateField.cs b/Terminal.Gui/Views/TextInput/DateField.cs index 2da26f9a05..74c48f5f3c 100644 --- a/Terminal.Gui/Views/TextInput/DateField.cs +++ b/Terminal.Gui/Views/TextInput/DateField.cs @@ -1,4 +1,4 @@ -#nullable enable + // // DateField.cs: text entry for date diff --git a/Terminal.Gui/Views/TextInput/HistoryText.cs b/Terminal.Gui/Views/TextInput/HistoryText.cs index 62fd65c8bc..7b04f16768 100644 --- a/Terminal.Gui/Views/TextInput/HistoryText.cs +++ b/Terminal.Gui/Views/TextInput/HistoryText.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/HistoryTextItemEventArgs.cs b/Terminal.Gui/Views/TextInput/HistoryTextItemEventArgs.cs index 875c039a38..bf67d14c21 100644 --- a/Terminal.Gui/Views/TextInput/HistoryTextItemEventArgs.cs +++ b/Terminal.Gui/Views/TextInput/HistoryTextItemEventArgs.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/ITextValidateProvider.cs b/Terminal.Gui/Views/TextInput/ITextValidateProvider.cs index e25306cd38..03c614da9c 100644 --- a/Terminal.Gui/Views/TextInput/ITextValidateProvider.cs +++ b/Terminal.Gui/Views/TextInput/ITextValidateProvider.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs b/Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs index f082807c61..0ac0659cd6 100644 --- a/Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs +++ b/Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.ComponentModel; +using System.ComponentModel; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/TextEditingLineStatus.cs b/Terminal.Gui/Views/TextInput/TextEditingLineStatus.cs index e71f6de1d6..0259b15ee3 100644 --- a/Terminal.Gui/Views/TextInput/TextEditingLineStatus.cs +++ b/Terminal.Gui/Views/TextInput/TextEditingLineStatus.cs @@ -9,6 +9,7 @@ /// line. /// // ReSharper disable once CheckNamespace +#nullable disable public enum TextEditingLineStatus { /// diff --git a/Terminal.Gui/Views/TextInput/TextField.cs b/Terminal.Gui/Views/TextInput/TextField.cs index bc2390fb62..37a92062b2 100644 --- a/Terminal.Gui/Views/TextInput/TextField.cs +++ b/Terminal.Gui/Views/TextInput/TextField.cs @@ -1,3 +1,4 @@ +#nullable disable using System.Globalization; namespace Terminal.Gui.Views; @@ -418,9 +419,6 @@ public TextField () KeyBindings.Remove (Key.Space); _currentCulture = Thread.CurrentThread.CurrentUICulture; - - CreateContextMenu (); - KeyBindings.Add (ContextMenu.Key, Command.Context); } /// @@ -864,16 +862,16 @@ protected override bool OnMouseEvent (MouseEventArgs ev) _isButtonReleased = false; PrepareSelection (x); - if (Application.Mouse.MouseGrabView is null) + if (App?.Mouse.MouseGrabView is null) { - Application.Mouse.GrabMouse (this); + App?.Mouse.GrabMouse (this); } } else if (ev.Flags == MouseFlags.Button1Released) { _isButtonReleased = true; _isButtonPressed = false; - Application.Mouse.UngrabMouse (); + App?.Mouse.UngrabMouse (); } else if (ev.Flags == MouseFlags.Button1DoubleClicked) { @@ -1016,13 +1014,10 @@ protected override bool OnDrawingContent () /// protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view) { - if (Application.Mouse.MouseGrabView is { } && Application.Mouse.MouseGrabView == this) + if (App?.Mouse.MouseGrabView is { } && App?.Mouse.MouseGrabView == this) { - Application.Mouse.UngrabMouse (); + App?.Mouse.UngrabMouse (); } - - //if (SelectedLength != 0 && !(Application.Mouse.MouseGrabView is MenuBar)) - // ClearAllSelection (); } /// @@ -1254,6 +1249,7 @@ private void CreateContextMenu () menu.KeyChanged += ContextMenu_KeyChanged; ContextMenu = menu; + App?.Popover.Register (ContextMenu); } private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace (e.OldKey.KeyCode, e.NewKey.KeyCode); } @@ -1730,10 +1726,7 @@ private void RenderCaption () GetAttributeForRole (VisualRole.Editable).Style | TextStyle.Underline); // Use TitleTextFormatter to render the caption with hotkey support - TitleTextFormatter.Draw ( - ViewportToScreen (new Rectangle (0, 0, Viewport.Width, 1)), - captionAttribute, - hotKeyAttribute); + TitleTextFormatter.Draw (driver: Driver, screen: ViewportToScreen (new Rectangle (0, 0, Viewport.Width, 1)), normalColor: captionAttribute, hotColor: hotKeyAttribute); } private void SetClipboard (IEnumerable text) @@ -1770,11 +1763,6 @@ private void ShowContextMenu (bool keyboard) if (!Equals (_currentCulture, Thread.CurrentThread.CurrentUICulture)) { _currentCulture = Thread.CurrentThread.CurrentUICulture; - - if (ContextMenu is { }) - { - CreateContextMenu (); - } } if (keyboard) @@ -1817,6 +1805,10 @@ private void TextField_Initialized (object sender, EventArgs e) Autocomplete.HostControl = this; Autocomplete.PopupInsideContainer = false; } + + CreateContextMenu (); + KeyBindings.Add (ContextMenu?.Key, Command.Context); + } private void DisposeContextMenu () diff --git a/Terminal.Gui/Views/TextInput/TextModel.cs b/Terminal.Gui/Views/TextInput/TextModel.cs index 275c8a9083..81d07848ab 100644 --- a/Terminal.Gui/Views/TextInput/TextModel.cs +++ b/Terminal.Gui/Views/TextInput/TextModel.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/TextRegexProvider.cs b/Terminal.Gui/Views/TextInput/TextRegexProvider.cs index 753c8c1bc6..34ad5e6ffa 100644 --- a/Terminal.Gui/Views/TextInput/TextRegexProvider.cs +++ b/Terminal.Gui/Views/TextInput/TextRegexProvider.cs @@ -1,4 +1,4 @@ -#nullable enable + using System.Text.RegularExpressions; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/TextValidateField.cs b/Terminal.Gui/Views/TextInput/TextValidateField.cs index f7e897feb2..db4cbbd547 100644 --- a/Terminal.Gui/Views/TextInput/TextValidateField.cs +++ b/Terminal.Gui/Views/TextInput/TextValidateField.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/TextView.cs b/Terminal.Gui/Views/TextInput/TextView.cs index f9e2031d94..8b5da5ef38 100644 --- a/Terminal.Gui/Views/TextInput/TextView.cs +++ b/Terminal.Gui/Views/TextInput/TextView.cs @@ -1,6 +1,3 @@ -#nullable enable - -// TextView.cs: multi-line text editing using System.Globalization; using System.Runtime.CompilerServices; @@ -116,7 +113,7 @@ public TextView () Used = true; // By default, disable hotkeys (in case someone sets Title) - HotKeySpecifier = new ('\xffff'); + base.HotKeySpecifier = new ('\xffff'); _model.LinesLoaded += Model_LinesLoaded!; _historyText.ChangeText += HistoryText_ChangeText!; @@ -629,9 +626,6 @@ public TextView () #endif _currentCulture = Thread.CurrentThread.CurrentUICulture; - - ContextMenu = CreateContextMenu (); - KeyBindings.Add (ContextMenu.Key, Command.Context); } // BUGBUG: AllowsReturn is mis-named. It should be EnterKeyAccepts. @@ -1676,15 +1670,15 @@ protected override bool OnMouseEvent (MouseEventArgs ev) _lastWasKill = false; _columnTrack = CurrentColumn; - if (Application.Mouse.MouseGrabView is null) + if (App?.Mouse.MouseGrabView is null) { - Application.Mouse.GrabMouse (this); + App?.Mouse.GrabMouse (this); } } else if (ev.Flags.HasFlag (MouseFlags.Button1Released)) { _isButtonReleased = true; - Application.Mouse.UngrabMouse (); + App?.Mouse.UngrabMouse (); } else if (ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked)) { @@ -1886,9 +1880,9 @@ protected override bool OnDrawingContent () /// protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view) { - if (Application.Mouse.MouseGrabView is { } && Application.Mouse.MouseGrabView == this) + if (App?.Mouse.MouseGrabView is { } && App?.Mouse.MouseGrabView == this) { - Application.Mouse.UngrabMouse (); + App?.Mouse.UngrabMouse (); } } @@ -2027,12 +2021,12 @@ public void Paste () { ProcessAutocomplete (); - if (!CanFocus || !Enabled || Application.Driver is null) + if (!CanFocus || !Enabled || Driver is null) { return null; } - if (Application.Mouse.MouseGrabView == this && IsSelecting) + if (App?.Mouse.MouseGrabView == this && IsSelecting) { // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method. //var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Viewport.Height); @@ -4578,6 +4572,7 @@ private void ShowContextMenu (Point? mousePosition) { mousePosition = ViewportToScreen (new Point (CursorPosition.X, CursorPosition.Y)); } + ContextMenu?.MakeVisible (mousePosition); } @@ -4653,6 +4648,11 @@ private void TextView_Initialized (object sender, EventArgs e) Autocomplete.HostControl = this; } + + ContextMenu = CreateContextMenu (); + App?.Popover?.Register (ContextMenu); + KeyBindings.Add (ContextMenu.Key, Command.Context); + OnContentsChanged (); } @@ -4790,15 +4790,15 @@ protected override void Dispose (bool disposing) public class TextViewAutocomplete : PopupAutocomplete { /// - protected override void DeleteTextBackwards () { ((TextView)HostControl).DeleteCharLeft (); } + protected override void DeleteTextBackwards () { ((TextView)HostControl!).DeleteCharLeft (); } /// - protected override void InsertText (string accepted) { ((TextView)HostControl).InsertText (accepted); } + protected override void InsertText (string accepted) { ((TextView)HostControl!).InsertText (accepted); } /// protected override void SetCursorPosition (int column) { - ((TextView)HostControl).CursorPosition = + ((TextView)HostControl!).CursorPosition = new (column, ((TextView)HostControl).CurrentRow); } } diff --git a/Terminal.Gui/Views/TextInput/TimeField.cs b/Terminal.Gui/Views/TextInput/TimeField.cs index 59a906f63f..e61e48d07b 100644 --- a/Terminal.Gui/Views/TextInput/TimeField.cs +++ b/Terminal.Gui/Views/TextInput/TimeField.cs @@ -5,6 +5,7 @@ // // Licensed under the MIT license +#nullable disable using System.Globalization; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TextInput/WordWrapManager.cs b/Terminal.Gui/Views/TextInput/WordWrapManager.cs index ced8c2a7c1..705e075275 100644 --- a/Terminal.Gui/Views/TextInput/WordWrapManager.cs +++ b/Terminal.Gui/Views/TextInput/WordWrapManager.cs @@ -1,4 +1,3 @@ -#nullable enable namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs index 926eb6aefa..9f3854f7e1 100644 --- a/Terminal.Gui/Views/Toplevel.cs +++ b/Terminal.Gui/Views/Toplevel.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Views; /// @@ -9,15 +7,15 @@ namespace Terminal.Gui.Views; /// /// /// Toplevel views can run as modal (popup) views, started by calling -/// . They return control to the caller when -/// has been called (which sets the +/// . They return control to the caller when +/// has been called (which sets the /// property to false). /// /// -/// A Toplevel is created when an application initializes Terminal.Gui by calling . -/// The application Toplevel can be accessed via . Additional Toplevels can be created +/// A Toplevel is created when an application initializes Terminal.Gui by calling . +/// The application Toplevel can be accessed via . Additional Toplevels can be created /// and run (e.g. s). To run a Toplevel, create the and call -/// . +/// . /// /// public partial class Toplevel : View @@ -84,12 +82,12 @@ public Toplevel () // TODO: IRunnable: Re-implement as a property on IRunnable /// Gets or sets whether the main loop for this is running or not. - /// Setting this property directly is discouraged. Use instead. + /// Setting this property directly is discouraged. Use instead. public bool Running { get; set; } // TODO: IRunnable: Re-implement in IRunnable /// - /// if was already loaded by the + /// if was already loaded by the /// , otherwise. /// public bool IsLoaded { get; private set; } @@ -102,12 +100,12 @@ public Toplevel () /// Invoked when the Toplevel ceases to be active. public event EventHandler? Deactivate; - /// Invoked when the Toplevel's is closed by . + /// Invoked when the Toplevel's is closed by . public event EventHandler? Closed; /// /// Invoked when the Toplevel's is being closed by - /// . + /// . /// public event EventHandler? Closing; @@ -118,7 +116,7 @@ public Toplevel () public event EventHandler? Loaded; /// - /// Called from before the redraws for the first + /// Called from before the redraws for the first /// time. /// /// @@ -143,23 +141,23 @@ public virtual void OnLoaded () /// perform tasks when the has been laid out and focus has been set. changes. /// /// A Ready event handler is a good place to finalize initialization after calling - /// on this . + /// on this . /// /// public event EventHandler? Ready; /// /// Stops and closes this . If this Toplevel is the top-most Toplevel, - /// will be called, causing the application to exit. + /// will be called, causing the application to exit. /// public virtual void RequestStop () { - Application.RequestStop (Application.Top); + App?.RequestStop (App?.Current); } /// /// Invoked when the Toplevel has been unloaded. A Unloaded event handler is a good place - /// to dispose objects after calling . + /// to dispose objects after calling . /// public event EventHandler? Unloaded; @@ -191,7 +189,7 @@ internal virtual void OnReady () Ready?.Invoke (this, EventArgs.Empty); } - /// Called from before the is disposed. + /// Called from before the is disposed. internal virtual void OnUnloaded () { foreach (var view in SubViews.Where (v => v is Toplevel)) @@ -246,7 +244,7 @@ out int ny } // BUGBUG: The && true is a temp hack - if ((superView != top || top?.SuperView is { } || (top != Application.Top && top!.Modal) || (top == Application.Top && top?.SuperView is null)) + if ((superView != top || top?.SuperView is { } || (top != App?.Current && top!.Modal) || (top == App?.Current && top?.SuperView is null)) && (top!.Frame.X + top.Frame.Width > maxWidth || ny > top.Frame.Y)) { diff --git a/Terminal.Gui/Views/ToplevelEventArgs.cs b/Terminal.Gui/Views/ToplevelEventArgs.cs index 0c8d15fc76..52e02d0c88 100644 --- a/Terminal.Gui/Views/ToplevelEventArgs.cs +++ b/Terminal.Gui/Views/ToplevelEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TreeView/AspectGetterDelegate.cs b/Terminal.Gui/Views/TreeView/AspectGetterDelegate.cs index 4382da3008..df04b90472 100644 --- a/Terminal.Gui/Views/TreeView/AspectGetterDelegate.cs +++ b/Terminal.Gui/Views/TreeView/AspectGetterDelegate.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Delegates of this type are used to fetch string representations of user's model objects diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index 811824dbfc..9c48a0a0df 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TreeView/DelegateTreeBuilder.cs b/Terminal.Gui/Views/TreeView/DelegateTreeBuilder.cs index 131dff2d65..7bc59d1b09 100644 --- a/Terminal.Gui/Views/TreeView/DelegateTreeBuilder.cs +++ b/Terminal.Gui/Views/TreeView/DelegateTreeBuilder.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Implementation of that uses user defined functions diff --git a/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs b/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs index 1ef543eeb4..2b27697d01 100644 --- a/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs +++ b/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable // This code is based on http://objectlistview.sourceforge.net (GPLv3 tree/list controls // by phillip.piper@gmail.com). Phillip has explicitly granted permission for his design // and code to be used in this library under the MIT license. diff --git a/Terminal.Gui/Views/TreeView/ITreeBuilder.cs b/Terminal.Gui/Views/TreeView/ITreeBuilder.cs index f03129525b..c4c4be1dda 100644 --- a/Terminal.Gui/Views/TreeView/ITreeBuilder.cs +++ b/Terminal.Gui/Views/TreeView/ITreeBuilder.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/TreeView/ITreeViewFilter.cs b/Terminal.Gui/Views/TreeView/ITreeViewFilter.cs index dde9a3a98c..3285d6ed16 100644 --- a/Terminal.Gui/Views/TreeView/ITreeViewFilter.cs +++ b/Terminal.Gui/Views/TreeView/ITreeViewFilter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Provides filtering for a . diff --git a/Terminal.Gui/Views/TreeView/ObjectActivatedEventArgs.cs b/Terminal.Gui/Views/TreeView/ObjectActivatedEventArgs.cs index 195f629452..ac9685342a 100644 --- a/Terminal.Gui/Views/TreeView/ObjectActivatedEventArgs.cs +++ b/Terminal.Gui/Views/TreeView/ObjectActivatedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Event args for the event diff --git a/Terminal.Gui/Views/TreeView/SelectionChangedEventArgs.cs b/Terminal.Gui/Views/TreeView/SelectionChangedEventArgs.cs index 4b9eb93b9d..d0efe03925 100644 --- a/Terminal.Gui/Views/TreeView/SelectionChangedEventArgs.cs +++ b/Terminal.Gui/Views/TreeView/SelectionChangedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Event arguments describing a change in selected object in a tree view diff --git a/Terminal.Gui/Views/TreeView/TreeBuilder.cs b/Terminal.Gui/Views/TreeView/TreeBuilder.cs index afb7f5e186..f17e5a92c7 100644 --- a/Terminal.Gui/Views/TreeView/TreeBuilder.cs +++ b/Terminal.Gui/Views/TreeView/TreeBuilder.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// Abstract implementation of . diff --git a/Terminal.Gui/Views/TreeView/TreeNode.cs b/Terminal.Gui/Views/TreeView/TreeNode.cs index 3377b92acf..750c1792b1 100644 --- a/Terminal.Gui/Views/TreeView/TreeNode.cs +++ b/Terminal.Gui/Views/TreeView/TreeNode.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/TreeView/TreeNodeBuilder.cs b/Terminal.Gui/Views/TreeView/TreeNodeBuilder.cs index 08dc931085..f09775a53a 100644 --- a/Terminal.Gui/Views/TreeView/TreeNodeBuilder.cs +++ b/Terminal.Gui/Views/TreeView/TreeNodeBuilder.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// implementation for objects diff --git a/Terminal.Gui/Views/TreeView/TreeStyle.cs b/Terminal.Gui/Views/TreeView/TreeStyle.cs index fe5eea7c47..14eb193f9b 100644 --- a/Terminal.Gui/Views/TreeView/TreeStyle.cs +++ b/Terminal.Gui/Views/TreeView/TreeStyle.cs @@ -1,3 +1,4 @@ +#nullable disable  namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index c640b4e250..fd33ad0ff9 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -2,6 +2,7 @@ // by phillip.piper@gmail.com). Phillip has explicitly granted permission for his design // and code to be used in this library under the MIT license. +#nullable disable using System.Collections.ObjectModel; namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/TreeView/TreeViewTextFilter.cs b/Terminal.Gui/Views/TreeView/TreeViewTextFilter.cs index 4be43e00a5..f79e6e674f 100644 --- a/Terminal.Gui/Views/TreeView/TreeViewTextFilter.cs +++ b/Terminal.Gui/Views/TreeView/TreeViewTextFilter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// diff --git a/Terminal.Gui/Views/View.cs b/Terminal.Gui/Views/View.cs deleted file mode 100644 index 0519ecba6e..0000000000 --- a/Terminal.Gui/Views/View.cs +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Terminal.Gui/Views/Window.cs b/Terminal.Gui/Views/Window.cs index f2b51ce40c..b374459a28 100644 --- a/Terminal.Gui/Views/Window.cs +++ b/Terminal.Gui/Views/Window.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.Gui/Views/Wizard/Wizard.cs b/Terminal.Gui/Views/Wizard/Wizard.cs index a9b011451f..fe3cfcc631 100644 --- a/Terminal.Gui/Views/Wizard/Wizard.cs +++ b/Terminal.Gui/Views/Wizard/Wizard.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Terminal.Gui.Views; /// @@ -44,7 +42,7 @@ namespace Terminal.Gui.Views; /// Application.RequestStop(); /// }; /// -/// Application.Top.Add (wizard); +/// Application.Current.Add (wizard); /// Application.Run (); /// Application.Shutdown (); /// @@ -123,7 +121,7 @@ public WizardStep? CurrentStep /// /// /// If a non-Modal Wizard is added to the application after - /// has + /// has /// been called the first step must be explicitly set by setting to /// : /// @@ -374,7 +372,7 @@ public bool GoToStep (WizardStep? newStep) /// /// is derived from and Dialog causes Esc to call - /// , closing the Dialog. Wizard overrides + /// , closing the Dialog. Wizard overrides /// to instead fire the event when Wizard is being used as a /// non-modal (see ). /// diff --git a/Terminal.Gui/Views/Wizard/WizardEventArgs.cs b/Terminal.Gui/Views/Wizard/WizardEventArgs.cs index 85444fd309..4aac631621 100644 --- a/Terminal.Gui/Views/Wizard/WizardEventArgs.cs +++ b/Terminal.Gui/Views/Wizard/WizardEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable namespace Terminal.Gui.Views; /// for transition events. diff --git a/Terminal.Gui/Views/Wizard/WizardStep.cs b/Terminal.Gui/Views/Wizard/WizardStep.cs index 6e211b1e80..56dd09a94c 100644 --- a/Terminal.Gui/Views/Wizard/WizardStep.cs +++ b/Terminal.Gui/Views/Wizard/WizardStep.cs @@ -1,4 +1,4 @@ -#nullable enable + namespace Terminal.Gui.Views; diff --git a/Terminal.sln b/Terminal.sln index 983bfdd290..050122e35e 100644 --- a/Terminal.sln +++ b/Terminal.sln @@ -27,6 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Settings", "Settings", "{B8 nuget.config = nuget.config Terminal.sln.DotSettings = Terminal.sln.DotSettings testenvironments.json = testenvironments.json + docfx\schemas\tui-config-schema.json = docfx\schemas\tui-config-schema.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{13BB2C46-B324-4B9C-92EB-CE6184D4736E}" @@ -45,11 +46,43 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{13BB2C EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{C7A51224-5E0F-4986-AB37-A6BF89966C12}" ProjectSection(SolutionItems) = preProject + docfx\docs\ansiparser.md = docfx\docs\ansiparser.md + docfx\docs\application.md = docfx\docs\application.md + docfx\docs\arrangement.md = docfx\docs\arrangement.md + docfx\docs\cancellable-work-pattern.md = docfx\docs\cancellable-work-pattern.md + docfx\docs\CharacterMap.md = docfx\docs\CharacterMap.md .github\CODEOWNERS = .github\CODEOWNERS CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md + docfx\docs\command.md = docfx\docs\command.md + docfx\docs\config.md = docfx\docs\config.md CONTRIBUTING.md = CONTRIBUTING.md .github\copilot-instructions.md = .github\copilot-instructions.md + docfx\docs\cursor.md = docfx\docs\cursor.md + docfx\docs\dimauto.md = docfx\docs\dimauto.md + docfx\docs\drawing.md = docfx\docs\drawing.md + docfx\docs\drivers.md = docfx\docs\drivers.md + docfx\docs\events.md = docfx\docs\events.md + docfx\docs\getting-started.md = docfx\docs\getting-started.md + docfx\docs\index.md = docfx\docs\index.md + docfx\docs\keyboard.md = docfx\docs\keyboard.md + docfx\docs\layout.md = docfx\docs\layout.md + docfx\docs\lexicon.md = docfx\docs\lexicon.md + docfx\docs\logging.md = docfx\docs\logging.md + docfx\docs\migratingfromv1.md = docfx\docs\migratingfromv1.md + docfx\docs\mouse.md = docfx\docs\mouse.md + docfx\docs\multitasking.md = docfx\docs\multitasking.md + docfx\docs\navigation.md = docfx\docs\navigation.md + docfx\docs\newinv2.md = docfx\docs\newinv2.md + docfx\docs\Popovers.md = docfx\docs\Popovers.md README.md = README.md + docfx\docs\scheme.md = docfx\docs\scheme.md + docfx\docs\scrolling.md = docfx\docs\scrolling.md + docfx\docs\showcase.md = docfx\docs\showcase.md + docfx\docs\tableview.md = docfx\docs\tableview.md + docfx\docs\toc.yml = docfx\docs\toc.yml + docfx\docs\treeview.md = docfx\docs\treeview.md + docfx\docs\View.md = docfx\docs\View.md + docfx\docs\views.md = docfx\docs\views.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SelfContained", "Examples\SelfContained\SelfContained.csproj", "{524DEA78-7E7C-474D-B42D-52ED4C04FF14}" @@ -163,10 +196,6 @@ Global {8C643A64-2A77-4432-987A-2E72BD9708E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C643A64-2A77-4432-987A-2E72BD9708E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C643A64-2A77-4432-987A-2E72BD9708E3}.Release|Any CPU.Build.0 = Release|Any CPU - {566AFB59-FF8C-FFF4-C1F4-049B6246E4A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {566AFB59-FF8C-FFF4-C1F4-049B6246E4A7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {566AFB59-FF8C-FFF4-C1F4-049B6246E4A7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {566AFB59-FF8C-FFF4-C1F4-049B6246E4A7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs b/Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs index 770078bb3d..6cc94ec45e 100644 --- a/Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs +++ b/Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs @@ -191,7 +191,7 @@ public void SaveFileDialog_PopTree_AndNavigate_PreserveFilenameOnDirectoryChange SaveDialog? sd = null; MockFileSystem? fs = null; using var c = With.A (() => NewSaveDialog (out sd, out fs, modal: false), 100, 20, d) - .Then (() => sd!.Style.PreserveFilenameOnDirectoryChanges = true) + .Then ((_) => sd!.Style.PreserveFilenameOnDirectoryChanges = true) .ScreenShot ("Save dialog", _out) .AssertTrue (sd!.Canceled) .Focus (_ => true) @@ -230,7 +230,7 @@ public void SaveFileDialog_PopTree_AndNavigate_PreserveFilenameOnDirectoryChange SaveDialog? sd = null; MockFileSystem? fs = null; using var c = With.A (() => NewSaveDialog (out sd, out fs, modal: false), 100, 20, d) - .Then (() => sd!.Style.PreserveFilenameOnDirectoryChanges = false) + .Then ((_) => sd!.Style.PreserveFilenameOnDirectoryChanges = false) .ScreenShot ("Save dialog", _out) .AssertTrue (sd!.Canceled) .Focus (_ => true) @@ -267,7 +267,7 @@ public void SaveFileDialog_TableView_UpDown_PreserveFilenameOnDirectoryChanges_T SaveDialog? sd = null; MockFileSystem? fs = null; using var c = With.A (() => NewSaveDialog (out sd, out fs, modal: false), 100, 20, d) - .Then (() => sd!.Style.PreserveFilenameOnDirectoryChanges = preserve) + .Then ((_) => sd!.Style.PreserveFilenameOnDirectoryChanges = preserve) .ScreenShot ("Save dialog", _out) .AssertTrue (sd!.Canceled) .Focus (_ => true) diff --git a/Tests/IntegrationTests/FluentTests/GuiTestContextKeyEventTests.cs b/Tests/IntegrationTests/FluentTests/GuiTestContextKeyEventTests.cs index 9c7c9b0aa1..51097c74c7 100644 --- a/Tests/IntegrationTests/FluentTests/GuiTestContextKeyEventTests.cs +++ b/Tests/IntegrationTests/FluentTests/GuiTestContextKeyEventTests.cs @@ -16,10 +16,10 @@ public class GuiTestContextKeyEventTests (ITestOutputHelper outputHelper) public void QuitKey_ViaApplication_Stops (TestDriver d) { using GuiTestContext context = With.A (40, 10, d); - Assert.True (Application.Top!.Running); + Assert.True (context.App?.Current!.Running); - Toplevel top = Application.Top; - context.Then (() => Application.RaiseKeyDownEvent (Application.QuitKey)); + Toplevel? top = context.App?.Current; + context.Then ((_) => context!.App?.Keyboard.RaiseKeyDownEvent (Application.QuitKey)); Assert.False (top!.Running); } @@ -28,9 +28,9 @@ public void QuitKey_ViaApplication_Stops (TestDriver d) public void QuitKey_ViaEnqueueKey_Stops (TestDriver d) { using GuiTestContext context = With.A (40, 10, d, _out); - Assert.True (Application.Top!.Running); + Assert.True (context.App?.Current!.Running); - Toplevel top = Application.Top; + Toplevel? top = context.App?.Current; context.EnqueueKeyEvent (Application.QuitKey); Assert.False (top!.Running); @@ -46,7 +46,7 @@ public void EnqueueKey_AfterResizeConsole_StillWorks (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view) - .Then (() => view.SetFocus ()) + .Then ((_) => view.SetFocus ()) .ResizeConsole (50, 20) .EnqueueKeyEvent (Key.A); @@ -62,7 +62,7 @@ public void EnqueueKey_Backspace_DeletesCharacter (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (textField) .Focus (textField) - .Then (() => textField.CursorPosition = textField.Text.Length) + .Then ((_) => textField.CursorPosition = textField.Text.Length) .EnqueueKeyEvent (Key.Backspace) .EnqueueKeyEvent (Key.Backspace); @@ -81,14 +81,14 @@ public void EnqueueKey_ChainedWithOtherOperations_WorksCorrectly (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (textField) .Add (button) - .Then (() => textField.SetFocus ()) + .Then ((_) => textField.SetFocus ()) .EnqueueKeyEvent (Key.T.WithShift) .EnqueueKeyEvent (Key.E) .EnqueueKeyEvent (Key.S) .EnqueueKeyEvent (Key.T) .AssertEqual ("Test", textField.Text) .EnqueueKeyEvent (Key.Tab) - .Then (() => Assert.True (button.HasFocus)) + .Then ((_) => Assert.True (button.HasFocus)) .EnqueueKeyEvent (Key.Enter) .AssertEqual (1, clickedCount); } @@ -110,7 +110,7 @@ public void EnqueueKey_EnqueuesKeyAndProcessesIt (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view) - .Then (() => view.SetFocus ()) + .Then ((_) => view.SetFocus ()) .EnqueueKeyEvent (Key.A); Assert.True (keyReceived, "Key was not received by the view"); @@ -128,7 +128,7 @@ public void EnqueueKey_FunctionKeys_ProcessesCorrectly (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view) - .Then (() => view.SetFocus ()) + .Then ((_) => view.SetFocus ()) .EnqueueKeyEvent (Key.F1) .EnqueueKeyEvent (Key.F5) .EnqueueKeyEvent (Key.F12); @@ -150,7 +150,7 @@ public void EnqueueKey_MultipleKeys_ProcessesInOrder (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view) - .Then (() => view.SetFocus ()) + .Then ((_) => view.SetFocus ()) .EnqueueKeyEvent (Key.A) .EnqueueKeyEvent (Key.B) .EnqueueKeyEvent (Key.C); @@ -171,7 +171,7 @@ public void EnqueueKey_NavigationKeys_ChangeFocus (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view1) .Add (view2) - .Then (() => view1.SetFocus ()) + .Then ((_) => view1.SetFocus ()) .AssertTrue (view1.HasFocus) .AssertFalse (view2.HasFocus) .EnqueueKeyEvent (Key.Tab) @@ -190,7 +190,7 @@ public void EnqueueKey_NumericKeys_ProcessesCorrectly (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (textField) - .Then (() => textField.SetFocus ()) + .Then ((_) => textField.SetFocus ()) .EnqueueKeyEvent (Key.D1) .EnqueueKeyEvent (Key.D2) .EnqueueKeyEvent (Key.D3) @@ -210,7 +210,7 @@ public void EnqueueKey_RapidSequence_ProcessesAllKeys (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view) - .Then (() => view.SetFocus ()); + .Then ((_) => view.SetFocus ()); // Send 10 keys rapidly for (var i = 0; i < 10; i++) @@ -237,7 +237,7 @@ public void EnqueueKey_SpecialKeys_ProcessesCorrectly (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view) - .Then (() => view.SetFocus ()) + .Then ((_) => view.SetFocus ()) .EnqueueKeyEvent (Key.Enter) .EnqueueKeyEvent (Key.Tab) .EnqueueKeyEvent (Key.CursorUp) @@ -266,7 +266,7 @@ public void EnqueueKey_WithListView_NavigatesItems (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (listView) - .Then (() => listView.SetFocus ()) + .Then ((_) => listView.SetFocus ()) .AssertEqual (0, listView.SelectedItem) .EnqueueKeyEvent (Key.CursorDown) .AssertEqual (1, listView.SelectedItem) @@ -293,7 +293,7 @@ public void EnqueueKey_WithModifiers_ProcessesCorrectly (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view) - .Then (() => view.SetFocus ()) + .Then ((_) => view.SetFocus ()) .EnqueueKeyEvent (Key.A.WithCtrl); Assert.True (keyReceived); diff --git a/Tests/IntegrationTests/FluentTests/GuiTestContextMouseEventTests.cs b/Tests/IntegrationTests/FluentTests/GuiTestContextMouseEventTests.cs index a68c171535..0738529449 100644 --- a/Tests/IntegrationTests/FluentTests/GuiTestContextMouseEventTests.cs +++ b/Tests/IntegrationTests/FluentTests/GuiTestContextMouseEventTests.cs @@ -152,7 +152,7 @@ public void EnqueueMouseEvent_Click_SetsFocusOnView (TestDriver d) using GuiTestContext context = With.A (40, 10, d, _out) .Add (view1) .Add (view2) - .Then (() => view1.SetFocus ()) + .Then ((_) => view1.SetFocus ()) .AssertTrue (view1.HasFocus) .LeftClick (25, 7) // Click on view2 .AssertFalse (view1.HasFocus) diff --git a/Tests/IntegrationTests/FluentTests/GuiTestContextTests.cs b/Tests/IntegrationTests/FluentTests/GuiTestContextTests.cs index 91c0fb3dc3..30ea80cd84 100644 --- a/Tests/IntegrationTests/FluentTests/GuiTestContextTests.cs +++ b/Tests/IntegrationTests/FluentTests/GuiTestContextTests.cs @@ -43,8 +43,24 @@ public void ResizeConsole_Resizes (TestDriver d) public void With_New_A_Runs (TestDriver d) { using GuiTestContext context = With.A (40, 10, d, _out); - Assert.True (Application.Top!.Running); - Assert.NotEqual (Rectangle.Empty, Application.Screen); + Assert.True (context.App!.Current!.Running); + Assert.NotEqual (Rectangle.Empty, context.App!.Screen); + } + + [Theory] + [ClassData (typeof (TestDrivers))] + public void AnsiScreenShot_Renders_Ansi_Stream (TestDriver d) + { + using GuiTestContext context = With.A (10, 3, d, _out) + .Then ((app) => + { + app.Current!.BorderStyle = LineStyle.None; + app.Current!.Border!.Thickness = Thickness.Empty; + app.Current.Text = "hello"; + }) + .ScreenShot ("ScreenShot", _out) + .AnsiScreenShot ("AnsiScreenShot", _out) +; } [Theory] diff --git a/Tests/IntegrationTests/FluentTests/MenuBarv2Tests.cs b/Tests/IntegrationTests/FluentTests/MenuBarv2Tests.cs index 5972149298..bc96b6f957 100644 --- a/Tests/IntegrationTests/FluentTests/MenuBarv2Tests.cs +++ b/Tests/IntegrationTests/FluentTests/MenuBarv2Tests.cs @@ -24,7 +24,7 @@ public MenuBarv2Tests (ITestOutputHelper outputHelper) public void Initializes_WithNoItems (TestDriver d) { using GuiTestContext c = With.A (80, 25, d, _out) - .Then (() => + .Then ((_) => { // Create a menu bar with no items var menuBar = new MenuBarv2 (); @@ -42,7 +42,7 @@ public void Initializes_WithItems (TestDriver d) MenuBarItemv2 [] menuItems = []; using GuiTestContext c = With.A (80, 25, d, _out) - .Then (() => + .Then ((_) => { // Create items for the menu bar menuItems = @@ -79,7 +79,7 @@ public void Initializes_WithItems (TestDriver d) public void AddsItems_WithMenusProperty (TestDriver d) { using GuiTestContext c = With.A (80, 25, d, _out) - .Then (() => + .Then ((_) => { var menuBar = new MenuBarv2 (); @@ -100,7 +100,7 @@ public void AddsItems_WithMenusProperty (TestDriver d) public void ChangesKey_RaisesEvent (TestDriver d) { using GuiTestContext c = With.A (80, 25, d, _out) - .Then (() => + .Then ((_) => { var menuBar = new MenuBarv2 (); @@ -137,12 +137,13 @@ public void ChangesKey_RaisesEvent (TestDriver d) public void DefaultKey_Activates (TestDriver d) { MenuBarv2? menuBar = null; + Toplevel? top = null; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((app) => { menuBar = new MenuBarv2 (); - Toplevel top = Application.Top!; + top = app.Current!; top.Add ( new View () @@ -152,16 +153,16 @@ public void DefaultKey_Activates (TestDriver d) }); menuBar.EnableForDesign (ref top); - Application.Top!.Add (menuBar); + app.Current!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (Application.Navigation!.GetFocused ()) + .AssertIsNotType (top?.App?.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) .EnqueueKeyEvent (MenuBarv2.DefaultKey) .WaitIteration () .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) - .AssertEqual ("_New file", Application.Navigation!.GetFocused ()!.Title) - .AssertTrue (Application.Popover?.GetActivePopover () is PopoverMenu) + .AssertEqual ("_New file", top?.App?.Navigation!.GetFocused ()!.Title) + .AssertTrue (top?.App?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (menuBar?.IsOpen ()); } @@ -171,12 +172,13 @@ public void DefaultKey_Activates (TestDriver d) public void DefaultKey_DeActivates (TestDriver d) { MenuBarv2? menuBar = null; - + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((a) => { + app = a; menuBar = new MenuBarv2 (); - Toplevel top = Application.Top!; + Toplevel top = app.Current!; top.Add ( new View () @@ -186,26 +188,28 @@ public void DefaultKey_DeActivates (TestDriver d) }); menuBar.EnableForDesign (ref top); - Application.Top!.Add (menuBar); + app.Current!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (Application.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) .EnqueueKeyEvent (MenuBarv2.DefaultKey) .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) - .AssertEqual ("_New file", Application.Navigation!.GetFocused ()!.Title) + .AssertEqual ("_New file", app?.Navigation!.GetFocused ()!.Title) .EnqueueKeyEvent (MenuBarv2.DefaultKey) .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) - .AssertIsNotType (Application.Navigation!.GetFocused ()); + .AssertIsNotType (app?.Navigation!.GetFocused ()); } [Theory] [ClassData (typeof (TestDrivers))] public void ShowHidePopovers (TestDriver d) { + IApplication? app = null; using GuiTestContext c = With.A (80, 25, d, _out) - .Then (() => + .Then ((a) => { + app = a; // Create a menu bar with items that have submenus var fileMenuItem = new MenuBarItemv2 ( "_File", @@ -214,7 +218,7 @@ public void ShowHidePopovers (TestDriver d) new MenuItemv2 ("_Save", string.Empty, null) ]); - var menuBar = new MenuBarv2 ([fileMenuItem]); + var menuBar = new MenuBarv2 ([fileMenuItem]) { App = app }; // Initially, no menu should be open Assert.False (menuBar.IsOpen ()); @@ -259,13 +263,13 @@ public void ShowHidePopovers (TestDriver d) public void EnableForDesign_CreatesMenuItems (TestDriver d) { using GuiTestContext c = With.A (80, 25, d, _out) - .Then (() => + .Then ((app) => { var menuBar = new MenuBarv2 (); - Application.Top!.Add (menuBar); + app.Current!.Add (menuBar); // Call EnableForDesign - Toplevel top = Application.Top!; + Toplevel top = app.Current!; bool result = menuBar.EnableForDesign (ref top); // Should return true @@ -291,36 +295,38 @@ public void EnableForDesign_CreatesMenuItems (TestDriver d) public void Navigation_Left_Right_Wraps (TestDriver d) { MenuBarv2? menuBar = null; + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((a) => { + app = a; menuBar = new MenuBarv2 (); - Toplevel top = Application.Top!; + Toplevel top = app.Current!; menuBar.EnableForDesign (ref top); - Application.Top!.Add (menuBar); + app.Current!.Add (menuBar); }) .WaitIteration () .ScreenShot ("MenuBar initial state", _out) .EnqueueKeyEvent (MenuBarv2.DefaultKey) - .AssertTrue (Application.Popover?.GetActivePopover () is PopoverMenu) + .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (menuBar?.IsOpen ()) - .AssertEqual ("_New file", Application.Navigation?.GetFocused ()!.Title) + .AssertEqual ("_New file", app?.Navigation?.GetFocused ()!.Title) .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) .EnqueueKeyEvent (Key.CursorRight) - .AssertTrue (Application.Popover?.GetActivePopover () is PopoverMenu) + .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .ScreenShot ("After right arrow", _out) - .AssertEqual ("Cu_t", Application.Navigation?.GetFocused ()!.Title) + .AssertEqual ("Cu_t", app?.Navigation?.GetFocused ()!.Title) .EnqueueKeyEvent (Key.CursorRight) .ScreenShot ("After second right arrow", _out) - .AssertEqual ("_Online Help...", Application.Navigation?.GetFocused ()!.Title) + .AssertEqual ("_Online Help...", app?.Navigation?.GetFocused ()!.Title) .ScreenShot ("After third right arrow", _out) .EnqueueKeyEvent (Key.CursorRight) .ScreenShot ("After fourth right arrow", _out) - .AssertEqual ("_New file", Application.Navigation?.GetFocused ()!.Title) + .AssertEqual ("_New file", app?.Navigation?.GetFocused ()!.Title) .EnqueueKeyEvent (Key.CursorLeft) .ScreenShot ("After left arrow", _out) - .AssertEqual ("_Online Help...", Application.Navigation?.GetFocused ()!.Title); + .AssertEqual ("_Online Help...", app?.Navigation?.GetFocused ()!.Title); } @@ -329,12 +335,14 @@ public void Navigation_Left_Right_Wraps (TestDriver d) public void MenuBarItem_With_QuitKey_Open_QuitKey_Restores_Focus_Correctly (TestDriver d) { MenuBarv2? menuBar = null; + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((a) => { + app = a; menuBar = new MenuBarv2 (); - Toplevel top = Application.Top!; + Toplevel top = app.Current!; top.Add ( new View () @@ -344,19 +352,19 @@ public void MenuBarItem_With_QuitKey_Open_QuitKey_Restores_Focus_Correctly (Test }); menuBar.EnableForDesign (ref top); - Application.Top!.Add (menuBar); + app.Current!.Add (menuBar); }) - .AssertIsNotType (Application.Navigation!.GetFocused ()) + .AssertIsNotType (app!.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) .EnqueueKeyEvent (MenuBarv2.DefaultKey) - .AssertEqual ("_New file", Application.Navigation!.GetFocused ()!.Title) - .AssertTrue (Application.Popover?.GetActivePopover () is PopoverMenu) + .AssertEqual ("_New file", app.Navigation!.GetFocused ()!.Title) + .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (menuBar?.IsOpen ()) - .AssertEqual ("_New file", Application.Navigation?.GetFocused ()!.Title) + .AssertEqual ("_New file", app?.Navigation?.GetFocused ()!.Title) .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) .EnqueueKeyEvent (Application.QuitKey) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (Application.Navigation!.GetFocused ()); + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertIsNotType (app!.Navigation!.GetFocused ()); } [Theory] @@ -364,6 +372,7 @@ public void MenuBarItem_With_QuitKey_Open_QuitKey_Restores_Focus_Correctly (Test public void MenuBarItem_Without_QuitKey_Open_QuitKey_Restores_Focus_Correctly (TestDriver d) { MenuBarv2? menuBar = null; + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) .Add ( @@ -373,26 +382,27 @@ public void MenuBarItem_Without_QuitKey_Open_QuitKey_Restores_Focus_Correctly (T Id = "focusableView", }) - .Then (() => + .Then ((a) => { + app = a; menuBar = new MenuBarv2 (); - Toplevel? toplevel = Application.Top; + Toplevel? toplevel = app.Current; menuBar.EnableForDesign (ref toplevel!); - Application.Top!.Add (menuBar); + app.Current!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (Application.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) .EnqueueKeyEvent (MenuBarv2.DefaultKey) .EnqueueKeyEvent (Key.CursorRight) - .AssertEqual ("Cu_t", Application.Navigation!.GetFocused ()!.Title) - .AssertTrue (Application.Popover?.GetActivePopover () is PopoverMenu) + .AssertEqual ("Cu_t", app?.Navigation!.GetFocused ()!.Title) + .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (menuBar?.IsOpen ()) - .AssertEqual ("Cu_t", Application.Navigation?.GetFocused ()!.Title) + .AssertEqual ("Cu_t", app?.Navigation?.GetFocused ()!.Title) .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) .EnqueueKeyEvent (Application.QuitKey) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (Application.Navigation?.GetFocused ()); + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertIsNotType (app?.Navigation?.GetFocused ()); } [Theory] @@ -400,12 +410,14 @@ public void MenuBarItem_Without_QuitKey_Open_QuitKey_Restores_Focus_Correctly (T public void MenuBarItem_With_QuitKey_Open_QuitKey_Does_Not_Quit_App (TestDriver d) { MenuBarv2? menuBar = null; + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((a) => { + app = a; menuBar = new MenuBarv2 (); - Toplevel top = Application.Top!; + Toplevel top = app.Current!; top.Add ( new View () @@ -415,18 +427,18 @@ public void MenuBarItem_With_QuitKey_Open_QuitKey_Does_Not_Quit_App (TestDriver }); menuBar.EnableForDesign (ref top); - Application.Top!.Add (menuBar); + app.Current!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (Application.Navigation!.GetFocused ()) + .AssertIsNotType (app!.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) .EnqueueKeyEvent (MenuBarv2.DefaultKey) - .AssertEqual ("_New file", Application.Navigation!.GetFocused ()!.Title) - .AssertTrue (Application.Top!.Running) + .AssertEqual ("_New file", app.Navigation!.GetFocused ()!.Title) + .AssertTrue (app?.Current!.Running) .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) .EnqueueKeyEvent (Application.QuitKey) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertTrue (Application.Top!.Running); + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertTrue (app!.Current!.Running); } [Theory] @@ -434,12 +446,14 @@ public void MenuBarItem_With_QuitKey_Open_QuitKey_Does_Not_Quit_App (TestDriver public void MenuBarItem_Without_QuitKey_Open_QuitKey_Does_Not_Quit_MenuBar_SuperView (TestDriver d) { MenuBarv2? menuBar = null; + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((a) => { + app = a; menuBar = new MenuBarv2 (); - Toplevel top = Application.Top!; + Toplevel top = app.Current!; top.Add ( new View () @@ -456,17 +470,17 @@ public void MenuBarItem_Without_QuitKey_Open_QuitKey_Does_Not_Quit_MenuBar_Super item.Key = Key.Empty; } - Application.Top!.Add (menuBar); + app.Current!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (Application.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) .EnqueueKeyEvent (MenuBarv2.DefaultKey) - .AssertEqual ("_New file", Application.Navigation!.GetFocused ()!.Title) + .AssertEqual ("_New file", app?.Navigation!.GetFocused ()!.Title) .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) .EnqueueKeyEvent (Application.QuitKey) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertTrue (Application.Top!.Running); + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertTrue (app?.Current!.Running); } [Theory] @@ -489,12 +503,12 @@ public void MenuBar_Not_Active_DoesNotEat_Space (TestDriver d) }; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((a) => { var menuBar = new MenuBarv2 (); - Toplevel top = Application.Top!; + Toplevel top = a.Current!; menuBar.EnableForDesign (ref top); - Application.Top!.Add (menuBar); + a.Current!.Add (menuBar); }) .Add (testView) .WaitIteration () @@ -523,12 +537,12 @@ public void MenuBar_Not_Active_DoesNotEat_Enter (TestDriver d) }; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((a) => { var menuBar = new MenuBarv2 (); - Toplevel top = Application.Top!; + Toplevel top = a.Current!; menuBar.EnableForDesign (ref top); - Application.Top!.Add (menuBar); + a.Current!.Add (menuBar); }) .Add (testView) .WaitIteration () diff --git a/Tests/IntegrationTests/FluentTests/NavigationTests.cs b/Tests/IntegrationTests/FluentTests/NavigationTests.cs index 96883edcc1..fe74c59eab 100644 --- a/Tests/IntegrationTests/FluentTests/NavigationTests.cs +++ b/Tests/IntegrationTests/FluentTests/NavigationTests.cs @@ -21,7 +21,7 @@ public void Toplevel_TabGroup_Forward_Backward (TestDriver d) var v6 = new View { Id = "v6", CanFocus = true }; using GuiTestContext c = With.A (50, 20, d, _out) - .Then (() => + .Then ((app) => { var w1 = new Window { Id = "w1" }; w1.Add (v1, v2); @@ -29,8 +29,8 @@ public void Toplevel_TabGroup_Forward_Backward (TestDriver d) w2.Add (v3, v4); var w3 = new Window { Id = "w3" }; w3.Add (v5, v6); - Toplevel top = Application.Top!; - Application.Top!.Add (w1, w2, w3); + Toplevel top = app?.Current!; + app?.Current!.Add (w1, w2, w3); }) .AssertTrue (v5.HasFocus) .EnqueueKeyEvent (Key.F6) diff --git a/Tests/IntegrationTests/FluentTests/PopverMenuTests.cs b/Tests/IntegrationTests/FluentTests/PopverMenuTests.cs index d163a455b3..83a173944c 100644 --- a/Tests/IntegrationTests/FluentTests/PopverMenuTests.cs +++ b/Tests/IntegrationTests/FluentTests/PopverMenuTests.cs @@ -23,13 +23,13 @@ public PopoverMenuTests (ITestOutputHelper outputHelper) public void EnableForDesign_CreatesMenuItems (TestDriver d) { using GuiTestContext c = With.A (80, 25, d) - .Then (() => + .Then ((app) => { PopoverMenu popoverMenu = new (); - Application.Top!.Add (popoverMenu); + app.Current!.Add (popoverMenu); // Call EnableForDesign - Toplevel top = Application.Top; + Toplevel top = app.Current; bool result = popoverMenu.EnableForDesign (ref top); // Should return true @@ -54,13 +54,18 @@ public void Activate_Sets_Application_Navigation_Correctly (TestDriver d) { lock (o) { + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d) - .Then (() => + .Then ((a) => { - PopoverMenu popoverMenu = new (); + app = a; + PopoverMenu popoverMenu = new () + { + App = app + }; // Call EnableForDesign - Toplevel top = Application.Top!; + Toplevel top = app.Current!; popoverMenu.EnableForDesign (ref top); var view = new View @@ -71,22 +76,22 @@ public void Activate_Sets_Application_Navigation_Correctly (TestDriver d) Id = "focusableView", Text = "View" }; - Application.Top!.Add (view); + app.Current!.Add (view); // EnableForDesign sets to true; undo that popoverMenu.Visible = false; - Application.Popover!.Register (popoverMenu); + app?.Popover!.Register (popoverMenu); view.SetFocus (); }) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (Application.Navigation!.GetFocused ()) + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("PopoverMenu initial state", _out) - .Then (() => Application.Popover!.Show (Application.Popover.Popovers.First ())) + .Then ((_) => app?.Popover!.Show (app?.Popover.Popovers.First ())) .ScreenShot ("After Show", _out) - .AssertTrue (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertEqual ("Cu_t", Application.Navigation!.GetFocused ()!.Title); + .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertEqual ("Cu_t", app?.Navigation!.GetFocused ()!.Title); } } @@ -94,13 +99,18 @@ public void Activate_Sets_Application_Navigation_Correctly (TestDriver d) [ClassData (typeof (TestDrivers))] public void QuitKey_Hides (TestDriver d) { + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d) - .Then (() => + .Then ((a) => { - PopoverMenu popoverMenu = new (); + app = a; + PopoverMenu popoverMenu = new () + { + App = app + }; // Call EnableForDesign - Toplevel top = Application.Top!; + Toplevel top = app.Current!; bool result = popoverMenu.EnableForDesign (ref top); var view = new View @@ -111,38 +121,43 @@ public void QuitKey_Hides (TestDriver d) Id = "focusableView", Text = "View" }; - Application.Top!.Add (view); + app.Current!.Add (view); // EnableForDesign sets to true; undo that popoverMenu.Visible = false; - Application.Popover!.Register (popoverMenu); + app?.Popover!.Register (popoverMenu); view.SetFocus (); }) .ScreenShot ("PopoverMenu initial state", _out) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .Then (() => Application.Popover!.Show (Application.Popover.Popovers.First ())) + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .Then ((_) => app?.Popover!.Show (app?.Popover.Popovers.First ())) .ScreenShot ("After Show", _out) - .AssertTrue (Application.Popover?.GetActivePopover () is PopoverMenu) + .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .EnqueueKeyEvent (Application.QuitKey) .ScreenShot ($"After {Application.QuitKey}", _out) - .AssertFalse (Application.Popover!.Popovers.Cast ().FirstOrDefault ()!.Visible) - .AssertNull (Application.Popover!.GetActivePopover ()) - .AssertTrue (Application.Top!.Running); + .AssertFalse (app?.Popover!.Popovers.Cast ().FirstOrDefault ()!.Visible) + .AssertNull (app?.Popover!.GetActivePopover ()) + .AssertTrue (app?.Current!.Running); } [Theory] [ClassData (typeof (TestDrivers))] public void QuitKey_Restores_Focus_Correctly (TestDriver d) { + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d) - .Then (() => + .Then ((a) => { - PopoverMenu popoverMenu = new (); + app = a; + PopoverMenu popoverMenu = new () + { + App = app + }; // Call EnableForDesign - Toplevel top = Application.Top!; + Toplevel top = app.Current!; bool result = popoverMenu.EnableForDesign (ref top); var view = new View @@ -153,39 +168,45 @@ public void QuitKey_Restores_Focus_Correctly (TestDriver d) Id = "focusableView", Text = "View" }; - Application.Top!.Add (view); + app.Current!.Add (view); // EnableForDesign sets to true; undo that popoverMenu.Visible = false; - Application.Popover!.Register (popoverMenu); + app?.Popover!.Register (popoverMenu); view.SetFocus (); }) .ScreenShot ("PopoverMenu initial state", _out) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (Application.Navigation!.GetFocused ()) - .Then (() => Application.Popover!.Show (Application.Popover.Popovers.First ())) + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertIsNotType (app?.Navigation!.GetFocused ()) + .Then ((_) => app?.Popover!.Show (app?.Popover.Popovers.First ())) .ScreenShot ("After Show", _out) - .AssertTrue (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsType (Application.Navigation!.GetFocused ()) + .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertIsType (app?.Navigation!.GetFocused ()) .EnqueueKeyEvent (Application.QuitKey) .ScreenShot ($"After {Application.QuitKey}", _out) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (Application.Navigation!.GetFocused ()); + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertIsNotType (app?.Navigation!.GetFocused ()); } [Theory] [ClassData (typeof (TestDrivers))] public void MenuBarItem_With_QuitKey_Open_QuitKey_Does_Not_Quit_App (TestDriver d) { + IApplication? app = null; + using GuiTestContext c = With.A (50, 20, d) - .Then (() => + .Then ((a) => { - PopoverMenu popoverMenu = new (); + app = a; + PopoverMenu popoverMenu = new () + { + App = app + }; // Call EnableForDesign - Toplevel top = Application.Top!; + Toplevel top = app.Current!; bool result = popoverMenu.EnableForDesign (ref top); var view = new View @@ -196,25 +217,25 @@ public void MenuBarItem_With_QuitKey_Open_QuitKey_Does_Not_Quit_App (TestDriver Id = "focusableView", Text = "View" }; - Application.Top!.Add (view); + app.Current!.Add (view); // EnableForDesign sets to true; undo that popoverMenu.Visible = false; - Application.Popover!.Register (popoverMenu); + app?.Popover!.Register (popoverMenu); view.SetFocus (); }) - .AssertIsNotType (Application.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("PopoverMenu initial state", _out) - .Then (() => Application.Popover!.Show (Application.Popover.Popovers.First ())) + .Then ((_) => app?.Popover!.Show (app?.Popover.Popovers.First ())) .ScreenShot ("PopoverMenu after Show", _out) - .AssertEqual ("Cu_t", Application.Navigation!.GetFocused ()!.Title) - .AssertTrue (Application.Top!.Running) + .AssertEqual ("Cu_t", app?.Navigation!.GetFocused ()!.Title) + .AssertTrue (app?.Current!.Running) .EnqueueKeyEvent (Application.QuitKey) .ScreenShot ($"After {Application.QuitKey}", _out) - .AssertFalse (Application.Popover?.GetActivePopover () is PopoverMenu) - .AssertTrue (Application.Top!.Running); + .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) + .AssertTrue (app?.Current!.Running); } [Theory] @@ -237,13 +258,18 @@ public void Not_Active_DoesNotEat_Space (TestDriver d) } }; + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d) - .Then (() => + .Then ((a) => { - PopoverMenu popoverMenu = new (); - Toplevel top = Application.Top!; + app = a; + PopoverMenu popoverMenu = new () + { + App = app + }; + Toplevel top = app.Current!; popoverMenu.EnableForDesign (ref top); - Application.Popover!.Register (popoverMenu); + app?.Popover!.Register (popoverMenu); }) .Add (testView) .Focus (testView) @@ -271,13 +297,19 @@ public void Not_Active_DoesNotEat_Enter (TestDriver d) } }; + IApplication? app = null; + using GuiTestContext c = With.A (50, 20, d) - .Then (() => + .Then ((a) => { - PopoverMenu popoverMenu = new (); - Toplevel top = Application.Top!; + app = a; + PopoverMenu popoverMenu = new () + { + App = app + }; + Toplevel top = app.Current!; popoverMenu.EnableForDesign (ref top); - Application.Popover!.Register (popoverMenu); + app?.Popover!.Register (popoverMenu); }) .Add (testView) .Focus (testView) @@ -305,13 +337,18 @@ public void Not_Active_DoesNotEat_QuitKey (TestDriver d) } }; + IApplication? app = null; using GuiTestContext c = With.A (50, 20, d) - .Then (() => + .Then ((a) => { - PopoverMenu popoverMenu = new (); - Toplevel top = Application.Top!; + app = a; + PopoverMenu popoverMenu = new () + { + App = app + }; + Toplevel top = app.Current!; popoverMenu.EnableForDesign (ref top); - Application.Popover!.Register (popoverMenu); + app?.Popover!.Register (popoverMenu); }) .Add (testView) .EnqueueKeyEvent (Application.QuitKey) @@ -326,24 +363,25 @@ public void ContextMenu_CrashesOnRight (TestDriver d) MenuItemv2 [] menuItems = [new ("_New File", string.Empty, () => { clicked = true; })]; + IApplication? app = null; using GuiTestContext c = With.A (40, 10, d, _out) - .WithContextMenu (new (menuItems)) + .Then ((a) => app = a) + .WithContextMenu (new (menuItems) { App = app }) .ScreenShot ("Before open menu", _out) // Click in main area inside border .RightClick (1, 1) - .Then (() => + .Then ((_) => { // Test depends on menu having a border - IPopover? popover = Application.Popover!.GetActivePopover (); + IPopover? popover = app?.Popover!.GetActivePopover (); Assert.NotNull (popover); var popoverMenu = popover as PopoverMenu; popoverMenu!.Root!.BorderStyle = LineStyle.Single; }) .ScreenShot ("After open menu", _out) .LeftClick (2, 2) - ; - Assert.True (clicked); + .AssertTrue(clicked); } [Theory] @@ -374,8 +412,11 @@ public void ContextMenu_OpenSubmenu (TestDriver d) new ("Six", "", null) ]; + IApplication? app = null; + using GuiTestContext c = With.A (40, 10, d) - .WithContextMenu (new (menuItems)) + .Then ((a) => app = a) + .WithContextMenu (new (menuItems) { App = app }) .ScreenShot ("Before open menu", _out) // Click in main area inside border diff --git a/Tests/IntegrationTests/FluentTests/TreeViewFluentTests.cs b/Tests/IntegrationTests/FluentTests/TreeViewFluentTests.cs index 013bd32a52..01863f9622 100644 --- a/Tests/IntegrationTests/FluentTests/TreeViewFluentTests.cs +++ b/Tests/IntegrationTests/FluentTests/TreeViewFluentTests.cs @@ -55,7 +55,7 @@ public void TreeView_AllowReOrdering (TestDriver d) }) .AssertIsAssignableFrom (tv.SelectedObject) .Then ( - () => + (_) => { // Re order root.Children = [bike, car, lorry]; @@ -150,7 +150,7 @@ public void TreeViewReOrder_PreservesExpansion (TestDriver d) Assert.Equal (mrE, tv.GetObjectOnRow (8)); }) .Then ( - () => + (_) => { // Re order root.Children = [bike, car, lorry]; diff --git a/Tests/IntegrationTests/UICatalog/ScenarioTests.cs b/Tests/IntegrationTests/UICatalog/ScenarioTests.cs index a7943d4e55..69021ab153 100644 --- a/Tests/IntegrationTests/UICatalog/ScenarioTests.cs +++ b/Tests/IntegrationTests/UICatalog/ScenarioTests.cs @@ -20,8 +20,6 @@ public ScenarioTests (ITestOutputHelper output) private readonly ITestOutputHelper _output; - private object? _timeoutLock; - /// /// This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run. /// Should find any Scenarios which crash on load or do not respond to . @@ -37,18 +35,14 @@ public void All_Scenarios_Quit_And_Init_Shutdown_Properly (Type scenarioType) return; } - Assert.Null (_timeoutLock); - _timeoutLock = new (); - - ConfigurationManager.Disable (true); - - // If a previous test failed, this will ensure that the Application is in a clean state Application.ResetState (true); _output.WriteLine ($"Running Scenario '{scenarioType}'"); Scenario? scenario = null; var scenarioName = string.Empty; - object? timeout = null; + // Do not use Application.AddTimer for out-of-band watchdogs as + // they will be stopped by Shutdown/ResetState. + Timer? watchdogTimer = null; var timeoutFired = false; // Increase timeout for macOS - it's consistently slower @@ -90,14 +84,7 @@ public void All_Scenarios_Quit_And_Init_Shutdown_Properly (Type scenarioType) iterationHandlerRemoved = true; } - lock (_timeoutLock) - { - if (timeout is { }) - { - Application.RemoveTimeout (timeout); - timeout = null; - } - } + watchdogTimer?.Dispose (); scenario?.Dispose (); scenario = null; @@ -130,10 +117,8 @@ void OnApplicationOnInitializedChanged (object? s, EventArgs a) Application.Iteration += OnApplicationOnIteration; initialized = true; - lock (_timeoutLock) - { - timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback); - } + // Use a System.Threading.Timer for the watchdog to ensure it's not affected by Application.StopAllTimers + watchdogTimer = new Timer (_ => ForceCloseCallback (), null, (int)abortTime, System.Threading.Timeout.Infinite); } else { @@ -144,13 +129,9 @@ void OnApplicationOnInitializedChanged (object? s, EventArgs a) } // If the scenario doesn't close within abortTime ms, this will force it to quit - bool ForceCloseCallback () + void ForceCloseCallback () { - lock (_timeoutLock) - { - timeoutFired = true; - timeout = null; - } + timeoutFired = true; _output.WriteLine ($"TIMEOUT FIRED for {scenarioName} after {abortTime}ms. Attempting graceful shutdown."); @@ -167,8 +148,6 @@ bool ForceCloseCallback () { _output.WriteLine ($"Exception during timeout callback: {ex.Message}"); } - - return false; } void OnApplicationOnIteration (object? s, IterationEventArgs a) @@ -219,7 +198,7 @@ public void Run_All_Views_Tester_Scenario () List posNames = ["Percent", "AnchorEnd", "Center", "Absolute"]; List dimNames = ["Auto", "Percent", "Fill", "Absolute"]; - Application.Init (null, "fake"); + Application.Init ("fake"); var top = new Toplevel (); diff --git a/Tests/StressTests/ApplicationStressTests.cs b/Tests/StressTests/ApplicationStressTests.cs index d58b521c67..0feadf2109 100644 --- a/Tests/StressTests/ApplicationStressTests.cs +++ b/Tests/StressTests/ApplicationStressTests.cs @@ -71,8 +71,8 @@ static void RunTest (Random r, TextField tf, int numPasses, int numIncrements, i { int tbNow = _tbCounter; - // Wait for Application.Top to be running to ensure timed events can be processed - while (Application.Top is null || Application.Top is { Running: false }) + // Wait for Application.Current to be running to ensure timed events can be processed + while (Application.Current is null || Application.Current is { Running: false }) { Thread.Sleep (1); } diff --git a/Tests/StressTests/ScenariosStressTests.cs b/Tests/StressTests/ScenariosStressTests.cs index 9e047f1ab1..fead31b848 100644 --- a/Tests/StressTests/ScenariosStressTests.cs +++ b/Tests/StressTests/ScenariosStressTests.cs @@ -126,7 +126,7 @@ void OnApplicationOnIteration (object? s, IterationEventArgs a) void OnApplicationSessionBegun (object? sender, SessionTokenEventArgs e) { - // Get a list of all subviews under Application.Top (and their subviews, etc.) + // Get a list of all subviews under Application.Current (and their subviews, etc.) // and subscribe to their DrawComplete event void SubscribeAllSubViews (View view) { @@ -140,7 +140,7 @@ void SubscribeAllSubViews (View view) } } - SubscribeAllSubViews (Application.Top!); + SubscribeAllSubViews (Application.Current!); } // If the scenario doesn't close within the abort time, this will force it to quit diff --git a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationFactory.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationFactory.cs index 92f644bbba..7e51c3c740 100644 --- a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationFactory.cs +++ b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationFactory.cs @@ -21,17 +21,14 @@ public IDisposable SetupFakeApplication () FakeOutput output = new (); output.SetSize (80, 25); - IApplication origApp = ApplicationImpl.Instance; - SizeMonitorImpl sizeMonitor = new (output); ApplicationImpl impl = new (new FakeComponentFactory (fakeInput, output, sizeMonitor)); - - ApplicationImpl.ChangeInstance (impl); + ApplicationImpl.SetInstance (impl); // Initialize with a fake driver - impl.Init (null, "fake"); + impl.Init ("fake"); - return new FakeApplicationLifecycle (origApp, hardStopTokenSource); + return new FakeApplicationLifecycle (hardStopTokenSource); } } diff --git a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationLifecycle.cs b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationLifecycle.cs index 7bfbac632b..5fced93382 100644 --- a/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationLifecycle.cs +++ b/Tests/TerminalGuiFluentTesting/FakeDriver/FakeApplicationLifecycle.cs @@ -5,17 +5,15 @@ namespace Terminal.Gui.Drivers; /// Implements a fake application lifecycle for testing purposes. Cleans up the application on dispose by cancelling /// the provided and shutting down the application. /// -/// /// -internal class FakeApplicationLifecycle (IApplication origApp, CancellationTokenSource hardStop) : IDisposable +internal class FakeApplicationLifecycle (CancellationTokenSource hardStop) : IDisposable { /// public void Dispose () { hardStop.Cancel (); - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.Shutdown (); - ApplicationImpl.ChangeInstance (origApp); } } diff --git a/Tests/TerminalGuiFluentTesting/GuiTestContext.ContextMenu.cs b/Tests/TerminalGuiFluentTesting/GuiTestContext.ContextMenu.cs index c6f45f5106..359fd7a0a5 100644 --- a/Tests/TerminalGuiFluentTesting/GuiTestContext.ContextMenu.cs +++ b/Tests/TerminalGuiFluentTesting/GuiTestContext.ContextMenu.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; + #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member namespace TerminalGuiFluentTesting; @@ -12,13 +14,17 @@ public partial class GuiTestContext /// public GuiTestContext WithContextMenu (PopoverMenu? contextMenu) { - LastView.MouseEvent += (s, e) => + if (contextMenu?.App is null) + { + Fail (@"PopoverMenu's must have their App property set."); + } + LastView.MouseEvent += (_, e) => { if (e.Flags.HasFlag (MouseFlags.Button3Clicked)) { // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused // and the context menu is disposed when it is closed. - Application.Popover?.Register (contextMenu); + App.Popover?.Register (contextMenu); contextMenu?.MakeVisible (e.ScreenPosition); } }; diff --git a/Tests/TerminalGuiFluentTesting/GuiTestContext.Input.cs b/Tests/TerminalGuiFluentTesting/GuiTestContext.Input.cs index 2d10217317..6a77c0a72b 100644 --- a/Tests/TerminalGuiFluentTesting/GuiTestContext.Input.cs +++ b/Tests/TerminalGuiFluentTesting/GuiTestContext.Input.cs @@ -58,13 +58,13 @@ public GuiTestContext LeftClick (Func evaluator) where TView private GuiTestContext EnqueueMouseEvent (MouseEventArgs mouseEvent) { // Enqueue the mouse event - WaitIteration (() => + WaitIteration ((app) => { - if (Application.Driver is { }) + if (app.Driver is { }) { mouseEvent.Position = mouseEvent.ScreenPosition; - Application.Driver.InputProcessor.EnqueueMouseEvent (mouseEvent); + app.Driver.InputProcessor.EnqueueMouseEvent (mouseEvent); } else { @@ -81,7 +81,7 @@ private GuiTestContext EnqueueMouseEvent (MouseEventArgs mouseEvent, Func { var screen = Point.Empty; - GuiTestContext ctx = WaitIteration (() => + GuiTestContext ctx = WaitIteration ((_) => { TView v = Find (evaluator); screen = v.ViewportToScreen (new Point (0, 0)); @@ -199,10 +199,10 @@ public GuiTestContext EnqueueKeyEvent (Key key) // We do this by subscribing to the Driver.KeyDown event and waiting until it is raised. // This prevents the application from missing the key event if we enqueue it and immediately return. bool keyReceived = false; - if (_applicationImpl?.Driver is { }) + if (App?.Driver is { }) { - _applicationImpl.Driver.KeyDown += DriverOnKeyDown; - _applicationImpl.Driver.EnqueueKeyEvent (key); + App.Driver.KeyDown += DriverOnKeyDown; + App.Driver.EnqueueKeyEvent (key); WaitUntil (() => keyReceived); } else @@ -215,7 +215,7 @@ public GuiTestContext EnqueueKeyEvent (Key key) void DriverOnKeyDown (object? sender, Key e) { - _applicationImpl.Driver.KeyDown -= DriverOnKeyDown; + App.Driver.KeyDown -= DriverOnKeyDown; keyReceived = true; } diff --git a/Tests/TerminalGuiFluentTesting/GuiTestContext.Navigation.cs b/Tests/TerminalGuiFluentTesting/GuiTestContext.Navigation.cs index 49bb1a9b53..9bd8f5bc90 100644 --- a/Tests/TerminalGuiFluentTesting/GuiTestContext.Navigation.cs +++ b/Tests/TerminalGuiFluentTesting/GuiTestContext.Navigation.cs @@ -40,13 +40,13 @@ public GuiTestContext Focus (View toFocus) public GuiTestContext Focus (Func? evaluator = null) where T : View { evaluator ??= _ => true; - Toplevel? t = Application.Top; + Toplevel? t = App?.Current; HashSet seen = new (); if (t == null) { - Fail ("Application.Top was null when trying to set focus"); + Fail ("Application.Current was null when trying to set focus"); return this; } @@ -62,7 +62,7 @@ public GuiTestContext Focus (Func? evaluator = null) where T : View } // No, try tab to the next (or first) - EnqueueKeyEvent (Application.NextTabKey); + EnqueueKeyEvent (Terminal.Gui.App.Application.NextTabKey); WaitIteration (); next = t.MostFocused; diff --git a/Tests/TerminalGuiFluentTesting/GuiTestContext.ViewBase.cs b/Tests/TerminalGuiFluentTesting/GuiTestContext.ViewBase.cs index d8848f080f..74eafc77ee 100644 --- a/Tests/TerminalGuiFluentTesting/GuiTestContext.ViewBase.cs +++ b/Tests/TerminalGuiFluentTesting/GuiTestContext.ViewBase.cs @@ -12,9 +12,9 @@ public partial class GuiTestContext /// public GuiTestContext Add (View v) { - WaitIteration (() => + WaitIteration ((app) => { - Toplevel top = Application.Top ?? throw new ("Top was null so could not add view"); + Toplevel top = app.Current ?? throw new ("Top was null so could not add view"); top.Add (v); top.Layout (); _lastView = v; @@ -28,15 +28,15 @@ public GuiTestContext Add (View v) /// /// The last view added (e.g. with ) or the root/current top. /// - public View LastView => _lastView ?? Application.Top ?? throw new ("Could not determine which view to add to"); + public View LastView => _lastView ?? App.Current ?? throw new ("Could not determine which view to add to"); private T Find (Func evaluator) where T : View { - Toplevel? t = Application.Top; + Toplevel? t = App.Current; if (t == null) { - Fail ("Application.Top was null when attempting to find view"); + Fail ("App.Current was null when attempting to find view"); } T? f = FindRecursive (t!, evaluator); diff --git a/Tests/TerminalGuiFluentTesting/GuiTestContext.cs b/Tests/TerminalGuiFluentTesting/GuiTestContext.cs index de581408f0..73960cd0a8 100644 --- a/Tests/TerminalGuiFluentTesting/GuiTestContext.cs +++ b/Tests/TerminalGuiFluentTesting/GuiTestContext.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using System.Diagnostics; +using System.Diagnostics; using System.Drawing; using System.Text; using Microsoft.Extensions.Logging; @@ -32,10 +31,15 @@ public partial class GuiTestContext : IDisposable private IOutput? _output; private SizeMonitorImpl? _sizeMonitor; private ApplicationImpl? _applicationImpl; + + /// + /// The IApplication instance that was created. + /// + public IApplication? App => _applicationImpl; + private TestDriver _driverType; // ===== Application State Preservation (for restoration) ===== - private IApplication? _originalApplicationInstance; private ILogger? _originalLogger; // ===== Test Configuration ===== @@ -68,7 +72,7 @@ public GuiTestContext (TestDriver driver, TextWriter? logWriter = null, TimeSpan _booting.Release (); // After Init, Application.Screen should be set by the driver - if (Application.Screen == Rectangle.Empty) + if (_applicationImpl?.Screen == Rectangle.Empty) { throw new InvalidOperationException ( "Driver bug: Application.Screen is empty after Init. The driver should set the screen size during Init."); @@ -121,11 +125,11 @@ internal GuiTestContext (Func topLevelBuilder, int width, int height, Toplevel t = topLevelBuilder (); t.Closed += (s, e) => { Finished = true; }; - Application.Run (t); // This will block, but it's on a background thread now + App?.Run (t); // This will block, but it's on a background thread now t.Dispose (); Logging.Trace ("Application.Run completed"); - Application.Shutdown (); + App?.Shutdown (); _runCancellationTokenSource.Cancel (); } catch (OperationCanceledException) @@ -163,9 +167,7 @@ internal GuiTestContext (Func topLevelBuilder, int width, int height, private void InitializeApplication () { - ApplicationImpl.ChangeInstance (_applicationImpl); - - _applicationImpl?.Init (null, GetDriverName ()); + App?.Init (GetDriverName ()); } @@ -175,7 +177,6 @@ private void InitializeApplication () private void CommonInit (int width, int height, TestDriver driverType, TimeSpan? timeout) { _timeout = timeout ?? TimeSpan.FromSeconds (10); - _originalApplicationInstance = ApplicationImpl.Instance; _originalLogger = Logging.Logger; _logsSb = new (); _driverType = driverType; @@ -207,38 +208,36 @@ private void CommonInit (int width, int height, TestDriver driverType, TimeSpan? // Remove frame limit Application.MaximumIterationsPerSecond = ushort.MaxValue; - //// Only set size if explicitly provided (width and height > 0) - //if (width > 0 && height > 0) - //{ - // _output.SetSize (width, height); - //} - IComponentFactory? cf = null; + _output = new FakeOutput (); + + // Only set size if explicitly provided (width and height > 0) + if (width > 0 && height > 0) + { + _output.SetSize (width, height); + } + // TODO: As each drivers' IInput/IOutput implementations are made testable (e.g. // TODO: safely injectable/mocked), we can expand this switch to use them. switch (driverType) { case TestDriver.DotNet: - _output = new FakeOutput (); _sizeMonitor = new (_output); cf = new FakeComponentFactory (_fakeInput, _output, _sizeMonitor); break; case TestDriver.Windows: - _output = new FakeOutput (); _sizeMonitor = new (_output); cf = new FakeComponentFactory (_fakeInput, _output, _sizeMonitor); break; case TestDriver.Unix: - _output = new FakeOutput (); _sizeMonitor = new (_output); cf = new FakeComponentFactory (_fakeInput, _output, _sizeMonitor); break; case TestDriver.Fake: - _output = new FakeOutput (); _sizeMonitor = new (_output); cf = new FakeComponentFactory (_fakeInput, _output, _sizeMonitor); @@ -278,7 +277,7 @@ public bool Finished /// /// /// - public GuiTestContext Then (Action doAction) + public GuiTestContext Then (Action doAction) { try { @@ -302,7 +301,7 @@ public GuiTestContext Then (Action doAction) /// /// /// - public GuiTestContext WaitIteration (Action? action = null) + public GuiTestContext WaitIteration (Action? action = null) { // If application has already exited don't wait! if (Finished || _runCancellationTokenSource.Token.IsCancellationRequested || _fakeInput.ExternalCancellationTokenSource!.Token.IsCancellationRequested) @@ -312,31 +311,34 @@ public GuiTestContext WaitIteration (Action? action = null) return this; } - if (Thread.CurrentThread.ManagedThreadId == Application.MainThreadId) + if (Thread.CurrentThread.ManagedThreadId == _applicationImpl?.MainThreadId) { throw new NotSupportedException ("Cannot WaitIteration during Invoke"); } Logging.Trace ($"WaitIteration started"); - action ??= () => { }; - CancellationTokenSource ctsActionCompleted = new (); - - Application.Invoke (() => + if (action is null) { - try - { - action (); + action = (app) => { }; + } + CancellationTokenSource ctsActionCompleted = new (); - //Logging.Trace ("Action completed"); - ctsActionCompleted.Cancel (); - } - catch (Exception e) - { - Logging.Warning ($"Action failed with exception: {e}"); - _backgroundException = e; - _fakeInput.ExternalCancellationTokenSource?.Cancel (); - } - }); + App?.Invoke (app => + { + try + { + action (app); + + //Logging.Trace ("Action completed"); + ctsActionCompleted.Cancel (); + } + catch (Exception e) + { + Logging.Warning ($"Action failed with exception: {e}"); + _backgroundException = e; + _fakeInput.ExternalCancellationTokenSource?.Cancel (); + } + }); // Blocks until either the token or the hardStopToken is cancelled. // With linked tokens, we only need to wait on _runCancellationTokenSource and ctsLocal @@ -384,15 +386,27 @@ public GuiTestContext WaitUntil (Func condition) /// new Width for the console. /// new Height for the console. /// - public GuiTestContext ResizeConsole (int width, int height) { return WaitIteration (() => { Application.Driver!.SetScreenSize (width, height); }); } + public GuiTestContext ResizeConsole (int width, int height) { return WaitIteration ((app) => { app.Driver!.SetScreenSize (width, height); }); } public GuiTestContext ScreenShot (string title, TextWriter? writer) { //Logging.Trace ($"{title}"); - return WaitIteration (() => + return WaitIteration ((app) => + { + writer?.WriteLine (title + ":"); + var text = app.Driver?.ToString (); + + writer?.WriteLine (text); + }); + } + + public GuiTestContext AnsiScreenShot (string title, TextWriter? writer) + { + //Logging.Trace ($"{title}"); + return WaitIteration ((app) => { writer?.WriteLine (title + ":"); - var text = Application.ToString (); + var text = app.Driver?.ToAnsi (); writer?.WriteLine (text); }); @@ -412,7 +426,7 @@ public GuiTestContext Stop () { try { - Application.Shutdown (); + App?.Shutdown (); } catch { @@ -425,7 +439,7 @@ public GuiTestContext Stop () return this; } - WaitIteration (() => { Application.RequestStop (); }); + WaitIteration ((app) => { app.RequestStop (); }); // Wait for the application to stop, but give it a 1-second timeout const int WAIT_TIMEOUT_MS = 1000; @@ -440,8 +454,8 @@ public GuiTestContext Stop () // If this doesn't work there will be test failures as the main loop continues to run during next test. try { - Application.RequestStop (); - Application.Shutdown (); + App?.RequestStop (); + App?.Shutdown (); } catch (Exception ex) { @@ -516,9 +530,8 @@ private void CleanupApplication () Logging.Trace ("CleanupApplication"); _fakeInput.ExternalCancellationTokenSource = null; - Application.ResetState (true); - ApplicationImpl.ChangeInstance (_originalApplicationInstance); - Logging.Logger = _originalLogger; + App?.ResetState (true); + Logging.Logger = _originalLogger!; Finished = true; Application.MaximumIterationsPerSecond = Application.DefaultMaximumIterationsPerSecond; diff --git a/Tests/UnitTests/Application/Application.NavigationTests.cs b/Tests/UnitTests/Application/Application.NavigationTests.cs index 93ad1ae91f..21cafa1320 100644 --- a/Tests/UnitTests/Application/Application.NavigationTests.cs +++ b/Tests/UnitTests/Application/Application.NavigationTests.cs @@ -61,9 +61,7 @@ public void Focused_Change_Raises_FocusedChanged () { var raised = false; - Application.Navigation = new (); - - Application.Navigation.FocusedChanged += ApplicationNavigationOnFocusedChanged; + Application.Navigation!.FocusedChanged += ApplicationNavigationOnFocusedChanged; Application.Navigation.SetFocused (new () { CanFocus = true, HasFocus = true }); @@ -74,8 +72,6 @@ public void Focused_Change_Raises_FocusedChanged () Application.Navigation.FocusedChanged -= ApplicationNavigationOnFocusedChanged; - Application.Navigation = null; - return; void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e) { raised = true; } @@ -84,12 +80,13 @@ public void Focused_Change_Raises_FocusedChanged () [Fact] public void GetFocused_Returns_Focused_View () { - Application.Navigation = new (); + IApplication app = Application.Create (); - Application.Top = new () + app.Current = new () { Id = "top", - CanFocus = true + CanFocus = true, + App = app }; var subView1 = new View @@ -103,30 +100,28 @@ public void GetFocused_Returns_Focused_View () Id = "subView2", CanFocus = true }; - Application.Top.Add (subView1, subView2); - Assert.False (Application.Top.HasFocus); - Application.Top.SetFocus (); - Assert.True (subView1.HasFocus); - Assert.Equal (subView1, Application.Navigation.GetFocused ()); + app.Current?.Add (subView1, subView2); + Assert.False (app.Current?.HasFocus); - Application.Navigation.AdvanceFocus (NavigationDirection.Forward, null); - Assert.Equal (subView2, Application.Navigation.GetFocused ()); + app.Current?.SetFocus (); + Assert.True (subView1.HasFocus); + Assert.Equal (subView1, app.Navigation?.GetFocused ()); - Application.Top.Dispose (); - Application.Top = null; - Application.Navigation = null; + app.Navigation?.AdvanceFocus (NavigationDirection.Forward, null); + Assert.Equal (subView2, app.Navigation?.GetFocused ()); } [Fact] public void GetFocused_Returns_Null_If_No_Focused_View () { - Application.Navigation = new (); + IApplication app = Application.Create (); - Application.Top = new () + app.Current = new () { Id = "top", - CanFocus = true + CanFocus = true, + App = app }; var subView1 = new View @@ -135,24 +130,21 @@ public void GetFocused_Returns_Null_If_No_Focused_View () CanFocus = true }; - Application.Top.Add (subView1); - Assert.False (Application.Top.HasFocus); + app!.Current.Add (subView1); + Assert.False (app.Current.HasFocus); - Application.Top.SetFocus (); + app.Current.SetFocus (); Assert.True (subView1.HasFocus); - Assert.Equal (subView1, Application.Navigation.GetFocused ()); + Assert.Equal (subView1, app.Navigation!.GetFocused ()); subView1.HasFocus = false; Assert.False (subView1.HasFocus); - Assert.True (Application.Top.HasFocus); - Assert.Equal (Application.Top, Application.Navigation.GetFocused ()); + Assert.True (app.Current.HasFocus); + Assert.Equal (app.Current, app.Navigation.GetFocused ()); - Application.Top.HasFocus = false; - Assert.False (Application.Top.HasFocus); - Assert.Null (Application.Navigation.GetFocused ()); + app.Current.HasFocus = false; + Assert.False (app.Current.HasFocus); + Assert.Null (app.Navigation.GetFocused ()); - Application.Top.Dispose (); - Application.Top = null; - Application.Navigation = null; } } diff --git a/Tests/UnitTests/Application/ApplicationImplBeginEndTests.cs b/Tests/UnitTests/Application/ApplicationImplBeginEndTests.cs new file mode 100644 index 0000000000..da9faad9d9 --- /dev/null +++ b/Tests/UnitTests/Application/ApplicationImplBeginEndTests.cs @@ -0,0 +1,505 @@ +#nullable enable +using Xunit.Abstractions; + +namespace UnitTests.ApplicationTests; + +/// +/// Comprehensive tests for ApplicationImpl.Begin/End logic that manages Current and SessionStack. +/// These tests ensure the fragile state management logic is robust and catches regressions. +/// Tests work directly with ApplicationImpl instances to avoid global Application state issues. +/// +public class ApplicationImplBeginEndTests +{ + private readonly ITestOutputHelper _output; + + public ApplicationImplBeginEndTests (ITestOutputHelper output) { _output = output; } + + private IApplication NewApplicationImpl () + { + IApplication app = Application.Create (); + + return app; + } + + [Fact] + public void Begin_WithNullToplevel_ThrowsArgumentNullException () + { + IApplication app = NewApplicationImpl (); + + try + { + Assert.Throws (() => app.Begin (null!)); + } + finally + { + app.Shutdown (); + } + } + + [Fact] + public void Begin_SetsCurrent_WhenCurrentIsNull () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel = null; + + try + { + toplevel = new (); + Assert.Null (app.Current); + + app.Begin (toplevel); + + Assert.NotNull (app.Current); + Assert.Same (toplevel, app.Current); + Assert.Single (app.SessionStack); + } + finally + { + toplevel?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void Begin_PushesToSessionStack () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel1 = null; + Toplevel? toplevel2 = null; + + try + { + toplevel1 = new() { Id = "1" }; + toplevel2 = new() { Id = "2" }; + + app.Begin (toplevel1); + Assert.Single (app.SessionStack); + Assert.Same (toplevel1, app.Current); + + app.Begin (toplevel2); + Assert.Equal (2, app.SessionStack.Count); + Assert.Same (toplevel2, app.Current); + } + finally + { + toplevel1?.Dispose (); + toplevel2?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void Begin_SetsUniqueToplevelId_WhenIdIsEmpty () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel1 = null; + Toplevel? toplevel2 = null; + Toplevel? toplevel3 = null; + + try + { + toplevel1 = new (); + toplevel2 = new (); + toplevel3 = new (); + + Assert.Empty (toplevel1.Id); + Assert.Empty (toplevel2.Id); + Assert.Empty (toplevel3.Id); + + app.Begin (toplevel1); + app.Begin (toplevel2); + app.Begin (toplevel3); + + Assert.NotEmpty (toplevel1.Id); + Assert.NotEmpty (toplevel2.Id); + Assert.NotEmpty (toplevel3.Id); + + // IDs should be unique + Assert.NotEqual (toplevel1.Id, toplevel2.Id); + Assert.NotEqual (toplevel2.Id, toplevel3.Id); + Assert.NotEqual (toplevel1.Id, toplevel3.Id); + } + finally + { + toplevel1?.Dispose (); + toplevel2?.Dispose (); + toplevel3?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void End_WithNullSessionToken_ThrowsArgumentNullException () + { + IApplication app = NewApplicationImpl (); + + try + { + Assert.Throws (() => app.End (null!)); + } + finally + { + app.Shutdown (); + } + } + + [Fact] + public void End_PopsSessionStack () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel1 = null; + Toplevel? toplevel2 = null; + + try + { + toplevel1 = new() { Id = "1" }; + toplevel2 = new() { Id = "2" }; + + SessionToken token1 = app.Begin (toplevel1); + SessionToken token2 = app.Begin (toplevel2); + + Assert.Equal (2, app.SessionStack.Count); + + app.End (token2); + + Assert.Single (app.SessionStack); + Assert.Same (toplevel1, app.Current); + + app.End (token1); + + Assert.Empty (app.SessionStack); + } + finally + { + toplevel1?.Dispose (); + toplevel2?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void End_ThrowsArgumentException_WhenNotBalanced () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel1 = null; + Toplevel? toplevel2 = null; + + try + { + toplevel1 = new() { Id = "1" }; + toplevel2 = new() { Id = "2" }; + + SessionToken token1 = app.Begin (toplevel1); + SessionToken token2 = app.Begin (toplevel2); + + // Trying to end token1 when token2 is on top should throw + // NOTE: This throws but has the side effect of popping token2 from the stack + Assert.Throws (() => app.End (token1)); + + // Don't try to clean up with more End calls - the state is now inconsistent + // Let Shutdown/ResetState handle cleanup + } + finally + { + // Dispose toplevels BEFORE Shutdown to satisfy DEBUG_IDISPOSABLE assertions + toplevel1?.Dispose (); + toplevel2?.Dispose (); + + // Shutdown will call ResetState which clears any remaining state + app.Shutdown (); + } + } + + [Fact] + public void End_RestoresCurrentToPreviousToplevel () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel1 = null; + Toplevel? toplevel2 = null; + Toplevel? toplevel3 = null; + + try + { + toplevel1 = new() { Id = "1" }; + toplevel2 = new() { Id = "2" }; + toplevel3 = new() { Id = "3" }; + + SessionToken token1 = app.Begin (toplevel1); + SessionToken token2 = app.Begin (toplevel2); + SessionToken token3 = app.Begin (toplevel3); + + Assert.Same (toplevel3, app.Current); + + app.End (token3); + Assert.Same (toplevel2, app.Current); + + app.End (token2); + Assert.Same (toplevel1, app.Current); + + app.End (token1); + } + finally + { + toplevel1?.Dispose (); + toplevel2?.Dispose (); + toplevel3?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void MultipleBeginEnd_MaintainsStackIntegrity () + { + IApplication app = NewApplicationImpl (); + List toplevels = new (); + List tokens = new (); + + try + { + // Begin multiple toplevels + for (var i = 0; i < 5; i++) + { + var toplevel = new Toplevel { Id = $"toplevel-{i}" }; + toplevels.Add (toplevel); + tokens.Add (app.Begin (toplevel)); + } + + Assert.Equal (5, app.SessionStack.Count); + Assert.Same (toplevels [4], app.Current); + + // End them in reverse order (LIFO) + for (var i = 4; i >= 0; i--) + { + app.End (tokens [i]); + + if (i > 0) + { + Assert.Equal (i, app.SessionStack.Count); + Assert.Same (toplevels [i - 1], app.Current); + } + else + { + Assert.Empty (app.SessionStack); + } + } + } + finally + { + foreach (Toplevel toplevel in toplevels) + { + toplevel.Dispose (); + } + + app.Shutdown (); + } + } + + [Fact] + public void End_UpdatesCachedSessionTokenToplevel () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel = null; + + try + { + toplevel = new (); + + SessionToken token = app.Begin (toplevel); + Assert.Null (app.CachedSessionTokenToplevel); + + app.End (token); + + Assert.Same (toplevel, app.CachedSessionTokenToplevel); + } + finally + { + toplevel?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void End_NullsSessionTokenToplevel () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel = null; + + try + { + toplevel = new (); + + SessionToken token = app.Begin (toplevel); + Assert.Same (toplevel, token.Toplevel); + + app.End (token); + + Assert.Null (token.Toplevel); + } + finally + { + toplevel?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void ResetState_ClearsSessionStack () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel1 = null; + Toplevel? toplevel2 = null; + + try + { + toplevel1 = new() { Id = "1" }; + toplevel2 = new() { Id = "2" }; + + app.Begin (toplevel1); + app.Begin (toplevel2); + + Assert.Equal (2, app.SessionStack.Count); + Assert.NotNull (app.Current); + } + finally + { + // Dispose toplevels BEFORE Shutdown to satisfy DEBUG_IDISPOSABLE assertions + toplevel1?.Dispose (); + toplevel2?.Dispose (); + + // Shutdown calls ResetState, which will clear SessionStack and set Current to null + app.Shutdown (); + + // Verify cleanup happened + Assert.Empty (app.SessionStack); + Assert.Null (app.Current); + Assert.Null (app.CachedSessionTokenToplevel); + } + } + + [Fact] + public void ResetState_StopsAllRunningToplevels () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel1 = null; + Toplevel? toplevel2 = null; + + try + { + toplevel1 = new() { Id = "1", Running = true }; + toplevel2 = new() { Id = "2", Running = true }; + + app.Begin (toplevel1); + app.Begin (toplevel2); + + Assert.True (toplevel1.Running); + Assert.True (toplevel2.Running); + } + finally + { + // Dispose toplevels BEFORE Shutdown to satisfy DEBUG_IDISPOSABLE assertions + toplevel1?.Dispose (); + toplevel2?.Dispose (); + + // Shutdown calls ResetState, which will stop all running toplevels + app.Shutdown (); + + // Verify toplevels were stopped + Assert.False (toplevel1!.Running); + Assert.False (toplevel2!.Running); + } + } + + [Fact] + public void Begin_ActivatesNewToplevel_WhenCurrentExists () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel1 = null; + Toplevel? toplevel2 = null; + + try + { + toplevel1 = new() { Id = "1" }; + toplevel2 = new() { Id = "2" }; + + var toplevel1Deactivated = false; + var toplevel2Activated = false; + + toplevel1.Deactivate += (s, e) => toplevel1Deactivated = true; + toplevel2.Activate += (s, e) => toplevel2Activated = true; + + app.Begin (toplevel1); + app.Begin (toplevel2); + + Assert.True (toplevel1Deactivated); + Assert.True (toplevel2Activated); + Assert.Same (toplevel2, app.Current); + } + finally + { + toplevel1?.Dispose (); + toplevel2?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void Begin_DoesNotDuplicateToplevel_WhenIdAlreadyExists () + { + IApplication app = NewApplicationImpl (); + Toplevel? toplevel = null; + + try + { + toplevel = new() { Id = "test-id" }; + + app.Begin (toplevel); + Assert.Single (app.SessionStack); + + // Calling Begin again with same toplevel should not duplicate + app.Begin (toplevel); + Assert.Single (app.SessionStack); + } + finally + { + toplevel?.Dispose (); + app.Shutdown (); + } + } + + [Fact] + public void SessionStack_ContainsAllBegunToplevels () + { + IApplication app = NewApplicationImpl (); + List toplevels = new (); + + try + { + for (var i = 0; i < 10; i++) + { + var toplevel = new Toplevel { Id = $"toplevel-{i}" }; + toplevels.Add (toplevel); + app.Begin (toplevel); + } + + // All toplevels should be in the stack + Assert.Equal (10, app.SessionStack.Count); + + // Verify stack contains all toplevels + List stackList = app.SessionStack.ToList (); + + foreach (Toplevel toplevel in toplevels) + { + Assert.Contains (toplevel, stackList); + } + } + finally + { + foreach (Toplevel toplevel in toplevels) + { + toplevel.Dispose (); + } + + app.Shutdown (); + } + } +} diff --git a/Tests/UnitTests/Application/ApplicationImplTests.cs b/Tests/UnitTests/Application/ApplicationImplTests.cs index d31a254d3d..faa4cf7a89 100644 --- a/Tests/UnitTests/Application/ApplicationImplTests.cs +++ b/Tests/UnitTests/Application/ApplicationImplTests.cs @@ -1,7 +1,6 @@ #nullable enable using System.Collections.Concurrent; using Moq; -using TerminalGuiFluentTesting; namespace UnitTests.ApplicationTests; @@ -10,7 +9,7 @@ public class ApplicationImplTests /// /// Crates a new ApplicationImpl instance for testing. The input, output, and size monitor components are mocked. /// - private ApplicationImpl NewMockedApplicationImpl () + private IApplication? NewMockedApplicationImpl () { Mock netInput = new (); SetupRunInputMockMethodToBlock (netInput); @@ -21,132 +20,30 @@ private ApplicationImpl NewMockedApplicationImpl () Mock consoleOutput = new (); var size = new Size (80, 25); + consoleOutput.Setup (o => o.SetSize (It.IsAny (), It.IsAny ())) - .Callback ((w, h) => size = new Size (w, h)); + .Callback ((w, h) => size = new (w, h)); consoleOutput.Setup (o => o.GetSize ()).Returns (() => size); m.Setup (f => f.CreateOutput ()).Returns (consoleOutput.Object); m.Setup (f => f.CreateSizeMonitor (It.IsAny (), It.IsAny ())).Returns (Mock.Of ()); - return new (m.Object); + return new ApplicationImpl (m.Object); } [Fact] public void Init_CreatesKeybindings () { - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); - - Application.KeyBindings.Clear (); - - Assert.Empty (Application.KeyBindings.GetBindings ()); - - v2.Init (null, "fake"); - - Assert.NotEmpty (Application.KeyBindings.GetBindings ()); - - v2.Shutdown (); - - ApplicationImpl.ChangeInstance (orig); - } - - /* - [Fact] - public void Init_ExplicitlyRequestWin () - { - var orig = ApplicationImpl.Instance; - - Assert.Null (Application.Driver); - var netInput = new Mock (MockBehavior.Strict); - var netOutput = new Mock (MockBehavior.Strict); - var winInput = new Mock (MockBehavior.Strict); - var winOutput = new Mock (MockBehavior.Strict); - - winInput.Setup (i => i.Initialize (It.IsAny> ())) - .Verifiable (Times.Once); - SetupRunInputMockMethodToBlock (winInput); - winInput.Setup (i => i.Dispose ()) - .Verifiable (Times.Once); - winOutput.Setup (i => i.Dispose ()) - .Verifiable (Times.Once); - - var v2 = new ApplicationV2 ( - () => netInput.Object, - () => netOutput.Object, - () => winInput.Object, - () => winOutput.Object); - ApplicationImpl.ChangeInstance (v2); - - Assert.Null (Application.Driver); - v2.Init (null, "v2win"); - Assert.NotNull (Application.Driver); - - var type = Application.Driver.GetType (); - Assert.True (type.IsGenericType); - Assert.True (type.GetGenericTypeDefinition () == typeof (ConsoleDriverFacade<>)); - v2.Shutdown (); - - Assert.Null (Application.Driver); - - winInput.VerifyAll (); - - ApplicationImpl.ChangeInstance (orig); - } - - [Fact] - public void Init_ExplicitlyRequestNet () - { - var orig = ApplicationImpl.Instance; + IApplication? app = NewMockedApplicationImpl (); - var netInput = new Mock (MockBehavior.Strict); - var netOutput = new Mock (MockBehavior.Strict); - var winInput = new Mock (MockBehavior.Strict); - var winOutput = new Mock (MockBehavior.Strict); + app?.Keyboard.KeyBindings.Clear (); - netInput.Setup (i => i.Initialize (It.IsAny> ())) - .Verifiable (Times.Once); - SetupRunInputMockMethodToBlock (netInput); - netInput.Setup (i => i.Dispose ()) - .Verifiable (Times.Once); - netOutput.Setup (i => i.Dispose ()) - .Verifiable (Times.Once); - var v2 = new ApplicationV2 ( - () => netInput.Object, - () => netOutput.Object, - () => winInput.Object, - () => winOutput.Object); - ApplicationImpl.ChangeInstance (v2); - - Assert.Null (Application.Driver); - v2.Init (null, "v2net"); - Assert.NotNull (Application.Driver); - - var type = Application.Driver.GetType (); - Assert.True (type.IsGenericType); - Assert.True (type.GetGenericTypeDefinition () == typeof (ConsoleDriverFacade<>)); - v2.Shutdown (); + Assert.Empty (app?.Keyboard?.KeyBindings.GetBindings ()!); - Assert.Null (Application.Driver); + app?.Init ("fake"); - netInput.VerifyAll (); + Assert.NotEmpty (app?.Keyboard?.KeyBindings.GetBindings ()!); - ApplicationImpl.ChangeInstance (orig); - } -*/ - private void SetupRunInputMockMethodToBlock (Mock> winInput) - { - winInput.Setup (r => r.Run (It.IsAny ())) - .Callback (token => - { - // Simulate an infinite loop that checks for cancellation - while (!token.IsCancellationRequested) - { - // Perform the action that should repeat in the loop - // This could be some mock behavior or just an empty loop depending on the context - } - }) - .Verifiable (Times.Once); + app?.Shutdown (); } private void SetupRunInputMockMethodToBlock (Mock netInput) @@ -167,125 +64,105 @@ private void SetupRunInputMockMethodToBlock (Mock netInput) [Fact] public void NoInitThrowOnRun () { - IApplication orig = ApplicationImpl.Instance; - - Assert.Null (Application.Driver); - ApplicationImpl app = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (app); - - var ex = Assert.Throws (() => app.Run (new Window ())); + IApplication? app = NewMockedApplicationImpl (); + var ex = Assert.Throws (() => app?.Run (new Window ())); Assert.Equal ("Run cannot be accessed before Initialization", ex.Message); - app.Shutdown (); - - ApplicationImpl.ChangeInstance (orig); + app?.Shutdown (); } [Fact] public void InitRunShutdown_Top_Set_To_Null_After_Shutdown () { - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + IApplication? app = NewMockedApplicationImpl (); - v2.Init (null, "fake"); + app?.Init ("fake"); - object timeoutToken = v2.AddTimeout ( - TimeSpan.FromMilliseconds (150), - () => - { - if (Application.Top != null) - { - Application.RequestStop (); + object? timeoutToken = app?.AddTimeout ( + TimeSpan.FromMilliseconds (150), + () => + { + if (app.Current is { }) + { + app.RequestStop (); - return false; - } + return false; + } - return false; - } - ); - Assert.Null (Application.Top); + return false; + } + ); + Assert.Null (app?.Current); // Blocks until the timeout call is hit - v2.Run (new Window ()); + app?.Run (new Window ()); // We returned false above, so we should not have to remove the timeout - Assert.False (v2.RemoveTimeout (timeoutToken)); + Assert.False (app?.RemoveTimeout (timeoutToken!)); - Assert.NotNull (Application.Top); - Application.Top?.Dispose (); - v2.Shutdown (); - Assert.Null (Application.Top); - - ApplicationImpl.ChangeInstance (orig); + Assert.NotNull (app?.Current); + app.Current?.Dispose (); + app.Shutdown (); + Assert.Null (app.Current); } [Fact] public void InitRunShutdown_Running_Set_To_False () { - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + IApplication app = NewMockedApplicationImpl ()!; - v2.Init (null, "fake"); + app.Init ("fake"); Toplevel top = new Window { Title = "InitRunShutdown_Running_Set_To_False" }; - object timeoutToken = v2.AddTimeout ( - TimeSpan.FromMilliseconds (150), - () => - { - Assert.True (top!.Running); + object timeoutToken = app.AddTimeout ( + TimeSpan.FromMilliseconds (150), + () => + { + Assert.True (top!.Running); - if (Application.Top != null) - { - Application.RequestStop (); + if (app.Current != null) + { + app.RequestStop (); - return false; - } + return false; + } - return false; - } - ); + return false; + } + ); Assert.False (top!.Running); // Blocks until the timeout call is hit - v2.Run (top); + app.Run (top); // We returned false above, so we should not have to remove the timeout - Assert.False (v2.RemoveTimeout (timeoutToken)); + Assert.False (app.RemoveTimeout (timeoutToken)); Assert.False (top!.Running); // BUGBUG: Shutdown sets Top to null, not End. - //Assert.Null (Application.Top); - Application.Top?.Dispose (); - v2.Shutdown (); - - ApplicationImpl.ChangeInstance (orig); + //Assert.Null (Application.Current); + app.Current?.Dispose (); + app.Shutdown (); } - [Fact] public void InitRunShutdown_StopAfterFirstIteration_Stops () { - IApplication orig = ApplicationImpl.Instance; + IApplication app = NewMockedApplicationImpl ()!; - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + Assert.Null (app.Current); + Assert.Null (app.Driver); - Assert.Null (Application.Top); - Assert.Null (Application.Driver); - - v2.Init (null, "fake"); + app.Init ("fake"); Toplevel top = new Window (); + app.Current = top; var closedCount = 0; @@ -297,45 +174,40 @@ public void InitRunShutdown_StopAfterFirstIteration_Stops () top.Unloaded += (_, a) => { unloadedCount++; }; - object timeoutToken = v2.AddTimeout ( - TimeSpan.FromMilliseconds (150), - () => - { - Assert.Fail (@"Didn't stop after first iteration."); - return false; - } - ); + object timeoutToken = app.AddTimeout ( + TimeSpan.FromMilliseconds (150), + () => + { + Assert.Fail (@"Didn't stop after first iteration."); + + return false; + } + ); Assert.Equal (0, closedCount); Assert.Equal (0, unloadedCount); - v2.StopAfterFirstIteration = true; - v2.Run (top); + app.StopAfterFirstIteration = true; + app.Run (top); Assert.Equal (1, closedCount); Assert.Equal (1, unloadedCount); - Application.Top?.Dispose (); - v2.Shutdown (); + app.Current?.Dispose (); + app.Shutdown (); Assert.Equal (1, closedCount); Assert.Equal (1, unloadedCount); - - ApplicationImpl.ChangeInstance (orig); } - [Fact] public void InitRunShutdown_End_Is_Called () { - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + IApplication app = NewMockedApplicationImpl ()!; - Assert.Null (Application.Top); - Assert.Null (Application.Driver); + Assert.Null (app.Current); + Assert.Null (app.Driver); - v2.Init (null, "fake"); + app.Init ("fake"); Toplevel top = new Window (); @@ -350,125 +222,110 @@ public void InitRunShutdown_End_Is_Called () top.Unloaded += (_, a) => { unloadedCount++; }; - object timeoutToken = v2.AddTimeout ( - TimeSpan.FromMilliseconds (150), - () => - { - Assert.True (top!.Running); + object timeoutToken = app.AddTimeout ( + TimeSpan.FromMilliseconds (150), + () => + { + Assert.True (top!.Running); - if (Application.Top != null) - { - Application.RequestStop (); + if (app.Current != null) + { + app.RequestStop (); - return false; - } + return false; + } - return false; - } - ); + return false; + } + ); Assert.Equal (0, closedCount); Assert.Equal (0, unloadedCount); // Blocks until the timeout call is hit - v2.Run (top); + app.Run (top); Assert.Equal (1, closedCount); Assert.Equal (1, unloadedCount); // We returned false above, so we should not have to remove the timeout - Assert.False (v2.RemoveTimeout (timeoutToken)); + Assert.False (app.RemoveTimeout (timeoutToken)); - Application.Top?.Dispose (); - v2.Shutdown (); + app.Current?.Dispose (); + app.Shutdown (); Assert.Equal (1, closedCount); Assert.Equal (1, unloadedCount); - - ApplicationImpl.ChangeInstance (orig); } [Fact] public void InitRunShutdown_QuitKey_Quits () { - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + IApplication app = NewMockedApplicationImpl ()!; - v2.Init (null, "fake"); + app.Init ("fake"); Toplevel top = new Window { Title = "InitRunShutdown_QuitKey_Quits" }; - object timeoutToken = v2.AddTimeout ( - TimeSpan.FromMilliseconds (150), - () => - { - Assert.True (top!.Running); + object timeoutToken = app.AddTimeout ( + TimeSpan.FromMilliseconds (150), + () => + { + Assert.True (top!.Running); - if (Application.Top != null) - { - Application.RaiseKeyDownEvent (Application.QuitKey); - } + if (app.Current != null) + { + app.Keyboard.RaiseKeyDownEvent (app.Keyboard.QuitKey); + } - return false; - } - ); + return false; + } + ); Assert.False (top!.Running); // Blocks until the timeout call is hit - v2.Run (top); + app.Run (top); // We returned false above, so we should not have to remove the timeout - Assert.False (v2.RemoveTimeout (timeoutToken)); + Assert.False (app.RemoveTimeout (timeoutToken)); Assert.False (top!.Running); - Assert.NotNull (Application.Top); + Assert.NotNull (app.Current); top.Dispose (); - v2.Shutdown (); - Assert.Null (Application.Top); - - ApplicationImpl.ChangeInstance (orig); + app.Shutdown (); + Assert.Null (app.Current); } [Fact] public void InitRunShutdown_Generic_IdleForExit () { - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + IApplication app = NewMockedApplicationImpl ()!; - v2.Init (null, "fake"); + app.Init ("fake"); - v2.AddTimeout (TimeSpan.Zero, IdleExit); - Assert.Null (Application.Top); + app.AddTimeout (TimeSpan.Zero, () => IdleExit (app)); + Assert.Null (app.Current); // Blocks until the timeout call is hit - v2.Run (); + app.Run (); - Assert.NotNull (Application.Top); - Application.Top?.Dispose (); - v2.Shutdown (); - Assert.Null (Application.Top); - - ApplicationImpl.ChangeInstance (orig); + Assert.NotNull (app.Current); + app.Current?.Dispose (); + app.Shutdown (); + Assert.Null (app.Current); } [Fact] public void Shutdown_Closing_Closed_Raised () { - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + IApplication app = NewMockedApplicationImpl ()!; - v2.Init (null, "fake"); + app.Init ("fake"); var closing = 0; var closed = 0; @@ -494,69 +351,37 @@ public void Shutdown_Closing_Closed_Raised () Assert.Same (t, a.Toplevel); }; - v2.AddTimeout (TimeSpan.Zero, IdleExit); + app.AddTimeout (TimeSpan.Zero, () => IdleExit (app)); // Blocks until the timeout call is hit - v2.Run (t); + app.Run (t); - Application.Top?.Dispose (); - v2.Shutdown (); - - ApplicationImpl.ChangeInstance (orig); + app.Current?.Dispose (); + app.Shutdown (); Assert.Equal (2, closing); Assert.Equal (1, closed); } - private bool IdleExit () + private bool IdleExit (IApplication app) { - if (Application.Top != null) + if (app.Current != null) { - Application.RequestStop (); + app.RequestStop (); return true; } return true; } - /* - [Fact] - public void Shutdown_Called_Repeatedly_DoNotDuplicateDisposeOutput () - { - var orig = ApplicationImpl.Instance; - - var netInput = new Mock (); - SetupRunInputMockMethodToBlock (netInput); - Mock? outputMock = null; - - - var v2 = new ApplicationV2 ( - () => netInput.Object, - () => (outputMock = new Mock ()).Object, - Mock.Of, - Mock.Of); - ApplicationImpl.ChangeInstance (v2); - - v2.Init (null, "v2net"); - - - v2.Shutdown (); - outputMock!.Verify (o => o.Dispose (), Times.Once); - - ApplicationImpl.ChangeInstance (orig); - } - */ [Fact] public void Open_Calls_ContinueWith_On_UIThread () { - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + IApplication app = NewMockedApplicationImpl ()!; - v2.Init (null, "fake"); + app.Init ("fake"); var b = new Button (); var result = false; @@ -569,30 +394,30 @@ public void Open_Calls_ContinueWith_On_UIThread () (t, _) => { // no longer loading - Application.Invoke (() => - { - result = true; - Application.RequestStop (); - }); + app.Invoke (() => + { + result = true; + app.RequestStop (); + }); }, TaskScheduler.FromCurrentSynchronizationContext ()); }; - v2.AddTimeout ( - TimeSpan.FromMilliseconds (150), - () => - { - // Run asynchronous logic inside Task.Run - if (Application.Top != null) - { - b.NewKeyDownEvent (Key.Enter); - b.NewKeyUpEvent (Key.Enter); - } + app.AddTimeout ( + TimeSpan.FromMilliseconds (150), + () => + { + // Run asynchronous logic inside Task.Run + if (app.Current != null) + { + b.NewKeyDownEvent (Key.Enter); + b.NewKeyUpEvent (Key.Enter); + } - return false; - }); + return false; + }); - Assert.Null (Application.Top); + Assert.Null (app.Current); var w = new Window { @@ -601,14 +426,12 @@ public void Open_Calls_ContinueWith_On_UIThread () w.Add (b); // Blocks until the timeout call is hit - v2.Run (w); + app.Run (w); - Assert.NotNull (Application.Top); - Application.Top?.Dispose (); - v2.Shutdown (); - Assert.Null (Application.Top); - - ApplicationImpl.ChangeInstance (orig); + Assert.NotNull (app.Current); + app.Current?.Dispose (); + app.Shutdown (); + Assert.Null (app.Current); Assert.True (result); } @@ -617,47 +440,36 @@ public void Open_Calls_ContinueWith_On_UIThread () public void ApplicationImpl_UsesInstanceFields_NotStaticReferences () { // This test verifies that ApplicationImpl uses instance fields instead of static Application references - IApplication orig = ApplicationImpl.Instance; - - ApplicationImpl v2 = NewMockedApplicationImpl (); - ApplicationImpl.ChangeInstance (v2); + IApplication v2 = NewMockedApplicationImpl ()!; // Before Init, all fields should be null/default Assert.Null (v2.Driver); Assert.False (v2.Initialized); - Assert.Null (v2.Popover); - Assert.Null (v2.Navigation); - Assert.Null (v2.Top); - Assert.Empty (v2.TopLevels); + + //Assert.Null (v2.Popover); + //Assert.Null (v2.Navigation); + Assert.Null (v2.Current); + Assert.Empty (v2.SessionStack); // Init should populate instance fields - v2.Init (null, "fake"); + v2.Init ("fake"); // After Init, Driver, Navigation, and Popover should be populated Assert.NotNull (v2.Driver); Assert.True (v2.Initialized); Assert.NotNull (v2.Popover); Assert.NotNull (v2.Navigation); - Assert.Null (v2.Top); // Top is still null until Run - - // Verify that static Application properties delegate to instance - Assert.Equal (v2.Driver, Application.Driver); - Assert.Equal (v2.Initialized, Application.Initialized); - Assert.Equal (v2.Popover, Application.Popover); - Assert.Equal (v2.Navigation, Application.Navigation); - Assert.Equal (v2.Top, Application.Top); - Assert.Same (v2.TopLevels, Application.TopLevels); + Assert.Null (v2.Current); // Top is still null until Run // Shutdown should clean up instance fields v2.Shutdown (); Assert.Null (v2.Driver); Assert.False (v2.Initialized); - Assert.Null (v2.Popover); - Assert.Null (v2.Navigation); - Assert.Null (v2.Top); - Assert.Empty (v2.TopLevels); - ApplicationImpl.ChangeInstance (orig); + //Assert.Null (v2.Popover); + //Assert.Null (v2.Navigation); + Assert.Null (v2.Current); + Assert.Empty (v2.SessionStack); } } diff --git a/Tests/UnitTests/Application/ApplicationPopoverTests.cs b/Tests/UnitTests/Application/ApplicationPopoverTests.cs index 9cee1ed7cb..1ca1944d09 100644 --- a/Tests/UnitTests/Application/ApplicationPopoverTests.cs +++ b/Tests/UnitTests/Application/ApplicationPopoverTests.cs @@ -9,8 +9,7 @@ public void Application_Init_Initializes_PopoverManager () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); + Application.Init ("fake"); // Act Assert.NotNull (Application.Popover); @@ -27,8 +26,8 @@ public void Application_Shutdown_Resets_PopoverManager () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); + + Application.Init ("fake"); // Act Assert.NotNull (Application.Popover); @@ -36,7 +35,6 @@ public void Application_Shutdown_Resets_PopoverManager () Application.Shutdown (); // Test - Assert.Null (Application.Popover); } finally { @@ -52,12 +50,11 @@ public void Application_End_Does_Not_Reset_PopoverManager () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); + Application.Init ("fake"); Assert.NotNull (Application.Popover); Application.StopAfterFirstIteration = true; - top = new Toplevel (); + top = new (); SessionToken rs = Application.Begin (top); // Act @@ -81,15 +78,15 @@ public void Application_End_Hides_Active () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); + Application.Init ("fake"); Application.StopAfterFirstIteration = true; - top = new Toplevel (); + top = new (); SessionToken rs = Application.Begin (top); PopoverTestClass? popover = new (); + Application.Popover?.Register (popover); Application.Popover?.Show (popover); Assert.True (popover.Visible); @@ -116,8 +113,8 @@ public void Application_Shutdown_Disposes_Registered_Popovers () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); + + Application.Init ("fake"); PopoverTestClass? popover = new (); @@ -140,8 +137,8 @@ public void Application_Shutdown_Does_Not_Dispose_DeRegistered_Popovers () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); + + Application.Init ("fake"); PopoverTestClass? popover = new (); @@ -169,11 +166,11 @@ public void Application_Shutdown_Does_Not_Dispose_ActiveNotRegistered_Popover () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); - PopoverTestClass? popover = new (); + Application.Init ("fake"); + PopoverTestClass? popover = new (); + Application.Popover?.Register (popover); Application.Popover?.Show (popover); Application.Popover?.DeRegister (popover); @@ -198,16 +195,16 @@ public void Register_SetsTopLevel () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); - Application.Top = new Toplevel (); + + Application.Init ("fake"); + Application.Current = new (); PopoverTestClass? popover = new (); // Act Application.Popover?.Register (popover); // Assert - Assert.Equal (Application.Top, popover.Toplevel); + Assert.Equal (Application.Current, popover.Current); } finally { @@ -221,23 +218,23 @@ public void Keyboard_Events_Go_Only_To_Popover_Associated_With_Toplevel () try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); - Application.Top = new Toplevel () { Id = "initialTop" }; - PopoverTestClass? popover = new (); - int keyDownEvents = 0; + Application.Init ("fake"); + Application.Current = new() { Id = "initialTop" }; + PopoverTestClass? popover = new () { }; + var keyDownEvents = 0; + popover.KeyDown += (s, e) => - { - keyDownEvents++; - e.Handled = true; - }; // Ensure it handles the key + { + keyDownEvents++; + e.Handled = true; + }; // Ensure it handles the key Application.Popover?.Register (popover); // Act Application.RaiseKeyDownEvent (Key.A); // Goes to initialTop - Application.Top = new Toplevel () { Id = "secondaryTop" }; + Application.Current = new() { Id = "secondaryTop" }; Application.RaiseKeyDownEvent (Key.A); // Goes to secondaryTop // Test @@ -268,9 +265,9 @@ public void GetViewsUnderMouse_Supports_ActivePopover (int mouseX, int mouseY, s try { // Arrange - Assert.Null (Application.Popover); - Application.Init (null, "fake"); - Application.Top = new () + Application.Init ("fake"); + + Application.Current = new () { Frame = new (0, 0, 10, 10), Id = "top" @@ -282,10 +279,10 @@ public void GetViewsUnderMouse_Supports_ActivePopover (int mouseX, int mouseY, s X = 1, Y = 1, Width = 2, - Height = 2, + Height = 2 }; - Application.Top.Add (view); + Application.Current.Add (view); popover = new () { @@ -293,7 +290,7 @@ public void GetViewsUnderMouse_Supports_ActivePopover (int mouseX, int mouseY, s X = 5, Y = 5, Width = 3, - Height = 3, + Height = 3 }; // at 5,5 to 8,8 (screen) View? popoverSubView = new () @@ -302,14 +299,15 @@ public void GetViewsUnderMouse_Supports_ActivePopover (int mouseX, int mouseY, s X = 1, Y = 1, Width = 1, - Height = 1, + Height = 1 }; popover.Add (popoverSubView); + Application.Popover?.Register (popover); Application.Popover?.Show (popover); - List found = View.GetViewsUnderLocation (new (mouseX, mouseY), ViewportSettingsFlags.TransparentMouse); + List found = view.GetViewsUnderLocation (new (mouseX, mouseY), ViewportSettingsFlags.TransparentMouse); string [] foundIds = found.Select (v => v!.Id).ToArray (); @@ -318,7 +316,7 @@ public void GetViewsUnderMouse_Supports_ActivePopover (int mouseX, int mouseY, s finally { popover?.Dispose (); - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.ResetState (true); } } @@ -361,4 +359,4 @@ protected override void Dispose (bool disposing) DisposedCount++; } } -} \ No newline at end of file +} diff --git a/Tests/UnitTests/Application/ApplicationScreenTests.cs b/Tests/UnitTests/Application/ApplicationScreenTests.cs index fd4f27be89..9d4185adf3 100644 --- a/Tests/UnitTests/Application/ApplicationScreenTests.cs +++ b/Tests/UnitTests/Application/ApplicationScreenTests.cs @@ -15,7 +15,7 @@ public void ClearScreenNextIteration_Resets_To_False_After_LayoutAndDraw () { // Arrange Application.ResetState (true); - Application.Init (null, "fake"); + Application.Init ("fake"); // Act Application.ClearScreenNextIteration = true; @@ -46,35 +46,35 @@ public void ClearContents_Called_When_Top_Frame_Changes () Assert.Equal (0, clearedContentsRaised); // Act - Application.Top!.SetNeedsLayout (); + Application.Current!.SetNeedsLayout (); Application.LayoutAndDraw (); // Assert Assert.Equal (0, clearedContentsRaised); // Act - Application.Top.X = 1; + Application.Current.X = 1; Application.LayoutAndDraw (); // Assert Assert.Equal (1, clearedContentsRaised); // Act - Application.Top.Width = 10; + Application.Current.Width = 10; Application.LayoutAndDraw (); // Assert Assert.Equal (2, clearedContentsRaised); // Act - Application.Top.Y = 1; + Application.Current.Y = 1; Application.LayoutAndDraw (); // Assert Assert.Equal (3, clearedContentsRaised); // Act - Application.Top.Height = 10; + Application.Current.Height = 10; Application.LayoutAndDraw (); // Assert diff --git a/Tests/UnitTests/Application/ApplicationTests.cs b/Tests/UnitTests/Application/ApplicationTests.cs index a0909f5518..f9230fccd6 100644 --- a/Tests/UnitTests/Application/ApplicationTests.cs +++ b/Tests/UnitTests/Application/ApplicationTests.cs @@ -21,120 +21,41 @@ public ApplicationTests (ITestOutputHelper output) private readonly ITestOutputHelper _output; - private object _timeoutLock; - - [Fact (Skip = "Hangs with SetupFakeApplication")] - [SetupFakeApplication] + [Fact] public void AddTimeout_Fires () { - Assert.Null (_timeoutLock); - _timeoutLock = new (); - - uint timeoutTime = 250; - var initialized = false; - var iteration = 0; - var shutdown = false; - object timeout = null; - var timeoutCount = 0; - - Application.InitializedChanged += OnApplicationOnInitializedChanged; - - _output.WriteLine ("Application.Run ().Dispose ().."); - Application.Run ().Dispose (); - _output.WriteLine ("Back from Application.Run ().Dispose ()"); - - Assert.True (initialized); - Assert.False (shutdown); - - Assert.Equal (1, timeoutCount); - Application.Shutdown (); - - Application.InitializedChanged -= OnApplicationOnInitializedChanged; - - lock (_timeoutLock) - { - if (timeout is { }) - { - Application.RemoveTimeout (timeout); - timeout = null; - } - } - - Assert.True (initialized); - Assert.True (shutdown); - -#if DEBUG_IDISPOSABLE - Assert.Empty (View.Instances); -#endif - lock (_timeoutLock) - { - _timeoutLock = null; - } + IApplication app = Application.Create (); + app.Init ("fake"); - return; + uint timeoutTime = 100; + var timeoutFired = false; - void OnApplicationOnInitializedChanged (object s, EventArgs a) - { - if (a.Value) - { - Application.Iteration += OnApplicationOnIteration; - initialized = true; + // Setup a timeout that will fire + app.AddTimeout ( + TimeSpan.FromMilliseconds (timeoutTime), + () => + { + timeoutFired = true; - lock (_timeoutLock) - { - _output.WriteLine ($"Setting timeout for {timeoutTime}ms"); - timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (timeoutTime), TimeoutCallback); - } - } - else - { - Application.Iteration -= OnApplicationOnIteration; - shutdown = true; - } - } + // Return false so the timer does not repeat + return false; + } + ); - bool TimeoutCallback () - { - lock (_timeoutLock) - { - _output.WriteLine ($"TimeoutCallback. Count: {++timeoutCount}. Application Iteration: {iteration}"); + // The timeout has not fired yet + Assert.False (timeoutFired); - if (timeout is { }) - { - _output.WriteLine (" Nulling timeout."); - timeout = null; - } - } + // Block the thread to prove the timeout does not fire on a background thread + Thread.Sleep ((int)timeoutTime * 2); + Assert.False (timeoutFired); - // False means "don't re-do timer and remove it" - return false; - } - - void OnApplicationOnIteration (object s, IterationEventArgs a) - { - lock (_timeoutLock) - { - if (timeoutCount > 0) - { - _output.WriteLine ($"Iteration #{iteration} - Timeout fired. Calling Application.RequestStop."); - Application.RequestStop (); - - return; - } - } + app.StopAfterFirstIteration = true; + app.Run ().Dispose (); - iteration++; + // The timeout should have fired + Assert.True (timeoutFired); - // Simulate a delay - Thread.Sleep ((int)timeoutTime / 10); - - // Worst case scenario - something went wrong - if (Application.Initialized && iteration > 25) - { - _output.WriteLine ($"Too many iterations ({iteration}): Calling Application.RequestStop."); - Application.RequestStop (); - } - } + app.Shutdown (); } [Fact] @@ -149,13 +70,13 @@ public void Begin_Null_Toplevel_Throws () [SetupFakeApplication] public void Begin_Sets_Application_Top_To_Console_Size () { - Assert.Null (Application.Top); + Assert.Null (Application.Current); Application.Driver!.SetScreenSize (80, 25); Toplevel top = new (); Application.Begin (top); - Assert.Equal (new (0, 0, 80, 25), Application.Top!.Frame); + Assert.Equal (new (0, 0, 80, 25), Application.Current!.Frame); Application.Driver!.SetScreenSize (5, 5); - Assert.Equal (new (0, 0, 5, 5), Application.Top!.Frame); + Assert.Equal (new (0, 0, 5, 5), Application.Current!.Frame); top.Dispose (); } @@ -163,21 +84,21 @@ public void Begin_Sets_Application_Top_To_Console_Size () [SetupFakeApplication] public void End_And_Shutdown_Should_Not_Dispose_ApplicationTop () { - Assert.Null (Application.Top); + Assert.Null (Application.Current); SessionToken rs = Application.Begin (new ()); - Application.Top!.Title = "End_And_Shutdown_Should_Not_Dispose_ApplicationTop"; - Assert.Equal (rs.Toplevel, Application.Top); + Application.Current!.Title = "End_And_Shutdown_Should_Not_Dispose_ApplicationTop"; + Assert.Equal (rs.Toplevel, Application.Current); Application.End (rs); #if DEBUG_IDISPOSABLE Assert.True (rs.WasDisposed); - Assert.False (Application.Top!.WasDisposed); // Is true because the rs.Toplevel is the same as Application.Top + Assert.False (Application.Current!.WasDisposed); // Is true because the rs.Toplevel is the same as Application.Current #endif Assert.Null (rs.Toplevel); - Toplevel top = Application.Top; + Toplevel top = Application.Current; #if DEBUG_IDISPOSABLE Exception exception = Record.Exception (Application.Shutdown); @@ -211,12 +132,12 @@ public void Init_Begin_End_Cleans_Up () Assert.NotNull (sessionToken); Assert.Equal (rs, sessionToken); - Assert.Equal (topLevel, Application.Top); + Assert.Equal (topLevel, Application.Current); Application.SessionBegun -= newSessionTokenFn; Application.End (sessionToken); - Assert.NotNull (Application.Top); + Assert.NotNull (Application.Current); Assert.NotNull (Application.Driver); topLevel.Dispose (); @@ -240,7 +161,7 @@ public void Init_KeyBindings_Are_Not_Reset () Application.QuitKey = Key.Q; Assert.Equal (Key.Q, Application.QuitKey); - Application.Init (null, "fake"); + Application.Init ("fake"); Assert.Equal (Key.Q, Application.QuitKey); } @@ -313,8 +234,6 @@ public void Init_ResetState_Resets_Properties () // Mouse Application.LastMousePosition = new Point (1, 1); - Application.Navigation = new (); - Application.ResetState (); CheckReset (); @@ -327,7 +246,7 @@ void CheckReset () // Check that all fields and properties are set to their default values // Public Properties - Assert.Null (Application.Top); + Assert.Null (Application.Current); Assert.Null (Application.Mouse.MouseGrabView); // Don't check Application.ForceDriver @@ -350,7 +269,7 @@ void CheckReset () Assert.Equal (Application.GetSupportedCultures (), Application.SupportedCultures); Assert.Equal (Application.GetAvailableCulturesFromEmbeddedResources (), Application.SupportedCultures); Assert.Null (Application.MainThreadId); - Assert.Empty (Application.TopLevels); + Assert.Empty (Application.SessionStack); Assert.Empty (Application.CachedViewsUnderMouse); // Mouse @@ -358,10 +277,10 @@ void CheckReset () //Assert.Null (Application._lastMousePosition); // Navigation - Assert.Null (Application.Navigation); + // Assert.Null (Application.Navigation); // Popover - Assert.Null (Application.Popover); + //Assert.Null (Application.Popover); // Events - Can't check //Assert.Null (GetEventSubscribers (typeof (Application), "InitializedChanged")); @@ -380,7 +299,7 @@ public void Init_Shutdown_Cleans_Up () // Verify initial state is per spec //Pre_Init_State (); - Application.Init (null, "fake"); + Application.Init ("fake"); // Verify post-Init state is correct //Post_Init_State (); @@ -436,7 +355,7 @@ void OnApplicationOnInitializedChanged (object s, EventArgs a) public void Init_Unbalanced_Throws () { Assert.Throws (() => - Application.Init (null, "fake") + Application.Init ("fake") ); } @@ -445,7 +364,7 @@ public void Init_Unbalanced_Throws () public void Init_Unbalanced_Throws2 () { // Now try the other way - Assert.Throws (() => Application.Init (null, "fake")); + Assert.Throws (() => Application.Init ("fake")); } [Fact] @@ -456,7 +375,7 @@ public void Init_WithoutTopLevelFactory_Begin_End_Cleans_Up () // NOTE: Run, when called after Init has been called behaves differently than // when called if Init has not been called. Toplevel topLevel = new (); - Application.Init (null, "fake"); + Application.Init ("fake"); SessionToken sessionToken = null; @@ -472,18 +391,18 @@ public void Init_WithoutTopLevelFactory_Begin_End_Cleans_Up () Assert.NotNull (sessionToken); Assert.Equal (rs, sessionToken); - Assert.Equal (topLevel, Application.Top); + Assert.Equal (topLevel, Application.Current); Application.SessionBegun -= newSessionTokenFn; Application.End (sessionToken); - Assert.NotNull (Application.Top); + Assert.NotNull (Application.Current); Assert.NotNull (Application.Driver); topLevel.Dispose (); Application.Shutdown (); - Assert.Null (Application.Top); + Assert.Null (Application.Current); Assert.Null (Application.Driver); } @@ -492,11 +411,11 @@ public void Init_WithoutTopLevelFactory_Begin_End_Cleans_Up () public void Internal_Properties_Correct () { Assert.True (Application.Initialized); - Assert.Null (Application.Top); + Assert.Null (Application.Current); SessionToken rs = Application.Begin (new ()); - Assert.Equal (Application.Top, rs.Toplevel); + Assert.Equal (Application.Current, rs.Toplevel); Assert.Null (Application.Mouse.MouseGrabView); // public - Application.Top!.Dispose (); + Application.Current!.Dispose (); } // Invoke Tests @@ -509,7 +428,7 @@ public void Invoke_Adds_Idle () SessionToken rs = Application.Begin (top); var actionCalled = 0; - Application.Invoke (() => { actionCalled++; }); + Application.Invoke ((_) => { actionCalled++; }); Application.TimedEvents!.RunTimers (); Assert.Equal (1, actionCalled); top.Dispose (); @@ -520,7 +439,7 @@ public void Run_Iteration_Fires () { var iteration = 0; - Application.Init (null, "fake"); + Application.Init ("fake"); Application.Iteration += Application_Iteration; Application.Run ().Dispose (); @@ -592,9 +511,9 @@ public void Run_T_After_InitWithDriver_with_TopLevel_Does_Not_Throws () // Run when already initialized or not with a Driver will not throw (because Window is derived from Toplevel) // Using another type not derived from Toplevel will throws at compile time Application.Run (); - Assert.True (Application.Top is Window); + Assert.True (Application.Current is Window); - Application.Top!.Dispose (); + Application.Current!.Dispose (); } [Fact] @@ -605,15 +524,15 @@ public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Does_Not_Throws // Run when already initialized or not with a Driver will not throw (because Window is derived from Toplevel) // Using another type not derived from Toplevel will throws at compile time Application.Run (null, "fake"); - Assert.True (Application.Top is Window); + Assert.True (Application.Current is Window); - Application.Top!.Dispose (); + Application.Current!.Dispose (); // Run when already initialized or not with a Driver will not throw (because Dialog is derived from Toplevel) Application.Run (null, "fake"); - Assert.True (Application.Top is Dialog); + Assert.True (Application.Current is Dialog); - Application.Top!.Dispose (); + Application.Current!.Dispose (); Application.Shutdown (); } @@ -621,7 +540,7 @@ public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Does_Not_Throws [SetupFakeApplication] public void Run_T_After_Init_Does_Not_Disposes_Application_Top () { - // Init doesn't create a Toplevel and assigned it to Application.Top + // Init doesn't create a Toplevel and assigned it to Application.Current // but Begin does var initTop = new Toplevel (); @@ -635,13 +554,13 @@ public void Run_T_After_Init_Does_Not_Disposes_Application_Top () initTop.Dispose (); Assert.True (initTop.WasDisposed); #endif - Application.Top!.Dispose (); + Application.Current!.Dispose (); return; void OnApplicationOnIteration (object s, IterationEventArgs a) { - Assert.NotEqual (initTop, Application.Top); + Assert.NotEqual (initTop, Application.Current); #if DEBUG_IDISPOSABLE Assert.False (initTop.WasDisposed); #endif @@ -658,7 +577,7 @@ public void Run_T_After_InitWithDriver_with_TestTopLevel_DoesNotThrow () // Init has been called and we're passing no driver to Run. This is ok. Application.Run (); - Application.Top!.Dispose (); + Application.Current!.Dispose (); } [Fact] @@ -670,7 +589,7 @@ public void Run_T_After_InitNullDriver_with_TestTopLevel_DoesNotThrow () // Init has been called, selecting FakeDriver; we're passing no driver to Run. Should be fine. Application.Run (); - Application.Top!.Dispose (); + Application.Current!.Dispose (); } [Fact] @@ -701,7 +620,7 @@ public void Run_T_NoInit_WithDriver_DoesNotThrow () // Init has NOT been called and we're passing a valid driver to Run. This is ok. Application.Run (null, "fake"); - Application.Top!.Dispose (); + Application.Current!.Dispose (); } [Fact] @@ -825,9 +744,9 @@ public void End_Does_Not_Dispose () Assert.NotNull (w); Assert.Equal (string.Empty, w.Title); // Valid - w has not been disposed. The user may want to run it again - Assert.NotNull (Application.Top); - Assert.Equal (w, Application.Top); - Assert.NotEqual (top, Application.Top); + Assert.NotNull (Application.Current); + Assert.Equal (w, Application.Current); + Assert.NotEqual (top, Application.Current); Application.Run (w); // Valid - w has not been disposed. @@ -841,7 +760,7 @@ public void End_Does_Not_Dispose () //exception = Record.Exception ( // () => Application.Run ( - // w)); // Invalid - w has been disposed. Run it in debug mode will throw, otherwise the user may want to run it again + // w)); // Invalid - w has not been disposed. Run it in debug mode will throw, otherwise the user may want to run it again //Assert.NotNull (exception); // TODO: Re-enable this when we are done debug logging of ctx.Source.Title in RaiseSelecting @@ -855,14 +774,14 @@ public void End_Does_Not_Dispose () [Fact] public void Run_Creates_Top_Without_Init () { - Assert.Null (Application.Top); + Assert.Null (Application.Current); Application.StopAfterFirstIteration = true; Application.Iteration += OnApplicationOnIteration; Toplevel top = Application.Run (null, "fake"); Application.Iteration -= OnApplicationOnIteration; #if DEBUG_IDISPOSABLE - Assert.Equal (top, Application.Top); + Assert.Equal (top, Application.Current); Assert.False (top.WasDisposed); Exception exception = Record.Exception (Application.Shutdown); Assert.NotNull (exception); @@ -875,38 +794,38 @@ public void Run_Creates_Top_Without_Init () #if DEBUG_IDISPOSABLE Assert.True (top.WasDisposed); #endif - Assert.NotNull (Application.Top); + Assert.NotNull (Application.Current); Application.Shutdown (); - Assert.Null (Application.Top); + Assert.Null (Application.Current); return; - void OnApplicationOnIteration (object s, IterationEventArgs e) { Assert.NotNull (Application.Top); } + void OnApplicationOnIteration (object s, IterationEventArgs e) { Assert.NotNull (Application.Current); } } [Fact] public void Run_T_Creates_Top_Without_Init () { - Assert.Null (Application.Top); + Assert.Null (Application.Current); Application.StopAfterFirstIteration = true; Application.Run (null, "fake"); #if DEBUG_IDISPOSABLE - Assert.False (Application.Top!.WasDisposed); + Assert.False (Application.Current!.WasDisposed); Exception exception = Record.Exception (Application.Shutdown); Assert.NotNull (exception); - Assert.False (Application.Top!.WasDisposed); + Assert.False (Application.Current!.WasDisposed); // It's up to caller to dispose it - Application.Top!.Dispose (); - Assert.True (Application.Top!.WasDisposed); + Application.Current!.Dispose (); + Assert.True (Application.Current!.WasDisposed); #endif - Assert.NotNull (Application.Top); + Assert.NotNull (Application.Current); Application.Shutdown (); - Assert.Null (Application.Top); + Assert.Null (Application.Current); } [Fact] @@ -920,35 +839,35 @@ public void Run_t_Does_Not_Creates_Top_Without_Init () // the new(Toplevel) may be a derived class that is possible using Application static // properties that is only available after the Application.Init was called - Assert.Null (Application.Top); + Assert.Null (Application.Current); Assert.Throws (() => Application.Run (new Toplevel ())); - Application.Init (null, "fake"); + Application.Init ("fake"); - Application.Iteration += OnApplicationOnIteration; + Application.Iteration += OnApplication_OnIteration; Application.Run (new Toplevel ()); - Application.Iteration -= OnApplicationOnIteration; + Application.Iteration -= OnApplication_OnIteration; #if DEBUG_IDISPOSABLE - Assert.False (Application.Top!.WasDisposed); + Assert.False (Application.Current!.WasDisposed); Exception exception = Record.Exception (Application.Shutdown); Assert.NotNull (exception); - Assert.False (Application.Top!.WasDisposed); + Assert.False (Application.Current!.WasDisposed); // It's up to caller to dispose it - Application.Top!.Dispose (); - Assert.True (Application.Top!.WasDisposed); + Application.Current!.Dispose (); + Assert.True (Application.Current!.WasDisposed); #endif - Assert.NotNull (Application.Top); + Assert.NotNull (Application.Current); Application.Shutdown (); - Assert.Null (Application.Top); + Assert.Null (Application.Current); return; - void OnApplicationOnIteration (object s, IterationEventArgs e) + void OnApplication_OnIteration (object s, IterationEventArgs e) { - Assert.NotNull (Application.Top); + Assert.NotNull (Application.Current); Application.RequestStop (); } } @@ -960,7 +879,7 @@ private class TestToplevel : Toplevel public void Run_T_With_V2_Driver_Does_Not_Call_ResetState_After_Init () { Assert.False (Application.Initialized); - Application.Init (null, "fake"); + Application.Init ("fake"); Assert.True (Application.Initialized); Task.Run (() => { Task.Delay (300).Wait (); }) @@ -968,14 +887,14 @@ public void Run_T_With_V2_Driver_Does_Not_Call_ResetState_After_Init () (t, _) => { // no longer loading - Application.Invoke (() => { Application.RequestStop (); }); + Application.Invoke ((app) => { app.RequestStop (); }); }, TaskScheduler.FromCurrentSynchronizationContext ()); Application.Run (); Assert.NotNull (Application.Driver); - Assert.NotNull (Application.Top); - Assert.False (Application.Top!.Running); - Application.Top!.Dispose (); + Assert.NotNull (Application.Current); + Assert.False (Application.Current!.Running); + Application.Current!.Dispose (); Application.Shutdown (); } diff --git a/Tests/UnitTests/Application/CursorTests.cs b/Tests/UnitTests/Application/CursorTests.cs index 69d4722907..bf5cb12478 100644 --- a/Tests/UnitTests/Application/CursorTests.cs +++ b/Tests/UnitTests/Application/CursorTests.cs @@ -1,5 +1,4 @@ -using UnitTests; -using Xunit.Abstractions; +using Xunit.Abstractions; namespace UnitTests.ApplicationTests; @@ -7,22 +6,20 @@ public class CursorTests { private readonly ITestOutputHelper _output; - public CursorTests (ITestOutputHelper output) - { - _output = output; - } + public CursorTests (ITestOutputHelper output) { _output = output; } private class TestView : View { public Point? TestLocation { get; set; } - /// + /// public override Point? PositionCursor () { if (TestLocation.HasValue && HasFocus) { - Driver.SetCursorVisibility (CursorVisibility.Default); + Driver?.SetCursorVisibility (CursorVisibility.Default); } + return TestLocation; } } @@ -31,7 +28,6 @@ private class TestView : View [AutoInitShutdown] public void PositionCursor_No_Focus_Returns_False () { - Application.Navigation = new (); Application.Navigation.SetFocused (null); Assert.False (Application.PositionCursor ()); @@ -40,7 +36,7 @@ public void PositionCursor_No_Focus_Returns_False () { CanFocus = false, Width = 1, - Height = 1, + Height = 1 }; view.TestLocation = new Point (0, 0); Assert.False (Application.PositionCursor ()); @@ -50,12 +46,11 @@ public void PositionCursor_No_Focus_Returns_False () [AutoInitShutdown] public void PositionCursor_No_Position_Returns_False () { - Application.Navigation = new (); TestView view = new () { CanFocus = false, Width = 1, - Height = 1, + Height = 1 }; view.CanFocus = true; @@ -67,11 +62,10 @@ public void PositionCursor_No_Position_Returns_False () [AutoInitShutdown] public void PositionCursor_No_IntersectSuperView_Returns_False () { - Application.Navigation = new (); View superView = new () { Width = 1, - Height = 1, + Height = 1 }; TestView view = new () @@ -80,7 +74,7 @@ public void PositionCursor_No_IntersectSuperView_Returns_False () X = 1, Y = 1, Width = 1, - Height = 1, + Height = 1 }; superView.Add (view); @@ -94,11 +88,10 @@ public void PositionCursor_No_IntersectSuperView_Returns_False () [AutoInitShutdown] public void PositionCursor_Position_OutSide_SuperView_Returns_False () { - Application.Navigation = new (); View superView = new () { Width = 1, - Height = 1, + Height = 1 }; TestView view = new () @@ -107,7 +100,7 @@ public void PositionCursor_Position_OutSide_SuperView_Returns_False () X = 0, Y = 0, Width = 2, - Height = 2, + Height = 2 }; superView.Add (view); @@ -121,12 +114,12 @@ public void PositionCursor_Position_OutSide_SuperView_Returns_False () [AutoInitShutdown] public void PositionCursor_Focused_With_Position_Returns_True () { - Application.Navigation = new (); TestView view = new () { CanFocus = false, Width = 1, Height = 1, + App = ApplicationImpl.Instance }; view.CanFocus = true; view.SetFocus (); @@ -138,12 +131,11 @@ public void PositionCursor_Focused_With_Position_Returns_True () [AutoInitShutdown] public void PositionCursor_Defaults_Invisible () { - Application.Navigation = new (); View view = new () { CanFocus = true, Width = 1, - Height = 1, + Height = 1 }; view.SetFocus (); diff --git a/Tests/UnitTests/Application/MainLoopCoordinatorTests.cs b/Tests/UnitTests/Application/MainLoopCoordinatorTests.cs index 6c7f1d123a..b0e1e57088 100644 --- a/Tests/UnitTests/Application/MainLoopCoordinatorTests.cs +++ b/Tests/UnitTests/Application/MainLoopCoordinatorTests.cs @@ -26,7 +26,7 @@ public async Task TestMainLoopCoordinator_InputCrashes_ExceptionSurfacesMainThre // StartAsync boots the main loop and the input thread. But if the input class bombs // on startup it is important that the exception surface at the call site and not lost - var ex = await Assert.ThrowsAsync(c.StartInputTaskAsync); + var ex = await Assert.ThrowsAsync(() => c.StartInputTaskAsync (null)); Assert.Equal ("Crash on boot", ex.InnerExceptions [0].Message); diff --git a/Tests/UnitTests/Application/Mouse/ApplicationMouseEnterLeaveTests.cs b/Tests/UnitTests/Application/Mouse/ApplicationMouseEnterLeaveTests.cs index cbe800a98a..06c3cc4d14 100644 --- a/Tests/UnitTests/Application/Mouse/ApplicationMouseEnterLeaveTests.cs +++ b/Tests/UnitTests/Application/Mouse/ApplicationMouseEnterLeaveTests.cs @@ -41,9 +41,9 @@ protected override void OnMouseLeave () public void RaiseMouseEnterLeaveEvents_MouseEntersView_CallsOnMouseEnter () { // Arrange - Application.Top = new () { Frame = new (0, 0, 10, 10) }; + Application.Current = new () { Frame = new (0, 0, 10, 10) }; var view = new TestView (); - Application.Top.Add (view); + Application.Current.Add (view); var mousePosition = new Point (1, 1); List currentViewsUnderMouse = new () { view }; @@ -66,7 +66,7 @@ public void RaiseMouseEnterLeaveEvents_MouseEntersView_CallsOnMouseEnter () finally { // Cleanup - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.ResetState (); } } @@ -75,9 +75,9 @@ public void RaiseMouseEnterLeaveEvents_MouseEntersView_CallsOnMouseEnter () public void RaiseMouseEnterLeaveEvents_MouseLeavesView_CallsOnMouseLeave () { // Arrange - Application.Top = new () { Frame = new (0, 0, 10, 10) }; + Application.Current = new () { Frame = new (0, 0, 10, 10) }; var view = new TestView (); - Application.Top.Add (view); + Application.Current.Add (view); var mousePosition = new Point (0, 0); List currentViewsUnderMouse = new (); var mouseEvent = new MouseEventArgs (); @@ -97,7 +97,7 @@ public void RaiseMouseEnterLeaveEvents_MouseLeavesView_CallsOnMouseLeave () finally { // Cleanup - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.ResetState (); } } @@ -106,7 +106,7 @@ public void RaiseMouseEnterLeaveEvents_MouseLeavesView_CallsOnMouseLeave () public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMouseEnterAndLeave () { // Arrange - Application.Top = new () { Frame = new (0, 0, 10, 10) }; + Application.Current = new () { Frame = new (0, 0, 10, 10) }; var view1 = new TestView (); // at 1,1 to 2,2 var view2 = new TestView () // at 2,2 to 3,3 @@ -114,8 +114,8 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMou X = 2, Y = 2 }; - Application.Top.Add (view1); - Application.Top.Add (view2); + Application.Current.Add (view1); + Application.Current.Add (view2); Application.CachedViewsUnderMouse.Clear (); @@ -126,7 +126,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMou Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (0, view1.OnMouseEnterCalled); @@ -139,7 +139,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMou Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -152,7 +152,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMou Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -165,7 +165,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMou Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -178,7 +178,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMou Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -189,7 +189,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMou finally { // Cleanup - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.ResetState (); } } @@ -198,9 +198,9 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMou public void RaiseMouseEnterLeaveEvents_NoViewsUnderMouse_DoesNotCallOnMouseEnterOrLeave () { // Arrange - Application.Top = new () { Frame = new (0, 0, 10, 10) }; + Application.Current = new () { Frame = new (0, 0, 10, 10) }; var view = new TestView (); - Application.Top.Add (view); + Application.Current.Add (view); var mousePosition = new Point (0, 0); List currentViewsUnderMouse = new (); var mouseEvent = new MouseEventArgs (); @@ -219,7 +219,7 @@ public void RaiseMouseEnterLeaveEvents_NoViewsUnderMouse_DoesNotCallOnMouseEnter finally { // Cleanup - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.ResetState (); } } @@ -228,7 +228,7 @@ public void RaiseMouseEnterLeaveEvents_NoViewsUnderMouse_DoesNotCallOnMouseEnter public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_CallsOnMouseEnterAndLeave () { // Arrange - Application.Top = new () { Frame = new (0, 0, 10, 10) }; + Application.Current = new () { Frame = new (0, 0, 10, 10) }; var view1 = new TestView { @@ -241,8 +241,8 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal X = 2, Y = 2 }; - Application.Top.Add (view1); - Application.Top.Add (view2); + Application.Current.Add (view1); + Application.Current.Add (view2); Application.CachedViewsUnderMouse.Clear (); @@ -253,7 +253,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (0, view1.OnMouseEnterCalled); @@ -266,7 +266,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -279,7 +279,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -292,7 +292,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -305,7 +305,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -318,7 +318,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -329,7 +329,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal finally { // Cleanup - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.ResetState (); } } @@ -338,7 +338,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_Cal public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_CallsOnMouseEnterAndLeave () { // Arrange - Application.Top = new () { Frame = new (0, 0, 10, 10) }; + Application.Current = new () { Frame = new (0, 0, 10, 10) }; var view1 = new TestView { @@ -358,7 +358,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Arrangement = ViewArrangement.Overlapped }; // at 2,2 to 4,4 (screen) view1.Add (subView); - Application.Top.Add (view1); + Application.Current.Add (view1); Application.CachedViewsUnderMouse.Clear (); @@ -372,7 +372,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (0, view1.OnMouseEnterCalled); @@ -385,7 +385,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -398,7 +398,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -411,7 +411,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (1, view1.OnMouseEnterCalled); @@ -424,7 +424,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (2, view1.OnMouseEnterCalled); @@ -437,7 +437,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (2, view1.OnMouseEnterCalled); @@ -450,7 +450,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (2, view1.OnMouseEnterCalled); @@ -463,7 +463,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call Application.RaiseMouseEnterLeaveEvents ( mousePosition, - View.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); + Application.Current.GetViewsUnderLocation (mousePosition, ViewportSettingsFlags.TransparentMouse)); // Assert Assert.Equal (3, view1.OnMouseEnterCalled); @@ -474,7 +474,7 @@ public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_Call finally { // Cleanup - Application.Top?.Dispose (); + Application.Current?.Dispose (); Application.ResetState (); } } diff --git a/Tests/UnitTests/Application/Mouse/ApplicationMouseTests.cs b/Tests/UnitTests/Application/Mouse/ApplicationMouseTests.cs index 0451264ed7..6116bcf630 100644 --- a/Tests/UnitTests/Application/Mouse/ApplicationMouseTests.cs +++ b/Tests/UnitTests/Application/Mouse/ApplicationMouseTests.cs @@ -202,15 +202,15 @@ bool expectedClicked var clicked = false; - Application.Top = new Toplevel () + Application.Current = new Toplevel () { Id = "top", }; - Application.Top.X = 0; - Application.Top.Y = 0; - Application.Top.Width = size.Width * 2; - Application.Top.Height = size.Height * 2; - Application.Top.BorderStyle = LineStyle.None; + Application.Current.X = 0; + Application.Current.Y = 0; + Application.Current.Width = size.Width * 2; + Application.Current.Height = size.Height * 2; + Application.Current.BorderStyle = LineStyle.None; var view = new View { Id = "view", X = pos.X, Y = pos.Y, Width = size.Width, Height = size.Height }; @@ -218,7 +218,7 @@ bool expectedClicked view.BorderStyle = LineStyle.Single; view.CanFocus = true; - Application.Top.Add (view); + Application.Current.Add (view); var mouseEvent = new MouseEventArgs { Position = new (clickX, clickY), ScreenPosition = new (clickX, clickY), Flags = MouseFlags.Button1Clicked }; @@ -231,7 +231,7 @@ bool expectedClicked Application.RaiseMouseEvent (mouseEvent); Assert.Equal (expectedClicked, clicked); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (ignoreDisposed: true); } diff --git a/Tests/UnitTests/Application/SessionTokenTests.cs b/Tests/UnitTests/Application/SessionTokenTests.cs index d01bc1c592..1e642d04d0 100644 --- a/Tests/UnitTests/Application/SessionTokenTests.cs +++ b/Tests/UnitTests/Application/SessionTokenTests.cs @@ -1,4 +1,5 @@ +#nullable enable namespace UnitTests.ApplicationTests; /// These tests focus on Application.SessionToken and the various ways it can be changed. @@ -19,14 +20,16 @@ public SessionTokenTests () public void Begin_End_Cleans_Up_SessionToken () { // Test null Toplevel - Assert.Throws (() => Application.Begin (null)); + Assert.Throws (() => Application.Begin (null!)); - var top = new Toplevel (); + Assert.NotNull (Application.Driver); + + Toplevel top = new Toplevel (); SessionToken rs = Application.Begin (top); Assert.NotNull (rs); Application.End (rs); - Assert.NotNull (Application.Top); + Assert.NotNull (Application.Current); // v2 does not use main loop, it uses MainLoop and its internal //Assert.NotNull (Application.MainLoop); @@ -42,7 +45,7 @@ public void Begin_End_Cleans_Up_SessionToken () [Fact] public void Dispose_Cleans_Up_SessionToken () { - var rs = new SessionToken (null); + var rs = new SessionToken (null!); Assert.NotNull (rs); // Should not throw because Toplevel was null @@ -57,7 +60,7 @@ public void Dispose_Cleans_Up_SessionToken () // Should throw because Toplevel was not cleaned up Assert.Throws (() => rs.Dispose ()); - rs.Toplevel.Dispose (); + rs.Toplevel?.Dispose (); rs.Toplevel = null; rs.Dispose (); #if DEBUG_IDISPOSABLE @@ -69,7 +72,7 @@ public void Dispose_Cleans_Up_SessionToken () [Fact] public void New_Creates_SessionToken () { - var rs = new SessionToken (null); + var rs = new SessionToken (null!); Assert.Null (rs.Toplevel); var top = new Toplevel (); diff --git a/Tests/UnitTests/Application/SynchronizatonContextTests.cs b/Tests/UnitTests/Application/SynchronizatonContextTests.cs index 9f8d34b0e2..915cb30245 100644 --- a/Tests/UnitTests/Application/SynchronizatonContextTests.cs +++ b/Tests/UnitTests/Application/SynchronizatonContextTests.cs @@ -9,7 +9,7 @@ public class SyncrhonizationContextTests [Fact] public void SynchronizationContext_CreateCopy () { - Application.Init (null, "fake"); + Application.Init ("fake"); SynchronizationContext context = SynchronizationContext.Current; Assert.NotNull (context); @@ -31,7 +31,7 @@ public void SynchronizationContext_Post (string driverName = null) { lock (_lockPost) { - Application.Init (null, driverName: driverName); + Application.Init (driverName); SynchronizationContext context = SynchronizationContext.Current; @@ -39,7 +39,7 @@ public void SynchronizationContext_Post (string driverName = null) Task.Run (() => { - while (Application.Top is null || Application.Top is { Running: false }) + while (Application.Current is null || Application.Current is { Running: false }) { Thread.Sleep (500); } @@ -56,7 +56,7 @@ public void SynchronizationContext_Post (string driverName = null) null ); - if (Application.Top is { Running: true }) + if (Application.Current is { Running: true }) { Assert.False (success); } diff --git a/Tests/UnitTests/Application/TimedEventsTests.cs b/Tests/UnitTests/Application/TimedEventsTests.cs index 4877a6b2ce..98d061159c 100644 --- a/Tests/UnitTests/Application/TimedEventsTests.cs +++ b/Tests/UnitTests/Application/TimedEventsTests.cs @@ -134,4 +134,35 @@ public void Multiple_TimeSpan_Zero_Timeouts_All_Execute () Assert.Equal (expected, executeCount); } + + [Fact] + public void StopAll_Stops_All_Timeouts () + { + var timedEvents = new TimedEvents (); + var executeCount = 0; + var expected = 100; + + for (var i = 0; i < expected; i++) + { + timedEvents.Add ( + TimeSpan.Zero, + () => + { + Interlocked.Increment (ref executeCount); + + return false; + }); + } + + Assert.Equal (expected, timedEvents.Timeouts.Count); + + timedEvents.StopAll (); + + Assert.Empty (timedEvents.Timeouts); + + // Run timers once + timedEvents.RunTimers (); + + Assert.Equal (0, executeCount); + } } diff --git a/Tests/UnitTests/AutoInitShutdownAttribute.cs b/Tests/UnitTests/AutoInitShutdownAttribute.cs index 0b3d2af075..a0536503b6 100644 --- a/Tests/UnitTests/AutoInitShutdownAttribute.cs +++ b/Tests/UnitTests/AutoInitShutdownAttribute.cs @@ -25,20 +25,16 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute /// be used when Application.Init is called. If not specified FakeDriver will be used. Only valid if /// is true. /// - /// If true and is true, the test will fail. public AutoInitShutdownAttribute ( bool autoInit = true, - string forceDriver = null, - bool verifyShutdown = false + string forceDriver = null ) { - AutoInit = autoInit; + _autoInit = autoInit; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US"); _forceDriver = forceDriver; - _verifyShutdown = verifyShutdown; } - private readonly bool _verifyShutdown; private readonly string _forceDriver; private IDisposable _v2Cleanup; @@ -51,15 +47,10 @@ public override void After (MethodInfo methodUnderTest) _v2Cleanup?.Dispose (); - if (AutoInit) + if (_autoInit) { - // try + try { - if (!_verifyShutdown) - { - Application.ResetState (ignoreDisposed: true); - } - Application.Shutdown (); #if DEBUG_IDISPOSABLE if (View.Instances.Count == 0) @@ -74,14 +65,15 @@ public override void After (MethodInfo methodUnderTest) } //catch (Exception e) //{ - // Assert.Fail ($"Application.Shutdown threw an exception after the test exited: {e}"); + // Debug.WriteLine ($"Application.Shutdown threw an exception after the test exited: {e}"); //} - //finally + finally { #if DEBUG_IDISPOSABLE View.Instances.Clear (); Application.ResetState (true); #endif + ApplicationImpl.SetInstance (null); } } @@ -102,7 +94,7 @@ public override void Before (MethodInfo methodUnderTest) //Debug.Assert(!CM.IsEnabled, "Some other test left ConfigurationManager enabled."); - if (AutoInit) + if (_autoInit) { #if DEBUG_IDISPOSABLE View.EnableDebugIDisposableAsserts = true; @@ -117,7 +109,7 @@ public override void Before (MethodInfo methodUnderTest) View.Instances.Clear (); } #endif - if (string.IsNullOrEmpty(_forceDriver) || _forceDriver.ToLowerInvariant () == "fake") + if (string.IsNullOrEmpty (_forceDriver) || _forceDriver.ToLowerInvariant () == "fake") { var fa = new FakeApplicationFactory (); _v2Cleanup = fa.SetupFakeApplication (); @@ -131,7 +123,7 @@ public override void Before (MethodInfo methodUnderTest) } } - private bool AutoInit { get; } + private bool _autoInit { get; } /// /// Runs a single iteration of the main loop (layout, draw, run timed events etc.) diff --git a/Tests/UnitTests/Configuration/ThemeScopeTests.cs b/Tests/UnitTests/Configuration/ThemeScopeTests.cs index 25c881d64e..ca1e05aa14 100644 --- a/Tests/UnitTests/Configuration/ThemeScopeTests.cs +++ b/Tests/UnitTests/Configuration/ThemeScopeTests.cs @@ -86,6 +86,43 @@ public void Serialize_Themes_RoundTrip () Disable (true); } + + [Fact] + public void DeSerialize_Themes_UpdateFrom_Updates () + { + Enable (ConfigLocations.HardCoded); + + IDictionary initial = ThemeManager.Themes!; + + string serialized = """ + { + "Default": { + "Button.DefaultShadow": "None" + } + } + """; + ConcurrentDictionary? deserialized = + JsonSerializer.Deserialize> (serialized, SerializerContext.Options); + + ShadowStyle initialShadowStyle = (ShadowStyle)(initial! ["Default"] ["Button.DefaultShadow"].PropertyValue!); + Assert.Equal (ShadowStyle.Opaque, initialShadowStyle); + + ShadowStyle deserializedShadowStyle = (ShadowStyle)(deserialized! ["Default"] ["Button.DefaultShadow"].PropertyValue!); + Assert.Equal (ShadowStyle.None, deserializedShadowStyle); + + initial ["Default"].UpdateFrom (deserialized ["Default"]); + initialShadowStyle = (ShadowStyle)(initial! ["Default"] ["Button.DefaultShadow"].PropertyValue!); + Assert.Equal (ShadowStyle.None, initialShadowStyle); + + Assert.Equal(ShadowStyle.Opaque, Button.DefaultShadow); + initial ["Default"].Apply (); + Assert.Equal (ShadowStyle.None, Button.DefaultShadow); + + Disable (true); + Assert.Equal (ShadowStyle.Opaque, Button.DefaultShadow); + + } + [Fact] public void Serialize_New_RoundTrip () { diff --git a/Tests/UnitTests/Dialogs/DialogTests.cs b/Tests/UnitTests/Dialogs/DialogTests.cs index b37f3225d0..a4ba9591a4 100644 --- a/Tests/UnitTests/Dialogs/DialogTests.cs +++ b/Tests/UnitTests/Dialogs/DialogTests.cs @@ -902,8 +902,8 @@ public void Can_Access_Cancel_Property_After_Run () #if DEBUG_IDISPOSABLE Assert.False (dlg.WasDisposed); - Assert.False (Application.Top!.WasDisposed); - Assert.Equal (dlg, Application.Top); + Assert.False (Application.Current!.WasDisposed); + Assert.Equal (dlg, Application.Current); #endif Assert.True (dlg.Canceled); @@ -925,8 +925,8 @@ public void Can_Access_Cancel_Property_After_Run () Application.Run (dlg2); Assert.True (dlg.WasDisposed); - Assert.False (Application.Top.WasDisposed); - Assert.Equal (dlg2, Application.Top); + Assert.False (Application.Current.WasDisposed); + Assert.Equal (dlg2, Application.Current); Assert.False (dlg2.WasDisposed); dlg2.Dispose (); @@ -937,10 +937,10 @@ public void Can_Access_Cancel_Property_After_Run () //Assert.NotNull (exception); //Assert.StartsWith ("Cannot access a disposed object.", exception.Message); - Assert.True (Application.Top.WasDisposed); + Assert.True (Application.Current.WasDisposed); Application.Shutdown (); Assert.True (dlg2.WasDisposed); - Assert.Null (Application.Top); + Assert.Null (Application.Current); #endif return; @@ -1174,8 +1174,8 @@ void OnApplicationOnIteration (object? s, IterationEventArgs a) switch (iterations) { case 0: - Application.Top!.SetNeedsLayout (); - Application.Top.SetNeedsDraw (); + Application.Current!.SetNeedsLayout (); + Application.Current.SetNeedsDraw (); break; @@ -1216,7 +1216,7 @@ void OnApplicationOnIteration (object? s, IterationEventArgs a) └───────────────────────┘", output); - Assert.False (Application.Top!.NewKeyDownEvent (Key.Enter)); + Assert.False (Application.Current!.NewKeyDownEvent (Key.Enter)); break; case 7: @@ -1410,9 +1410,9 @@ public void Run_Does_Not_Dispose_Dialog () #if DEBUG_IDISPOSABLE Assert.False (dlg.WasDisposed); - Assert.False (Application.Top!.WasDisposed); - Assert.NotEqual (top, Application.Top); - Assert.Equal (dlg, Application.Top); + Assert.False (Application.Current!.WasDisposed); + Assert.NotEqual (top, Application.Current); + Assert.Equal (dlg, Application.Current); #endif // dlg wasn't disposed yet and it's possible to access to his properties @@ -1426,11 +1426,11 @@ public void Run_Does_Not_Dispose_Dialog () top.Dispose (); #if DEBUG_IDISPOSABLE Assert.True (dlg.WasDisposed); - Assert.True (Application.Top.WasDisposed); - Assert.NotNull (Application.Top); + Assert.True (Application.Current.WasDisposed); + Assert.NotNull (Application.Current); #endif Application.Shutdown (); - Assert.Null (Application.Top); + Assert.Null (Application.Current); return; diff --git a/Tests/UnitTests/Dialogs/MessageBoxTests.cs b/Tests/UnitTests/Dialogs/MessageBoxTests.cs index 075067f5fb..0699309b74 100644 --- a/Tests/UnitTests/Dialogs/MessageBoxTests.cs +++ b/Tests/UnitTests/Dialogs/MessageBoxTests.cs @@ -193,7 +193,7 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) } else if (iterations == 1) { - mbFrame = Application.Top!.Frame; + mbFrame = Application.Current!.Frame; Application.RequestStop (); } } @@ -378,8 +378,8 @@ public void Size_Not_Default_Message (int height, int width, string message) { AutoInitShutdownAttribute.RunIteration (); - Assert.IsType (Application.Top); - Assert.Equal (new (height, width), Application.Top.Frame.Size); + Assert.IsType (Application.Current); + Assert.Equal (new (height, width), Application.Current.Frame.Size); Application.RequestStop (); } @@ -415,8 +415,8 @@ public void Size_Not_Default_Message_Button (int height, int width, string messa { AutoInitShutdownAttribute.RunIteration (); - Assert.IsType (Application.Top); - Assert.Equal (new (height, width), Application.Top.Frame.Size); + Assert.IsType (Application.Current); + Assert.Equal (new (height, width), Application.Current.Frame.Size); Application.RequestStop (); } @@ -448,8 +448,8 @@ public void Size_Not_Default_No_Message (int height, int width) { AutoInitShutdownAttribute.RunIteration (); - Assert.IsType (Application.Top); - Assert.Equal (new (height, width), Application.Top.Frame.Size); + Assert.IsType (Application.Current); + Assert.Equal (new (height, width), Application.Current.Frame.Size); Application.RequestStop (); } diff --git a/Tests/UnitTests/DriverAssert.cs b/Tests/UnitTests/DriverAssert.cs index a1abc36b59..4e7795b137 100644 --- a/Tests/UnitTests/DriverAssert.cs +++ b/Tests/UnitTests/DriverAssert.cs @@ -60,7 +60,7 @@ params Attribute [] expectedAttributes { case 0: output.WriteLine ( - $"{Application.ToString (driver)}\n" + $"{driver.ToString ()}\n" + $"Expected Attribute {val} at Contents[{line},{c}] {contents [line, c]} was not found.\n" + $" Expected: {string.Join (",", expectedAttributes.Select (attr => attr))}\n" + $" But Was: " @@ -79,7 +79,7 @@ params Attribute [] expectedAttributes if (colorUsed != userExpected) { - output.WriteLine ($"{Application.ToString (driver)}"); + output.WriteLine ($"{driver.ToString ()}"); output.WriteLine ($"Unexpected Attribute at Contents[{line},{c}] = {contents [line, c]}."); output.WriteLine ($" Expected: {userExpected} ({expectedAttributes [int.Parse (userExpected.ToString ())]})"); output.WriteLine ($" But Was: {colorUsed} ({val})"); @@ -152,7 +152,9 @@ public static void AssertDriverContentsAre ( ) { #pragma warning restore xUnit1013 // Public method should be marked as test - var actualLook = Application.ToString (driver ?? Application.Driver); + driver ??= Application.Driver!; + + var actualLook = driver.ToString (); if (string.Equals (expectedLook, actualLook)) { @@ -198,7 +200,7 @@ public static Rectangle AssertDriverContentsWithFrameAre ( { List> lines = []; var sb = new StringBuilder (); - driver ??= Application.Driver; + driver ??= Application.Driver!; int x = -1; int y = -1; diff --git a/Tests/UnitTests/Drivers/ClipRegionTests.cs b/Tests/UnitTests/Drivers/ClipRegionTests.cs index 8ede58565a..013cdbe7a3 100644 --- a/Tests/UnitTests/Drivers/ClipRegionTests.cs +++ b/Tests/UnitTests/Drivers/ClipRegionTests.cs @@ -12,7 +12,7 @@ public class ClipRegionTests (ITestOutputHelper output) [Fact] public void AddRune_Is_Clipped () { - Application.Init (null, "fake"); + Application.Init ("fake"); Application.Driver!.Move (0, 0); Application.Driver!.AddRune ('x'); @@ -41,7 +41,7 @@ public void AddRune_Is_Clipped () [Fact] public void Clip_Set_To_Empty_AllInvalid () { - Application.Init (null, "fake"); + Application.Init ("fake"); // Define a clip rectangle Application.Driver!.Clip = new (Rectangle.Empty); @@ -64,7 +64,7 @@ public void Clip_Set_To_Empty_AllInvalid () [Fact] public void IsValidLocation () { - Application.Init (null, "fake"); + Application.Init ("fake"); Application.Driver!.Rows = 10; Application.Driver!.Cols = 10; diff --git a/Tests/UnitTests/Drivers/DriverTests.cs b/Tests/UnitTests/Drivers/DriverTests.cs index 558245c9e7..202ec72088 100644 --- a/Tests/UnitTests/Drivers/DriverTests.cs +++ b/Tests/UnitTests/Drivers/DriverTests.cs @@ -1,5 +1,4 @@ -using System.Text; -using UnitTests.ViewsTests; +#nullable enable using Xunit.Abstractions; namespace UnitTests.DriverTests; @@ -8,16 +7,16 @@ public class DriverTests (ITestOutputHelper output) { private readonly ITestOutputHelper _output = output; - [Theory] [InlineData ("fake")] [InlineData ("windows")] [InlineData ("dotnet")] [InlineData ("unix")] - public void All_Drivers_Init_Shutdown_Cross_Platform (string driverName = null) + public void All_Drivers_Init_Shutdown_Cross_Platform (string driverName) { - Application.Init (null, driverName: driverName); - Application.Shutdown (); + IApplication? app = Application.Create (); + app.Init (driverName); + app.Shutdown (); } [Theory] @@ -25,12 +24,13 @@ public void All_Drivers_Init_Shutdown_Cross_Platform (string driverName = null) [InlineData ("windows")] [InlineData ("dotnet")] [InlineData ("unix")] - public void All_Drivers_Run_Cross_Platform (string driverName = null) + public void All_Drivers_Run_Cross_Platform (string driverName) { - Application.Init (null, driverName: driverName); - Application.StopAfterFirstIteration = true; - Application.Run ().Dispose (); - Application.Shutdown (); + IApplication? app = Application.Create (); + app.Init (driverName); + app.StopAfterFirstIteration = true; + app.Run ().Dispose (); + app.Shutdown (); } [Theory] @@ -38,22 +38,22 @@ public void All_Drivers_Run_Cross_Platform (string driverName = null) [InlineData ("windows")] [InlineData ("dotnet")] [InlineData ("unix")] - public void All_Drivers_LayoutAndDraw_Cross_Platform (string driverName = null) + public void All_Drivers_LayoutAndDraw_Cross_Platform (string driverName) { - Application.Init (null, driverName: driverName); - Application.StopAfterFirstIteration = true; - Application.Run ().Dispose (); - - DriverAssert.AssertDriverContentsWithFrameAre (expectedLook: driverName!, _output); + IApplication? app = Application.Create (); + app.Init (driverName); + app.StopAfterFirstIteration = true; + app.Run ().Dispose (); - Application.Shutdown (); + DriverAssert.AssertDriverContentsWithFrameAre (driverName!, _output, app.Driver); + app.Shutdown (); } } public class TestTop : Toplevel { - /// + /// public override void BeginInit () { Text = Driver!.GetName ()!; diff --git a/Tests/UnitTests/SetupFakeApplicationAttribute.cs b/Tests/UnitTests/SetupFakeApplicationAttribute.cs index 930e44c41b..0b8633da7d 100644 --- a/Tests/UnitTests/SetupFakeApplicationAttribute.cs +++ b/Tests/UnitTests/SetupFakeApplicationAttribute.cs @@ -32,6 +32,7 @@ public override void After (MethodInfo methodUnderTest) _appDispose?.Dispose (); _appDispose = null; + ApplicationImpl.SetInstance (null); base.After (methodUnderTest); } diff --git a/Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs b/Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs index 52d5bc01e5..7bfa4747a4 100644 --- a/Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs +++ b/Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs @@ -14,14 +14,14 @@ public class AdornmentSubViewTests (ITestOutputHelper output) [InlineData (2, 1, true)] public void Adornment_WithSubView_Finds (int viewMargin, int subViewMargin, bool expectedFound) { - Application.Top = new Toplevel() + Application.Current = new Toplevel() { Width = 10, Height = 10 }; - Application.Top.Margin!.Thickness = new Thickness (viewMargin); + Application.Current.Margin!.Thickness = new Thickness (viewMargin); // Turn of TransparentMouse for the test - Application.Top.Margin!.ViewportSettings = ViewportSettingsFlags.None; + Application.Current.Margin!.ViewportSettings = ViewportSettingsFlags.None; var subView = new View () { @@ -34,26 +34,26 @@ public void Adornment_WithSubView_Finds (int viewMargin, int subViewMargin, bool // Turn of TransparentMouse for the test subView.Margin!.ViewportSettings = ViewportSettingsFlags.None; - Application.Top.Margin!.Add (subView); - Application.Top.Layout (); + Application.Current.Margin!.Add (subView); + Application.Current.Layout (); - var foundView = View.GetViewsUnderLocation (new Point(0, 0), ViewportSettingsFlags.None).LastOrDefault (); + var foundView = Application.Current.GetViewsUnderLocation (new Point(0, 0), ViewportSettingsFlags.None).LastOrDefault (); bool found = foundView == subView || foundView == subView.Margin; Assert.Equal (expectedFound, found); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (ignoreDisposed: true); } [Fact] public void Adornment_WithNonVisibleSubView_Finds_Adornment () { - Application.Top = new Toplevel () + Application.Current = new Toplevel () { Width = 10, Height = 10 }; - Application.Top.Padding.Thickness = new Thickness (1); + Application.Current.Padding.Thickness = new Thickness (1); var subView = new View () { @@ -63,11 +63,11 @@ public void Adornment_WithNonVisibleSubView_Finds_Adornment () Height = 1, Visible = false }; - Application.Top.Padding.Add (subView); - Application.Top.Layout (); + Application.Current.Padding.Add (subView); + Application.Current.Layout (); - Assert.Equal (Application.Top.Padding, View.GetViewsUnderLocation (new Point(0, 0), ViewportSettingsFlags.None).LastOrDefault ()); - Application.Top?.Dispose (); + Assert.Equal (Application.Current.Padding, Application.Current.GetViewsUnderLocation (new Point(0, 0), ViewportSettingsFlags.None).LastOrDefault ()); + Application.Current?.Dispose (); Application.ResetState (ignoreDisposed: true); } } diff --git a/Tests/UnitTests/View/Adornment/AdornmentTests.cs b/Tests/UnitTests/View/Adornment/AdornmentTests.cs index 176dbc7193..3ce9f3b5ef 100644 --- a/Tests/UnitTests/View/Adornment/AdornmentTests.cs +++ b/Tests/UnitTests/View/Adornment/AdornmentTests.cs @@ -9,7 +9,11 @@ public class AdornmentTests (ITestOutputHelper output) [SetupFakeApplication] public void Border_Is_Cleared_After_Margin_Thickness_Change () { - View view = new () { Text = "View", Width = 6, Height = 3, BorderStyle = LineStyle.Rounded }; + View view = new () + { + App = ApplicationImpl.Instance, + Text = "View", Width = 6, Height = 3, BorderStyle = LineStyle.Rounded + }; // Remove border bottom thickness view.Border!.Thickness = new (1, 1, 1, 0); @@ -59,7 +63,7 @@ public void Border_Is_Cleared_After_Margin_Thickness_Change () Assert.Equal (6, view.Width); Assert.Equal (3, view.Height); - View.SetClipToScreen (); + view.SetClipToScreen (); view.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( diff --git a/Tests/UnitTests/View/Adornment/BorderTests.cs b/Tests/UnitTests/View/Adornment/BorderTests.cs index 80a7692588..e7f02752df 100644 --- a/Tests/UnitTests/View/Adornment/BorderTests.cs +++ b/Tests/UnitTests/View/Adornment/BorderTests.cs @@ -8,7 +8,11 @@ public class BorderTests (ITestOutputHelper output) [SetupFakeApplication] public void Border_Parent_HasFocus_Title_Uses_FocusAttribute () { - var superView = new View { Width = 10, Height = 10, CanFocus = true }; + var superView = new View + { + Driver = ApplicationImpl.Instance.Driver, + Width = 10, Height = 10, CanFocus = true + }; var otherView = new View { Width = 0, Height = 0, CanFocus = true }; superView.Add (otherView); @@ -39,7 +43,7 @@ public void Border_Parent_HasFocus_Title_Uses_FocusAttribute () view.CanFocus = true; view.SetFocus (); - View.SetClipToScreen (); + 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); @@ -51,7 +55,11 @@ public void Border_Parent_HasFocus_Title_Uses_FocusAttribute () [SetupFakeApplication] public void Border_Uses_Parent_Scheme () { - var view = new View { Title = "A", Height = 2, Width = 5 }; + var view = new View + { + Driver = ApplicationImpl.Instance.Driver, + Title = "A", Height = 2, Width = 5 + }; view.Border!.Thickness = new (0, 1, 0, 0); view.Border!.LineStyle = LineStyle.Single; @@ -839,6 +847,7 @@ public void SuperViewRendersLineCanvas_No_SubViews_AutoJoinsLines (bool superVie { var superView = new View { + Driver = ApplicationImpl.Instance.Driver, Id = "superView", Width = 5, Height = 5, @@ -902,6 +911,7 @@ public void SuperViewRendersLineCanvas_Title_AutoJoinsLines (bool superViewRende { var superView = new View { + Driver = ApplicationImpl.Instance.Driver, Id = "superView", Title = "A", Width = 11, diff --git a/Tests/UnitTests/View/Adornment/MarginTests.cs b/Tests/UnitTests/View/Adornment/MarginTests.cs index 82053e1ef8..3a4bc20bab 100644 --- a/Tests/UnitTests/View/Adornment/MarginTests.cs +++ b/Tests/UnitTests/View/Adornment/MarginTests.cs @@ -15,27 +15,27 @@ public void Margin_Is_Transparent () view.Margin!.Diagnostics = ViewDiagnosticFlags.Thickness; view.Margin.Thickness = new (1); - Application.Top = new Toplevel (); - Application.TopLevels.Push (Application.Top); + Application.Current = new Toplevel (); + Application.SessionStack.Push (Application.Current); - Application.Top.SetScheme (new() + Application.Current.SetScheme (new() { Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red) }); - Application.Top.Add (view); + Application.Current.Add (view); Assert.Equal (ColorName16.Red, view.Margin.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); - Assert.Equal (ColorName16.Red, Application.Top.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); + Assert.Equal (ColorName16.Red, Application.Current.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); - Application.Top.BeginInit (); - Application.Top.EndInit (); + Application.Current.BeginInit (); + Application.Current.EndInit (); Application.LayoutAndDraw(); DriverAssert.AssertDriverContentsAre ( @"", output ); - DriverAssert.AssertDriverAttributesAre ("0", output, null, Application.Top.GetAttributeForRole (VisualRole.Normal)); + DriverAssert.AssertDriverAttributesAre ("0", output, null, Application.Current.GetAttributeForRole (VisualRole.Normal)); Application.ResetState (true); } @@ -51,20 +51,20 @@ public void Margin_ViewPortSettings_Not_Transparent_Is_NotTransparent () view.Margin.Thickness = new (1); view.Margin.ViewportSettings = ViewportSettingsFlags.None; - Application.Top = new Toplevel (); - Application.TopLevels.Push (Application.Top); + Application.Current = new Toplevel (); + Application.SessionStack.Push (Application.Current); - Application.Top.SetScheme (new () + Application.Current.SetScheme (new () { Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red) }); - Application.Top.Add (view); + Application.Current.Add (view); Assert.Equal (ColorName16.Red, view.Margin.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); - Assert.Equal (ColorName16.Red, Application.Top.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); + Assert.Equal (ColorName16.Red, Application.Current.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ()); - Application.Top.BeginInit (); - Application.Top.EndInit (); + Application.Current.BeginInit (); + Application.Current.EndInit (); Application.LayoutAndDraw (); DriverAssert.AssertDriverContentsAre ( @@ -74,7 +74,7 @@ M M MMM", output ); - DriverAssert.AssertDriverAttributesAre ("0", output, null, Application.Top.GetAttributeForRole (VisualRole.Normal)); + DriverAssert.AssertDriverAttributesAre ("0", output, null, Application.Current.GetAttributeForRole (VisualRole.Normal)); Application.ResetState (true); } diff --git a/Tests/UnitTests/View/Adornment/PaddingTests.cs b/Tests/UnitTests/View/Adornment/PaddingTests.cs index d869a8d268..f481913504 100644 --- a/Tests/UnitTests/View/Adornment/PaddingTests.cs +++ b/Tests/UnitTests/View/Adornment/PaddingTests.cs @@ -9,8 +9,12 @@ public class PaddingTests (ITestOutputHelper output) [SetupFakeApplication] public void Padding_Uses_Parent_Scheme () { - Application.Driver!.SetScreenSize (5, 5); - var view = new View { Height = 3, Width = 3 }; + ApplicationImpl.Instance.Driver!.SetScreenSize (5, 5); + var view = new View + { + App = ApplicationImpl.Instance, + 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 d640a314ca..48f24476c8 100644 --- a/Tests/UnitTests/View/Adornment/ShadowStyleTests.cs +++ b/Tests/UnitTests/View/Adornment/ShadowStyleTests.cs @@ -48,6 +48,7 @@ public void ShadowView_Colors (ShadowStyle style, string expectedAttrs) var superView = new Toplevel { + Driver = ApplicationImpl.Instance.Driver, Height = 3, Width = 3, Text = "012ABC!@#", @@ -65,7 +66,7 @@ public void ShadowView_Colors (ShadowStyle style, string expectedAttrs) view.SetScheme (new (Attribute.Default)); superView.Add (view); - Application.TopLevels.Push (superView); + Application.SessionStack.Push (superView); Application.LayoutAndDraw (true); DriverAssert.AssertDriverAttributesAre (expectedAttrs, output, Application.Driver, attributes); Application.ResetState (true); @@ -104,6 +105,7 @@ public void Visual_Test (ShadowStyle style, string expected) var superView = new Toplevel { + Driver = ApplicationImpl.Instance.Driver, Width = 4, Height = 4, Text = "!@#$".Repeat (4)! @@ -118,7 +120,7 @@ public void Visual_Test (ShadowStyle style, string expected) }; view.ShadowStyle = style; superView.Add (view); - Application.TopLevels.Push (superView); + Application.SessionStack.Push (superView); Application.LayoutAndDraw (true); DriverAssert.AssertDriverContentsWithFrameAre (expected, output); @@ -136,7 +138,8 @@ public void ShadowStyle_Button1Pressed_Causes_Movement (ShadowStyle style, int e { var superView = new View { - Height = 10, Width = 10 + Height = 10, Width = 10, + App = ApplicationImpl.Instance }; View view = new () diff --git a/Tests/UnitTests/View/ArrangementTests.cs b/Tests/UnitTests/View/ArrangementTests.cs new file mode 100644 index 0000000000..13e5d880a6 --- /dev/null +++ b/Tests/UnitTests/View/ArrangementTests.cs @@ -0,0 +1,217 @@ +using Xunit.Abstractions; + +namespace UnitTests.ViewTests; + +public class ArrangementTests (ITestOutputHelper output) +{ + private readonly ITestOutputHelper _output = output; + + [Fact] + public void MouseGrabHandler_WorksWithMovableView_UsingNewMouseEvent () + { + // This test proves that MouseGrabHandler works correctly with concurrent unit tests + // using NewMouseEvent directly on views, without requiring Application.Init + + var superView = new View + { + Width = 80, + Height = 25 + }; + superView.App = Application.Create (); + + var movableView = new View + { + Arrangement = ViewArrangement.Movable, + BorderStyle = LineStyle.Single, + X = 10, + Y = 10, + Width = 20, + Height = 10 + }; + + superView.Add (movableView); + + // Verify initial state + Assert.NotNull (movableView.Border); + Assert.Null (Application.Mouse.MouseGrabView); + + // Simulate mouse press on the border to start dragging + var pressEvent = new MouseEventArgs + { + Position = new (1, 0), // Top border area + Flags = MouseFlags.Button1Pressed + }; + + bool? result = movableView.Border.NewMouseEvent (pressEvent); + + // The border should have grabbed the mouse + Assert.True (result); + Assert.Equal (movableView.Border, superView.App.Mouse.MouseGrabView); + + // Simulate mouse drag + var dragEvent = new MouseEventArgs + { + Position = new (5, 2), + Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition + }; + + result = movableView.Border.NewMouseEvent (dragEvent); + Assert.True (result); + + // Mouse should still be grabbed + Assert.Equal (movableView.Border, superView.App.Mouse.MouseGrabView); + + // Simulate mouse release to end dragging + var releaseEvent = new MouseEventArgs + { + Position = new (5, 2), + Flags = MouseFlags.Button1Released + }; + + result = movableView.Border.NewMouseEvent (releaseEvent); + Assert.True (result); + + // Mouse should be released + Assert.Null (superView.App.Mouse.MouseGrabView); + } + + [Fact] + public void MouseGrabHandler_WorksWithResizableView_UsingNewMouseEvent () + { + // This test proves MouseGrabHandler works for resizing operations + + var superView = new View + { + App = Application.Create (), + Width = 80, + Height = 25 + }; + + var resizableView = new View + { + Arrangement = ViewArrangement.RightResizable, + BorderStyle = LineStyle.Single, + X = 10, + Y = 10, + Width = 20, + Height = 10 + }; + + superView.Add (resizableView); + + // Verify initial state + Assert.NotNull (resizableView.Border); + Assert.Null (Application.Mouse.MouseGrabView); + + // Calculate position on right border (border is at right edge) + // Border.Frame.X is relative to parent, so we use coordinates relative to the border + var pressEvent = new MouseEventArgs + { + Position = new (resizableView.Border.Frame.Width - 1, 5), // Right border area + Flags = MouseFlags.Button1Pressed + }; + + bool? result = resizableView.Border.NewMouseEvent (pressEvent); + + // The border should have grabbed the mouse for resizing + Assert.True (result); + Assert.Equal (resizableView.Border, superView.App.Mouse.MouseGrabView); + + // Simulate dragging to resize + var dragEvent = new MouseEventArgs + { + Position = new (resizableView.Border.Frame.Width + 3, 5), + Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition + }; + + result = resizableView.Border.NewMouseEvent (dragEvent); + Assert.True (result); + Assert.Equal (resizableView.Border, superView.App.Mouse.MouseGrabView); + + // Simulate mouse release + var releaseEvent = new MouseEventArgs + { + Position = new (resizableView.Border.Frame.Width + 3, 5), + Flags = MouseFlags.Button1Released + }; + + result = resizableView.Border.NewMouseEvent (releaseEvent); + Assert.True (result); + + // Mouse should be released + Assert.Null (superView.App.Mouse.MouseGrabView); + } + + [Fact] + public void MouseGrabHandler_ReleasesOnMultipleViews () + { + // This test verifies MouseGrabHandler properly releases when switching between views + + var superView = new View { Width = 80, Height = 25 }; + superView.App = Application.Create (); + + var view1 = new View + { + Arrangement = ViewArrangement.Movable, + BorderStyle = LineStyle.Single, + X = 10, + Y = 10, + Width = 15, + Height = 8 + }; + + var view2 = new View + { + Arrangement = ViewArrangement.Movable, + BorderStyle = LineStyle.Single, + X = 30, + Y = 10, + Width = 15, + Height = 8 + }; + + superView.Add (view1, view2); + superView.BeginInit (); + superView.EndInit (); + + // Grab mouse on first view + var pressEvent1 = new MouseEventArgs + { + Position = new (1, 0), + Flags = MouseFlags.Button1Pressed + }; + + view1.Border!.NewMouseEvent (pressEvent1); + Assert.Equal (view1.Border, superView.App.Mouse.MouseGrabView); + + // Release on first view + var releaseEvent1 = new MouseEventArgs + { + Position = new (1, 0), + Flags = MouseFlags.Button1Released + }; + + view1.Border.NewMouseEvent (releaseEvent1); + Assert.Null (Application.Mouse.MouseGrabView); + + // Grab mouse on second view + var pressEvent2 = new MouseEventArgs + { + Position = new (1, 0), + Flags = MouseFlags.Button1Pressed + }; + + view2.Border!.NewMouseEvent (pressEvent2); + Assert.Equal (view2.Border, superView.App.Mouse.MouseGrabView); + + // Release on second view + var releaseEvent2 = new MouseEventArgs + { + Position = new (1, 0), + Flags = MouseFlags.Button1Released + }; + + view2.Border.NewMouseEvent (releaseEvent2); + Assert.Null (superView.App.Mouse.MouseGrabView); + } +} diff --git a/Tests/UnitTests/View/Draw/ClearViewportTests.cs b/Tests/UnitTests/View/Draw/ClearViewportTests.cs index 3cdcc94016..db64eb72d2 100644 --- a/Tests/UnitTests/View/Draw/ClearViewportTests.cs +++ b/Tests/UnitTests/View/Draw/ClearViewportTests.cs @@ -101,7 +101,11 @@ public void DoClearViewport_RaisesClearingViewportEvent () [SetupFakeApplication] public void Clear_ClearsEntireViewport () { - var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () }; + var superView = new View + { + App = ApplicationImpl.Instance, + Width = Dim.Fill (), Height = Dim.Fill () + }; var view = new View { @@ -133,7 +137,7 @@ public void Clear_ClearsEntireViewport () └─┘", output); - View.SetClipToScreen (); + view.SetClipToScreen (); view.ClearViewport (); @@ -149,7 +153,11 @@ public void Clear_ClearsEntireViewport () [SetupFakeApplication] public void Clear_WithClearVisibleContentOnly_ClearsVisibleContentOnly () { - var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () }; + var superView = new View + { + App = ApplicationImpl.Instance, + Width = Dim.Fill (), Height = Dim.Fill () + }; var view = new View { @@ -172,7 +180,7 @@ public void Clear_WithClearVisibleContentOnly_ClearsVisibleContentOnly () │X│ └─┘", output); - View.SetClipToScreen (); + view.SetClipToScreen (); view.ClearViewport (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -203,7 +211,7 @@ public void Clear_Viewport_Can_Use_Driver_AddRune_Or_AddStr_Methods () } } - View.SetClip (savedClip); + view.SetClip (savedClip); e.Cancel = true; }; var top = new Toplevel (); @@ -268,7 +276,7 @@ public void Clear_Can_Use_Driver_AddRune_Or_AddStr_Methods () } } - View.SetClip (savedClip); + view.SetClip (savedClip); e.Cancel = true; }; var top = new Toplevel (); diff --git a/Tests/UnitTests/View/Draw/ClipTests.cs b/Tests/UnitTests/View/Draw/ClipTests.cs index 44b120d769..4e3f62e609 100644 --- a/Tests/UnitTests/View/Draw/ClipTests.cs +++ b/Tests/UnitTests/View/Draw/ClipTests.cs @@ -14,6 +14,7 @@ public void Move_Is_Not_Constrained_To_Viewport () { var view = new View { + App = ApplicationImpl.Instance, X = 1, Y = 1, Width = 3, Height = 3 @@ -36,6 +37,7 @@ public void AddRune_Is_Constrained_To_Viewport () { var view = new View { + App = ApplicationImpl.Instance, X = 1, Y = 1, Width = 3, Height = 3 @@ -67,7 +69,11 @@ public void AddRune_Is_Constrained_To_Viewport () [SetupFakeApplication] public void FillRect_Fills_HonorsClip (int x, int y, int width, int height) { - var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () }; + var superView = new View + { + App = ApplicationImpl.Instance, + Width = Dim.Fill (), Height = Dim.Fill () + }; var view = new View { @@ -91,7 +97,7 @@ public void FillRect_Fills_HonorsClip (int x, int y, int width, int height) _output); Rectangle toFill = new (x, y, width, height); - View.SetClipToScreen (); + superView.SetClipToScreen (); view.FillRect (toFill); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -133,7 +139,7 @@ public void FillRect_Fills_HonorsClip (int x, int y, int width, int height) _output); toFill = new (-1, -1, width + 1, height + 1); - View.SetClipToScreen (); + superView.SetClipToScreen (); view.FillRect (toFill); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -154,7 +160,7 @@ public void FillRect_Fills_HonorsClip (int x, int y, int width, int height) └─┘", _output); toFill = new (0, 0, width * 2, height * 2); - View.SetClipToScreen (); + superView.SetClipToScreen (); view.FillRect (toFill); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -175,6 +181,7 @@ public void Clipping_Wide_Runes () var top = new View { + App = ApplicationImpl.Instance, Id = "top", Width = Dim.Fill (), Height = Dim.Fill () @@ -193,7 +200,7 @@ public void Clipping_Wide_Runes () frameView.Border!.Thickness = new (1, 0, 0, 0); top.Add (frameView); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Layout (); top.Draw (); @@ -217,7 +224,7 @@ public void Clipping_Wide_Runes () top.Add (view); top.Layout (); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); // 012345678901234567890123456789012345678 @@ -252,19 +259,20 @@ public void SetClip_ClipVisibleContentOnly_VisibleContentIsClipped () { Width = Dim.Fill (), Height = Dim.Fill (), - ViewportSettings = ViewportSettingsFlags.ClipContentOnly + ViewportSettings = ViewportSettingsFlags.ClipContentOnly, + App = ApplicationImpl.Instance }; view.SetContentSize (new Size (10, 10)); view.Border!.Thickness = new (1); view.BeginInit (); view.EndInit (); - Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ()); + Assert.Equal (view.Frame, view.GetClip ()!.GetBounds ()); // Act view.AddViewportToClip (); // Assert - Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ()); + Assert.Equal (expectedClip, view.GetClip ()!.GetBounds ()); view.Dispose (); } @@ -286,20 +294,21 @@ public void SetClip_Default_ClipsToViewport () var view = new View { Width = Dim.Fill (), - Height = Dim.Fill () + Height = Dim.Fill (), + App = ApplicationImpl.Instance }; view.SetContentSize (new Size (10, 10)); view.Border!.Thickness = new (1); view.BeginInit (); view.EndInit (); - Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ()); + Assert.Equal (view.Frame, view.GetClip ()!.GetBounds ()); view.Viewport = view.Viewport with { X = 1, Y = 1 }; // Act view.AddViewportToClip (); // Assert - Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ()); + Assert.Equal (expectedClip, view.GetClip ()!.GetBounds ()); view.Dispose (); } } diff --git a/Tests/UnitTests/View/Draw/DrawTests.cs b/Tests/UnitTests/View/Draw/DrawTests.cs index 2526c2178f..0e84371d57 100644 --- a/Tests/UnitTests/View/Draw/DrawTests.cs +++ b/Tests/UnitTests/View/Draw/DrawTests.cs @@ -114,7 +114,11 @@ public void Colors_On_TextAlignment_Right_And_Bottom () [SetupFakeApplication] public void Draw_Minimum_Full_Border_With_Empty_Viewport () { - var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single }; + var view = new View + { + App = ApplicationImpl.Instance, + Width = 2, Height = 2, BorderStyle = LineStyle.Single + }; Assert.True (view.NeedsLayout); Assert.True (view.NeedsDraw); view.Layout (); @@ -139,7 +143,11 @@ public void Draw_Minimum_Full_Border_With_Empty_Viewport () [SetupFakeApplication] public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Bottom () { - var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single }; + var view = new View + { + App = ApplicationImpl.Instance, + Width = 2, Height = 1, BorderStyle = LineStyle.Single + }; view.Border!.Thickness = new (1, 1, 1, 0); view.BeginInit (); view.EndInit (); @@ -157,7 +165,11 @@ public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Bottom () [SetupFakeApplication] public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Left () { - var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single }; + var view = new View + { + App = ApplicationImpl.Instance, + Width = 1, Height = 2, BorderStyle = LineStyle.Single + }; view.Border!.Thickness = new (0, 1, 1, 1); view.BeginInit (); view.EndInit (); @@ -182,7 +194,11 @@ public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Left () [SetupFakeApplication] public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Right () { - var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single }; + var view = new View + { + App = ApplicationImpl.Instance, + Width = 1, Height = 2, BorderStyle = LineStyle.Single + }; view.Border!.Thickness = new (1, 1, 0, 1); view.BeginInit (); view.EndInit (); @@ -207,7 +223,11 @@ public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Right () [SetupFakeApplication] public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Top () { - var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single }; + var view = new View + { + App = ApplicationImpl.Instance, + Width = 2, Height = 1, BorderStyle = LineStyle.Single + }; view.Border!.Thickness = new (1, 0, 1, 1); view.BeginInit (); @@ -587,7 +607,11 @@ public void Draw_Negative_Viewport_Vertical () [InlineData ("a𐐀b")] public void DrawHotString_NonBmp (string expected) { - var view = new View { Width = 10, Height = 1 }; + var view = new View + { + App = ApplicationImpl.Instance, + Width = 10, Height = 1 + }; view.DrawHotString (expected, Attribute.Default, Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expected, output); @@ -760,7 +784,7 @@ and also with two lines. " Assert.Equal (new (3, 3, 10, 1), view.Frame); Assert.Equal (new (0, 0, 10, 1), view.Viewport); Assert.Equal (new (0, 0, 10, 1), view.NeedsDrawRect); - View.SetClipToScreen (); + view.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -859,7 +883,7 @@ and also with two lines. " Assert.Equal (new (1, 1, 10, 1), view.Frame); Assert.Equal (new (0, 0, 10, 1), view.Viewport); Assert.Equal (new (0, 0, 10, 1), view.NeedsDrawRect); - View.SetClipToScreen (); + view.SetClipToScreen (); top.Draw (); diff --git a/Tests/UnitTests/View/Draw/TransparentTests.cs b/Tests/UnitTests/View/Draw/TransparentTests.cs index 6a38f91e76..050ae18cb7 100644 --- a/Tests/UnitTests/View/Draw/TransparentTests.cs +++ b/Tests/UnitTests/View/Draw/TransparentTests.cs @@ -14,6 +14,7 @@ public void Transparent_Text_Occludes () { var super = new View { + App = ApplicationImpl.Instance, Id = "super", Width = 20, Height = 5, @@ -58,6 +59,7 @@ public void Transparent_SubView_Occludes () { var super = new View { + App = ApplicationImpl.Instance, Id = "super", Width = 20, Height = 5, diff --git a/Tests/UnitTests/View/Keyboard/KeyBindingsTests.cs b/Tests/UnitTests/View/Keyboard/KeyBindingsTests.cs index 74bfebcbec..236ba06008 100644 --- a/Tests/UnitTests/View/Keyboard/KeyBindingsTests.cs +++ b/Tests/UnitTests/View/Keyboard/KeyBindingsTests.cs @@ -160,14 +160,14 @@ public void HotKey_Raises_HotKeyCommand () var hotKeyRaised = false; var acceptRaised = false; var selectRaised = false; - Application.Top = new Toplevel (); + Application.Current = new Toplevel (); var view = new View { CanFocus = true, HotKeySpecifier = new Rune ('_'), Title = "_Test" }; - Application.Top.Add (view); + Application.Current.Add (view); view.HandlingHotKey += (s, e) => hotKeyRaised = true; view.Accepting += (s, e) => acceptRaised = true; view.Selecting += (s, e) => selectRaised = true; @@ -191,7 +191,7 @@ public void HotKey_Raises_HotKeyCommand () Assert.False (acceptRaised); Assert.False (selectRaised); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } // tests that test KeyBindingScope.Focus and KeyBindingScope.HotKey (tests for KeyBindingScope.Application are in Application/KeyboardTests.cs) diff --git a/Tests/UnitTests/View/Layout/GetViewsUnderLocationTests.cs b/Tests/UnitTests/View/Layout/GetViewsUnderLocationTests.cs index ec1fcdd080..97a4fc19d1 100644 --- a/Tests/UnitTests/View/Layout/GetViewsUnderLocationTests.cs +++ b/Tests/UnitTests/View/Layout/GetViewsUnderLocationTests.cs @@ -70,22 +70,22 @@ string [] expectedViewsFound ) { // Arrange - Application.Top = new () + Application.Current = new () { Id = "Top", Frame = new (frameX, frameY, 10, 10) }; - Application.Top.Margin!.Thickness = new (marginThickness); - Application.Top.Margin!.Id = "Margin"; - Application.Top.Border!.Thickness = new (borderThickness); - Application.Top.Border!.Id = "Border"; - Application.Top.Padding!.Thickness = new (paddingThickness); - Application.Top.Padding.Id = "Padding"; + Application.Current.Margin!.Thickness = new (marginThickness); + Application.Current.Margin!.Id = "Margin"; + Application.Current.Border!.Thickness = new (borderThickness); + Application.Current.Border!.Id = "Border"; + Application.Current.Padding!.Thickness = new (paddingThickness); + Application.Current.Padding.Id = "Padding"; var location = new Point (testX, testY); // Act - List viewsUnderMouse = View.GetViewsUnderLocation (location, ViewportSettingsFlags.TransparentMouse); + List viewsUnderMouse = Application.Current.GetViewsUnderLocation (location, ViewportSettingsFlags.TransparentMouse); // Assert if (expectedViewsFound.Length == 0) @@ -98,7 +98,7 @@ string [] expectedViewsFound Assert.Equal (expectedViewsFound, foundIds); } - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -109,7 +109,7 @@ string [] expectedViewsFound public void Returns_Top_If_No_SubViews (int testX, int testY) { // Arrange - Application.Top = new () + Application.Current = new () { Frame = new (0, 0, 10, 10) }; @@ -117,11 +117,11 @@ public void Returns_Top_If_No_SubViews (int testX, int testY) var location = new Point (testX, testY); // Act - List viewsUnderMouse = View.GetViewsUnderLocation (location, ViewportSettingsFlags.TransparentMouse); + List viewsUnderMouse = Application.Current.GetViewsUnderLocation (location, ViewportSettingsFlags.TransparentMouse); // Assert - Assert.Contains (viewsUnderMouse, v => v == Application.Top); - Application.Top.Dispose (); + Assert.Contains (viewsUnderMouse, v => v == Application.Current); + Application.Current.Dispose (); Application.ResetState (true); } @@ -134,13 +134,13 @@ public void Returns_Start_If_No_SubViews (int testX, int testY) { Application.ResetState (true); - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; - Assert.Same (Application.Top, View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ()); - Application.Top.Dispose (); + Assert.Same (Application.Current, Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ()); + Application.Current.Dispose (); Application.ResetState (true); } @@ -155,7 +155,7 @@ public void Returns_Start_If_No_SubViews (int testX, int testY) [InlineData (5, 6, true)] public void Returns_Correct_If_SubViews (int testX, int testY, bool expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; @@ -165,12 +165,12 @@ public void Returns_Correct_If_SubViews (int testX, int testY, bool expectedSubV X = 1, Y = 2, Width = 5, Height = 5 }; - Application.Top.Add (subview); + Application.Current.Add (subview); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, found == subview); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -184,7 +184,7 @@ public void Returns_Correct_If_SubViews (int testX, int testY, bool expectedSubV [InlineData (5, 6, false)] public void Returns_Null_If_SubView_NotVisible (int testX, int testY, bool expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; @@ -195,12 +195,12 @@ public void Returns_Null_If_SubView_NotVisible (int testX, int testY, bool expec Width = 5, Height = 5, Visible = false }; - Application.Top.Add (subview); + Application.Current.Add (subview); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, found == subview); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -214,7 +214,7 @@ public void Returns_Null_If_SubView_NotVisible (int testX, int testY, bool expec [InlineData (5, 6, false)] public void Returns_Null_If_Not_Visible_And_SubView_Visible (int testX, int testY, bool expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10, Visible = false @@ -225,14 +225,14 @@ public void Returns_Null_If_Not_Visible_And_SubView_Visible (int testX, int test X = 1, Y = 2, Width = 5, Height = 5 }; - Application.Top.Add (subview); + Application.Current.Add (subview); subview.Visible = true; Assert.True (subview.Visible); - Assert.False (Application.Top.Visible); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + Assert.False (Application.Current.Visible); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, found == subview); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -249,23 +249,23 @@ public void Returns_Null_If_Not_Visible_And_SubView_Visible (int testX, int test [InlineData (6, 7, true)] public void Returns_Correct_If_Start_Has_Adornments (int testX, int testY, bool expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; - Application.Top.Margin!.Thickness = new (1); + Application.Current.Margin!.Thickness = new (1); var subview = new View { X = 1, Y = 2, Width = 5, Height = 5 }; - Application.Top.Add (subview); + Application.Current.Add (subview); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, found == subview); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -280,24 +280,24 @@ public void Returns_Correct_If_Start_Has_Adornments (int testX, int testY, bool [InlineData (-1, 0, 0, false)] public void Returns_Correct_If_Start_Has_Offset_Viewport (int offset, int testX, int testY, bool expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10, ViewportSettings = ViewportSettingsFlags.AllowNegativeLocation }; - Application.Top.Viewport = new (offset, offset, 10, 10); + Application.Current.Viewport = new (offset, offset, 10, 10); var subview = new View { X = 1, Y = 1, Width = 2, Height = 2 }; - Application.Top.Add (subview); + Application.Current.Add (subview); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, found == subview); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -313,25 +313,25 @@ public void Returns_Correct_If_Start_Has_Offset_Viewport (int offset, int testX, [InlineData (6, 7, false)] public void Returns_Correct_If_Start_Has_Adornment_WithSubView (int testX, int testY, bool expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; - Application.Top.Padding!.Thickness = new (1); + Application.Current.Padding!.Thickness = new (1); var subview = new View { X = Pos.AnchorEnd (1), Y = Pos.AnchorEnd (1), Width = 1, Height = 1 }; - Application.Top.Padding.Add (subview); - Application.Top.BeginInit (); - Application.Top.EndInit (); + Application.Current.Padding.Add (subview); + Application.Current.BeginInit (); + Application.Current.EndInit (); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, found == subview); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -347,17 +347,17 @@ public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, str { Application.ResetState (true); - Application.Top = new () + Application.Current = new () { Id = "Top", Width = 10, Height = 10 }; - Application.Top.Margin!.Thickness = new (1); - Application.Top.Margin!.Id = "Margin"; - Application.Top.Border!.Thickness = new (1); - Application.Top.Border!.Id = "Border"; - Application.Top.Padding!.Thickness = new (1); - Application.Top.Padding.Id = "Padding"; + Application.Current.Margin!.Thickness = new (1); + Application.Current.Margin!.Id = "Margin"; + Application.Current.Border!.Thickness = new (1); + Application.Current.Border!.Id = "Border"; + Application.Current.Padding!.Thickness = new (1); + Application.Current.Padding.Id = "Padding"; var subview = new View { @@ -365,13 +365,13 @@ public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, str X = 1, Y = 1, Width = 1, Height = 1 }; - Application.Top.Add (subview); + Application.Current.Add (subview); - List viewsUnderMouse = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse); + List viewsUnderMouse = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse); string [] foundIds = viewsUnderMouse.Select (v => v!.Id).ToArray (); Assert.Equal (expectedViewsFound, foundIds); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -388,7 +388,7 @@ public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, str [InlineData (2, 3, new [] { "Top", "subview" })] public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, string [] expectedViewsFound) { - Application.Top = new () + Application.Current = new () { Id = "Top", Width = 10, Height = 10 @@ -402,13 +402,13 @@ public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, str }; subview.Border!.Thickness = new (1); subview.Border!.Id = "border"; - Application.Top.Add (subview); + Application.Current.Add (subview); - List viewsUnderMouse = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse); + List viewsUnderMouse = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse); string [] foundIds = viewsUnderMouse.Select (v => v!.Id).ToArray (); Assert.Equal (expectedViewsFound, foundIds); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -425,7 +425,7 @@ public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, str [InlineData (2, 3, new [] { "Top", "subview" })] public void Returns_Correct_If_SubView_Has_Adornments_With_TransparentMouse (int testX, int testY, string [] expectedViewsFound) { - Application.Top = new () + Application.Current = new () { Id = "Top", Width = 10, Height = 10 @@ -440,13 +440,13 @@ public void Returns_Correct_If_SubView_Has_Adornments_With_TransparentMouse (int subview.Border!.Thickness = new (1); subview.Border!.ViewportSettings = ViewportSettingsFlags.TransparentMouse; subview.Border!.Id = "border"; - Application.Top.Add (subview); + Application.Current.Add (subview); - List viewsUnderMouse = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse); + List viewsUnderMouse = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse); string [] foundIds = viewsUnderMouse.Select (v => v!.Id).ToArray (); Assert.Equal (expectedViewsFound, foundIds); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -463,7 +463,7 @@ public void Returns_Correct_If_SubView_Has_Adornments_With_TransparentMouse (int [InlineData (5, 5, true)] public void Returns_Correct_If_SubView_Has_Adornment_WithSubView (int testX, int testY, bool expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; @@ -486,14 +486,14 @@ public void Returns_Correct_If_SubView_Has_Adornment_WithSubView (int testX, int Height = 1 }; subview.Padding.Add (paddingSubView); - Application.Top.Add (subview); - Application.Top.BeginInit (); - Application.Top.EndInit (); + Application.Current.Add (subview); + Application.Current.BeginInit (); + Application.Current.EndInit (); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, found == paddingSubView); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -510,7 +510,7 @@ public void Returns_Correct_If_SubView_Has_Adornment_WithSubView (int testX, int [InlineData (5, 5, true)] public void Returns_Correct_If_SubView_Is_Scrolled_And_Has_Adornment_WithSubView (int testX, int testY, bool expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; @@ -537,14 +537,14 @@ public void Returns_Correct_If_SubView_Is_Scrolled_And_Has_Adornment_WithSubView Height = 1 }; subview.Padding.Add (paddingSubView); - Application.Top.Add (subview); - Application.Top.BeginInit (); - Application.Top.EndInit (); + Application.Current.Add (subview); + Application.Current.BeginInit (); + Application.Current.EndInit (); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, found == paddingSubView); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -560,7 +560,7 @@ public void Returns_Correct_If_SubView_Is_Scrolled_And_Has_Adornment_WithSubView [InlineData (5, 5, 2)] public void Returns_Correct_With_NestedSubViews (int testX, int testY, int expectedSubViewFound) { - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; @@ -583,11 +583,11 @@ public void Returns_Correct_With_NestedSubViews (int testX, int testY, int expec } } - Application.Top.Add (subviews [0]); + Application.Current.Add (subviews [0]); - View? found = View.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); + View? found = Application.Current.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault (); Assert.Equal (expectedSubViewFound, subviews.IndexOf (found!)); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -604,7 +604,7 @@ public void Returns_Correct_With_NestedSubViews (int testX, int testY, int expec public void Tiled_SubViews (int mouseX, int mouseY, string [] viewIdStrings) { // Arrange - Application.Top = new () + Application.Current = new () { Frame = new (0, 0, 10, 10), Id = "top" @@ -630,15 +630,15 @@ public void Tiled_SubViews (int mouseX, int mouseY, string [] viewIdStrings) Arrangement = ViewArrangement.Overlapped }; // at 2,2 to 4,3 (screen) view.Add (subView); - Application.Top.Add (view); + Application.Current.Add (view); - List found = View.GetViewsUnderLocation (new (mouseX, mouseY), ViewportSettingsFlags.TransparentMouse); + List found = Application.Current.GetViewsUnderLocation (new (mouseX, mouseY), ViewportSettingsFlags.TransparentMouse); string [] foundIds = found.Select (v => v!.Id).ToArray (); Assert.Equal (viewIdStrings, foundIds); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -656,7 +656,7 @@ public void Tiled_SubViews (int mouseX, int mouseY, string [] viewIdStrings) public void Popover (int mouseX, int mouseY, string [] viewIdStrings) { // Arrange - Application.Top = new () + Application.Current = new () { Frame = new (0, 0, 10, 10), Id = "top" @@ -683,15 +683,15 @@ public void Popover (int mouseX, int mouseY, string [] viewIdStrings) }; // at 2,2 to 4,3 (screen) view.Add (popOver); - Application.Top.Add (view); + Application.Current.Add (view); - List found = View.GetViewsUnderLocation (new (mouseX, mouseY), ViewportSettingsFlags.TransparentMouse); + List found = Application.Current.GetViewsUnderLocation (new (mouseX, mouseY), ViewportSettingsFlags.TransparentMouse); string [] foundIds = found.Select (v => v!.Id).ToArray (); Assert.Equal (viewIdStrings, foundIds); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -714,18 +714,18 @@ public void Returns_TopToplevel_When_Point_Inside_Only_TopToplevel () secondaryToplevel.Margin!.Thickness = new (1); secondaryToplevel.Layout (); - Application.TopLevels.Clear (); - Application.TopLevels.Push (topToplevel); - Application.TopLevels.Push (secondaryToplevel); - Application.Top = secondaryToplevel; + Application.SessionStack.Clear (); + Application.SessionStack.Push (topToplevel); + Application.SessionStack.Push (secondaryToplevel); + Application.Current = secondaryToplevel; - List found = View.GetViewsUnderLocation (new (2, 2), ViewportSettingsFlags.TransparentMouse); + List found = Application.Current.GetViewsUnderLocation (new (2, 2), ViewportSettingsFlags.TransparentMouse); Assert.Contains (found, v => v?.Id == topToplevel.Id); Assert.Contains (found, v => v == topToplevel); topToplevel.Dispose (); secondaryToplevel.Dispose (); - Application.TopLevels.Clear (); + Application.SessionStack.Clear (); Application.ResetState (true); } @@ -748,18 +748,18 @@ public void Returns_SecondaryToplevel_When_Point_Inside_Only_SecondaryToplevel ( secondaryToplevel.Margin!.Thickness = new (1); secondaryToplevel.Layout (); - Application.TopLevels.Clear (); - Application.TopLevels.Push (topToplevel); - Application.TopLevels.Push (secondaryToplevel); - Application.Top = secondaryToplevel; + Application.SessionStack.Clear (); + Application.SessionStack.Push (topToplevel); + Application.SessionStack.Push (secondaryToplevel); + Application.Current = secondaryToplevel; - List found = View.GetViewsUnderLocation (new (7, 7), ViewportSettingsFlags.TransparentMouse); + List found = Application.Current.GetViewsUnderLocation (new (7, 7), ViewportSettingsFlags.TransparentMouse); Assert.Contains (found, v => v?.Id == secondaryToplevel.Id); Assert.DoesNotContain (found, v => v?.Id == topToplevel.Id); topToplevel.Dispose (); secondaryToplevel.Dispose (); - Application.TopLevels.Clear (); + Application.SessionStack.Clear (); Application.ResetState (true); } @@ -781,27 +781,27 @@ public void Returns_Depends_On_Margin_ViewportSettings_When_Point_In_Margin_Of_S }; secondaryToplevel.Margin!.Thickness = new (1); - Application.TopLevels.Clear (); - Application.TopLevels.Push (topToplevel); - Application.TopLevels.Push (secondaryToplevel); - Application.Top = secondaryToplevel; + Application.SessionStack.Clear (); + Application.SessionStack.Push (topToplevel); + Application.SessionStack.Push (secondaryToplevel); + Application.Current = secondaryToplevel; secondaryToplevel.Margin!.ViewportSettings = ViewportSettingsFlags.None; - List found = View.GetViewsUnderLocation (new (5, 5), ViewportSettingsFlags.TransparentMouse); + List found = Application.Current.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; - found = View.GetViewsUnderLocation (new (5, 5), ViewportSettingsFlags.TransparentMouse); + found = Application.Current.GetViewsUnderLocation (new (5, 5), ViewportSettingsFlags.TransparentMouse); Assert.DoesNotContain (found, v => v == secondaryToplevel); Assert.DoesNotContain (found, v => v == secondaryToplevel.Margin); Assert.Contains (found, v => v?.Id == topToplevel.Id); topToplevel.Dispose (); secondaryToplevel.Dispose (); - Application.TopLevels.Clear (); + Application.SessionStack.Clear (); Application.ResetState (true); } @@ -824,17 +824,17 @@ public void Returns_Empty_When_Point_Outside_All_Toplevels () secondaryToplevel.Margin!.Thickness = new (1); secondaryToplevel.Layout (); - Application.TopLevels.Clear (); - Application.TopLevels.Push (topToplevel); - Application.TopLevels.Push (secondaryToplevel); - Application.Top = secondaryToplevel; + Application.SessionStack.Clear (); + Application.SessionStack.Push (topToplevel); + Application.SessionStack.Push (secondaryToplevel); + Application.Current = secondaryToplevel; - List found = View.GetViewsUnderLocation (new (20, 20), ViewportSettingsFlags.TransparentMouse); + List found = Application.Current.GetViewsUnderLocation (new (20, 20), ViewportSettingsFlags.TransparentMouse); Assert.Empty (found); topToplevel.Dispose (); secondaryToplevel.Dispose (); - Application.TopLevels.Clear (); + Application.SessionStack.Clear (); Application.ResetState (true); } } diff --git a/Tests/UnitTests/View/Layout/Pos.CombineTests.cs b/Tests/UnitTests/View/Layout/Pos.CombineTests.cs index f6ca4edcff..cbbf50da67 100644 --- a/Tests/UnitTests/View/Layout/Pos.CombineTests.cs +++ b/Tests/UnitTests/View/Layout/Pos.CombineTests.cs @@ -40,7 +40,7 @@ public void PosCombine_Will_Throws () [SetupFakeApplication] public void PosCombine_DimCombine_View_With_SubViews () { - Application.Top = new Toplevel () { Width = 80, Height = 25 }; + Application.Current = new Toplevel () { Width = 80, Height = 25 }; var win1 = new Window { Id = "win1", Width = 20, Height = 10 }; var view1 = new View { @@ -59,24 +59,24 @@ public void PosCombine_DimCombine_View_With_SubViews () view2.Add (view3); win2.Add (view2); win1.Add (view1, win2); - Application.Top.Add (win1); - Application.Top.Layout (); + Application.Current.Add (win1); + Application.Current.Layout (); - Assert.Equal (new Rectangle (0, 0, 80, 25), Application.Top.Frame); + Assert.Equal (new Rectangle (0, 0, 80, 25), Application.Current.Frame); Assert.Equal (new Rectangle (0, 0, 5, 1), view1.Frame); Assert.Equal (new Rectangle (0, 0, 20, 10), win1.Frame); Assert.Equal (new Rectangle (0, 2, 10, 3), win2.Frame); Assert.Equal (new Rectangle (0, 0, 8, 1), view2.Frame); Assert.Equal (new Rectangle (0, 0, 7, 1), view3.Frame); - var foundView = View.GetViewsUnderLocation (new Point(9, 4), ViewportSettingsFlags.None).LastOrDefault (); + var foundView = Application.Current.GetViewsUnderLocation (new Point(9, 4), ViewportSettingsFlags.None).LastOrDefault (); Assert.Equal (foundView, view2); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] public void PosCombine_Refs_SuperView_Throws () { - Application.Init (null, "fake"); + Application.Init ("fake"); var top = new Toplevel (); var w = new Window { X = Pos.Left (top) + 2, Y = Pos.Top (top) + 2 }; @@ -89,13 +89,13 @@ public void PosCombine_Refs_SuperView_Throws () top.Add (w); Application.Begin (top); - f.X = Pos.X (Application.Top) + Pos.X (v2) - Pos.X (v1); - f.Y = Pos.Y (Application.Top) + Pos.Y (v2) - Pos.Y (v1); + f.X = Pos.X (Application.Current) + Pos.X (v2) - Pos.X (v1); + f.Y = Pos.Y (Application.Current) + Pos.Y (v2) - Pos.Y (v1); - Application.Top.SubViewsLaidOut += (s, e) => + Application.Current.SubViewsLaidOut += (s, e) => { - Assert.Equal (0, Application.Top.Frame.X); - Assert.Equal (0, Application.Top.Frame.Y); + Assert.Equal (0, Application.Current.Frame.X); + Assert.Equal (0, Application.Current.Frame.Y); Assert.Equal (2, w.Frame.X); Assert.Equal (2, w.Frame.Y); Assert.Equal (2, f.Frame.X); @@ -109,7 +109,7 @@ public void PosCombine_Refs_SuperView_Throws () Application.StopAfterFirstIteration = true; Assert.Throws (() => Application.Run ()); - Application.Top.Dispose (); + Application.Current.Dispose (); top.Dispose (); Application.Shutdown (); } diff --git a/Tests/UnitTests/View/Layout/Pos.Tests.cs b/Tests/UnitTests/View/Layout/Pos.Tests.cs index 4d419b29a0..256c1c6e12 100644 --- a/Tests/UnitTests/View/Layout/Pos.Tests.cs +++ b/Tests/UnitTests/View/Layout/Pos.Tests.cs @@ -11,7 +11,7 @@ public class PosTests () public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute () { - Application.Init (null, "fake"); + Application.Init ("fake"); Toplevel t = new (); @@ -43,7 +43,7 @@ public void [TestRespondersDisposed] public void PosCombine_WHY_Throws () { - Application.Init (null, "fake"); + Application.Init ("fake"); Toplevel t = new Toplevel (); @@ -133,7 +133,7 @@ void OnInstanceOnIteration (object s, IterationEventArgs a) [TestRespondersDisposed] public void Pos_Subtract_Operator () { - Application.Init (null, "fake"); + Application.Init ("fake"); Toplevel top = new (); @@ -207,7 +207,7 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) [Fact] public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Null () { - Application.Init (null, "fake"); + Application.Init ("fake"); Toplevel t = new (); @@ -233,7 +233,7 @@ public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue [Fact] public void Validation_Does_Not_Throw_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Null () { - Application.Init (null, "fake"); + Application.Init ("fake"); Toplevel t = new Toplevel (); diff --git a/Tests/UnitTests/View/Layout/SetLayoutTests.cs b/Tests/UnitTests/View/Layout/SetLayoutTests.cs index ac3654e29b..7ccf52dca5 100644 --- a/Tests/UnitTests/View/Layout/SetLayoutTests.cs +++ b/Tests/UnitTests/View/Layout/SetLayoutTests.cs @@ -12,7 +12,7 @@ public class SetLayoutTests (ITestOutputHelper output) [AutoInitShutdown] public void Screen_Size_Change_Causes_Layout () { - Application.Top = new (); + Application.Current = new (); var view = new View { @@ -22,21 +22,21 @@ public void Screen_Size_Change_Causes_Layout () Height = 1, Text = "0123456789" }; - Application.Top.Add (view); + Application.Current.Add (view); - var rs = Application.Begin (Application.Top); + var rs = Application.Begin (Application.Current); 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); + Assert.Equal (new (0, 0, Application.Screen.Width, Application.Screen.Height), Application.Current.Frame); + Assert.Equal (new (0, 0, 80, 25), Application.Current.Frame); Application.Driver!.SetScreenSize (20, 10); - Assert.Equal (new (0, 0, Application.Screen.Width, Application.Screen.Height), Application.Top.Frame); + Assert.Equal (new (0, 0, Application.Screen.Width, Application.Screen.Height), Application.Current.Frame); - Assert.Equal (new (0, 0, 20, 10), Application.Top.Frame); + Assert.Equal (new (0, 0, 20, 10), Application.Current.Frame); Application.End (rs); - Application.Top.Dispose (); + Application.Current.Dispose (); } } diff --git a/Tests/UnitTests/View/Navigation/CanFocusTests.cs b/Tests/UnitTests/View/Navigation/CanFocusTests.cs index 679239fdce..8a82c605d0 100644 --- a/Tests/UnitTests/View/Navigation/CanFocusTests.cs +++ b/Tests/UnitTests/View/Navigation/CanFocusTests.cs @@ -88,20 +88,21 @@ public void CanFocus_Faced_With_Container_Before_Run () [Fact] public void CanFocus_Set_True_Get_AdvanceFocus_Works () { + IApplication app = Application.Create (); + app.Current = new () { App = app }; + Label label = new () { Text = "label" }; View view = new () { Text = "view", CanFocus = true }; - Application.Navigation = new (); - Application.Top = new (); - Application.Top.Add (label, view); + app.Current.Add (label, view); - Application.Top.SetFocus (); - Assert.Equal (view, Application.Navigation.GetFocused ()); + app.Current.SetFocus (); + Assert.Equal (view, app.Navigation!.GetFocused ()); Assert.False (label.CanFocus); Assert.False (label.HasFocus); Assert.True (view.CanFocus); Assert.True (view.HasFocus); - Assert.False (Application.Navigation.AdvanceFocus (NavigationDirection.Forward, null)); + Assert.False (app.Navigation.AdvanceFocus (NavigationDirection.Forward, null)); Assert.False (label.HasFocus); Assert.True (view.HasFocus); @@ -111,7 +112,7 @@ public void CanFocus_Set_True_Get_AdvanceFocus_Works () Assert.True (view.HasFocus); // label can now be focused, so AdvanceFocus should move to it. - Assert.True (Application.Navigation.AdvanceFocus (NavigationDirection.Forward, null)); + Assert.True (app.Navigation.AdvanceFocus (NavigationDirection.Forward, null)); Assert.True (label.HasFocus); Assert.False (view.HasFocus); @@ -120,11 +121,11 @@ public void CanFocus_Set_True_Get_AdvanceFocus_Works () Assert.False (label.HasFocus); Assert.True (view.HasFocus); - Assert.True (Application.RaiseKeyDownEvent (Key.Tab)); + Assert.True (app.Keyboard.RaiseKeyDownEvent (Key.Tab)); Assert.True (label.HasFocus); Assert.False (view.HasFocus); - Application.Top.Dispose (); - Application.ResetState (); + app.Current.Dispose (); + app.ResetState (); } } diff --git a/Tests/UnitTests/View/Navigation/NavigationTests.cs b/Tests/UnitTests/View/Navigation/NavigationTests.cs index 8198b14eb2..466d6a1ca0 100644 --- a/Tests/UnitTests/View/Navigation/NavigationTests.cs +++ b/Tests/UnitTests/View/Navigation/NavigationTests.cs @@ -27,8 +27,7 @@ public void AllViews_AtLeastOneNavKey_Advances (Type viewType) } Toplevel top = new (); - Application.Top = top; - Application.Navigation = new (); + Application.Current = top; View otherView = new () { @@ -118,8 +117,7 @@ public void AllViews_HasFocus_Changed_Event (Type viewType) } Toplevel top = new (); - Application.Top = top; - Application.Navigation = new (); + Application.Current = top; View otherView = new () { @@ -150,8 +148,8 @@ public void AllViews_HasFocus_Changed_Event (Type viewType) // Ensure the view is Visible view.Visible = true; - Application.Top.SetFocus (); - Assert.True (Application.Top!.HasFocus); + Application.Current.SetFocus (); + Assert.True (Application.Current!.HasFocus); Assert.True (top.HasFocus); // Start with the focus on our test view @@ -282,8 +280,7 @@ public void AllViews_Visible_False_No_HasFocus_Events (Type viewType) Toplevel top = new (); - Application.Top = top; - Application.Navigation = new (); + Application.Current = top; View otherView = new () { diff --git a/Tests/UnitTests/View/TextTests.cs b/Tests/UnitTests/View/TextTests.cs index 3e552204c1..625d0ce497 100644 --- a/Tests/UnitTests/View/TextTests.cs +++ b/Tests/UnitTests/View/TextTests.cs @@ -13,6 +13,7 @@ public class TextTests (ITestOutputHelper output) public void Setting_With_Height_Horizontal () { var top = new View { Width = 25, Height = 25 }; + top.App = ApplicationImpl.Instance; var label = new Label { Text = "Hello", /* Width = 10, Height = 2, */ ValidatePosDim = true }; var viewX = new View { Text = "X", X = Pos.Right (label), Width = 1, Height = 1 }; @@ -39,7 +40,7 @@ public void Setting_With_Height_Horizontal () Assert.Equal (new (0, 0, 10, 2), label.Frame); top.LayoutSubViews (); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); expected = @" @@ -394,7 +395,7 @@ public void View_IsEmpty_False_Minimum_Width () Assert.Equal (new (1, 5), view.TextFormatter.ConstrainToSize); Assert.Equal (new () { "Views" }, view.TextFormatter.GetLines ()); Assert.Equal (new (0, 0, 4, 10), win.Frame); - Assert.Equal (new (0, 0, 4, 10), Application.Top.Frame); + Assert.Equal (new (0, 0, 4, 10), Application.Current.Frame); var expected = @" ┌──┐ @@ -449,6 +450,7 @@ public void DimAuto_Vertical_TextDirection_Wide_Rune () var view = new View { + App = ApplicationImpl.Instance, TextDirection = TextDirection.TopBottom_LeftRight, Text = text, Width = Dim.Auto (), @@ -1000,6 +1002,7 @@ public void Narrow_Wide_Runes () { Application.Driver!.SetScreenSize (32, 32); var top = new View { Width = 32, Height = 32 }; + top.App = ApplicationImpl.Instance; var text = $"First line{Environment.NewLine}Second line"; var horizontalView = new View { Width = 20, Height = 1, Text = text }; @@ -1075,7 +1078,7 @@ public void Narrow_Wide_Runes () verticalView.Width = 2; verticalView.TextFormatter.ConstrainToSize = new (2, 20); Assert.True (verticalView.TextFormatter.NeedsFormat); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); Assert.Equal (new (0, 3, 2, 20), verticalView.Frame); @@ -1124,7 +1127,7 @@ public void SetText_RendersCorrectly () View view; var text = "test"; - view = new Label { Text = text }; + view = new Label { App = ApplicationImpl.Instance, Text = text }; view.BeginInit (); view.EndInit (); view.Draw (); diff --git a/Tests/UnitTests/View/ViewCommandTests.cs b/Tests/UnitTests/View/ViewCommandTests.cs index 9564aefdd7..cc48653ebb 100644 --- a/Tests/UnitTests/View/ViewCommandTests.cs +++ b/Tests/UnitTests/View/ViewCommandTests.cs @@ -46,9 +46,9 @@ public void Button_IsDefault_Raises_Accepted_Correctly () w.LayoutSubViews (); - Application.Top = w; - Application.TopLevels.Push (w); - Assert.Same (Application.Top, w); + Application.Current = w; + Application.SessionStack.Push (w); + Assert.Same (Application.Current, w); // Click button 2 Rectangle btn2Frame = btnB.FrameToScreen (); @@ -121,9 +121,9 @@ public void Button_CanFocus_False_Raises_Accepted_Correctly () w.Add (btn); - Application.Top = w; - Application.TopLevels.Push (w); - Assert.Same (Application.Top, w); + Application.Current = w; + Application.SessionStack.Push (w); + Assert.Same (Application.Current, w); w.LayoutSubViews (); diff --git a/Tests/UnitTests/View/ViewTests.cs b/Tests/UnitTests/View/ViewTests.cs index 42d89f39ad..f42715e6ea 100644 --- a/Tests/UnitTests/View/ViewTests.cs +++ b/Tests/UnitTests/View/ViewTests.cs @@ -332,6 +332,7 @@ public void New_Methods_Return_False () public void Test_Nested_Views_With_Height_Equal_To_One () { var v = new View { Width = 11, Height = 3 }; + v.App = ApplicationImpl.Instance; var top = new View { Width = Dim.Fill (), Height = 1 }; var bottom = new View { Width = Dim.Fill (), Height = 1, Y = 2 }; diff --git a/Tests/UnitTests/View/Viewport/ViewportSettings.TransparentMouseTests.cs b/Tests/UnitTests/View/Viewport/ViewportSettings.TransparentMouseTests.cs index 089753bf7f..2e66c4c9b1 100644 --- a/Tests/UnitTests/View/Viewport/ViewportSettings.TransparentMouseTests.cs +++ b/Tests/UnitTests/View/Viewport/ViewportSettings.TransparentMouseTests.cs @@ -23,7 +23,7 @@ public void TransparentMouse_Passes_Mouse_Events_To_Underlying_View () { Id = "top", }; - Application.Top = top; + Application.Current = top; var underlying = new MouseTrackingView { Id = "underlying", X = 0, Y = 0, Width = 10, Height = 10 }; var overlay = new MouseTrackingView { Id = "overlay", X = 0, Y = 0, Width = 10, Height = 10, ViewportSettings = ViewportSettingsFlags.TransparentMouse }; @@ -56,7 +56,7 @@ public void NonTransparentMouse_Consumes_Mouse_Events () { // Arrange var top = new Toplevel (); - Application.Top = top; + Application.Current = top; var underlying = new MouseTrackingView { X = 0, Y = 0, Width = 10, Height = 10 }; var overlay = new MouseTrackingView { X = 0, Y = 0, Width = 10, Height = 10, ViewportSettings = ViewportSettingsFlags.None }; @@ -90,7 +90,7 @@ public void TransparentMouse_Stacked_TransparentMouse_Views () { // Arrange var top = new Toplevel (); - Application.Top = top; + Application.Current = top; var underlying = new MouseTrackingView { X = 0, Y = 0, Width = 10, Height = 10, ViewportSettings = ViewportSettingsFlags.TransparentMouse }; var overlay = new MouseTrackingView { X = 0, Y = 0, Width = 10, Height = 10, ViewportSettings = ViewportSettingsFlags.TransparentMouse }; diff --git a/Tests/UnitTests/Views/AppendAutocompleteTests.cs b/Tests/UnitTests/Views/AppendAutocompleteTests.cs index a3c910710d..3fa75b7b85 100644 --- a/Tests/UnitTests/Views/AppendAutocompleteTests.cs +++ b/Tests/UnitTests/Views/AppendAutocompleteTests.cs @@ -13,9 +13,9 @@ public void TestAutoAppend_AfterCloseKey_NoAutocomplete () // f is typed and suggestion is "fish" Application.RaiseKeyDownEvent ('f'); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("f", tf.Text); @@ -25,17 +25,17 @@ public void TestAutoAppend_AfterCloseKey_NoAutocomplete () // Suggestion should disappear tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); DriverAssert.AssertDriverContentsAre ("f", output); Assert.Equal ("f", tf.Text); // Still has focus though - Assert.Same (tf, Application.Top.Focused); + Assert.Same (tf, Application.Current.Focused); // But can tab away Application.RaiseKeyDownEvent ('\t'); - Assert.NotSame (tf, Application.Top.Focused); - Application.Top.Dispose (); + Assert.NotSame (tf, Application.Current.Focused); + Application.Current.Dispose (); } [Fact] @@ -46,9 +46,9 @@ public void TestAutoAppend_AfterCloseKey_ReappearsOnLetter () // f is typed and suggestion is "fish" Application.RaiseKeyDownEvent ('f'); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("f", tf.Text); @@ -63,13 +63,13 @@ public void TestAutoAppend_AfterCloseKey_ReappearsOnLetter () // Should reappear when you press next letter Application.RaiseKeyDownEvent (Key.I); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("fi", tf.Text); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Theory] @@ -82,9 +82,9 @@ public void TestAutoAppend_CycleSelections (KeyCode cycleKey) // f is typed and suggestion is "fish" Application.RaiseKeyDownEvent ('f'); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("f", tf.Text); @@ -92,22 +92,22 @@ public void TestAutoAppend_CycleSelections (KeyCode cycleKey) // When cycling autocomplete Application.RaiseKeyDownEvent (cycleKey); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("friend", output); Assert.Equal ("f", tf.Text); // Should be able to cycle in circles endlessly Application.RaiseKeyDownEvent (cycleKey); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("f", tf.Text); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -118,9 +118,9 @@ public void TestAutoAppend_NoRender_WhenCursorNotAtEnd () // f is typed and suggestion is "fish" Application.RaiseKeyDownEvent ('f'); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("f", tf.Text); @@ -129,11 +129,11 @@ public void TestAutoAppend_NoRender_WhenCursorNotAtEnd () Application.RaiseKeyDownEvent (' '); Application.RaiseKeyDownEvent (Key.CursorLeft); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsAre ("f", output); Assert.Equal ("f ", tf.Text); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -144,20 +144,20 @@ public void TestAutoAppend_NoRender_WhenNoMatch () // f is typed and suggestion is "fish" Application.RaiseKeyDownEvent ('f'); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("f", tf.Text); // x is typed and suggestion should disappear Application.RaiseKeyDownEvent (Key.X); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsAre ("fx", output); Assert.Equal ("fx", tf.Text); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -170,9 +170,9 @@ public void TestAutoAppend_ShowThenAccept_CasesDiffer () var generator = (SingleWordSuggestionGenerator)tf.Autocomplete.SuggestionGenerator; generator.AllSuggestions = new() { "FISH" }; - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("", output); tf.NewKeyDownEvent (Key.M); @@ -182,20 +182,20 @@ public void TestAutoAppend_ShowThenAccept_CasesDiffer () Assert.Equal ("my f", tf.Text); // Even though there is no match on case we should still get the suggestion - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("my fISH", output); Assert.Equal ("my f", tf.Text); // When tab completing the case of the whole suggestion should be applied Application.RaiseKeyDownEvent ('\t'); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsAre ("my FISH", output); Assert.Equal ("my FISH", tf.Text); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -208,35 +208,35 @@ public void TestAutoAppend_ShowThenAccept_MatchCase () var generator = (SingleWordSuggestionGenerator)tf.Autocomplete.SuggestionGenerator; generator.AllSuggestions = new() { "fish" }; - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("", output); tf.NewKeyDownEvent (new ('f')); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("f", tf.Text); Application.RaiseKeyDownEvent ('\t'); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsAre ("fish", output); Assert.Equal ("fish", tf.Text); // Tab should autcomplete but not move focus - Assert.Same (tf, Application.Top.Focused); + Assert.Same (tf, Application.Current.Focused); // Second tab should move focus (nothing to autocomplete) Application.RaiseKeyDownEvent ('\t'); - Assert.NotSame (tf, Application.Top.Focused); - Application.Top.Dispose (); + Assert.NotSame (tf, Application.Current.Focused); + Application.Current.Dispose (); } [Theory] @@ -250,13 +250,13 @@ public void TestAutoAppendRendering_ShouldNotOverspill (string overspillUsing, s // f is typed we should only see 'f' up to size of View (10) Application.RaiseKeyDownEvent ('f'); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.PositionCursor (); DriverAssert.AssertDriverContentsAre (expectRender, output); Assert.Equal ("f", tf.Text); - Application.Top.Dispose (); + Application.Current.Dispose (); } private TextField GetTextFieldsInView () diff --git a/Tests/UnitTests/Views/ButtonTests.cs b/Tests/UnitTests/Views/ButtonTests.cs index d05a3fc53c..83d8cc0320 100644 --- a/Tests/UnitTests/Views/ButtonTests.cs +++ b/Tests/UnitTests/Views/ButtonTests.cs @@ -13,7 +13,10 @@ public void Constructors_Defaults () // Override CM Button.DefaultShadow = ShadowStyle.None; - var btn = new Button (); + var btn = new Button () + { + App = ApplicationImpl.Instance + }; Assert.Equal (string.Empty, btn.Text); btn.BeginInit (); btn.EndInit (); @@ -45,7 +48,8 @@ public void Constructors_Defaults () DriverAssert.AssertDriverContentsWithFrameAre (expected, output); btn.Dispose (); - btn = new () { Text = "_Test", IsDefault = true }; + btn = new () { App = ApplicationImpl.Instance, + Text = "_Test", IsDefault = true }; btn.Layout (); Assert.Equal (new (10, 1), btn.TextFormatter.ConstrainToSize); @@ -77,7 +81,8 @@ public void Constructors_Defaults () btn.Dispose (); - btn = new () { X = 1, Y = 2, Text = "_abc", IsDefault = true }; + btn = new () { App = ApplicationImpl.Instance, + X = 1, Y = 2, Text = "_abc", IsDefault = true }; btn.BeginInit (); btn.EndInit (); Assert.Equal ("_abc", btn.Text); @@ -92,13 +97,13 @@ public void Constructors_Defaults () Assert.Equal ('_', btn.HotKeySpecifier.Value); Assert.True (btn.CanFocus); - Application.Driver?.ClearContents (); + ApplicationImpl.Instance.Driver?.ClearContents (); btn.Draw (); expected = @$" {Glyphs.LeftBracket}{Glyphs.LeftDefaultIndicator} abc {Glyphs.RightDefaultIndicator}{Glyphs.RightBracket} "; - DriverAssert.AssertDriverContentsWithFrameAre (expected, output); + DriverAssert.AssertDriverContentsWithFrameAre (expected, output, ApplicationImpl.Instance.Driver); Assert.Equal (new (0, 0, 9, 1), btn.Viewport); Assert.Equal (new (1, 2, 9, 1), btn.Frame); @@ -204,7 +209,7 @@ public void KeyBindings_Command () clicked = false; // Toplevel does not handle Enter, so it should get passed on to button - Assert.False (Application.Top.NewKeyDownEvent (Key.Enter)); + Assert.False (Application.Current.NewKeyDownEvent (Key.Enter)); Assert.True (clicked); clicked = false; diff --git a/Tests/UnitTests/Views/CheckBoxTests.cs b/Tests/UnitTests/Views/CheckBoxTests.cs index adfde4db8d..39fe5db916 100644 --- a/Tests/UnitTests/Views/CheckBoxTests.cs +++ b/Tests/UnitTests/Views/CheckBoxTests.cs @@ -15,12 +15,11 @@ public class CheckBoxTests (ITestOutputHelper output) [Fact] public void Commands_Select () { - Application.Navigation = new (); - Application.Top = new (); + Application.Current = new (); View otherView = new () { CanFocus = true }; var ckb = new CheckBox (); - Application.Top.Add (ckb, otherView); - Application.Top.SetFocus (); + Application.Current.Add (ckb, otherView); + Application.Current.SetFocus (); Assert.True (ckb.HasFocus); var checkedStateChangingCount = 0; @@ -64,7 +63,7 @@ public void Commands_Select () Assert.Equal (3, selectCount); Assert.Equal (1, acceptCount); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (); } diff --git a/Tests/UnitTests/Views/ColorPickerTests.cs b/Tests/UnitTests/Views/ColorPickerTests.cs index e6beba010b..8e454a2895 100644 --- a/Tests/UnitTests/Views/ColorPickerTests.cs +++ b/Tests/UnitTests/Views/ColorPickerTests.cs @@ -12,7 +12,7 @@ public void ColorPicker_ChangeValueOnUI_UpdatesAllUIElements () var otherView = new View { CanFocus = true }; - Application.Top?.Add (otherView); // thi sets focus to otherView + Application.Current?.Add (otherView); // thi sets focus to otherView Assert.True (otherView.HasFocus); cp.SetFocus (); @@ -772,13 +772,11 @@ private ColorPicker GetColorPicker (ColorModel colorModel, bool showTextFields, cp.Style.ShowColorName = showName; cp.ApplyStyleChanges (); - Application.Navigation = new (); + Application.Current = new () { Width = 20, Height = 5 }; + Application.Current.Add (cp); - Application.Top = new () { Width = 20, Height = 5 }; - Application.Top.Add (cp); - - Application.Top.LayoutSubViews (); - Application.Top.SetFocus (); + Application.Current.LayoutSubViews (); + Application.Current.SetFocus (); return cp; } diff --git a/Tests/UnitTests/Views/ComboBoxTests.cs b/Tests/UnitTests/Views/ComboBoxTests.cs index e1280424cf..2602516599 100644 --- a/Tests/UnitTests/Views/ComboBoxTests.cs +++ b/Tests/UnitTests/Views/ComboBoxTests.cs @@ -565,7 +565,7 @@ public void HideDropdownListOnClick_True_Highlight_Current_Item () Assert.True (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); - View.SetClipToScreen (); + cb.SetClipToScreen (); cb.Draw (); DriverAssert.AssertDriverAttributesAre ( @@ -584,7 +584,7 @@ public void HideDropdownListOnClick_True_Highlight_Current_Item () Assert.True (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); - View.SetClipToScreen (); + cb.SetClipToScreen (); cb.Draw (); DriverAssert.AssertDriverAttributesAre ( @@ -609,7 +609,7 @@ public void HideDropdownListOnClick_True_Highlight_Current_Item () Assert.True (cb.IsShow); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - View.SetClipToScreen (); + cb.SetClipToScreen (); cb.Draw (); DriverAssert.AssertDriverAttributesAre ( @@ -628,7 +628,7 @@ public void HideDropdownListOnClick_True_Highlight_Current_Item () Assert.True (cb.IsShow); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - View.SetClipToScreen (); + cb.SetClipToScreen (); cb.Draw (); DriverAssert.AssertDriverAttributesAre ( @@ -647,7 +647,7 @@ public void HideDropdownListOnClick_True_Highlight_Current_Item () Assert.True (cb.IsShow); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - View.SetClipToScreen (); + cb.SetClipToScreen (); cb.Draw (); DriverAssert.AssertDriverAttributesAre ( @@ -928,7 +928,7 @@ public void KeyBindings_Command () Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); - View.SetClipToScreen (); + cb.SetClipToScreen (); cb.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -944,7 +944,7 @@ public void KeyBindings_Command () Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - View.SetClipToScreen (); + cb.SetClipToScreen (); cb.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1013,10 +1013,9 @@ public void KeyBindings_Command () [Fact] public void Source_Equal_Null_Or_Count_Equal_Zero_Sets_SelectedItem_Equal_To_Minus_One () { - Application.Navigation = new (); var cb = new ComboBox (); var top = new Toplevel (); - Application.Top = top; + Application.Current = top; top.Add (cb); top.FocusDeepest (NavigationDirection.Forward, null); @@ -1055,7 +1054,7 @@ public void Source_Equal_Null_Or_Count_Equal_Zero_Sets_SelectedItem_Equal_To_Min Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } } diff --git a/Tests/UnitTests/Views/GraphViewTests.cs b/Tests/UnitTests/Views/GraphViewTests.cs index d2184945b0..7bcb42a4e6 100644 --- a/Tests/UnitTests/Views/GraphViewTests.cs +++ b/Tests/UnitTests/Views/GraphViewTests.cs @@ -44,7 +44,7 @@ protected override void DrawAxisLine (GraphView graph, int x, int y) #endregion -public class GraphViewTests +public class GraphViewTests : FakeDriverBase { /// /// A cell size of 0 would result in mapping all graph space into the same cell of the console. Since @@ -74,7 +74,10 @@ public void CellSizeZero () /// public static GraphView GetGraph () { - var gv = new GraphView (); + var gv = new GraphView () + { + Driver = Application.Driver ?? CreateFakeDriver () + }; gv.BeginInit (); gv.EndInit (); @@ -650,7 +653,7 @@ public void TestRendering_MultibarSeries () fakeXAxis.LabelPoints.Clear (); gv.LayoutSubViews (); gv.SetNeedsDraw (); - View.SetClipToScreen (); + gv.SetClipToScreen (); gv.Draw (); Assert.Equal (3, fakeXAxis.LabelPoints.Count); @@ -677,12 +680,13 @@ public void TestRendering_MultibarSeries () } } -public class BarSeriesTests +public class BarSeriesTests : FakeDriverBase { [Fact] public void TestOneLongOneShortHorizontalBars_WithOffset () { GraphView graph = GetGraph (out FakeBarSeries barSeries, out FakeHAxis axisX, out FakeVAxis axisY); + graph.Driver = CreateFakeDriver (); graph.Draw (); // no bars @@ -1125,7 +1129,7 @@ public void TestTextAnnotation_GraphUnits () // user scrolls up one unit of graph space gv.ScrollOffset = new PointF (0, 1f); gv.SetNeedsDraw (); - View.SetClipToScreen (); + gv.SetClipToScreen (); gv.Draw (); // we expect the text annotation to go down one line since @@ -1222,7 +1226,7 @@ public void TestTextAnnotation_ScreenUnits () new TextAnnotation { Text = "hey!", ScreenPosition = new Point (3, 1) } ); gv.LayoutSubViews (); - View.SetClipToScreen (); + gv.SetClipToScreen (); gv.Draw (); var expected = @@ -1238,7 +1242,7 @@ public void TestTextAnnotation_ScreenUnits () // user scrolls up one unit of graph space gv.ScrollOffset = new PointF (0, 1f); gv.SetNeedsDraw (); - View.SetClipToScreen (); + gv.SetClipToScreen (); gv.Draw (); // we expect no change in the location of the annotation (only the axis label changes) @@ -1257,7 +1261,7 @@ public void TestTextAnnotation_ScreenUnits () // user scrolls up one unit of graph space gv.ScrollOffset = new PointF (0, 1f); gv.SetNeedsDraw (); - View.SetClipToScreen (); + gv.SetClipToScreen (); gv.Draw (); // we expect no change in the location of the annotation (only the axis label changes) @@ -1385,7 +1389,7 @@ public void MarginBottom_BiggerThanHeight_ExpectBlankGraph () "; - DriverAssert.AssertDriverContentsAre (expected, _output); + DriverAssert.AssertDriverContentsAre (expected, _output, gv.Driver); // Shutdown must be called to safely clean up Application if Init has been called Application.Shutdown (); @@ -1410,7 +1414,7 @@ public void MarginLeft_BiggerThanWidth_ExpectBlankGraph () "; - DriverAssert.AssertDriverContentsAre (expected, _output); + DriverAssert.AssertDriverContentsAre (expected, _output, gv.Driver); // Shutdown must be called to safely clean up Application if Init has been called Application.Shutdown (); @@ -1528,7 +1532,7 @@ public void ViewChangeText_RendersCorrectly (bool useFill) // change the text and redraw view.Text = "ff1234"; mount.SetNeedsDraw (); - View.SetClipToScreen (); + top.SetClipToScreen (); mount.Draw (); // should have the new text rendered diff --git a/Tests/UnitTests/Views/HexViewTests.cs b/Tests/UnitTests/Views/HexViewTests.cs index 83d661f6fa..63ea65cea0 100644 --- a/Tests/UnitTests/Views/HexViewTests.cs +++ b/Tests/UnitTests/Views/HexViewTests.cs @@ -1,6 +1,5 @@ #nullable enable using System.Text; -using JetBrains.Annotations; namespace UnitTests.ViewsTests; @@ -32,10 +31,10 @@ public void BytesPerLine_Calculates_Correctly (int width, int expectedBpl) public void ReadOnly_Prevents_Edits () { var hv = new HexView (LoadStream (null, out _, true)) { Width = 20, Height = 20 }; - Application.Navigation = new ApplicationNavigation (); - Application.Top = new Toplevel (); - Application.Top.Add (hv); - Application.Top.SetFocus (); + + Application.Current = new (); + Application.Current.Add (hv); + Application.Current.SetFocus (); // Needed because HexView relies on LayoutComplete to calc sizes hv.LayoutSubViews (); @@ -70,15 +69,14 @@ public void ReadOnly_Prevents_Edits () Assert.Empty (hv.Edits); Assert.Equal (127, hv.Source.Length); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } [Fact] public void ApplyEdits_With_Argument () { - Application.Navigation = new ApplicationNavigation (); - Application.Top = new Toplevel (); + Application.Current = new (); byte [] buffer = Encoding.Default.GetBytes ("Fest"); var original = new MemoryStream (); @@ -89,8 +87,8 @@ public void ApplyEdits_With_Argument () original.CopyTo (copy); copy.Flush (); var hv = new HexView (copy) { Width = Dim.Fill (), Height = Dim.Fill () }; - Application.Top.Add (hv); - Application.Top.SetFocus (); + Application.Current.Add (hv); + Application.Current.SetFocus (); // Needed because HexView relies on LayoutComplete to calc sizes hv.LayoutSubViews (); @@ -107,7 +105,7 @@ public void ApplyEdits_With_Argument () Assert.Equal ("Test", Encoding.Default.GetString (readBuffer)); Assert.True (Application.RaiseKeyDownEvent (Key.Tab)); // Move to right side - Assert.True (Application.RaiseKeyDownEvent (Key.CursorLeft)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorLeft)); Assert.True (Application.RaiseKeyDownEvent (Key.Z.WithShift)); readBuffer [hv.Edits.ToList () [0].Key] = hv.Edits.ToList () [0].Value; Assert.Equal ("Zest", Encoding.Default.GetString (readBuffer)); @@ -121,7 +119,7 @@ public void ApplyEdits_With_Argument () Assert.Equal ("Zest", Encoding.Default.GetString (readBuffer)); Assert.Equal (Encoding.Default.GetString (buffer), Encoding.Default.GetString (readBuffer)); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -144,13 +142,11 @@ public void Constructors_Defaults () [Fact] public void Position_Encoding_Default () { - Application.Navigation = new ApplicationNavigation (); - var hv = new HexView (LoadStream (null, out _)) { Width = 100, Height = 100 }; - Application.Top = new Toplevel (); - Application.Top.Add (hv); + Application.Current = new (); + Application.Current.Add (hv); - Application.Top.LayoutSubViews (); + Application.Current.LayoutSubViews (); Assert.Equal (63, hv.Source!.Length); Assert.Equal (20, hv.BytesPerLine); @@ -175,18 +171,16 @@ public void Position_Encoding_Default () Assert.Equal (new (3, 3), hv.GetPosition (hv.Address)); Assert.Equal (hv.Source!.Length, hv.Address); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } [Fact] public void Position_Encoding_Unicode () { - Application.Navigation = new ApplicationNavigation (); - - var hv = new HexView (LoadStream (null, out _, unicode: true)) { Width = 100, Height = 100 }; - Application.Top = new Toplevel (); - Application.Top.Add (hv); + var hv = new HexView (LoadStream (null, out _, true)) { Width = 100, Height = 100 }; + Application.Current = new (); + Application.Current.Add (hv); hv.LayoutSubViews (); @@ -212,7 +206,7 @@ public void Position_Encoding_Unicode () Assert.Equal (new (6, 6), hv.GetPosition (hv.Address)); Assert.Equal (hv.Source!.Length, hv.Address); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -264,10 +258,9 @@ public void Exceptions_Tests () [Fact] public void KeyBindings_Test_Movement_LeftSide () { - Application.Navigation = new ApplicationNavigation (); - Application.Top = new Toplevel (); + Application.Current = new (); var hv = new HexView (LoadStream (null, out _)) { Width = 20, Height = 10 }; - Application.Top.Add (hv); + Application.Current.Add (hv); hv.LayoutSubViews (); @@ -313,19 +306,18 @@ public void KeyBindings_Test_Movement_LeftSide () Assert.True (Application.RaiseKeyDownEvent (Key.CursorUp.WithCtrl)); Assert.Equal (0, hv.Address); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } [Fact] public void PositionChanged_Event () { - Application.Navigation = new ApplicationNavigation (); var hv = new HexView (LoadStream (null, out _)) { Width = 20, Height = 10 }; - Application.Top = new Toplevel (); - Application.Top.Add (hv); + Application.Current = new (); + Application.Current.Add (hv); - Application.Top.LayoutSubViews (); + Application.Current.LayoutSubViews (); HexViewEventArgs hexViewEventArgs = null!; hv.PositionChanged += (s, e) => hexViewEventArgs = e; @@ -339,41 +331,40 @@ public void PositionChanged_Event () Assert.Equal (4, hexViewEventArgs.BytesPerLine); Assert.Equal (new (1, 1), hexViewEventArgs.Position); Assert.Equal (5, hexViewEventArgs.Address); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } [Fact] public void Source_Sets_Address_To_Zero_If_Greater_Than_Source_Length () { - Application.Navigation = new ApplicationNavigation (); var hv = new HexView (LoadStream (null, out _)) { Width = 10, Height = 5 }; - Application.Top = new Toplevel (); - Application.Top.Add (hv); + Application.Current = new (); + Application.Current.Add (hv); - Application.Top.Layout (); + Application.Current.Layout (); Assert.True (hv.NewKeyDownEvent (Key.End)); Assert.Equal (MEM_STRING_LENGTH, hv.Address); hv.Source = new MemoryStream (); - Application.Top.Layout (); + Application.Current.Layout (); Assert.Equal (0, hv.Address); hv.Source = LoadStream (null, out _); hv.Width = Dim.Fill (); hv.Height = Dim.Fill (); - Application.Top.Layout (); + Application.Current.Layout (); Assert.Equal (0, hv.Address); Assert.True (hv.NewKeyDownEvent (Key.End)); Assert.Equal (MEM_STRING_LENGTH, hv.Address); hv.Source = new MemoryStream (); - Application.Top.Layout (); + Application.Current.Layout (); Assert.Equal (0, hv.Address); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -400,6 +391,7 @@ private Stream LoadStream (string? memString, out long numBytesInMemString, bool { bArray = Encoding.Default.GetBytes (memString); } + numBytesInMemString = bArray.Length; stream.Write (bArray); @@ -421,8 +413,8 @@ public override long Position } public override void Flush () { baseStream.Flush (); } - public override int Read (byte [] buffer, int offset, int count) { return baseStream.Read (buffer, offset, count); } - public override long Seek (long offset, SeekOrigin origin) { throw new NotImplementedException (); } + public override int Read (byte [] buffer, int offset, int count) => baseStream.Read (buffer, offset, count); + public override long Seek (long offset, SeekOrigin origin) => throw new NotImplementedException (); public override void SetLength (long value) { throw new NotSupportedException (); } public override void Write (byte [] buffer, int offset, int count) { baseStream.Write (buffer, offset, count); } } diff --git a/Tests/UnitTests/Views/LabelTests.cs b/Tests/UnitTests/Views/LabelTests.cs index 550abf360b..7b949c4599 100644 --- a/Tests/UnitTests/Views/LabelTests.cs +++ b/Tests/UnitTests/Views/LabelTests.cs @@ -1,5 +1,4 @@ -using UnitTests; -using Xunit.Abstractions; +using Xunit.Abstractions; namespace UnitTests.ViewsTests; @@ -112,9 +111,17 @@ public void Label_Draw_Fill_Remaining () AutoInitShutdownAttribute.RunIteration (); - tf1.Draw (new (new (0, 1), tfSize), label.GetAttributeForRole (VisualRole.Normal), label.GetAttributeForRole (VisualRole.HotNormal)); + tf1.Draw ( + Application.Driver, + new (new (0, 1), tfSize), + label.GetAttributeForRole (VisualRole.Normal), + label.GetAttributeForRole (VisualRole.HotNormal)); - tf2.Draw (new (new (0, 2), tfSize), label.GetAttributeForRole (VisualRole.Normal), label.GetAttributeForRole (VisualRole.HotNormal)); + tf2.Draw ( + Application.Driver, + new (new (0, 2), tfSize), + label.GetAttributeForRole (VisualRole.Normal), + label.GetAttributeForRole (VisualRole.HotNormal)); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -135,10 +142,20 @@ This TextFormatter (tf2) with fill will be cleared on rewritten. ", label.Draw (); tf1.Text = "This TextFormatter (tf1) is rewritten."; - tf1.Draw (new (new (0, 1), tfSize), label.GetAttributeForRole (VisualRole.Normal), label.GetAttributeForRole (VisualRole.HotNormal)); + + tf1.Draw ( + Application.Driver, + new (new (0, 1), tfSize), + label.GetAttributeForRole (VisualRole.Normal), + label.GetAttributeForRole (VisualRole.HotNormal)); tf2.Text = "This TextFormatter (tf2) is rewritten."; - tf2.Draw (new (new (0, 2), tfSize), label.GetAttributeForRole (VisualRole.Normal), label.GetAttributeForRole (VisualRole.HotNormal)); + + tf2.Draw ( + Application.Driver, + new (new (0, 2), tfSize), + label.GetAttributeForRole (VisualRole.Normal), + label.GetAttributeForRole (VisualRole.HotNormal)); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -301,7 +318,11 @@ public void Update_Parameterless_Only_On_Or_After_Initialize () [SetupFakeApplication] public void Full_Border () { - var label = new Label { BorderStyle = LineStyle.Single, Text = "Test" }; + var label = new Label + { + Driver = Application.Driver, + BorderStyle = LineStyle.Single, Text = "Test" + }; label.BeginInit (); label.EndInit (); label.SetRelativeLayout (Application.Screen.Size); @@ -699,6 +720,7 @@ public void AnchorEnd_Better_Than_Bottom_Equal_Inside_Window () var label = new Label { Text = "This should be the last line.", + //Width = Dim.Fill (), X = 0, // keep unit test focused; don't use Center here Y = Pos.AnchorEnd (1) @@ -745,6 +767,7 @@ public void Bottom_Equal_Inside_Window () var label = new Label { Text = "This should be the last line.", + //Width = Dim.Fill (), X = 0, Y = Pos.Bottom (win) @@ -907,7 +930,11 @@ public void Label_Height_Zero_Stays_Zero () label.Width = Dim.Fill () - text.Length; label.Height = 0; - var win = new View { CanFocus = true, BorderStyle = LineStyle.Single, Width = Dim.Fill (), Height = Dim.Fill () }; + var win = new View + { + App = ApplicationImpl.Instance, + CanFocus = true, BorderStyle = LineStyle.Single, Width = Dim.Fill (), Height = Dim.Fill () + }; win.Add (label); win.BeginInit (); win.EndInit (); @@ -1071,7 +1098,7 @@ public void Label_IsEmpty_False_Minimum_Height () Assert.Equal (new (5, 1), label.TextFormatter.ConstrainToSize); Assert.Equal (["Label"], label.TextFormatter.GetLines ()); Assert.Equal (new (0, 0, 10, 4), win.Frame); - Assert.Equal (new (0, 0, 10, 4), Application.Top.Frame); + Assert.Equal (new (0, 0, 10, 4), Application.Current.Frame); var expected = @" ┌────────┐ @@ -1130,7 +1157,7 @@ public void Label_IsEmpty_False_Never_Return_Null_Lines () Assert.Equal (new (5, 1), label.TextFormatter.ConstrainToSize); Assert.Equal (["Label"], label.TextFormatter.GetLines ()); Assert.Equal (new (0, 0, 10, 4), win.Frame); - Assert.Equal (new (0, 0, 10, 4), Application.Top.Frame); + Assert.Equal (new (0, 0, 10, 4), Application.Current.Frame); var expected = @" ┌────────┐ @@ -1202,11 +1229,11 @@ public void CanFocus_False_HotKey_SetsFocus_Next () Text = "nextView", CanFocus = true }; - Application.Navigation = new (); - Application.Top = new (); - Application.Top.Add (otherView, label, nextView); - Application.Top.SetFocus (); + Application.Current = new (); + Application.Current.Add (otherView, label, nextView); + + Application.Current.SetFocus (); Assert.True (otherView.HasFocus); Assert.True (Application.RaiseKeyDownEvent (label.HotKey)); @@ -1214,7 +1241,7 @@ public void CanFocus_False_HotKey_SetsFocus_Next () Assert.False (label.HasFocus); Assert.True (nextView.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (); } @@ -1224,19 +1251,18 @@ public void CanFocus_False_MouseClick_SetsFocus_Next () View otherView = new () { X = 0, Y = 0, Width = 1, Height = 1, Id = "otherView", CanFocus = true }; Label label = new () { X = 0, Y = 1, Text = "_label" }; View nextView = new () { X = Pos.Right (label), Y = Pos.Top (label), Width = 1, Height = 1, Id = "nextView", CanFocus = true }; - Application.Navigation = new (); - Application.Top = new (); - Application.Top.Add (otherView, label, nextView); - Application.Top.Layout (); + Application.Current = new (); + Application.Current.Add (otherView, label, nextView); + Application.Current.Layout (); - Application.Top.SetFocus (); + Application.Current.SetFocus (); // click on label Application.RaiseMouseEvent (new () { ScreenPosition = label.Frame.Location, Flags = MouseFlags.Button1Clicked }); Assert.False (label.HasFocus); Assert.True (nextView.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (); } @@ -1254,9 +1280,9 @@ public void CanFocus_True_HotKey_SetsFocus () Text = "view", CanFocus = true }; - Application.Navigation = new (); - Application.Top = new (); - Application.Top.Add (label, view); + + Application.Current = new (); + Application.Current.Add (label, view); view.SetFocus (); Assert.True (label.CanFocus); @@ -1269,15 +1295,13 @@ public void CanFocus_True_HotKey_SetsFocus () Assert.True (label.HasFocus); Assert.False (view.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (); } [Fact] public void CanFocus_True_MouseClick_Focuses () { - Application.Navigation = new (); - Label label = new () { Text = "label", @@ -1296,14 +1320,14 @@ public void CanFocus_True_MouseClick_Focuses () CanFocus = true }; - Application.Top = new () + Application.Current = new () { Width = 10, Height = 10 }; - Application.Top.Add (label, otherView); - Application.Top.SetFocus (); - Application.Top.Layout (); + Application.Current.Add (label, otherView); + Application.Current.SetFocus (); + Application.Current.Layout (); Assert.True (label.CanFocus); Assert.True (label.HasFocus); @@ -1323,7 +1347,7 @@ public void CanFocus_True_MouseClick_Focuses () Assert.False (label.HasFocus); Assert.True (otherView.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (); } diff --git a/Tests/UnitTests/Views/MenuBarTests.cs b/Tests/UnitTests/Views/MenuBarTests.cs index 8ad5f05eb3..b77ebc1ad8 100644 --- a/Tests/UnitTests/Views/MenuBarTests.cs +++ b/Tests/UnitTests/Views/MenuBarTests.cs @@ -10,18 +10,33 @@ public class MenuBarTests () public void DefaultKey_Activates_And_Opens () { // Arrange + var top = new Toplevel () + { + App = ApplicationImpl.Instance + }; + + var menuBar = new MenuBarv2 () { Id = "menuBar" }; + top.Add (menuBar); + var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; var menu = new Menuv2 ([menuItem]) { Id = "menu" }; var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); + + menuBar.Add (menuBarItem); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; - menuBar.Add (menuBarItem); + + + Assert.NotNull (menuBar.App); + Assert.NotNull (menu.App); + Assert.NotNull (menuItem.App); + Assert.NotNull (menuBarItem); + Assert.NotNull (menuBarItemPopover); + Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); - var top = new Toplevel (); - top.Add (menuBar); + SessionToken rs = Application.Begin (top); Assert.False (menuBar.Active); @@ -43,17 +58,10 @@ public void DefaultKey_Activates_And_Opens () public void DefaultKey_Deactivates () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; - var menuBarItemPopover = new PopoverMenu (); - menuBarItem.PopoverMenu = menuBarItemPopover; - menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; - menuBar.Add (menuBarItem); - Assert.Single (menuBar.SubViews); - Assert.Single (menuBarItem.SubViews); - var top = new Toplevel (); + var top = new Toplevel () { App = ApplicationImpl.Instance }; + MenuBarv2 menuBar = new MenuBarv2 () { App = ApplicationImpl.Instance }; + menuBar.EnableForDesign (ref top); + top.Add (menuBar); SessionToken rs = Application.Begin (top); Assert.False (menuBar.Active); @@ -61,15 +69,12 @@ public void DefaultKey_Deactivates () // Act Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey); Assert.True (menuBar.IsOpen ()); - Assert.True (menuBarItem.PopoverMenu.Visible); Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey); Assert.False (menuBar.Active); Assert.False (menuBar.IsOpen ()); Assert.False (menuBar.HasFocus); Assert.False (menuBar.CanFocus); - Assert.False (menuBarItem.PopoverMenu.Visible); - Assert.False (menuBarItem.PopoverMenu.HasFocus); Application.End (rs); top.Dispose (); @@ -377,17 +382,10 @@ public void Mouse_Enter_Activates_But_Does_Not_Open () public void Mouse_Click_Activates_And_Opens () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; - var menuBarItemPopover = new PopoverMenu (); - menuBarItem.PopoverMenu = menuBarItemPopover; - menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; - menuBar.Add (menuBarItem); - Assert.Single (menuBar.SubViews); - Assert.Single (menuBarItem.SubViews); - var top = new Toplevel (); + var top = new Toplevel () { App = ApplicationImpl.Instance }; + MenuBarv2 menuBar = new MenuBarv2 () { App = ApplicationImpl.Instance }; + menuBar.EnableForDesign (ref top); + top.Add (menuBar); SessionToken rs = Application.Begin (top); Assert.False (menuBar.Active); @@ -401,8 +399,6 @@ public void Mouse_Click_Activates_And_Opens () Assert.True (menuBar.IsOpen ()); Assert.True (menuBar.HasFocus); Assert.True (menuBar.CanFocus); - Assert.True (menuBarItem.PopoverMenu.Visible); - Assert.True (menuBarItem.PopoverMenu.HasFocus); Application.End (rs); top.Dispose (); @@ -483,7 +479,7 @@ public void Dynamic_Change_MenuItem_Title () Application.RaiseKeyDownEvent (Key.N.WithAlt); Assert.Equal (0, action); - Assert.Equal(Key.I, menuItem.HotKey); + Assert.Equal (Key.I, menuItem.HotKey); Application.RaiseKeyDownEvent (Key.I); Assert.Equal (1, action); Assert.False (menuBar.Active); diff --git a/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs b/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs index 31ec42bdec..1241716ca6 100644 --- a/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs +++ b/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs @@ -698,7 +698,7 @@ public void Draw_A_Menu_Over_A_Top_Dialog () Dialog.DefaultShadow = ShadowStyle.None; Button.DefaultShadow = ShadowStyle.None; - Assert.Equal (new (0, 0, 40, 15), View.GetClip ()!.GetBounds ()); + Assert.Equal (new (0, 0, 40, 15), Application.Current!.GetClip ()!.GetBounds ()); DriverAssert.AssertDriverContentsWithFrameAre (@"", output); List items = new () @@ -1100,11 +1100,11 @@ public void HotKey_MenuBar_OnKeyDown_OnKeyUp_ProcessKeyPressed () Assert.False (copyAction); #if SUPPORT_ALT_TO_ACTIVATE_MENU - Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); - Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); - Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); + Assert.False (Application.Current.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); + Assert.False (Application.Current.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); + Assert.True (Application.Current.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); Assert.True (menu.IsMenuOpen); - Application.Top.Draw (); + Application.Current.Draw (); string expected = @" File Edit @@ -1113,26 +1113,26 @@ File Edit var pos = DriverAsserts.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 0, 11, 1), pos); - Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.N))); + Assert.True (Application.Current.ProcessKeyDown (new KeyEventArgs (Key.N))); AutoInitShutdownAttribute.RunIteration (); Assert.False (newAction); // not yet, hot keys don't work if the item is not visible - Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.F))); + Assert.True (Application.Current.ProcessKeyDown (new KeyEventArgs (Key.F))); AutoInitShutdownAttribute.RunIteration (); - Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.N))); + Assert.True (Application.Current.ProcessKeyDown (new KeyEventArgs (Key.N))); AutoInitShutdownAttribute.RunIteration (); Assert.True (newAction); - Application.Top.Draw (); + Application.Current.Draw (); expected = @" File Edit "; - Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); - Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); - Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); + Assert.False (Application.Current.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); + Assert.True (Application.Current.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); + Assert.True (Application.Current.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); Assert.True (menu.IsMenuOpen); - Application.Top.Draw (); + Application.Current.Draw (); expected = @" File Edit @@ -1141,8 +1141,8 @@ File Edit pos = DriverAsserts.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 0, 11, 1), pos); - Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.CursorRight))); - Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.C))); + Assert.True (Application.Current.ProcessKeyDown (new KeyEventArgs (Key.CursorRight))); + Assert.True (Application.Current.ProcessKeyDown (new KeyEventArgs (Key.C))); AutoInitShutdownAttribute.RunIteration (); Assert.True (copyAction); #endif @@ -1211,19 +1211,19 @@ .Children [0] Assert.True (menu.NewKeyDownEvent (Key.F.WithAlt)); Assert.True (menu.IsMenuOpen); - Application.Top.Draw (); + Application.Current.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.N)); + Assert.True (Application.Current.SubViews.ElementAt (1).NewKeyDownEvent (Key.N)); AutoInitShutdownAttribute.RunIteration (); Assert.True (newAction); Assert.True (menu.NewKeyDownEvent (Key.E.WithAlt)); Assert.True (menu.IsMenuOpen); - Application.Top.Draw (); + Application.Current.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); - Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.C)); + Assert.True (Application.Current.SubViews.ElementAt (1).NewKeyDownEvent (Key.C)); AutoInitShutdownAttribute.RunIteration (); Assert.True (copyAction); top.Dispose (); @@ -1594,7 +1594,7 @@ .Children [0] Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu })); Assert.False (menu.IsMenuOpen); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); top.Dispose (); @@ -1692,7 +1692,7 @@ public void MenuBar_In_Window_Without_Other_Views_With_Top_Init () ); Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1804,7 +1804,7 @@ public void MenuBar_In_Window_Without_Other_Views_With_Top_Init_With_Parameterle ); Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1945,7 +1945,7 @@ public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init_With_Run_T () Application.AddTimeout (TimeSpan.Zero, () => { - Toplevel top = Application.Top; + Toplevel top = Application.Current; AutoInitShutdownAttribute.RunIteration (); @@ -2015,7 +2015,7 @@ public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init_With_Run_T () Assert.True ( ((MenuBar)top.SubViews.ElementAt (0))._openMenu.NewKeyDownEvent (Key.CursorRight) ); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -2090,16 +2090,16 @@ public void MenuBar_Position_And_Size_With_HotKeys_Is_The_Same_As_Without_HotKey DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); // Open second - Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight)); + Assert.True (Application.Current.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight)); Assert.True (menu.IsMenuOpen); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); // Close menu Assert.True (menu.NewKeyDownEvent (menu.Key)); Assert.False (menu.IsMenuOpen); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); @@ -2133,21 +2133,21 @@ public void MenuBar_Position_And_Size_With_HotKeys_Is_The_Same_As_Without_HotKey // Open first Assert.True (menu.NewKeyDownEvent (menu.Key)); Assert.True (menu.IsMenuOpen); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); // Open second Assert.True (top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight)); Assert.True (menu.IsMenuOpen); - View.SetClipToScreen (); - Application.Top.Draw (); + top.SetClipToScreen (); + Application.Current.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); // Close menu Assert.True (menu.NewKeyDownEvent (menu.Key)); Assert.False (menu.IsMenuOpen); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); top.Dispose (); @@ -2216,7 +2216,7 @@ public void MenuBar_Submenus_Alignment_Correct () top.Add (menu); Application.Begin (top); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); @@ -2224,7 +2224,7 @@ public void MenuBar_Submenus_Alignment_Correct () { menu.OpenMenu (i); Assert.True (menu.IsMenuOpen); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (i), output); } @@ -2483,7 +2483,7 @@ public void MenuOpening_MenuOpened_MenuClosing_Events () Assert.True (menu.NewKeyDownEvent (menu.Key)); Assert.True (menu.IsMenuOpen); Assert.False (isMenuClosed); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); expected = @" @@ -2498,7 +2498,7 @@ public void MenuOpening_MenuOpened_MenuClosing_Events () Assert.True (menu.NewKeyDownEvent (menu.Key)); Assert.False (menu.IsMenuOpen); Assert.True (isMenuClosed); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); expected = @" @@ -2655,7 +2655,7 @@ expectedMenu.Menus [i].Children.Length > 0 Assert.Equal (1, menu._selected); Assert.Equal (-1, menu._selectedSub); Assert.Null (menu._openSubMenu); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); @@ -2663,7 +2663,7 @@ expectedMenu.Menus [i].Children.Length > 0 Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); Assert.True (menu.IsMenuOpen); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output); @@ -2671,21 +2671,21 @@ expectedMenu.Menus [i].Children.Length > 0 Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft)); Assert.True (menu.IsMenuOpen); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft)); Assert.True (menu.IsMenuOpen); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); Assert.True (Application.RaiseKeyDownEvent (menu.Key)); Assert.False (menu.IsMenuOpen); Assert.True (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); top.Dispose (); @@ -2756,7 +2756,7 @@ public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse () ); Assert.True (menu.IsMenuOpen); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); @@ -2767,7 +2767,7 @@ public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse () ); Assert.True (menu.IsMenuOpen); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output); @@ -2778,7 +2778,7 @@ public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse () ); Assert.True (menu.IsMenuOpen); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); @@ -2789,14 +2789,14 @@ public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse () ); Assert.True (menu.IsMenuOpen); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); Assert.True (menu.NewMouseEvent (new () { Position = new (8, 0), Flags = MouseFlags.Button1Pressed, View = menu })); Assert.False (menu.IsMenuOpen); Assert.True (tf.HasFocus); - View.SetClipToScreen (); + top.SetClipToScreen (); top.Draw (); DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); top.Dispose (); @@ -3075,7 +3075,7 @@ public void UseSubMenusSingleFrame_False_By_Keyboard () pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorDown)); + Assert.True (Application.Current.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorDown)); top.Draw (); expected = @" @@ -3090,7 +3090,7 @@ public void UseSubMenusSingleFrame_False_By_Keyboard () pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.True (Application.Top.SubViews.ElementAt (2).NewKeyDownEvent (Key.CursorLeft)); + Assert.True (Application.Current.SubViews.ElementAt (2).NewKeyDownEvent (Key.CursorLeft)); top.Draw (); expected = @" @@ -3104,7 +3104,7 @@ public void UseSubMenusSingleFrame_False_By_Keyboard () pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.Esc)); + Assert.True (Application.Current.SubViews.ElementAt (1).NewKeyDownEvent (Key.Esc)); top.Draw (); expected = @" @@ -3186,7 +3186,7 @@ public void UseSubMenusSingleFrame_False_By_Mouse () menu.NewMouseEvent ( new () { - Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = Application.Top.SubViews.ElementAt (1) + Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = Application.Current.SubViews.ElementAt (1) } ); top.Draw (); @@ -3208,7 +3208,7 @@ public void UseSubMenusSingleFrame_False_By_Mouse () menu.NewMouseEvent ( new () { - Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = Application.Top.SubViews.ElementAt (1) + Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = Application.Current.SubViews.ElementAt (1) } ) ); @@ -3227,7 +3227,7 @@ public void UseSubMenusSingleFrame_False_By_Mouse () Assert.Equal (new (1, 0, 10, 6), pos); menu.NewMouseEvent ( - new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top } + new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Current } ); top.Draw (); diff --git a/Tests/UnitTests/Views/ProgressBarTests.cs b/Tests/UnitTests/Views/ProgressBarTests.cs index 26b70fe0ed..b7bb6aaa29 100644 --- a/Tests/UnitTests/Views/ProgressBarTests.cs +++ b/Tests/UnitTests/Views/ProgressBarTests.cs @@ -26,9 +26,13 @@ public void Default_Constructor () [AutoInitShutdown] public void Fraction_Redraw () { - var driver = Application.Driver; + var driver = ApplicationImpl.Instance.Driver; - var pb = new ProgressBar { Width = 5 }; + var pb = new ProgressBar + { + Driver = driver, + Width = 5 + }; pb.BeginInit (); pb.EndInit (); @@ -37,7 +41,7 @@ public void Fraction_Redraw () for (var i = 0; i <= pb.Frame.Width; i++) { pb.Fraction += 0.2F; - View.SetClipToScreen (); + pb.SetClipToScreen (); pb.Draw (); if (i == 0) @@ -161,10 +165,11 @@ public void ProgressBarStyle_Setter () [AutoInitShutdown] public void Pulse_Redraw_BidirectionalMarquee_False () { - var driver = Application.Driver; + var driver = ApplicationImpl.Instance.Driver; var pb = new ProgressBar { + Driver = driver, Width = 15, ProgressBarStyle = ProgressBarStyle.MarqueeBlocks, BidirectionalMarquee = false }; @@ -175,7 +180,7 @@ public void Pulse_Redraw_BidirectionalMarquee_False () for (var i = 0; i < 38; i++) { pb.Pulse (); - View.SetClipToScreen (); + pb.SetClipToScreen (); pb.Draw (); if (i == 0) @@ -869,9 +874,13 @@ public void Pulse_Redraw_BidirectionalMarquee_False () [AutoInitShutdown] public void Pulse_Redraw_BidirectionalMarquee_True_Default () { - var driver = Application.Driver; + var driver = ApplicationImpl.Instance.Driver; - var pb = new ProgressBar { Width = 15, ProgressBarStyle = ProgressBarStyle.MarqueeBlocks }; + var pb = new ProgressBar + { + Driver = driver, + Width = 15, ProgressBarStyle = ProgressBarStyle.MarqueeBlocks + }; pb.BeginInit (); pb.EndInit (); @@ -880,7 +889,7 @@ public void Pulse_Redraw_BidirectionalMarquee_True_Default () for (var i = 0; i < 38; i++) { pb.Pulse (); - View.SetClipToScreen (); + pb.SetClipToScreen (); pb.Draw (); if (i == 0) diff --git a/Tests/UnitTests/Views/ScrollBarTests.cs b/Tests/UnitTests/Views/ScrollBarTests.cs index 9d04338325..4a5cdf7ad4 100644 --- a/Tests/UnitTests/Views/ScrollBarTests.cs +++ b/Tests/UnitTests/Views/ScrollBarTests.cs @@ -495,6 +495,8 @@ public void Draws_Correctly_Default_Settings (int width, int height, int content { var super = new Window { + Driver = ApplicationImpl.Instance.Driver, + Id = "super", Width = width + 2, Height = height + 2, diff --git a/Tests/UnitTests/Views/ScrollSliderTests.cs b/Tests/UnitTests/Views/ScrollSliderTests.cs deleted file mode 100644 index 5397b16b13..0000000000 --- a/Tests/UnitTests/Views/ScrollSliderTests.cs +++ /dev/null @@ -1,340 +0,0 @@ -using UnitTests; -using Xunit.Abstractions; - -namespace UnitTests.ViewsTests; - -public class ScrollSliderTests (ITestOutputHelper output) -{ - [Theory] - [SetupFakeApplication] - [InlineData ( - 3, - 10, - 1, - 0, - Orientation.Vertical, - @" -┌───┐ -│███│ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -└───┘")] - [InlineData ( - 10, - 1, - 3, - 0, - Orientation.Horizontal, - @" -┌──────────┐ -│███ │ -└──────────┘")] - [InlineData ( - 3, - 10, - 3, - 0, - Orientation.Vertical, - @" -┌───┐ -│███│ -│███│ -│███│ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -└───┘")] - - - - [InlineData ( - 3, - 10, - 5, - 0, - Orientation.Vertical, - @" -┌───┐ -│███│ -│███│ -│███│ -│███│ -│███│ -│ │ -│ │ -│ │ -│ │ -│ │ -└───┘")] - - [InlineData ( - 3, - 10, - 5, - 1, - Orientation.Vertical, - @" -┌───┐ -│ │ -│███│ -│███│ -│███│ -│███│ -│███│ -│ │ -│ │ -│ │ -│ │ -└───┘")] - [InlineData ( - 3, - 10, - 5, - 4, - Orientation.Vertical, - @" -┌───┐ -│ │ -│ │ -│ │ -│ │ -│███│ -│███│ -│███│ -│███│ -│███│ -│ │ -└───┘")] - [InlineData ( - 3, - 10, - 5, - 5, - Orientation.Vertical, - @" -┌───┐ -│ │ -│ │ -│ │ -│ │ -│ │ -│███│ -│███│ -│███│ -│███│ -│███│ -└───┘")] - [InlineData ( - 3, - 10, - 5, - 6, - Orientation.Vertical, - @" -┌───┐ -│ │ -│ │ -│ │ -│ │ -│ │ -│███│ -│███│ -│███│ -│███│ -│███│ -└───┘")] - - [InlineData ( - 3, - 10, - 10, - 0, - Orientation.Vertical, - @" -┌───┐ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -└───┘")] - - [InlineData ( - 3, - 10, - 10, - 5, - Orientation.Vertical, - @" -┌───┐ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -└───┘")] - [InlineData ( - 3, - 10, - 11, - 0, - Orientation.Vertical, - @" -┌───┐ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -│███│ -└───┘")] - - [InlineData ( - 10, - 3, - 5, - 0, - Orientation.Horizontal, - @" -┌──────────┐ -│█████ │ -│█████ │ -│█████ │ -└──────────┘")] - - [InlineData ( - 10, - 3, - 5, - 1, - Orientation.Horizontal, - @" -┌──────────┐ -│ █████ │ -│ █████ │ -│ █████ │ -└──────────┘")] - [InlineData ( - 10, - 3, - 5, - 4, - Orientation.Horizontal, - @" -┌──────────┐ -│ █████ │ -│ █████ │ -│ █████ │ -└──────────┘")] - [InlineData ( - 10, - 3, - 5, - 5, - Orientation.Horizontal, - @" -┌──────────┐ -│ █████│ -│ █████│ -│ █████│ -└──────────┘")] - [InlineData ( - 10, - 3, - 5, - 6, - Orientation.Horizontal, - @" -┌──────────┐ -│ █████│ -│ █████│ -│ █████│ -└──────────┘")] - - [InlineData ( - 10, - 3, - 10, - 0, - Orientation.Horizontal, - @" -┌──────────┐ -│██████████│ -│██████████│ -│██████████│ -└──────────┘")] - - [InlineData ( - 10, - 3, - 10, - 5, - Orientation.Horizontal, - @" -┌──────────┐ -│██████████│ -│██████████│ -│██████████│ -└──────────┘")] - [InlineData ( - 10, - 3, - 11, - 0, - Orientation.Horizontal, - @" -┌──────────┐ -│██████████│ -│██████████│ -│██████████│ -└──────────┘")] - public void Draws_Correctly (int superViewportWidth, int superViewportHeight, int sliderSize, int position, Orientation orientation, string expected) - { - var super = new Window - { - Id = "super", - Width = superViewportWidth + 2, - Height = superViewportHeight + 2 - }; - - var scrollSlider = new ScrollSlider - { - Orientation = orientation, - Size = sliderSize, - //Position = position, - }; - Assert.Equal (sliderSize, scrollSlider.Size); - super.Add (scrollSlider); - scrollSlider.Position = position; - - super.Layout (); - super.Draw (); - - _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - } -} diff --git a/Tests/UnitTests/Views/ShortcutTests.cs b/Tests/UnitTests/Views/ShortcutTests.cs index 09b4e66d11..79da2dbc6c 100644 --- a/Tests/UnitTests/Views/ShortcutTests.cs +++ b/Tests/UnitTests/Views/ShortcutTests.cs @@ -23,7 +23,7 @@ public class ShortcutTests [InlineData (9, 0)] public void MouseClick_Raises_Accepted (int x, int expectedAccepted) { - Application.Top = new (); + Application.Current = new (); var shortcut = new Shortcut { @@ -31,8 +31,8 @@ public void MouseClick_Raises_Accepted (int x, int expectedAccepted) Text = "0", Title = "C" }; - Application.Top.Add (shortcut); - Application.Top.Layout (); + Application.Current.Add (shortcut); + Application.Current.Layout (); var accepted = 0; shortcut.Accepting += (s, e) => accepted++; @@ -46,7 +46,7 @@ public void MouseClick_Raises_Accepted (int x, int expectedAccepted) Assert.Equal (expectedAccepted, accepted); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -74,7 +74,7 @@ public void MouseClick_Default_CommandView_Raises_Accepted_Selected_Correctly ( int expectedShortcutSelected ) { - Application.Top = new (); + Application.Current = new (); var shortcut = new Shortcut { @@ -93,9 +93,9 @@ int expectedShortcutSelected var shortcutSelectCount = 0; shortcut.Selecting += (s, e) => { shortcutSelectCount++; }; - Application.Top.Add (shortcut); - Application.Top.SetRelativeLayout (new (100, 100)); - Application.Top.LayoutSubViews (); + Application.Current.Add (shortcut); + Application.Current.SetRelativeLayout (new (100, 100)); + Application.Current.LayoutSubViews (); Application.RaiseMouseEvent ( new () @@ -109,7 +109,7 @@ int expectedShortcutSelected Assert.Equal (expectedCommandViewAccepted, commandViewAcceptCount); Assert.Equal (expectedCommandViewSelected, commandViewSelectCount); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -130,7 +130,7 @@ int expectedShortcutSelected [InlineData (9, 0, 0)] public void MouseClick_Button_CommandView_Raises_Shortcut_Accepted (int mouseX, int expectedAccept, int expectedButtonAccept) { - Application.Top = new (); + Application.Current = new (); var shortcut = new Shortcut { @@ -147,9 +147,9 @@ public void MouseClick_Button_CommandView_Raises_Shortcut_Accepted (int mouseX, }; var buttonAccepted = 0; shortcut.CommandView.Accepting += (s, e) => { buttonAccepted++; }; - Application.Top.Add (shortcut); - Application.Top.SetRelativeLayout (new (100, 100)); - Application.Top.LayoutSubViews (); + Application.Current.Add (shortcut); + Application.Current.SetRelativeLayout (new (100, 100)); + Application.Current.LayoutSubViews (); var accepted = 0; shortcut.Accepting += (s, e) => { accepted++; }; @@ -164,7 +164,7 @@ public void MouseClick_Button_CommandView_Raises_Shortcut_Accepted (int mouseX, Assert.Equal (expectedAccept, accepted); Assert.Equal (expectedButtonAccept, buttonAccepted); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -186,7 +186,7 @@ public void MouseClick_Button_CommandView_Raises_Shortcut_Accepted (int mouseX, [InlineData (10, 1, 0)] public void MouseClick_CheckBox_CommandView_Raises_Shortcut_Accepted_Selected_Correctly (int mouseX, int expectedAccepted, int expectedCheckboxAccepted) { - Application.Top = new (); + Application.Current = new (); var shortcut = new Shortcut { @@ -212,9 +212,9 @@ public void MouseClick_CheckBox_CommandView_Raises_Shortcut_Accepted_Selected_Co checkboxSelected++; }; - Application.Top.Add (shortcut); - Application.Top.SetRelativeLayout (new (100, 100)); - Application.Top.LayoutSubViews (); + Application.Current.Add (shortcut); + Application.Current.SetRelativeLayout (new (100, 100)); + Application.Current.LayoutSubViews (); var selected = 0; shortcut.Selecting += (s, e) => @@ -241,7 +241,7 @@ public void MouseClick_CheckBox_CommandView_Raises_Shortcut_Accepted_Selected_Co Assert.Equal (expectedCheckboxAccepted, checkboxAccepted); Assert.Equal (expectedCheckboxAccepted, checkboxSelected); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -260,7 +260,7 @@ public void MouseClick_CheckBox_CommandView_Raises_Shortcut_Accepted_Selected_Co [InlineData (false, KeyCode.F1, 0, 0)] public void KeyDown_Raises_Accepted_Selected (bool canFocus, KeyCode key, int expectedAccept, int expectedSelect) { - Application.Top = new (); + Application.Current = new (); var shortcut = new Shortcut { @@ -269,7 +269,7 @@ public void KeyDown_Raises_Accepted_Selected (bool canFocus, KeyCode key, int ex Title = "_C", CanFocus = canFocus }; - Application.Top.Add (shortcut); + Application.Current.Add (shortcut); shortcut.SetFocus (); Assert.Equal (canFocus, shortcut.HasFocus); @@ -285,7 +285,7 @@ public void KeyDown_Raises_Accepted_Selected (bool canFocus, KeyCode key, int ex Assert.Equal (expectedAccept, accepted); Assert.Equal (expectedSelect, selected); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -305,7 +305,7 @@ public void KeyDown_Raises_Accepted_Selected (bool canFocus, KeyCode key, int ex [InlineData (false, KeyCode.F1, 0, 0)] public void KeyDown_CheckBox_Raises_Accepted_Selected (bool canFocus, KeyCode key, int expectedAccept, int expectedSelect) { - Application.Top = new (); + Application.Current = new (); var shortcut = new Shortcut { @@ -317,7 +317,7 @@ public void KeyDown_CheckBox_Raises_Accepted_Selected (bool canFocus, KeyCode ke }, CanFocus = canFocus }; - Application.Top.Add (shortcut); + Application.Current.Add (shortcut); shortcut.SetFocus (); Assert.Equal (canFocus, shortcut.HasFocus); @@ -337,7 +337,7 @@ public void KeyDown_CheckBox_Raises_Accepted_Selected (bool canFocus, KeyCode ke Assert.Equal (expectedAccept, accepted); Assert.Equal (expectedSelect, selected); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } [Theory] @@ -349,17 +349,17 @@ public void KeyDown_CheckBox_Raises_Accepted_Selected (bool canFocus, KeyCode ke [InlineData (KeyCode.F1, 0)] public void KeyDown_App_Scope_Invokes_Accept (KeyCode key, int expectedAccept) { - Application.Top = new (); + Application.Current = new () { App = Application.Create () }; var shortcut = new Shortcut { Key = Key.A, - BindKeyToApplication = true, Text = "0", Title = "_C" }; - Application.Top.Add (shortcut); - Application.Top.SetFocus (); + Application.Current.Add (shortcut); + shortcut.BindKeyToApplication = true; + Application.Current.SetFocus (); var accepted = 0; shortcut.Accepting += (s, e) => accepted++; @@ -368,7 +368,7 @@ public void KeyDown_App_Scope_Invokes_Accept (KeyCode key, int expectedAccept) Assert.Equal (expectedAccept, accepted); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -427,19 +427,24 @@ public void KeyDown_Invokes_Action (bool canFocus, KeyCode key, int expectedActi [InlineData (false, KeyCode.F1, 0)] public void KeyDown_App_Scope_Invokes_Action (bool canFocus, KeyCode key, int expectedAction) { - Application.Top = new (); + Application.Current = new (); var shortcut = new Shortcut { - Key = Key.A, BindKeyToApplication = true, + Key = Key.A, Text = "0", Title = "_C", CanFocus = canFocus }; - Application.Top.Add (shortcut); - Application.Top.SetFocus (); + Application.Current.Add (shortcut); + + // Shortcut requires Init for App scoped hotkeys to work + Application.Current.BeginInit (); + Application.Current.EndInit (); + + Application.Current.SetFocus (); var action = 0; shortcut.Action += () => action++; @@ -448,7 +453,7 @@ public void KeyDown_App_Scope_Invokes_Action (bool canFocus, KeyCode key, int ex Assert.Equal (expectedAction, action); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (true); } @@ -456,11 +461,11 @@ public void KeyDown_App_Scope_Invokes_Action (bool canFocus, KeyCode key, int ex [Fact] public void Scheme_SetScheme_Does_Not_Fault_3664 () { - Application.Top = new (); - Application.Navigation = new (); + Application.Current = new (); + var shortcut = new Shortcut (); - Application.Top.SetScheme (null); + Application.Current.SetScheme (null); Assert.False (shortcut.HasScheme); Assert.NotNull (shortcut.GetScheme ()); @@ -470,7 +475,7 @@ public void Scheme_SetScheme_Does_Not_Fault_3664 () Assert.False (shortcut.HasScheme); Assert.NotNull (shortcut.GetScheme ()); - Application.Top.Dispose (); + Application.Current.Dispose (); Application.ResetState (); } } diff --git a/Tests/UnitTests/Views/SpinnerViewTests.cs b/Tests/UnitTests/Views/SpinnerViewTests.cs index f68b96b0a4..35b25e039e 100644 --- a/Tests/UnitTests/Views/SpinnerViewTests.cs +++ b/Tests/UnitTests/Views/SpinnerViewTests.cs @@ -40,7 +40,7 @@ public void TestSpinnerView_AutoSpin (bool callStop) // Dispose clears timeout view.Dispose (); Assert.Empty (Application.TimedEvents.Timeouts); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -57,12 +57,12 @@ public void TestSpinnerView_NoThrottle () DriverAssert.AssertDriverContentsWithFrameAre (expected, output); view.AdvanceAnimation (); - View.SetClipToScreen (); + view.SetClipToScreen (); view.Draw (); expected = "/"; DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -95,7 +95,7 @@ public void TestSpinnerView_ThrottlesAnimation () //expected = "|"; //DriverAsserts.AssertDriverContentsWithFrameAre (expected, output); - Application.Top.Dispose (); + Application.Current.Dispose (); } private SpinnerView GetSpinnerView () diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index 746f0307df..db11ed0eb8 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -664,7 +664,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ( Assert.Equal (tab2, tv.SubViews.First (v => v.Id.Contains ("tabRow")).MostFocused); tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -683,7 +683,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ( tab1.DisplayText = "12345678910"; tab2.DisplayText = "13"; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -700,7 +700,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ( tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -717,7 +717,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ( tab2.DisplayText = "abcdefghijklmnopq"; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -810,7 +810,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () Assert.Equal (tab2, tv.SubViews.First (v => v.Id.Contains ("tabRow")).MostFocused); tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -830,7 +830,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () tab2.DisplayText = "13"; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -847,7 +847,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -865,7 +865,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () tab2.DisplayText = "abcdefghijklmnopq"; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -910,7 +910,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 () tv.Height = 5; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -952,7 +952,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -972,7 +972,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () tab2.DisplayText = "13"; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -989,7 +989,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1007,7 +1007,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () tab2.DisplayText = "abcdefghijklmnopq"; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1049,7 +1049,7 @@ public void ShowTopLine_True_TabsOnBottom_False_With_Unicode () tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1144,7 +1144,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () tab2.DisplayText = "13"; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1161,7 +1161,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1179,7 +1179,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () tab2.DisplayText = "abcdefghijklmnopq"; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1223,7 +1223,7 @@ public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1337,7 +1337,7 @@ public void Add_Three_TabsOnTop_ChangesTab () tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1354,7 +1354,7 @@ public void Add_Three_TabsOnTop_ChangesTab () tv.SelectedTab = tab3; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1404,7 +1404,7 @@ public void Add_Three_TabsOnBottom_ChangesTab () tv.SelectedTab = tab2; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1421,7 +1421,7 @@ public void Add_Three_TabsOnBottom_ChangesTab () tv.SelectedTab = tab3; tv.Layout (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1489,7 +1489,11 @@ public void Mouse_Wheel_Changes_Tab () private TabView GetTabView (out Tab tab1, out Tab tab2) { - var tv = new TabView () { Id = "tv " }; + var tv = new TabView () + { + Driver = ApplicationImpl.Instance.Driver, + Id = "tv " + }; tv.BeginInit (); tv.EndInit (); //tv.Scheme = new (); diff --git a/Tests/UnitTests/Views/TableViewTests.cs b/Tests/UnitTests/Views/TableViewTests.cs index 885bb435ae..4c162a0376 100644 --- a/Tests/UnitTests/Views/TableViewTests.cs +++ b/Tests/UnitTests/Views/TableViewTests.cs @@ -57,7 +57,11 @@ public static DataTableSource BuildTable (int cols, int rows, out DataTable dt) [AutoInitShutdown] public void CellEventsBackgroundFill () { - var tv = new TableView { Width = 20, Height = 4 }; + var tv = new TableView + { + Driver = ApplicationImpl.Instance.Driver, + Width = 20, Height = 4 + }; var dt = new DataTable (); dt.Columns.Add ("C1"); @@ -677,7 +681,10 @@ public void ScrollDown_OneLineAtATime () [SetupFakeApplication] public void ScrollIndicators () { - var tableView = new TableView (); + var tableView = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; tableView.BeginInit (); tableView.EndInit (); @@ -722,7 +729,7 @@ public void ScrollIndicators () // since A is now pushed off screen we get indicator showing // that user can scroll left to see first column - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); expected = @@ -737,7 +744,7 @@ public void ScrollIndicators () tableView.NewKeyDownEvent (new () { KeyCode = KeyCode.CursorRight }); tableView.NewKeyDownEvent (new () { KeyCode = KeyCode.CursorRight }); - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); expected = @@ -756,7 +763,10 @@ public void ScrollIndicators () [SetupFakeApplication] public void ScrollRight_SmoothScrolling () { - var tableView = new TableView (); + var tableView = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; tableView.BeginInit (); tableView.EndInit (); @@ -796,7 +806,7 @@ public void ScrollRight_SmoothScrolling () // Scroll right tableView.NewKeyDownEvent (new () { KeyCode = KeyCode.CursorRight }); - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); // Note that with SmoothHorizontalScrolling only a single new column @@ -818,7 +828,11 @@ public void ScrollRight_SmoothScrolling () [SetupFakeApplication] public void ScrollRight_WithoutSmoothScrolling () { - var tableView = new TableView (); + var tableView = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; + tableView.BeginInit (); tableView.EndInit (); tableView.SchemeName = "TopLevel"; @@ -844,7 +858,7 @@ public void ScrollRight_WithoutSmoothScrolling () // select last visible column tableView.SelectedColumn = 2; // column C - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); var expected = @@ -856,7 +870,7 @@ public void ScrollRight_WithoutSmoothScrolling () // Scroll right tableView.NewKeyDownEvent (new () { KeyCode = KeyCode.CursorRight }); - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); // notice that without smooth scrolling we just update the first column @@ -1563,7 +1577,10 @@ public void TableViewMultiSelect_CannotFallOffTop () [AutoInitShutdown] public void Test_CollectionNavigator () { - var tv = new TableView (); + var tv = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; tv.SchemeName = "TopLevel"; tv.Viewport = new (0, 0, 50, 7); @@ -1974,7 +1991,7 @@ public void TestColumnStyle_PreceedingColumnsInvisible_NoScrollIndicator () ◄─┼─┼─┤ │2│3│4│"; tableView.SetNeedsDraw (); - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); DriverAssert.AssertDriverContentsAre (expected, output); @@ -1988,7 +2005,7 @@ public void TestColumnStyle_PreceedingColumnsInvisible_NoScrollIndicator () ├─┼─┼─┤ │2│3│4│"; tableView.SetNeedsDraw (); - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); DriverAssert.AssertDriverContentsAre (expected, output); @@ -2004,7 +2021,7 @@ public void TestColumnStyle_RemainingColumnsInvisible_NoScrollIndicator () tableView.Style.ShowHorizontalHeaderUnderline = true; tableView.LayoutSubViews (); tableView.SetNeedsDraw (); - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); // normally we should have scroll indicators because DEF are of screen @@ -2027,7 +2044,7 @@ public void TestColumnStyle_RemainingColumnsInvisible_NoScrollIndicator () ├─┼─┼─┤ │1│2│3│"; tableView.SetNeedsDraw (); - View.SetClipToScreen (); + tableView.SetClipToScreen (); tableView.Draw (); DriverAssert.AssertDriverContentsAre (expected, output); } @@ -2206,8 +2223,11 @@ public void TestControlClick_MultiSelect_ThreeRowTable_FullRowSelect () [SetupFakeApplication] public void TestEnumerableDataSource_BasicTypes () { - Application.Driver!.SetScreenSize (100, 100); - var tv = new TableView (); + ApplicationImpl.Instance.Driver!.SetScreenSize (100, 100); + var tv = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; tv.SchemeName = "TopLevel"; tv.Viewport = new (0, 0, 50, 6); @@ -2411,7 +2431,10 @@ public void TestListTableSource (Orientation orient, bool parallel) { IList list = BuildList (16); - var tv = new TableView (); + var tv = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; //tv.BeginInit (); tv.EndInit (); tv.SchemeName = "TopLevel"; @@ -2600,7 +2623,7 @@ public void TestTableViewCheckboxes_ByObject () Assert.True (pets.First ().IsPicked); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2620,7 +2643,7 @@ public void TestTableViewCheckboxes_ByObject () Assert.True (pets.ElementAt (0).IsPicked); Assert.True (pets.ElementAt (1).IsPicked); Assert.False (pets.ElementAt (2).IsPicked); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2640,7 +2663,7 @@ public void TestTableViewCheckboxes_ByObject () Assert.False (pets.ElementAt (0).IsPicked); Assert.True (pets.ElementAt (1).IsPicked); Assert.False (pets.ElementAt (2).IsPicked); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2668,7 +2691,7 @@ public void TestTableViewCheckboxes_MultiSelectIsUnion_WhenToggling () wrapper.CheckedRows.Add (0); wrapper.CheckedRows.Add (2); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); var expected = @@ -2692,7 +2715,7 @@ public void TestTableViewCheckboxes_MultiSelectIsUnion_WhenToggling () Assert.Contains (2, wrapper.CheckedRows); Assert.Equal (3, wrapper.CheckedRows.Count); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2708,7 +2731,7 @@ public void TestTableViewCheckboxes_MultiSelectIsUnion_WhenToggling () // Untoggle the top 2 tv.NewKeyDownEvent (Key.Space); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2737,7 +2760,7 @@ public void TestTableViewCheckboxes_SelectAllToggle () tv.NewKeyDownEvent (Key.A.WithCtrl); tv.NewKeyDownEvent (Key.Space); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); var expected = @@ -2757,7 +2780,7 @@ public void TestTableViewCheckboxes_SelectAllToggle () // Untoggle all again tv.NewKeyDownEvent (Key.Space); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2798,7 +2821,7 @@ public void TestTableViewCheckboxes_SelectAllToggle_ByObject () Assert.True (pets.All (p => p.IsPicked)); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); var expected = @@ -2818,7 +2841,7 @@ public void TestTableViewCheckboxes_SelectAllToggle_ByObject () Assert.Empty (pets.Where (p => p.IsPicked)); #pragma warning restore xUnit2029 - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2845,7 +2868,7 @@ public void TestTableViewCheckboxes_Simple () var wrapper = new CheckBoxTableSourceWrapperByIndex (tv, tv.Table); tv.Table = wrapper; - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); var expected = @@ -2865,7 +2888,7 @@ public void TestTableViewCheckboxes_Simple () Assert.Single (wrapper.CheckedRows, 0); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2885,7 +2908,7 @@ public void TestTableViewCheckboxes_Simple () Assert.Contains (1, wrapper.CheckedRows); Assert.Equal (2, wrapper.CheckedRows.Count); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2904,7 +2927,7 @@ public void TestTableViewCheckboxes_Simple () Assert.Single (wrapper.CheckedRows, 1); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2936,7 +2959,7 @@ public void TestTableViewRadioBoxes_Simple_ByObject () wrapper.UseRadioButtons = true; tv.Table = wrapper; - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); var expected = @@ -2959,7 +2982,7 @@ public void TestTableViewRadioBoxes_Simple_ByObject () Assert.True (pets.First ().IsPicked); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -2980,7 +3003,7 @@ public void TestTableViewRadioBoxes_Simple_ByObject () Assert.True (pets.ElementAt (1).IsPicked); Assert.False (pets.ElementAt (2).IsPicked); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -3001,7 +3024,7 @@ public void TestTableViewRadioBoxes_Simple_ByObject () Assert.False (pets.ElementAt (1).IsPicked); Assert.False (pets.ElementAt (2).IsPicked); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -3234,19 +3257,19 @@ public void CanTabOutOfTableViewUsingCursor_Left () // Pressing left should move us to the first column without changing focus Application.RaiseKeyDownEvent (Key.CursorLeft); - Assert.Same (tableView, Application.Top!.MostFocused); + Assert.Same (tableView, Application.Current!.MostFocused); Assert.True (tableView.HasFocus); // Because we are now on the leftmost cell a further left press should move focus Application.RaiseKeyDownEvent (Key.CursorLeft); - Assert.NotSame (tableView, Application.Top.MostFocused); + Assert.NotSame (tableView, Application.Current.MostFocused); Assert.False (tableView.HasFocus); - Assert.Same (tf1, Application.Top.MostFocused); + Assert.Same (tf1, Application.Current.MostFocused); Assert.True (tf1.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -3259,19 +3282,19 @@ public void CanTabOutOfTableViewUsingCursor_Up () // First press should move us up Application.RaiseKeyDownEvent (Key.CursorUp); - Assert.Same (tableView, Application.Top!.MostFocused); + Assert.Same (tableView, Application.Current!.MostFocused); Assert.True (tableView.HasFocus); // Because we are now on the top row a further press should move focus Application.RaiseKeyDownEvent (Key.CursorUp); - Assert.NotSame (tableView, Application.Top.MostFocused); + Assert.NotSame (tableView, Application.Current.MostFocused); Assert.False (tableView.HasFocus); - Assert.Same (tf1, Application.Top.MostFocused); + Assert.Same (tf1, Application.Current.MostFocused); Assert.True (tf1.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -3284,19 +3307,19 @@ public void CanTabOutOfTableViewUsingCursor_Right () // First press should move us to the rightmost column without changing focus Application.RaiseKeyDownEvent (Key.CursorRight); - Assert.Same (tableView, Application.Top!.MostFocused); + Assert.Same (tableView, Application.Current!.MostFocused); Assert.True (tableView.HasFocus); // Because we are now on the rightmost cell, a further right press should move focus Application.RaiseKeyDownEvent (Key.CursorRight); - Assert.NotSame (tableView, Application.Top.MostFocused); + Assert.NotSame (tableView, Application.Current.MostFocused); Assert.False (tableView.HasFocus); - Assert.Same (tf2, Application.Top.MostFocused); + Assert.Same (tf2, Application.Current.MostFocused); Assert.True (tf2.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -3309,19 +3332,19 @@ public void CanTabOutOfTableViewUsingCursor_Down () // First press should move us to the bottommost row without changing focus Application.RaiseKeyDownEvent (Key.CursorDown); - Assert.Same (tableView, Application.Top!.MostFocused); + Assert.Same (tableView, Application.Current!.MostFocused); Assert.True (tableView.HasFocus); // Because we are now on the bottommost cell, a further down press should move focus Application.RaiseKeyDownEvent (Key.CursorDown); - Assert.NotSame (tableView, Application.Top.MostFocused); + Assert.NotSame (tableView, Application.Current.MostFocused); Assert.False (tableView.HasFocus); - Assert.Same (tf2, Application.Top.MostFocused); + Assert.Same (tf2, Application.Current.MostFocused); Assert.True (tf2.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -3334,7 +3357,7 @@ public void CanTabOutOfTableViewUsingCursor_Left_ClearsSelectionFirst () // Pressing shift-left should give us a multi selection Application.RaiseKeyDownEvent (Key.CursorLeft.WithShift); - Assert.Same (tableView, Application.Top!.MostFocused); + Assert.Same (tableView, Application.Current!.MostFocused); Assert.True (tableView.HasFocus); Assert.Equal (2, tableView.GetAllSelectedCells ().Count ()); @@ -3345,19 +3368,19 @@ public void CanTabOutOfTableViewUsingCursor_Left_ClearsSelectionFirst () // Selection 'clears' just to the single cell and we remain focused Assert.Single (tableView.GetAllSelectedCells ()); - Assert.Same (tableView, Application.Top.MostFocused); + Assert.Same (tableView, Application.Current.MostFocused); Assert.True (tableView.HasFocus); // A further left will switch focus Application.RaiseKeyDownEvent (Key.CursorLeft); - Assert.NotSame (tableView, Application.Top.MostFocused); + Assert.NotSame (tableView, Application.Current.MostFocused); Assert.False (tableView.HasFocus); - Assert.Same (tf1, Application.Top.MostFocused); + Assert.Same (tf1, Application.Current.MostFocused); Assert.True (tf1.HasFocus); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Theory] @@ -3397,17 +3420,17 @@ private void GetTableViewWithSiblings (out TextField tf1, out TableView tableVie tableView.BeginInit (); tableView.EndInit (); - Application.Navigation = new (); - Application.Top = new (); + + Application.Current = new (); tf1 = new (); tf2 = new (); - Application.Top.Add (tf1); - Application.Top.Add (tableView); - Application.Top.Add (tf2); + Application.Current.Add (tf1); + Application.Current.Add (tableView); + Application.Current.Add (tf2); tableView.SetFocus (); - Assert.Same (tableView, Application.Top.MostFocused); + Assert.Same (tableView, Application.Current.MostFocused); Assert.True (tableView.HasFocus); // Set big table @@ -3416,7 +3439,10 @@ private void GetTableViewWithSiblings (out TextField tf1, out TableView tableVie private TableView GetABCDEFTableView (out DataTable dt) { - var tableView = new TableView (); + var tableView = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; tableView.BeginInit (); tableView.EndInit (); @@ -3445,9 +3471,12 @@ private TableView GetABCDEFTableView (out DataTable dt) private TableView GetPetTable (out EnumerableTableSource source) { - var tv = new TableView (); - tv.SchemeName = "TopLevel"; - tv.Viewport = new (0, 0, 25, 6); + var tv = new TableView () + { + Driver = ApplicationImpl.Instance.Driver, + SchemeName = "TopLevel", + Viewport = new (0, 0, 25, 6) + }; List pets = new () { @@ -3473,7 +3502,10 @@ private TableView GetPetTable (out EnumerableTableSource source) private TableView GetTwoRowSixColumnTable (out DataTable dt) { - var tableView = new TableView (); + var tableView = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; tableView.SchemeName = "TopLevel"; // 3 columns are visible @@ -3503,7 +3535,10 @@ private TableView GetTwoRowSixColumnTable (out DataTable dt) private TableView SetUpMiniTable (out DataTable dt) { - var tv = new TableView (); + var tv = new TableView () + { + Driver = ApplicationImpl.Instance.Driver + }; tv.BeginInit (); tv.EndInit (); tv.Viewport = new (0, 0, 10, 4); diff --git a/Tests/UnitTests/Views/TextFieldTests.cs b/Tests/UnitTests/Views/TextFieldTests.cs index b4d27b93b0..9221784092 100644 --- a/Tests/UnitTests/Views/TextFieldTests.cs +++ b/Tests/UnitTests/Views/TextFieldTests.cs @@ -84,7 +84,7 @@ public void CaptionedTextField_DoesNotOverspillBounds (string caption, string ex tf.Draw (); DriverAssert.AssertDriverContentsAre (expectedRender, output); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -104,7 +104,7 @@ public void CaptionedTextField_DoesNotOverspillViewport_Unicode () tf.Draw (); DriverAssert.AssertDriverContentsAre ("Misérables", output); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Theory (Skip = "Broke with ContextMenuv2")] @@ -123,16 +123,16 @@ public void CaptionedTextField_DoNotRenderCaption_WhenTextPresent (string conten // Caption should appear when not focused and no text Assert.False (tf.HasFocus); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsAre ("Enter txt", output); // but disapear when text is added tf.Text = content; - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsAre (content, output); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -147,17 +147,17 @@ public void CaptionedTextField_RendersCaption_WhenNotFocused () // Caption has no effect when focused tf.Title = "Enter txt"; Assert.True (tf.HasFocus); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsAre ("", output); Application.RaiseKeyDownEvent ('\t'); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsAre ("Enter txt", output); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -173,7 +173,7 @@ public void Title_RendersAsCaption_WithCorrectAttributes () Application.RaiseKeyDownEvent ('\t'); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); // Verify the caption text is rendered @@ -187,7 +187,7 @@ public void Title_RendersAsCaption_WithCorrectAttributes () // All characters in "Enter text" should have the caption attribute DriverAssert.AssertDriverAttributesAre ("0000000000", output, Application.Driver, captionAttr); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -203,7 +203,7 @@ public void Title_WithHotkey_RendersUnderlined () Application.RaiseKeyDownEvent ('\t'); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); // The hotkey character 'F' should be rendered (without the underscore in the actual text) @@ -221,7 +221,7 @@ public void Title_WithHotkey_RendersUnderlined () // F is underlined (index 1), remaining characters use normal caption attribute (index 0) DriverAssert.AssertDriverAttributesAre ("1000", output, Application.Driver, captionAttr, hotkeyAttr); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -237,7 +237,7 @@ public void Title_WithHotkey_MiddleCharacter_RendersUnderlined () Application.RaiseKeyDownEvent ('\t'); Assert.False (tf.HasFocus); - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); // The underscore should not be rendered, 'T' should be underlined @@ -255,7 +255,7 @@ public void Title_WithHotkey_MiddleCharacter_RendersUnderlined () // "Enter " (6 chars) + "T" (underlined) + "ext" (3 chars) DriverAssert.AssertDriverAttributesAre ("0000001000", output, Application.Driver, captionAttr, hotkeyAttr); - Application.Top.Dispose (); + Application.Current.Dispose (); } [Fact] @@ -1620,7 +1620,11 @@ public void [SetupFakeApplication] public void Words_With_Accents_Incorrect_Order_Will_Result_With_Wrong_Accent_Place () { - var tf = new TextField { Width = 30, Text = "Les Misérables" }; + var tf = new TextField + { + Driver = ApplicationImpl.Instance.Driver, + Width = 30, Text = "Les Misérables" + }; tf.SetRelativeLayout (new (100, 100)); tf.Draw (); @@ -1641,7 +1645,7 @@ public void Words_With_Accents_Incorrect_Order_Will_Result_With_Wrong_Accent_Pla // incorrect order will result with a wrong accent place tf.Text = "Les Mis" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "erables"; - View.SetClipToScreen (); + tf.SetClipToScreen (); tf.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -1686,7 +1690,7 @@ public override void Before (MethodInfo methodUnderTest) { base.Before (methodUnderTest); - //Application.Top.Scheme = Colors.Schemes ["Base"]; + //Application.Current.Scheme = Colors.Schemes ["Base"]; _textField = new () { // 1 2 3 @@ -1701,7 +1705,11 @@ public override void Before (MethodInfo methodUnderTest) [AutoInitShutdown] public void Draw_Esc_Rune () { - var tf = new TextField { Width = 5, Text = "\u001b" }; + var tf = new TextField + { + Driver = ApplicationImpl.Instance.Driver, + Width = 5, Text = "\u001b" + }; tf.BeginInit (); tf.EndInit (); tf.Draw (); diff --git a/Tests/UnitTests/Views/TextViewTests.cs b/Tests/UnitTests/Views/TextViewTests.cs index d00000d2e0..5d03983eb9 100644 --- a/Tests/UnitTests/Views/TextViewTests.cs +++ b/Tests/UnitTests/Views/TextViewTests.cs @@ -109,7 +109,7 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) Assert.Equal (leftCol, _textView.LeftColumn); } - Application.Top.Remove (_textView); + Application.Current.Remove (_textView); Application.RequestStop (); } } @@ -6805,7 +6805,10 @@ public void WordWrap_ReadOnly_CursorPosition_SelectedText_Copy () ); tv.WordWrap = true; - var top = new Toplevel (); + var top = new Toplevel () + { + Driver = ApplicationImpl.Instance.Driver, + }; top.Add (tv); top.Layout (); tv.Draw (); @@ -6827,7 +6830,7 @@ This is tv.CursorPosition = new (6, 2); Assert.Equal (new (5, 2), tv.CursorPosition); top.LayoutSubViews (); - View.SetClipToScreen (); + top.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsWithFrameAre ( @@ -6891,7 +6894,10 @@ public void WordWrap_WrapModel_Output () ); tv.WordWrap = true; - var top = new Toplevel (); + var top = new Toplevel () + { + Driver = ApplicationImpl.Instance.Driver, + }; top.Add (tv); top.Layout (); @@ -7005,7 +7011,11 @@ public override void Before (MethodInfo methodUnderTest) [SetupFakeApplication] public void Draw_Esc_Rune () { - var tv = new TextView { Width = 5, Height = 1, Text = "\u001b" }; + var tv = new TextView + { + Driver = ApplicationImpl.Instance.Driver, + Width = 5, Height = 1, Text = "\u001b" + }; tv.BeginInit (); tv.EndInit (); tv.Draw (); diff --git a/Tests/UnitTests/Views/ToplevelTests.cs b/Tests/UnitTests/Views/ToplevelTests.cs index ad47948a04..365ebd8bfe 100644 --- a/Tests/UnitTests/Views/ToplevelTests.cs +++ b/Tests/UnitTests/Views/ToplevelTests.cs @@ -69,11 +69,11 @@ public void Internal_Tests () #endif Application.Begin (top); - Assert.Equal (top, Application.Top); + Assert.Equal (top, Application.Current); - // Application.Top without menu and status bar. + // Application.Current without menu and status bar. View supView = View.GetLocationEnsuringFullVisibility (top, 2, 2, out int nx, out int ny /*, out StatusBar sb*/); - Assert.Equal (Application.Top, supView); + Assert.Equal (Application.Current, supView); Assert.Equal (0, nx); Assert.Equal (0, ny); @@ -82,7 +82,7 @@ public void Internal_Tests () top.Add (new MenuBar ()); Assert.NotNull (top.MenuBar); - // Application.Top with a menu and without status bar. + // Application.Current with a menu and without status bar. View.GetLocationEnsuringFullVisibility (top, 2, 2, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); Assert.Equal (1, ny); @@ -92,11 +92,11 @@ public void Internal_Tests () //top.Add (new StatusBar ()); //Assert.NotNull (top.StatusBar); - // Application.Top with a menu and status bar. + // Application.Current with a menu and status bar. View.GetLocationEnsuringFullVisibility (top, 2, 2, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); - // The available height is lower than the Application.Top height minus + // The available height is lower than the Application.Current height minus // the menu bar and status bar, then the top can go beyond the bottom // Assert.Equal (2, ny); //Assert.NotNull (sb); @@ -106,11 +106,11 @@ public void Internal_Tests () Assert.Null (top.MenuBar); Assert.NotNull (menuBar); - // Application.Top without a menu and with a status bar. + // Application.Current without a menu and with a status bar. View.GetLocationEnsuringFullVisibility (top, 2, 2, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); - // The available height is lower than the Application.Top height minus + // The available height is lower than the Application.Current height minus // the status bar, then the top can go beyond the bottom // Assert.Equal (2, ny); //Assert.NotNull (sb); @@ -127,11 +127,11 @@ public void Internal_Tests () // The SuperView is always the same regardless of the caller. supView = View.GetLocationEnsuringFullVisibility (win, 0, 0, out nx, out ny /*, out sb*/); - Assert.Equal (Application.Top, supView); + Assert.Equal (Application.Current, supView); supView = View.GetLocationEnsuringFullVisibility (win, 0, 0, out nx, out ny /*, out sb*/); - Assert.Equal (Application.Top, supView); + Assert.Equal (Application.Current, supView); - // Application.Top without menu and status bar. + // Application.Current without menu and status bar. View.GetLocationEnsuringFullVisibility (win, 0, 0, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); Assert.Equal (0, ny); @@ -141,7 +141,7 @@ public void Internal_Tests () top.Add (new MenuBar ()); Assert.NotNull (top.MenuBar); - // Application.Top with a menu and without status bar. + // Application.Current with a menu and without status bar. View.GetLocationEnsuringFullVisibility (win, 2, 2, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); Assert.Equal (1, ny); @@ -152,11 +152,11 @@ public void Internal_Tests () //Assert.NotNull (top.StatusBar); - // Application.Top with a menu and status bar. + // Application.Current with a menu and status bar. View.GetLocationEnsuringFullVisibility (win, 30, 20, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); - // The available height is lower than the Application.Top height minus + // The available height is lower than the Application.Current height minus // the menu bar and status bar, then the top can go beyond the bottom //Assert.Equal (20, ny); //Assert.NotNull (sb); @@ -177,7 +177,7 @@ public void Internal_Tests () win = new () { Width = 60, Height = 15 }; top.Add (win); - // Application.Top without menu and status bar. + // Application.Current without menu and status bar. View.GetLocationEnsuringFullVisibility (win, 0, 0, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); Assert.Equal (0, ny); @@ -187,7 +187,7 @@ public void Internal_Tests () top.Add (new MenuBar ()); Assert.NotNull (top.MenuBar); - // Application.Top with a menu and without status bar. + // Application.Current with a menu and without status bar. View.GetLocationEnsuringFullVisibility (win, 2, 2, out nx, out ny /*, out sb*/); Assert.Equal (2, nx); Assert.Equal (2, ny); @@ -198,7 +198,7 @@ public void Internal_Tests () //Assert.NotNull (top.StatusBar); - // Application.Top with a menu and status bar. + // Application.Current with a menu and status bar. View.GetLocationEnsuringFullVisibility (win, 30, 20, out nx, out ny /*, out sb*/); Assert.Equal (20, nx); // 20+60=80 @@ -249,7 +249,7 @@ public void SuperViewChanged_Should_Not_Be_Used_To_Initialize_Toplevel_Events () var win = new Window (); win.Add (view); - Application.Init (null, "fake"); + Application.Init ("fake"); Toplevel top = new (); top.Add (win); @@ -307,7 +307,7 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) } else if (iterations == 1) { - Assert.Equal (new (2, 2), Application.Top!.Frame.Location); + Assert.Equal (new (2, 2), Application.Current!.Frame.Location); } else if (iterations == 2) { @@ -316,12 +316,12 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) // Grab the mouse Application.RaiseMouseEvent (new () { ScreenPosition = new (3, 2), Flags = MouseFlags.Button1Pressed }); - Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView); - Assert.Equal (new (2, 2, 10, 3), Application.Top.Frame); + Assert.Equal (Application.Current!.Border, Application.Mouse.MouseGrabView); + Assert.Equal (new (2, 2, 10, 3), Application.Current.Frame); } else if (iterations == 3) { - Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView); + Assert.Equal (Application.Current!.Border, Application.Mouse.MouseGrabView); // Drag to left Application.RaiseMouseEvent ( @@ -333,38 +333,38 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) }); AutoInitShutdownAttribute.RunIteration (); - Assert.Equal (Application.Top.Border, Application.Mouse.MouseGrabView); - Assert.Equal (new (1, 2, 10, 3), Application.Top.Frame); + Assert.Equal (Application.Current.Border, Application.Mouse.MouseGrabView); + Assert.Equal (new (1, 2, 10, 3), Application.Current.Frame); } else if (iterations == 4) { - Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView); - Assert.Equal (new (1, 2), Application.Top.Frame.Location); + Assert.Equal (Application.Current!.Border, Application.Mouse.MouseGrabView); + Assert.Equal (new (1, 2), Application.Current.Frame.Location); - Assert.Equal (Application.Top.Border, Application.Mouse.MouseGrabView); + Assert.Equal (Application.Current.Border, Application.Mouse.MouseGrabView); } else if (iterations == 5) { - Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView); + Assert.Equal (Application.Current!.Border, Application.Mouse.MouseGrabView); // Drag up Application.RaiseMouseEvent (new () { ScreenPosition = new (2, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition }); AutoInitShutdownAttribute.RunIteration (); - Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView); - Assert.Equal (new (1, 1, 10, 3), Application.Top.Frame); + Assert.Equal (Application.Current!.Border, Application.Mouse.MouseGrabView); + Assert.Equal (new (1, 1, 10, 3), Application.Current.Frame); } else if (iterations == 6) { - Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView); - Assert.Equal (new (1, 1), Application.Top.Frame.Location); + Assert.Equal (Application.Current!.Border, Application.Mouse.MouseGrabView); + Assert.Equal (new (1, 1), Application.Current.Frame.Location); - Assert.Equal (Application.Top.Border, Application.Mouse.MouseGrabView); - Assert.Equal (new (1, 1, 10, 3), Application.Top.Frame); + Assert.Equal (Application.Current.Border, Application.Mouse.MouseGrabView); + Assert.Equal (new (1, 1, 10, 3), Application.Current.Frame); } else if (iterations == 7) { - Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView); + Assert.Equal (Application.Current!.Border, Application.Mouse.MouseGrabView); // Ungrab the mouse Application.RaiseMouseEvent (new () { ScreenPosition = new (2, 1), Flags = MouseFlags.Button1Released }); @@ -739,7 +739,7 @@ public void Activating_MenuBar_By_Alt_Key_Does_Not_Throw () [Fact] public void Multi_Thread_Toplevels () { - Application.Init (null, "fake"); + Application.Init ("fake"); Toplevel t = new (); var w = new Window (); diff --git a/Tests/UnitTests/Views/TreeTableSourceTests.cs b/Tests/UnitTests/Views/TreeTableSourceTests.cs index 43d5a20592..0f5fd42c25 100644 --- a/Tests/UnitTests/Views/TreeTableSourceTests.cs +++ b/Tests/UnitTests/Views/TreeTableSourceTests.cs @@ -55,7 +55,7 @@ public void TestTreeTableSource_BasicExpanding_WithKeyboard () // when pressing right we should expand the top route tv.NewKeyDownEvent (Key.CursorRight); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -73,7 +73,7 @@ public void TestTreeTableSource_BasicExpanding_WithKeyboard () // when pressing left we should collapse the top route again tv.NewKeyDownEvent (Key.CursorLeft); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -97,7 +97,7 @@ public void TestTreeTableSource_BasicExpanding_WithMouse () tv.Style.GetOrCreateColumnStyle (1).MinAcceptableWidth = 1; - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); var expected = @@ -117,7 +117,7 @@ public void TestTreeTableSource_BasicExpanding_WithMouse () Assert.True (tv.NewMouseEvent (new MouseEventArgs { Position = new (2, 2), Flags = MouseFlags.Button1Clicked })); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -142,7 +142,7 @@ public void TestTreeTableSource_BasicExpanding_WithMouse () // Clicking on the + again should collapse tv.NewMouseEvent (new MouseEventArgs { Position = new (2, 2), Flags = MouseFlags.Button1Clicked }); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -195,7 +195,7 @@ public void TestTreeTableSource_CombinedWithCheckboxes () Application.RaiseKeyDownEvent (Key.CursorRight); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -213,7 +213,7 @@ public void TestTreeTableSource_CombinedWithCheckboxes () tv.NewKeyDownEvent (Key.CursorDown); tv.NewKeyDownEvent (Key.Space); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); expected = @@ -239,7 +239,10 @@ public void TestTreeTableSource_CombinedWithCheckboxes () private TableView GetTreeTable (out TreeView tree) { - var tableView = new TableView (); + var tableView = new TableView () + { + Driver = ApplicationImpl.Instance.Driver, + }; tableView.SchemeName = "TopLevel"; tableView.Viewport = new Rectangle (0, 0, 40, 6); diff --git a/Tests/UnitTests/Views/TreeViewTests.cs b/Tests/UnitTests/Views/TreeViewTests.cs index 88b9cde4be..f573300548 100644 --- a/Tests/UnitTests/Views/TreeViewTests.cs +++ b/Tests/UnitTests/Views/TreeViewTests.cs @@ -93,7 +93,7 @@ public void ContentWidth_VisibleVsAll () [AutoInitShutdown] public void CursorVisibility_MultiSelect () { - var tv = new TreeView { Width = 20, Height = 10 }; + var tv = new TreeView () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; var n1 = new TreeNode ("normal"); var n2 = new TreeNode ("pink"); @@ -698,7 +698,11 @@ public void ScrollOffset_CannotBeNegative () [SetupFakeApplication] public void TestBottomlessTreeView_MaxDepth_3 () { - TreeView tv = new () { Width = 20, Height = 10 }; + TreeView tv = new () + { + Driver = ApplicationImpl.Instance.Driver, + Width = 20, Height = 10 + }; tv.TreeBuilder = new DelegateTreeBuilder ( s => new [] { (int.Parse (s) + 1).ToString () } @@ -718,7 +722,7 @@ public void TestBottomlessTreeView_MaxDepth_3 () ); tv.MaxDepth = 3; tv.ExpandAll (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); // Normal drawing of the tree view @@ -737,7 +741,7 @@ public void TestBottomlessTreeView_MaxDepth_3 () [SetupFakeApplication] public void TestBottomlessTreeView_MaxDepth_5 () { - TreeView tv = new () { Width = 20, Height = 10 }; + TreeView tv = new () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; tv.TreeBuilder = new DelegateTreeBuilder ( s => new [] { (int.Parse (s) + 1).ToString () } @@ -757,7 +761,7 @@ public void TestBottomlessTreeView_MaxDepth_5 () ); tv.MaxDepth = 5; tv.ExpandAll (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); @@ -785,7 +789,7 @@ public void TestBottomlessTreeView_MaxDepth_5 () Assert.True (tv.CanExpand ("5")); Assert.False (tv.IsExpanded ("5")); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); @@ -806,7 +810,7 @@ public void TestBottomlessTreeView_MaxDepth_5 () [SetupFakeApplication] public void TestGetObjectOnRow () { - var tv = new TreeView { Width = 20, Height = 10 }; + var tv = new TreeView () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; tv.BeginInit (); tv.EndInit (); var n1 = new TreeNode ("normal"); @@ -840,7 +844,7 @@ public void TestGetObjectOnRow () Assert.Null (tv.GetObjectOnRow (4)); tv.Collapse (n1); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); @@ -862,7 +866,7 @@ public void TestGetObjectOnRow () [SetupFakeApplication] public void TestGetObjectRow () { - var tv = new TreeView { Width = 20, Height = 10 }; + var tv = new TreeView () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; var n1 = new TreeNode ("normal"); var n1_1 = new TreeNode ("pink"); @@ -877,7 +881,7 @@ public void TestGetObjectRow () tv.SetScheme (new Scheme ()); tv.LayoutSubViews (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsAre ( @@ -897,7 +901,7 @@ public void TestGetObjectRow () tv.Collapse (n1); tv.LayoutSubViews (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsAre ( @@ -915,7 +919,7 @@ public void TestGetObjectRow () tv.ScrollOffsetVertical = 1; tv.LayoutSubViews (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsAre ( @@ -933,7 +937,7 @@ public void TestGetObjectRow () [SetupFakeApplication] public void TestTreeView_DrawLineEvent () { - var tv = new TreeView { Width = 20, Height = 10 }; + var tv = new TreeView () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; List> eventArgs = new (); @@ -952,7 +956,7 @@ public void TestTreeView_DrawLineEvent () tv.SetScheme (new Scheme ()); tv.LayoutSubViews (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); // Normal drawing of the tree view @@ -1000,7 +1004,7 @@ public void TestTreeView_DrawLineEvent () [SetupFakeApplication] public void TestTreeView_DrawLineEvent_Handled () { - var tv = new TreeView { Width = 20, Height = 10 }; + var tv = new TreeView () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; tv.DrawLine += (s, e) => { @@ -1046,7 +1050,7 @@ public void TestTreeView_DrawLineEvent_Handled () [SetupFakeApplication] public void TestTreeView_DrawLineEvent_WithScrolling () { - var tv = new TreeView { Width = 20, Height = 10 }; + var tv = new TreeView () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; List> eventArgs = new (); @@ -1109,7 +1113,7 @@ oot two [SetupFakeApplication] public void TestTreeView_Filter () { - var tv = new TreeView { Width = 20, Height = 10 }; + var tv = new TreeView () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; var n1 = new TreeNode ("root one"); var n1_1 = new TreeNode ("leaf 1"); @@ -1141,7 +1145,7 @@ public void TestTreeView_Filter () // matches nothing filter.Text = "asdfjhasdf"; - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); // Normal drawing of the tree view @@ -1152,7 +1156,7 @@ public void TestTreeView_Filter () // Matches everything filter.Text = "root"; - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsAre ( @@ -1167,7 +1171,7 @@ public void TestTreeView_Filter () // Matches 2 leaf nodes filter.Text = "leaf"; - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsAre ( @@ -1181,7 +1185,7 @@ public void TestTreeView_Filter () // Matches 1 leaf nodes filter.Text = "leaf 1"; - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); DriverAssert.AssertDriverContentsAre ( @@ -1197,7 +1201,7 @@ public void TestTreeView_Filter () [SetupFakeApplication] public void TestTreeViewColor () { - var tv = new TreeView { Width = 20, Height = 10 }; + var tv = new TreeView () { Driver = ApplicationImpl.Instance.Driver, Width = 20, Height = 10 }; tv.BeginInit (); tv.EndInit (); var n1 = new TreeNode ("normal"); @@ -1253,7 +1257,7 @@ public void TestTreeViewColor () // redraw now that the custom color // delegate is registered tv.SetNeedsDraw (); - View.SetClipToScreen (); + tv.SetClipToScreen (); tv.Draw (); // Same text diff --git a/Tests/UnitTestsParallelizable/Application/ApplicationPopoverTests.cs b/Tests/UnitTestsParallelizable/Application/ApplicationPopoverTests.cs index 5bb470c7e0..914d676eec 100644 --- a/Tests/UnitTestsParallelizable/Application/ApplicationPopoverTests.cs +++ b/Tests/UnitTestsParallelizable/Application/ApplicationPopoverTests.cs @@ -1,5 +1,6 @@ #nullable enable using Moq; +using Terminal.Gui.App; namespace UnitTests_Parallelizable.ApplicationTests; @@ -42,6 +43,7 @@ public void Show_SetsActivePopover () // Arrange var popover = new Mock ().Object; var popoverManager = new ApplicationPopover (); + popoverManager.Register (popover); // Act popoverManager.Show (popover); @@ -56,6 +58,7 @@ public void Hide_ClearsActivePopover () // Arrange var popover = new Mock ().Object; var popoverManager = new ApplicationPopover (); + popoverManager.Register (popover); popoverManager.Show (popover); // Act @@ -72,6 +75,8 @@ public void DispatchKeyDown_ActivePopoverGetsKey () // Arrange var popover = new PopoverTestClass (); var popoverManager = new ApplicationPopover (); + popoverManager.Register (popover); + popoverManager.Show (popover); // Act @@ -88,6 +93,7 @@ public void DispatchKeyDown_ActivePopoverGetsHotKey () // Arrange var popover = new PopoverTestClass (); var popoverManager = new ApplicationPopover (); + popoverManager.Register (popover); popoverManager.Show (popover); // Act @@ -106,6 +112,8 @@ public void DispatchKeyDown_InactivePopoverGetsHotKey () var activePopover = new PopoverTestClass () { Id = "activePopover" }; var inactivePopover = new PopoverTestClass () { Id = "inactivePopover" }; ; var popoverManager = new ApplicationPopover (); + + popoverManager.Register (activePopover); popoverManager.Show (activePopover); popoverManager.Register (inactivePopover); @@ -126,6 +134,8 @@ public void DispatchKeyDown_InactivePopoverDoesGetKey () var activePopover = new PopoverTestClass (); var inactivePopover = new PopoverTestClass (); var popoverManager = new ApplicationPopover (); + popoverManager.Register (activePopover); + popoverManager.Show (activePopover); popoverManager.Register (inactivePopover); @@ -181,6 +191,6 @@ protected override bool OnKeyDown (Key key) } /// - public Toplevel? Toplevel { get; set; } + public Toplevel? Current { get; set; } } } diff --git a/Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs b/Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs index 69b64f51a4..22421f90b9 100644 --- a/Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs +++ b/Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs @@ -41,7 +41,7 @@ public void Mouse_LastMousePosition_CanBeSetAndRetrieved (int x, int y) // Assert Assert.Equal (testPosition, mouse.LastMousePosition); - Assert.Equal (testPosition, mouse.GetLastMousePosition ()); + Assert.Equal (testPosition, mouse.LastMousePosition); } [Fact] diff --git a/Tests/UnitTestsParallelizable/Application/MouseTests.cs b/Tests/UnitTestsParallelizable/Application/MouseTests.cs index fdd3260a41..d5dba4dd4f 100644 --- a/Tests/UnitTestsParallelizable/Application/MouseTests.cs +++ b/Tests/UnitTestsParallelizable/Application/MouseTests.cs @@ -32,7 +32,7 @@ public void Mouse_LastMousePosition_CanBeSetAndRetrieved () // Act mouse.LastMousePosition = expectedPosition; - Point? actualPosition = mouse.GetLastMousePosition (); + Point? actualPosition = mouse.LastMousePosition; // Assert Assert.Equal (expectedPosition, actualPosition); diff --git a/Tests/UnitTestsParallelizable/Application/PopoverBaseImplTests.cs b/Tests/UnitTestsParallelizable/Application/PopoverBaseImplTests.cs index 69b795479d..7d2d27d50a 100644 --- a/Tests/UnitTestsParallelizable/Application/PopoverBaseImplTests.cs +++ b/Tests/UnitTestsParallelizable/Application/PopoverBaseImplTests.cs @@ -27,8 +27,8 @@ public void Toplevel_Property_CanBeSetAndGet () { var popover = new TestPopover (); var top = new Toplevel (); - popover.Toplevel = top; - Assert.Same (top, popover.Toplevel); + popover.Current = top; + Assert.Same (top, popover.Current); } [Fact] @@ -59,11 +59,11 @@ public void Show_ThrowsIfPopoverMissingQuitCommand () } [Fact] - public void Show_DoesNotThrow_BasePopoverImpl () + public void Show_Throw_If_Not_Registered () { var popover = new TestPopover (); var popoverManager = new ApplicationPopover (); - popoverManager.Show (popover); + Assert.Throws (() => popoverManager.Show (popover)); } } diff --git a/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs b/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs index 253acc8c04..aa3017d518 100644 --- a/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs +++ b/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs @@ -218,6 +218,33 @@ public void Update_WithValidJson_UpdatesSettingsScope () Assert.Contains (source, sourcesManager.Sources.Values); } + + //[Fact] + //public void Update_WithValidJson_UpdatesThemeScope () + //{ + // // Arrange + // var sourcesManager = new SourcesManager (); + // var themeScope = new ThemeScope (); + // themeScope.LoadHardCodedDefaults (); + // themeScope ["Button.DefaultShadowStyle"].PropertyValue = ShadowStyle.Opaque; + + // var json = """ + // { + // "Button.DefaultShadowStyle": "None" + // } + // """; + // var source = "test.json"; + // var location = ConfigLocations.HardCoded; + + // // Act + // bool result = sourcesManager.Load (themeScope, json, source, location); + + // // Assert + // Assert.True (result); + // Assert.Equal (Key.Z.WithCtrl, themeScope ["Application.QuitKey"].PropertyValue as Key); + // Assert.Contains (source, sourcesManager.Sources.Values); + //} + #endregion #region Load diff --git a/Tests/UnitTestsParallelizable/Drawing/RulerTests.cs b/Tests/UnitTestsParallelizable/Drawing/RulerTests.cs index cb059d6393..9e2d9d3201 100644 --- a/Tests/UnitTestsParallelizable/Drawing/RulerTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/RulerTests.cs @@ -54,7 +54,7 @@ public void Draw_Default () IDriver driver = CreateFakeDriver (); var r = new Ruler (); - r.Draw (Point.Empty, driver: driver); + r.Draw (driver: driver, location: Point.Empty); DriverAssert.AssertDriverContentsWithFrameAre (@"", output, driver); } @@ -69,7 +69,7 @@ public void Draw_Horizontal () Assert.Equal (Orientation.Horizontal, r.Orientation); r.Length = len; - r.Draw (Point.Empty, driver: driver); + r.Draw (driver: driver, location: Point.Empty); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -79,7 +79,7 @@ public void Draw_Horizontal () ); // Postive offset - r.Draw (new (1, 1), driver: driver); + r.Draw (driver: driver, location: new (1, 1)); DriverAssert.AssertDriverContentsAre ( @" @@ -91,7 +91,7 @@ public void Draw_Horizontal () ); // Negative offset - r.Draw (new (-1, 3), driver: driver); + r.Draw (driver: driver, location: new (-1, 3)); DriverAssert.AssertDriverContentsAre ( @" @@ -114,7 +114,7 @@ public void Draw_Vertical () var r = new Ruler (); r.Orientation = Orientation.Vertical; r.Length = len; - r.Draw (Point.Empty, driver: driver); + r.Draw (driver: driver, location: Point.Empty); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -137,7 +137,7 @@ public void Draw_Vertical () driver ); - r.Draw (new (1, 1), driver: driver); + r.Draw (driver: driver, location: new (1, 1)); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -162,7 +162,7 @@ public void Draw_Vertical () ); // Negative offset - r.Draw (new (2, -1), driver: driver); + r.Draw (driver: driver, location: new (2, -1)); DriverAssert.AssertDriverContentsWithFrameAre ( @" diff --git a/Tests/UnitTestsParallelizable/Drawing/ThicknessTests.cs b/Tests/UnitTestsParallelizable/Drawing/ThicknessTests.cs index a5cfc04362..65105cb890 100644 --- a/Tests/UnitTestsParallelizable/Drawing/ThicknessTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/ThicknessTests.cs @@ -1,4 +1,5 @@ using System.Text; +using Terminal.Gui.Drivers; using UnitTests; using Xunit.Abstractions; @@ -634,7 +635,7 @@ public void DrawTests () new (0, 0, driver!.Cols, driver!.Rows), (Rune)' ' ); - t.Draw (r, ViewDiagnosticFlags.Thickness, "Test", driver); + t.Draw (driver, r, ViewDiagnosticFlags.Thickness, "Test"); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -650,7 +651,7 @@ public void DrawTests () new (0, 0, driver!.Cols, driver!.Rows), (Rune)' ' ); - t.Draw (r, ViewDiagnosticFlags.Thickness, "Test", driver); + t.Draw (driver, r, ViewDiagnosticFlags.Thickness, "Test"); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -680,7 +681,7 @@ T T new (0, 0, driver!.Cols, driver!.Rows), (Rune)' ' ); - t.Draw (r, ViewDiagnosticFlags.Thickness, "Test", driver); + t.Draw (driver, r, ViewDiagnosticFlags.Thickness, "Test"); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -710,7 +711,7 @@ T TTT new (0, 0, driver!.Cols, driver!.Rows), (Rune)' ' ); - t.Draw (r, ViewDiagnosticFlags.Thickness, "Test", driver); + t.Draw (driver, r, ViewDiagnosticFlags.Thickness, "Test"); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -754,7 +755,8 @@ public void DrawTests_Ruler () var r = new Rectangle (2, 2, 40, 15); top.Draw (); - t.Draw (r, ViewDiagnosticFlags.Ruler, "Test", driver); + top.SetClipToScreen (); + t.Draw (driver, r, ViewDiagnosticFlags.Ruler, "Test"); DriverAssert.AssertDriverContentsAre ( @" @@ -786,7 +788,8 @@ public void DrawTests_Ruler () r = new (1, 1, 40, 15); top.SetNeedsDraw (); top.Draw (); - t.Draw (r, ViewDiagnosticFlags.Ruler, "Test", driver); + top.SetClipToScreen (); + t.Draw (driver, r, ViewDiagnosticFlags.Ruler, "Test"); DriverAssert.AssertDriverContentsAre ( @" @@ -818,7 +821,8 @@ public void DrawTests_Ruler () r = new (2, 2, 40, 15); top.SetNeedsDraw (); top.Draw (); - t.Draw (r, ViewDiagnosticFlags.Ruler, "Test", driver); + top.SetClipToScreen (); + t.Draw (driver, r, ViewDiagnosticFlags.Ruler, "Test"); DriverAssert.AssertDriverContentsWithFrameAre ( @" @@ -850,7 +854,8 @@ public void DrawTests_Ruler () r = new (5, 5, 40, 15); top.SetNeedsDraw (); top.Draw (); - t.Draw (r, ViewDiagnosticFlags.Ruler, "Test", driver); + top.SetClipToScreen (); + t.Draw (driver, r, ViewDiagnosticFlags.Ruler, "Test"); DriverAssert.AssertDriverContentsWithFrameAre ( @" diff --git a/Tests/UnitTestsParallelizable/Drivers/AnsiRequestSchedulerTests.cs b/Tests/UnitTestsParallelizable/Drivers/AnsiRequestSchedulerTests.cs index d643c51124..aafa8243f1 100644 --- a/Tests/UnitTestsParallelizable/Drivers/AnsiRequestSchedulerTests.cs +++ b/Tests/UnitTestsParallelizable/Drivers/AnsiRequestSchedulerTests.cs @@ -34,7 +34,7 @@ public void SendOrSchedule_SendsDeviceAttributeRequest_WhenNoOutstandingRequests _parserMock.Setup (p => p.ExpectResponse ("c", It.IsAny> (), null, false)).Verifiable (Times.Once); // Act - bool result = _scheduler.SendOrSchedule (request); + bool result = _scheduler.SendOrSchedule (null, request); // Assert Assert.Empty (_scheduler.QueuedRequests); // We sent it i.e. we did not queue it for later @@ -57,7 +57,7 @@ public void SendOrSchedule_QueuesRequest_WhenOutstandingRequestExists () _parserMock.Setup (p => p.IsExpecting ("c")).Returns (true).Verifiable (Times.Once); // Act - bool result = _scheduler.SendOrSchedule (request1); + bool result = _scheduler.SendOrSchedule (null, request1); // Assert Assert.Single (_scheduler.QueuedRequests); // Ensure only one request is in the queue @@ -80,7 +80,7 @@ public void RunSchedule_ThrottleNotExceeded_AllowSend () _parserMock.Setup (p => p.IsExpecting ("c")).Returns (false).Verifiable (Times.Exactly (2)); _parserMock.Setup (p => p.ExpectResponse ("c", It.IsAny> (), null, false)).Verifiable (Times.Exactly (2)); - _scheduler.SendOrSchedule (request); + _scheduler.SendOrSchedule (null, request); // Simulate time passing beyond throttle SetTime (101); // Exceed throttle limit @@ -88,7 +88,7 @@ public void RunSchedule_ThrottleNotExceeded_AllowSend () // Act // Send another request after the throttled time limit - bool result = _scheduler.SendOrSchedule (request); + bool result = _scheduler.SendOrSchedule (null, request); // Assert Assert.Empty (_scheduler.QueuedRequests); // Should send and clear the request @@ -111,7 +111,7 @@ public void RunSchedule_ThrottleExceeded_QueueRequest () _parserMock.Setup (p => p.IsExpecting ("c")).Returns (false).Verifiable (Times.Exactly (2)); _parserMock.Setup (p => p.ExpectResponse ("c", It.IsAny> (), null, false)).Verifiable (Times.Exactly (2)); - _scheduler.SendOrSchedule (request); + _scheduler.SendOrSchedule (null, request); // Simulate time passing SetTime (55); // Does not exceed throttle limit @@ -119,24 +119,24 @@ public void RunSchedule_ThrottleExceeded_QueueRequest () // Act // Send another request after the throttled time limit - bool result = _scheduler.SendOrSchedule (request); + bool result = _scheduler.SendOrSchedule (null, request); // Assert Assert.Single (_scheduler.QueuedRequests); // Should have been queued Assert.False (result); // Should have been queued // Throttle still not exceeded - Assert.False (_scheduler.RunSchedule ()); + Assert.False (_scheduler.RunSchedule (null)); SetTime (90); // Throttle still not exceeded - Assert.False (_scheduler.RunSchedule ()); + Assert.False (_scheduler.RunSchedule (null)); SetTime (105); // Throttle exceeded - so send the request - Assert.True (_scheduler.RunSchedule ()); + Assert.True (_scheduler.RunSchedule (null)); _parserMock.Verify (); } @@ -156,13 +156,13 @@ public void EvictStaleRequests_RemovesStaleRequest_AfterTimeout () _parserMock.Setup (p => p.IsExpecting ("c")).Returns (false).Verifiable (Times.Once); _parserMock.Setup (p => p.ExpectResponse ("c", It.IsAny> (), null, false)).Verifiable (Times.Exactly (2)); - Assert.True (_scheduler.SendOrSchedule (request1)); + Assert.True (_scheduler.SendOrSchedule (null, request1)); // Parser already has an ongoing request for "c" _parserMock.Setup (p => p.IsExpecting ("c")).Returns (true).Verifiable (Times.Exactly (2)); // Cannot send because there is already outstanding request - Assert.False (_scheduler.SendOrSchedule (request1)); + Assert.False (_scheduler.SendOrSchedule (null, request1)); Assert.Single (_scheduler.QueuedRequests); // Simulate request going stale @@ -178,7 +178,7 @@ public void EvictStaleRequests_RemovesStaleRequest_AfterTimeout () .Verifiable (); // When we send again the evicted one should be - bool evicted = _scheduler.RunSchedule (); + bool evicted = _scheduler.RunSchedule (null); Assert.True (evicted); // Stale request should be evicted Assert.Empty (_scheduler.QueuedRequests); @@ -191,7 +191,7 @@ public void EvictStaleRequests_RemovesStaleRequest_AfterTimeout () public void RunSchedule_DoesNothing_WhenQueueIsEmpty () { // Act - bool result = _scheduler.RunSchedule (); + bool result = _scheduler.RunSchedule (null); // Assert Assert.False (result); // No requests to process @@ -213,8 +213,8 @@ public void SendOrSchedule_ManagesIndependentTerminatorsCorrectly () _parserMock.Setup (p => p.ExpectResponse ("x", It.IsAny> (), null, false)).Verifiable (Times.Once); // Act - bool a = _scheduler.SendOrSchedule (request1); - bool b = _scheduler.SendOrSchedule (request2); + bool a = _scheduler.SendOrSchedule (null, request1); + bool b = _scheduler.SendOrSchedule (null, request2); // Assert Assert.False (a); diff --git a/Tests/UnitTestsParallelizable/Drivers/ToAnsiTests.cs b/Tests/UnitTestsParallelizable/Drivers/ToAnsiTests.cs new file mode 100644 index 0000000000..e17c28cdf7 --- /dev/null +++ b/Tests/UnitTestsParallelizable/Drivers/ToAnsiTests.cs @@ -0,0 +1,342 @@ +using System.Text; +using UnitTests; +using Xunit.Abstractions; + +namespace UnitTests_Parallelizable.DriverTests; + +/// +/// Tests for the ToAnsi functionality that generates ANSI escape sequences from buffer contents. +/// +public class ToAnsiTests : FakeDriverBase +{ + [Fact] + public void ToAnsi_Empty_Buffer () + { + IDriver driver = CreateFakeDriver (10, 5); + string ansi = driver.ToAnsi (); + + // Empty buffer should have newlines for each row + Assert.Contains ("\n", ansi); + // Should have 5 newlines (one per row) + Assert.Equal (5, ansi.Count (c => c == '\n')); + } + + [Fact] + public void ToAnsi_Simple_Text () + { + IDriver driver = CreateFakeDriver (10, 3); + driver.AddStr ("Hello"); + driver.Move (0, 1); + driver.AddStr ("World"); + + string ansi = driver.ToAnsi (); + + // Should contain the text + Assert.Contains ("Hello", ansi); + Assert.Contains ("World", ansi); + + // Should have proper structure with newlines + string[] lines = ansi.Split (['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); + Assert.Equal (3, lines.Length); + } + + [Fact] + public void ToAnsi_With_Colors () + { + IDriver driver = CreateFakeDriver (10, 2); + + // Set red foreground + driver.CurrentAttribute = new Attribute (Color.Red, Color.Black); + driver.AddStr ("Red"); + driver.Move (0, 1); + + // Set blue foreground + driver.CurrentAttribute = new Attribute (Color.Blue, Color.Black); + driver.AddStr ("Blue"); + + string ansi = driver.ToAnsi (); + + // Should contain ANSI color codes + Assert.Contains ("\u001b[31m", ansi); // Red foreground + Assert.Contains ("\u001b[34m", ansi); // Blue foreground + Assert.Contains ("Red", ansi); + Assert.Contains ("Blue", ansi); + } + + [Fact] + public void ToAnsi_With_Background_Colors () + { + IDriver driver = CreateFakeDriver (10, 2); + + // Set background color + driver.CurrentAttribute = new Attribute (Color.White, Color.Red); + driver.AddStr ("WhiteOnRed"); + + string ansi = driver.ToAnsi (); + + // Should contain ANSI background color code + Assert.Contains ("\u001b[41m", ansi); // Red background + Assert.Contains ("WhiteOnRed", ansi); + } + + [Fact] + public void ToAnsi_With_Text_Styles () + { + IDriver driver = CreateFakeDriver (10, 3); + + // Bold text + driver.CurrentAttribute = new Attribute (Color.White, Color.Black, TextStyle.Bold); + driver.AddStr ("Bold"); + driver.Move (0, 1); + + // Italic text + driver.CurrentAttribute = new Attribute (Color.White, Color.Black, TextStyle.Italic); + driver.AddStr ("Italic"); + driver.Move (0, 2); + + // Underline text + driver.CurrentAttribute = new Attribute (Color.White, Color.Black, TextStyle.Underline); + driver.AddStr ("Underline"); + + string ansi = driver.ToAnsi (); + + // Should contain ANSI style codes + Assert.Contains ("\u001b[1m", ansi); // Bold + Assert.Contains ("\u001b[3m", ansi); // Italic + Assert.Contains ("\u001b[4m", ansi); // Underline + } + + [Fact] + public void ToAnsi_With_Wide_Characters () + { + IDriver driver = CreateFakeDriver (10, 2); + + // Add a wide character (Chinese character) + driver.AddStr ("??"); + driver.Move (0, 1); + driver.AddStr ("??"); + + string ansi = driver.ToAnsi (); + + Assert.Contains ("??", ansi); + Assert.Contains ("??", ansi); + } + + [Fact] + public void ToAnsi_With_Unicode_Characters () + { + IDriver driver = CreateFakeDriver (10, 2); + + // Add various Unicode characters + driver.AddStr ("???"); // Greek letters + driver.Move (0, 1); + driver.AddStr ("???"); // Emoji + + string ansi = driver.ToAnsi (); + + Assert.Contains ("???", ansi); + Assert.Contains ("???", ansi); + } + + [Fact] + public void ToAnsi_Attribute_Changes_Within_Line () + { + IDriver driver = CreateFakeDriver (20, 1); + + driver.AddStr ("Normal"); + driver.CurrentAttribute = new Attribute (Color.Red, Color.Black); + driver.AddStr ("Red"); + driver.CurrentAttribute = new Attribute (Color.Blue, Color.Black); + driver.AddStr ("Blue"); + + string ansi = driver.ToAnsi (); + + // Should contain color changes within the line + Assert.Contains ("Normal", ansi); + Assert.Contains ("\u001b[31m", ansi); // Red + Assert.Contains ("\u001b[34m", ansi); // Blue + } + + [Fact] + public void ToAnsi_Large_Buffer () + { + // Test with a larger buffer to stress performance + IDriver driver = CreateFakeDriver (200, 50); + + // Fill with some content + for (int row = 0; row < 50; row++) + { + driver.Move (0, row); + driver.CurrentAttribute = new Attribute ((ColorName16)(row % 16), Color.Black); + driver.AddStr ($"Row {row:D2} content"); + } + + string ansi = driver.ToAnsi (); + + // Should contain all rows + Assert.Contains ("Row 00", ansi); + Assert.Contains ("Row 49", ansi); + + // Should have proper newlines (50 content lines + 50 newlines) + Assert.Equal (50, ansi.Count (c => c == '\n')); + } + + [Fact] + public void ToAnsi_RGB_Colors () + { + IDriver driver = CreateFakeDriver (10, 1); + + // Use RGB colors (when not forcing 16 colors) + Application.Force16Colors = false; + try + { + driver.CurrentAttribute = new Attribute (new Color (255, 0, 0), new Color (0, 255, 0)); + driver.AddStr ("RGB"); + + string ansi = driver.ToAnsi (); + + // Should contain RGB color codes + Assert.Contains ("\u001b[38;2;255;0;0m", ansi); // Red foreground RGB + Assert.Contains ("\u001b[48;2;0;255;0m", ansi); // Green background RGB + } + finally + { + Application.Force16Colors = true; // Reset + } + } + + [Fact] + public void ToAnsi_Force16Colors () + { + IDriver driver = CreateFakeDriver (10, 1); + + // Force 16 colors + Application.Force16Colors = true; + driver.CurrentAttribute = new Attribute (Color.Red, Color.Blue); + driver.AddStr ("16Color"); + + string ansi = driver.ToAnsi (); + + // Should contain 16-color codes, not RGB + Assert.Contains ("\u001b[31m", ansi); // Red foreground (16-color) + Assert.Contains ("\u001b[44m", ansi); // Blue background (16-color) + Assert.DoesNotContain ("\u001b[38;2;", ansi); // No RGB codes + } + + [Fact] + public void ToAnsi_Multiple_Attributes_Per_Line () + { + IDriver driver = CreateFakeDriver (50, 1); + + // Create a line with many attribute changes + string[] colors = { "Red", "Green", "Blue", "Yellow", "Magenta", "Cyan" }; + + foreach (string colorName in colors) + { + Color fg = colorName switch + { + "Red" => Color.Red, + "Green" => Color.Green, + "Blue" => Color.Blue, + "Yellow" => Color.Yellow, + "Magenta" => Color.Magenta, + "Cyan" => Color.Cyan, + _ => Color.White + }; + + driver.CurrentAttribute = new Attribute (fg, Color.Black); + driver.AddStr (colorName); + } + + string ansi = driver.ToAnsi (); + + // Should contain multiple color codes + Assert.Contains ("\u001b[31m", ansi); // Red + Assert.Contains ("\u001b[32m", ansi); // Green + Assert.Contains ("\u001b[34m", ansi); // Blue + Assert.Contains ("\u001b[33m", ansi); // Yellow + Assert.Contains ("\u001b[35m", ansi); // Magenta + Assert.Contains ("\u001b[36m", ansi); // Cyan + } + + [Fact] + public void ToAnsi_Special_Characters () + { + IDriver driver = CreateFakeDriver (20, 1); + + // Test backslash character + driver.AddStr ("Backslash:"); + driver.AddRune ('\\'); + + string ansi = driver.ToAnsi (); + + Assert.Contains ("Backslash:", ansi); + Assert.Contains ("\\", ansi); + } + + [Fact] + public void ToAnsi_Buffer_Boundary_Conditions () + { + // Test with minimum buffer size + IDriver driver = CreateFakeDriver (1, 1); + driver.AddStr ("X"); + + string ansi = driver.ToAnsi (); + + Assert.Contains ("X", ansi); + Assert.Contains ("\n", ansi); + + // Test with very wide buffer + driver = CreateFakeDriver (1000, 1); + driver.AddStr ("Wide"); + + ansi = driver.ToAnsi (); + + Assert.Contains ("Wide", ansi); + Assert.True (ansi.Length > 1000); // Should have many spaces + } + + [Fact] + public void ToAnsi_Empty_Lines () + { + IDriver driver = CreateFakeDriver (10, 3); + + // Only write to first and third lines + driver.AddStr ("First"); + driver.Move (0, 2); + driver.AddStr ("Third"); + + string ansi = driver.ToAnsi (); + + string[] lines = ansi.Split ('\n'); + Assert.Equal (4, lines.Length); // 3 content lines + 1 empty line at end + Assert.Contains ("First", lines[0]); + Assert.Contains ("Third", lines[2]); + } + + [Fact] + public void ToAnsi_Performance_Stress_Test () + { + // Create a large buffer and fill it completely + const int width = 200; + const int height = 100; + IDriver driver = CreateFakeDriver (width, height); + + // Fill every cell with different content and colors + for (int row = 0; row < height; row++) + { + for (int col = 0; col < width; col++) + { + driver.Move (col, row); + driver.CurrentAttribute = new Attribute ((ColorName16)((row + col) % 16), Color.Black); + driver.AddRune ((char)('A' + ((row + col) % 26))); + } + } + + // This should complete in reasonable time and not throw + string ansi = driver.ToAnsi (); + + Assert.NotNull (ansi); + Assert.True (ansi.Length > width * height); // Should contain all characters plus ANSI codes + } +} \ No newline at end of file diff --git a/Tests/UnitTestsParallelizable/TestSetup.cs b/Tests/UnitTestsParallelizable/TestSetup.cs index 2a6402c119..a8d37578ee 100644 --- a/Tests/UnitTestsParallelizable/TestSetup.cs +++ b/Tests/UnitTestsParallelizable/TestSetup.cs @@ -39,7 +39,7 @@ private void CheckDefaultState () // Check that all Application fields and properties are set to their default values // Public Properties - Assert.Null (Application.Top); + Assert.Null (Application.Current); Assert.Null (Application.Mouse.MouseGrabView); // Don't check Application.ForceDriver @@ -59,7 +59,7 @@ private void CheckDefaultState () Assert.Equal (Application.GetSupportedCultures (), Application.SupportedCultures); Assert.Equal (Application.GetAvailableCulturesFromEmbeddedResources (), Application.SupportedCultures); Assert.Null (Application.MainThreadId); - Assert.Empty (Application.TopLevels); + Assert.Empty (Application.SessionStack); Assert.Empty (Application.CachedViewsUnderMouse); // Mouse @@ -67,10 +67,10 @@ private void CheckDefaultState () //Assert.Null (Application._lastMousePosition); // Navigation - Assert.Null (Application.Navigation); + // Assert.Null (Application.Navigation); // Popover - Assert.Null (Application.Popover); + //Assert.Null (Application.Popover); // Events - Can't check //Assert.Null (Application.SessionBegun); diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs index 0c142f08b2..17ac27ab57 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs @@ -36,7 +36,7 @@ public void Draw_Horizontal_RightLeft_BottomTop (string text, int width, int hei tf.ConstrainToWidth = width; tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, width, height), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -65,7 +65,7 @@ public void Draw_Horizontal_RightLeft_TopBottom (string text, int width, int hei tf.ConstrainToWidth = width; tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, width, height), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -105,7 +105,7 @@ public void Draw_Vertical_Bottom_Horizontal_Right (string text, int width, int h tf.ConstrainToWidth = width; tf.ConstrainToHeight = height; - tf.Draw (new (Point.Empty, new (width, height)), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (Point.Empty, new (width, height)), normalColor: Attribute.Default, hotColor: Attribute.Default); Rectangle rect = DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); Assert.Equal (expectedY, rect.Y); } @@ -134,7 +134,7 @@ public void Draw_Vertical_BottomTop_LeftRight (string text, int width, int heigh tf.ConstrainToWidth = width; tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, width, height), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -163,7 +163,7 @@ public void Draw_Vertical_BottomTop_RightLeft (string text, int width, int heigh tf.ConstrainToWidth = width; tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, width, height), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -217,7 +217,7 @@ public void Draw_Vertical_TopBottom_LeftRight (string text, int width, int heigh tf.ConstrainToWidth = width; tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, 20, 20), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, 20, 20), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -267,7 +267,7 @@ public void Draw_Vertical_TopBottom_LeftRight_Middle (string text, int height, s tf.ConstrainToWidth = width; tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, 5, height), normalColor: Attribute.Default, hotColor: Attribute.Default); Rectangle rect = DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); Assert.Equal (expectedY, rect.Y); @@ -328,7 +328,7 @@ public void Draw_Vertical_TopBottom_LeftRight_Top (string text, int height, stri tf.ConstrainToWidth = 5; tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, 5, height), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -346,11 +346,7 @@ public void FillRemaining_True_False () }; var tf = new TextFormatter { ConstrainToSize = new (14, 3), Text = "Test\nTest long\nTest long long\n", MultiLine = true }; - tf.Draw ( - new (1, 1, 19, 3), - attrs [1], - attrs [2], - driver: driver); + tf.Draw (driver: driver, screen: new (1, 1, 19, 3), normalColor: attrs [1], hotColor: attrs [2]); Assert.False (tf.FillRemaining); @@ -375,11 +371,7 @@ Test long tf.FillRemaining = true; - tf.Draw ( - new (1, 1, 19, 3), - attrs [1], - attrs [2], - driver: driver); + tf.Draw (driver: driver, screen: new (1, 1, 19, 3), normalColor: attrs [1], hotColor: attrs [2]); DriverAssert.AssertDriverAttributesAre ( @" @@ -422,7 +414,7 @@ public void Justify_Horizontal (string text, int width, int height, string expec MultiLine = true }; - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, width, height), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -448,7 +440,7 @@ public void UICatalog_AboutBox_Text () driver!.SetScreenSize (tfSize.Width, tfSize.Height); driver.FillRect (driver.Screen, (Rune)'*'); - tf.Draw (driver.Screen, Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: driver.Screen, normalColor: Attribute.Default, hotColor: Attribute.Default); var expectedText = """ UI Catalog: A comprehensive sample library and test app for @@ -575,7 +567,7 @@ string expectedDraw Size size = tf.FormatAndGetSize (); Assert.Equal (new (expectedWidth, expectedHeight), size); - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, width, height), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output, driver); } @@ -660,7 +652,7 @@ string expectedDraw Size size = tf.FormatAndGetSize (); Assert.Equal (new (expectedWidth, expectedHeight), size); - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, width, height), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output, driver); } diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterJustificationTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterJustificationTests.cs index 4bae12df38..8990e1698b 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterJustificationTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterJustificationTests.cs @@ -3423,7 +3423,7 @@ public void Draw_Text_Justification (string text, Alignment horizontalTextAlignm }; driver.FillRect (new (0, 0, 7, 7), (Rune)'*'); - tf.Draw (new (0, 0, 7, 7), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver: driver, screen: new (0, 0, 7, 7), normalColor: Attribute.Default, hotColor: Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } } diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs index 88e95fd7f2..8fc71180c0 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs @@ -1142,6 +1142,7 @@ IEnumerable resultLines [Fact] public void NeedsFormat_Sets () { + IDriver driver = CreateFakeDriver (); var testText = "test"; var testBounds = new Rectangle (0, 0, 100, 1); var tf = new TextFormatter (); @@ -1151,7 +1152,7 @@ public void NeedsFormat_Sets () Assert.NotEmpty (tf.GetLines ()); Assert.False (tf.NeedsFormat); // get_Lines causes a Format Assert.Equal (testText, tf.Text); - tf.Draw (testBounds, new (), new ()); + tf.Draw (driver: driver, screen: testBounds, normalColor: new (), hotColor: new ()); Assert.False (tf.NeedsFormat); tf.ConstrainToSize = new (1, 1); @@ -2987,7 +2988,7 @@ public void Draw_Horizontal_Centered (string text, int width, string expectedTex ConstrainToHeight = 1 }; - tf.Draw (new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default, default, driver); + tf.Draw (driver, new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default, default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -3016,7 +3017,7 @@ public void Draw_Horizontal_Justified (string text, int width, string expectedTe ConstrainToHeight = 1 }; - tf.Draw (new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default, default, driver); + tf.Draw (driver, new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default, default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -3042,7 +3043,7 @@ public void Draw_Horizontal_Left (string text, int width, string expectedText) ConstrainToHeight = 1 }; - tf.Draw (new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default, default, driver); + tf.Draw (driver, new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default, default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -3068,7 +3069,7 @@ public void Draw_Horizontal_Right (string text, int width, string expectedText) ConstrainToHeight = 1 }; - tf.Draw (new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default, default, driver); + tf.Draw (driver, new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default, default); DriverAssert.AssertDriverContentsWithFrameAre (expectedText, output, driver); } @@ -3100,12 +3101,11 @@ public void Draw_With_Combining_Runes (int width, int height, TextDirection text tf.ConstrainToSize = new (width, height); tf.Draw ( + driver, new (0, 0, width, height), new (ColorName16.White, ColorName16.Black), new (ColorName16.Blue, ColorName16.Black), - default (Rectangle), - driver - ); + default (Rectangle)); DriverAssert.AssertDriverContentsWithFrameAre (expected, output, driver); driver.End (); @@ -3139,12 +3139,11 @@ string expected Assert.True (tf.WordWrap); tf.Draw ( + driver, new (0, 0, width, height), new (ColorName16.White, ColorName16.Black), new (ColorName16.Blue, ColorName16.Black), - default (Rectangle), - driver - ); + default (Rectangle)); DriverAssert.AssertDriverContentsWithFrameAre (expected, output, driver); driver.End (); @@ -3178,12 +3177,11 @@ string expected Assert.False (tf.PreserveTrailingSpaces); tf.Draw ( + driver, new (0, 0, width, height), new (ColorName16.White, ColorName16.Black), new (ColorName16.Blue, ColorName16.Black), - default (Rectangle), - driver - ); + default (Rectangle)); DriverAssert.AssertDriverContentsWithFrameAre (expected, output, driver); driver.End (); @@ -3217,12 +3215,11 @@ string expected Assert.False (tf.PreserveTrailingSpaces); tf.Draw ( + driver, new (0, 0, width, height), new (ColorName16.White, ColorName16.Black), new (ColorName16.Blue, ColorName16.Black), - default (Rectangle), - driver - ); + default (Rectangle)); DriverAssert.AssertDriverContentsWithFrameAre (expected, output, driver); driver.End (); diff --git a/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj b/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj index cf0f77f7c3..35233cc037 100644 --- a/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj +++ b/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj @@ -69,4 +69,7 @@ + + + \ No newline at end of file diff --git a/Tests/UnitTestsParallelizable/View/ArrangementTests.cs b/Tests/UnitTestsParallelizable/View/ArrangementTests.cs index 842a7070b8..cc156b7967 100644 --- a/Tests/UnitTestsParallelizable/View/ArrangementTests.cs +++ b/Tests/UnitTestsParallelizable/View/ArrangementTests.cs @@ -28,11 +28,11 @@ public void ViewArrangement_Flags_HaveCorrectValues () [Fact] public void ViewArrangement_Resizable_IsCombinationOfAllResizableFlags () { - ViewArrangement expected = ViewArrangement.LeftResizable - | ViewArrangement.RightResizable - | ViewArrangement.TopResizable + ViewArrangement expected = ViewArrangement.LeftResizable + | ViewArrangement.RightResizable + | ViewArrangement.TopResizable | ViewArrangement.BottomResizable; - + Assert.Equal (ViewArrangement.Resizable, expected); } @@ -40,7 +40,7 @@ public void ViewArrangement_Resizable_IsCombinationOfAllResizableFlags () public void ViewArrangement_CanCombineFlags () { ViewArrangement arrangement = ViewArrangement.Movable | ViewArrangement.LeftResizable; - + Assert.True (arrangement.HasFlag (ViewArrangement.Movable)); Assert.True (arrangement.HasFlag (ViewArrangement.LeftResizable)); Assert.False (arrangement.HasFlag (ViewArrangement.RightResizable)); @@ -67,11 +67,11 @@ public void View_Arrangement_CanBeSet () [Fact] public void View_Arrangement_CanSetMultipleFlags () { - var view = new View - { - Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable + var view = new View + { + Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.Movable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.LeftResizable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.RightResizable)); @@ -83,7 +83,7 @@ public void View_Arrangement_CanSetMultipleFlags () public void View_Arrangement_Overlapped_CanBeSetIndependently () { var view = new View { Arrangement = ViewArrangement.Overlapped }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.Overlapped)); Assert.False (view.Arrangement.HasFlag (ViewArrangement.Movable)); Assert.False (view.Arrangement.HasFlag (ViewArrangement.Resizable)); @@ -92,11 +92,11 @@ public void View_Arrangement_Overlapped_CanBeSetIndependently () [Fact] public void View_Arrangement_CanCombineOverlappedWithOtherFlags () { - var view = new View - { - Arrangement = ViewArrangement.Overlapped | ViewArrangement.Movable + var view = new View + { + Arrangement = ViewArrangement.Overlapped | ViewArrangement.Movable }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.Overlapped)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.Movable)); } @@ -108,12 +108,12 @@ public void View_Arrangement_CanCombineOverlappedWithOtherFlags () [Fact] public void TopResizable_WithoutMovable_IsAllowed () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.TopResizable, BorderStyle = LineStyle.Single }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); Assert.False (view.Arrangement.HasFlag (ViewArrangement.Movable)); } @@ -123,16 +123,16 @@ public void Movable_WithTopResizable_MovableWins () { // According to docs and Border.Arrangment.cs line 569: // TopResizable is only checked if NOT Movable - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.Movable | ViewArrangement.TopResizable, BorderStyle = LineStyle.Single }; - + // Both flags can be set on the property Assert.True (view.Arrangement.HasFlag (ViewArrangement.Movable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); - + // But the behavior in Border.DetermineArrangeModeFromClick // will prioritize Movable over TopResizable } @@ -140,12 +140,12 @@ public void Movable_WithTopResizable_MovableWins () [Fact] public void Resizable_WithMovable_IncludesTopResizable () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.Resizable | ViewArrangement.Movable, BorderStyle = LineStyle.Single }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.Movable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.LeftResizable)); @@ -160,12 +160,12 @@ public void Resizable_WithMovable_IncludesTopResizable () [Fact] public void Border_WithNoArrangement_HasNoArrangementOptions () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.Fixed, BorderStyle = LineStyle.Single }; - + Assert.NotNull (view.Border); Assert.Equal (ViewArrangement.Fixed, view.Arrangement); } @@ -174,8 +174,8 @@ public void Border_WithNoArrangement_HasNoArrangementOptions () public void Border_WithMovableArrangement_CanEnterArrangeMode () { var superView = new View (); - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.Movable, BorderStyle = LineStyle.Single, X = 0, @@ -184,7 +184,7 @@ public void Border_WithMovableArrangement_CanEnterArrangeMode () Height = 10 }; superView.Add (view); - + Assert.NotNull (view.Border); Assert.True (view.Arrangement.HasFlag (ViewArrangement.Movable)); } @@ -192,12 +192,12 @@ public void Border_WithMovableArrangement_CanEnterArrangeMode () [Fact] public void Border_WithResizableArrangement_HasResizableOptions () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.Resizable, BorderStyle = LineStyle.Single }; - + Assert.NotNull (view.Border); Assert.True (view.Arrangement.HasFlag (ViewArrangement.LeftResizable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.RightResizable)); @@ -212,15 +212,15 @@ public void Border_WithResizableArrangement_HasResizableOptions () [InlineData (ViewArrangement.BottomResizable)] public void Border_WithSingleResizableDirection_OnlyHasThatOption (ViewArrangement arrangement) { - var view = new View - { + var view = new View + { Arrangement = arrangement, BorderStyle = LineStyle.Single }; - + Assert.NotNull (view.Border); Assert.True (view.Arrangement.HasFlag (arrangement)); - + // Verify other directions are not set if (arrangement != ViewArrangement.LeftResizable) Assert.False (view.Arrangement.HasFlag (ViewArrangement.LeftResizable)); @@ -239,12 +239,12 @@ public void Border_WithSingleResizableDirection_OnlyHasThatOption (ViewArrangeme [Fact] public void Border_BottomRightResizable_CombinesBothFlags () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.BottomResizable | ViewArrangement.RightResizable, BorderStyle = LineStyle.Single }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.BottomResizable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.RightResizable)); Assert.False (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); @@ -254,12 +254,12 @@ public void Border_BottomRightResizable_CombinesBothFlags () [Fact] public void Border_BottomLeftResizable_CombinesBothFlags () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.BottomResizable | ViewArrangement.LeftResizable, BorderStyle = LineStyle.Single }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.BottomResizable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.LeftResizable)); Assert.False (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); @@ -269,12 +269,12 @@ public void Border_BottomLeftResizable_CombinesBothFlags () [Fact] public void Border_TopRightResizable_CombinesBothFlags () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.TopResizable | ViewArrangement.RightResizable, BorderStyle = LineStyle.Single }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.RightResizable)); Assert.False (view.Arrangement.HasFlag (ViewArrangement.BottomResizable)); @@ -284,12 +284,12 @@ public void Border_TopRightResizable_CombinesBothFlags () [Fact] public void Border_TopLeftResizable_CombinesBothFlags () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.TopResizable | ViewArrangement.LeftResizable, BorderStyle = LineStyle.Single }; - + Assert.True (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.LeftResizable)); Assert.False (view.Arrangement.HasFlag (ViewArrangement.BottomResizable)); @@ -326,8 +326,8 @@ public void MoveSubViewToEnd_ViewArrangement (ViewArrangement arrangement) [Fact] public void Overlapped_AllowsSubViewsToOverlap () { - var superView = new View - { + var superView = new View + { Arrangement = ViewArrangement.Overlapped, Width = 20, Height = 20 @@ -351,7 +351,7 @@ public void Overlapped_AllowsSubViewsToOverlap () public void LeftResizable_CanBeUsedForHorizontalSplitter () { var container = new View { Width = 80, Height = 25 }; - + var leftPane = new View { X = 0, @@ -359,7 +359,7 @@ public void LeftResizable_CanBeUsedForHorizontalSplitter () Width = 40, Height = Dim.Fill () }; - + var rightPane = new View { X = 40, @@ -369,9 +369,9 @@ public void LeftResizable_CanBeUsedForHorizontalSplitter () Arrangement = ViewArrangement.LeftResizable, BorderStyle = LineStyle.Single }; - + container.Add (leftPane, rightPane); - + Assert.True (rightPane.Arrangement.HasFlag (ViewArrangement.LeftResizable)); Assert.NotNull (rightPane.Border); } @@ -380,7 +380,7 @@ public void LeftResizable_CanBeUsedForHorizontalSplitter () public void TopResizable_CanBeUsedForVerticalSplitter () { var container = new View { Width = 80, Height = 25 }; - + var topPane = new View { X = 0, @@ -388,7 +388,7 @@ public void TopResizable_CanBeUsedForVerticalSplitter () Width = Dim.Fill (), Height = 10 }; - + var bottomPane = new View { X = 0, @@ -398,9 +398,9 @@ public void TopResizable_CanBeUsedForVerticalSplitter () Arrangement = ViewArrangement.TopResizable, BorderStyle = LineStyle.Single }; - + container.Add (topPane, bottomPane); - + Assert.True (bottomPane.Arrangement.HasFlag (ViewArrangement.TopResizable)); Assert.NotNull (bottomPane.Border); } @@ -412,11 +412,11 @@ public void TopResizable_CanBeUsedForVerticalSplitter () [Fact] public void View_WithoutBorderStyle_CanHaveArrangement () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.Movable }; - + // Arrangement can be set even without a border style // Border object still exists but has no visible style Assert.Equal (ViewArrangement.Movable, view.Arrangement); @@ -427,11 +427,11 @@ public void View_WithoutBorderStyle_CanHaveArrangement () [Fact] public void View_WithNoBorderStyle_ResizableCanBeSet () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.Resizable }; - + // Arrangement is set but has limited effect without a visible border style Assert.Equal (ViewArrangement.Resizable, view.Arrangement); Assert.NotNull (view.Border); @@ -448,8 +448,8 @@ public void DetermineArrangeModeFromClick_TopResizableIgnoredWhenMovable () // This test verifies the documented behavior that TopResizable is ignored // when Movable is also set (line 569 in Border.Arrangment.cs) var superView = new View { Width = 80, Height = 25 }; - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.TopResizable | ViewArrangement.Movable, BorderStyle = LineStyle.Single, X = 10, @@ -458,11 +458,11 @@ public void DetermineArrangeModeFromClick_TopResizableIgnoredWhenMovable () Height = 10 }; superView.Add (view); - + // The view has both flags set Assert.True (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.Movable)); - + // But Movable takes precedence in Border.DetermineArrangeModeFromClick // This is verified by the code at line 569 checking !Parent!.Arrangement.HasFlag(ViewArrangement.Movable) } @@ -471,8 +471,8 @@ public void DetermineArrangeModeFromClick_TopResizableIgnoredWhenMovable () public void DetermineArrangeModeFromClick_TopResizableWorksWithoutMovable () { var superView = new View { Width = 80, Height = 25 }; - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.TopResizable, BorderStyle = LineStyle.Single, X = 10, @@ -481,7 +481,7 @@ public void DetermineArrangeModeFromClick_TopResizableWorksWithoutMovable () Height = 10 }; superView.Add (view); - + // Only TopResizable is set Assert.True (view.Arrangement.HasFlag (ViewArrangement.TopResizable)); Assert.False (view.Arrangement.HasFlag (ViewArrangement.Movable)); @@ -491,20 +491,20 @@ public void DetermineArrangeModeFromClick_TopResizableWorksWithoutMovable () public void DetermineArrangeModeFromClick_AllCornerCombinationsSupported () { var superView = new View { Width = 80, Height = 25 }; - + // Test that all 4 corner combinations are recognized - var cornerCombinations = new[] + var cornerCombinations = new [] { ViewArrangement.BottomResizable | ViewArrangement.RightResizable, ViewArrangement.BottomResizable | ViewArrangement.LeftResizable, ViewArrangement.TopResizable | ViewArrangement.RightResizable, ViewArrangement.TopResizable | ViewArrangement.LeftResizable }; - + foreach (var arrangement in cornerCombinations) { - var view = new View - { + var view = new View + { Arrangement = arrangement, BorderStyle = LineStyle.Single, X = 10, @@ -513,10 +513,10 @@ public void DetermineArrangeModeFromClick_AllCornerCombinationsSupported () Height = 10 }; superView.Add (view); - + // Verify the flags are set correctly Assert.True (view.Arrangement == arrangement); - + superView.Remove (view); } } @@ -530,10 +530,10 @@ public void View_Arrangement_CanBeChangedAfterCreation () { var view = new View { Arrangement = ViewArrangement.Fixed }; Assert.Equal (ViewArrangement.Fixed, view.Arrangement); - + view.Arrangement = ViewArrangement.Movable; Assert.Equal (ViewArrangement.Movable, view.Arrangement); - + view.Arrangement = ViewArrangement.Resizable; Assert.Equal (ViewArrangement.Resizable, view.Arrangement); } @@ -542,7 +542,7 @@ public void View_Arrangement_CanBeChangedAfterCreation () public void View_Arrangement_CanAddFlags () { var view = new View { Arrangement = ViewArrangement.Movable }; - + view.Arrangement |= ViewArrangement.LeftResizable; Assert.True (view.Arrangement.HasFlag (ViewArrangement.Movable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.LeftResizable)); @@ -551,11 +551,11 @@ public void View_Arrangement_CanAddFlags () [Fact] public void View_Arrangement_CanRemoveFlags () { - var view = new View - { - Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable + var view = new View + { + Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable }; - + view.Arrangement &= ~ViewArrangement.Movable; Assert.False (view.Arrangement.HasFlag (ViewArrangement.Movable)); Assert.True (view.Arrangement.HasFlag (ViewArrangement.Resizable)); @@ -568,15 +568,15 @@ public void View_Arrangement_CanRemoveFlags () [Fact] public void SuperView_CanHaveMultipleArrangeableSubViews () { - var superView = new View - { + var superView = new View + { Arrangement = ViewArrangement.Overlapped, Width = 80, Height = 25 }; - - var movableView = new View - { + + var movableView = new View + { Arrangement = ViewArrangement.Movable, BorderStyle = LineStyle.Single, X = 0, @@ -584,9 +584,9 @@ public void SuperView_CanHaveMultipleArrangeableSubViews () Width = 20, Height = 10 }; - - var resizableView = new View - { + + var resizableView = new View + { Arrangement = ViewArrangement.Resizable, BorderStyle = LineStyle.Single, X = 25, @@ -594,9 +594,9 @@ public void SuperView_CanHaveMultipleArrangeableSubViews () Width = 20, Height = 10 }; - - var fixedView = new View - { + + var fixedView = new View + { Arrangement = ViewArrangement.Fixed, BorderStyle = LineStyle.Single, X = 50, @@ -604,9 +604,9 @@ public void SuperView_CanHaveMultipleArrangeableSubViews () Width = 20, Height = 10 }; - + superView.Add (movableView, resizableView, fixedView); - + Assert.Equal (3, superView.SubViews.Count); Assert.Equal (ViewArrangement.Movable, movableView.Arrangement); Assert.Equal (ViewArrangement.Resizable, resizableView.Arrangement); @@ -618,9 +618,9 @@ public void SubView_ArrangementIndependentOfSuperView () { var superView = new View { Arrangement = ViewArrangement.Fixed }; var subView = new View { Arrangement = ViewArrangement.Movable }; - + superView.Add (subView); - + // SubView arrangement is independent of SuperView arrangement Assert.Equal (ViewArrangement.Fixed, superView.Arrangement); Assert.Equal (ViewArrangement.Movable, subView.Arrangement); @@ -633,30 +633,30 @@ public void SubView_ArrangementIndependentOfSuperView () [Fact] public void Border_WithDefaultThickness_SupportsArrangement () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.Movable, BorderStyle = LineStyle.Single }; - + Assert.NotNull (view.Border); // Default thickness should be (1,1,1,1) for Single line style - Assert.True (view.Border.Thickness.Left > 0 || view.Border.Thickness.Right > 0 + Assert.True (view.Border.Thickness.Left > 0 || view.Border.Thickness.Right > 0 || view.Border.Thickness.Top > 0 || view.Border.Thickness.Bottom > 0); } [Fact] public void Border_WithCustomThickness_SupportsArrangement () { - var view = new View - { + var view = new View + { Arrangement = ViewArrangement.LeftResizable, BorderStyle = LineStyle.Single }; - + // Set custom thickness - only left border view.Border!.Thickness = new Thickness (2, 0, 0, 0); - + Assert.Equal (2, view.Border.Thickness.Left); Assert.Equal (0, view.Border.Thickness.Top); Assert.Equal (0, view.Border.Thickness.Right); @@ -696,7 +696,7 @@ public void View_Navigation_RespectsOverlappedFlag () // View.Navigation.cs checks Arrangement.HasFlag(ViewArrangement.Overlapped) var overlappedView = new View { Arrangement = ViewArrangement.Overlapped }; var tiledView = new View { Arrangement = ViewArrangement.Fixed }; - + Assert.True (overlappedView.Arrangement.HasFlag (ViewArrangement.Overlapped)); Assert.False (tiledView.Arrangement.HasFlag (ViewArrangement.Overlapped)); } @@ -708,9 +708,9 @@ public void View_Hierarchy_RespectsOverlappedFlag () var parent = new View { Arrangement = ViewArrangement.Overlapped }; var child1 = new View { X = 0, Y = 0, Width = 10, Height = 10 }; var child2 = new View { X = 5, Y = 5, Width = 10, Height = 10 }; - + parent.Add (child1, child2); - + Assert.True (parent.Arrangement.HasFlag (ViewArrangement.Overlapped)); Assert.Equal (2, parent.SubViews.Count); } @@ -719,209 +719,7 @@ public void View_Hierarchy_RespectsOverlappedFlag () #region Mouse Interaction Tests - [Fact] - public void MouseGrabHandler_WorksWithMovableView_UsingNewMouseEvent () - { - // This test proves that MouseGrabHandler works correctly with concurrent unit tests - // using NewMouseEvent directly on views, without requiring Application.Init - - var superView = new View - { - Width = 80, - Height = 25 - }; - - var movableView = new View - { - Arrangement = ViewArrangement.Movable, - BorderStyle = LineStyle.Single, - X = 10, - Y = 10, - Width = 20, - Height = 10 - }; - - superView.Add (movableView); - - // Verify initial state - Assert.NotNull (movableView.Border); - Assert.Null (Application.Mouse.MouseGrabView); - - // Simulate mouse press on the border to start dragging - var pressEvent = new MouseEventArgs - { - Position = new (1, 0), // Top border area - Flags = MouseFlags.Button1Pressed - }; - - bool? result = movableView.Border.NewMouseEvent (pressEvent); - - // The border should have grabbed the mouse - Assert.True (result); - Assert.Equal (movableView.Border, Application.Mouse.MouseGrabView); - - // Simulate mouse drag - var dragEvent = new MouseEventArgs - { - Position = new (5, 2), - Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition - }; - - result = movableView.Border.NewMouseEvent (dragEvent); - Assert.True (result); - - // Mouse should still be grabbed - Assert.Equal (movableView.Border, Application.Mouse.MouseGrabView); - - // Simulate mouse release to end dragging - var releaseEvent = new MouseEventArgs - { - Position = new (5, 2), - Flags = MouseFlags.Button1Released - }; - - result = movableView.Border.NewMouseEvent (releaseEvent); - Assert.True (result); - - // Mouse should be released - Assert.Null (Application.Mouse.MouseGrabView); - } - - [Fact] - public void MouseGrabHandler_WorksWithResizableView_UsingNewMouseEvent () - { - // This test proves MouseGrabHandler works for resizing operations - - var superView = new View - { - Width = 80, - Height = 25 - }; - - var resizableView = new View - { - Arrangement = ViewArrangement.RightResizable, - BorderStyle = LineStyle.Single, - X = 10, - Y = 10, - Width = 20, - Height = 10 - }; - - superView.Add (resizableView); - - // Verify initial state - Assert.NotNull (resizableView.Border); - Assert.Null (Application.Mouse.MouseGrabView); - - // Calculate position on right border (border is at right edge) - // Border.Frame.X is relative to parent, so we use coordinates relative to the border - var pressEvent = new MouseEventArgs - { - Position = new (resizableView.Border.Frame.Width - 1, 5), // Right border area - Flags = MouseFlags.Button1Pressed - }; - - bool? result = resizableView.Border.NewMouseEvent (pressEvent); - - // The border should have grabbed the mouse for resizing - Assert.True (result); - Assert.Equal (resizableView.Border, Application.Mouse.MouseGrabView); - - // Simulate dragging to resize - var dragEvent = new MouseEventArgs - { - Position = new (resizableView.Border.Frame.Width + 3, 5), - Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition - }; - - result = resizableView.Border.NewMouseEvent (dragEvent); - Assert.True (result); - Assert.Equal (resizableView.Border, Application.Mouse.MouseGrabView); - - // Simulate mouse release - var releaseEvent = new MouseEventArgs - { - Position = new (resizableView.Border.Frame.Width + 3, 5), - Flags = MouseFlags.Button1Released - }; - - result = resizableView.Border.NewMouseEvent (releaseEvent); - Assert.True (result); - - // Mouse should be released - Assert.Null (Application.Mouse.MouseGrabView); - } - - [Fact] - public void MouseGrabHandler_ReleasesOnMultipleViews () - { - // This test verifies MouseGrabHandler properly releases when switching between views - - var superView = new View { Width = 80, Height = 25 }; - - var view1 = new View - { - Arrangement = ViewArrangement.Movable, - BorderStyle = LineStyle.Single, - X = 10, - Y = 10, - Width = 15, - Height = 8 - }; - - var view2 = new View - { - Arrangement = ViewArrangement.Movable, - BorderStyle = LineStyle.Single, - X = 30, - Y = 10, - Width = 15, - Height = 8 - }; - - superView.Add (view1, view2); - - // Grab mouse on first view - var pressEvent1 = new MouseEventArgs - { - Position = new (1, 0), - Flags = MouseFlags.Button1Pressed - }; - - view1.Border!.NewMouseEvent (pressEvent1); - Assert.Equal (view1.Border, Application.Mouse.MouseGrabView); - - // Release on first view - var releaseEvent1 = new MouseEventArgs - { - Position = new (1, 0), - Flags = MouseFlags.Button1Released - }; - - view1.Border.NewMouseEvent (releaseEvent1); - Assert.Null (Application.Mouse.MouseGrabView); - - // Grab mouse on second view - var pressEvent2 = new MouseEventArgs - { - Position = new (1, 0), - Flags = MouseFlags.Button1Pressed - }; - - view2.Border!.NewMouseEvent (pressEvent2); - Assert.Equal (view2.Border, Application.Mouse.MouseGrabView); - - // Release on second view - var releaseEvent2 = new MouseEventArgs - { - Position = new (1, 0), - Flags = MouseFlags.Button1Released - }; - - view2.Border.NewMouseEvent (releaseEvent2); - Assert.Null (Application.Mouse.MouseGrabView); - } + // Not parallelizable due to Application.Mouse dependency in MouseGrabHandler #endregion @@ -954,13 +752,13 @@ public void AllArrangementTests_AreParallelizable () // This test verifies that all the arrangement tests in this file // can run without Application.Init, making them parallelizable. // If this test passes, it confirms no Application dependencies leaked in. - - var view = new View - { + + var view = new View + { Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable, BorderStyle = LineStyle.Single }; - + Assert.NotNull (view); Assert.NotNull (view.Border); Assert.True (view.Arrangement.HasFlag (ViewArrangement.Movable)); @@ -974,9 +772,9 @@ public void AllArrangementTests_AreParallelizable () [Fact] public void ViewArrangement_CanCombineAllResizableDirections () { - ViewArrangement arrangement = ViewArrangement.TopResizable - | ViewArrangement.BottomResizable - | ViewArrangement.LeftResizable + ViewArrangement arrangement = ViewArrangement.TopResizable + | ViewArrangement.BottomResizable + | ViewArrangement.LeftResizable | ViewArrangement.RightResizable; Assert.True (arrangement.HasFlag (ViewArrangement.TopResizable)); @@ -1090,7 +888,7 @@ public void View_ChangingToNoBorderStyle_PreservesArrangement () public void View_MultipleSubviewsWithDifferentArrangements_EachIndependent () { var container = new View (); - + var fixedView = new View { Id = "fixed", Arrangement = ViewArrangement.Fixed }; var movableView = new View { Id = "movable", Arrangement = ViewArrangement.Movable }; var resizableView = new View { Id = "resizable", Arrangement = ViewArrangement.Resizable }; @@ -1112,7 +910,7 @@ public void View_MultipleSubviewsWithDifferentArrangements_EachIndependent () public void Overlapped_ViewCanBeMovedToFront () { var container = new View { Arrangement = ViewArrangement.Overlapped }; - + var view1 = new View { Id = "view1" }; var view2 = new View { Id = "view2" }; var view3 = new View { Id = "view3" }; @@ -1133,7 +931,7 @@ public void Overlapped_ViewCanBeMovedToFront () public void Overlapped_ViewCanBeMovedToBack () { var container = new View { Arrangement = ViewArrangement.Overlapped }; - + var view1 = new View { Id = "view1" }; var view2 = new View { Id = "view2" }; var view3 = new View { Id = "view3" }; @@ -1142,12 +940,12 @@ public void Overlapped_ViewCanBeMovedToBack () // Initial order: [view1, view2, view3] Assert.Equal ([view1, view2, view3], container.SubViews.ToArray ()); - + // Move view3 to end (top of Z-order) container.MoveSubViewToEnd (view3); Assert.Equal (view3, container.SubViews.ToArray () [^1]); Assert.Equal ([view1, view2, view3], container.SubViews.ToArray ()); - + // Now move view1 to end (making it on top, pushing view3 down) container.MoveSubViewToEnd (view1); Assert.Equal ([view2, view3, view1], container.SubViews.ToArray ()); @@ -1157,7 +955,7 @@ public void Overlapped_ViewCanBeMovedToBack () public void Fixed_ViewAddOrderMattersForLayout () { var container = new View { Arrangement = ViewArrangement.Fixed }; - + var view1 = new View { Id = "view1", X = 0, Y = 0, Width = 10, Height = 5 }; var view2 = new View { Id = "view2", X = 5, Y = 2, Width = 10, Height = 5 }; diff --git a/Tests/UnitTestsParallelizable/View/Draw/NeedsDrawTests.cs b/Tests/UnitTestsParallelizable/View/Draw/NeedsDrawTests.cs index cf3b7a0c8b..b8b3e7d430 100644 --- a/Tests/UnitTestsParallelizable/View/Draw/NeedsDrawTests.cs +++ b/Tests/UnitTestsParallelizable/View/Draw/NeedsDrawTests.cs @@ -1,8 +1,10 @@ #nullable enable +using UnitTests; + namespace UnitTests_Parallelizable.ViewTests; [Trait ("Category", "Output")] -public class NeedsDrawTests +public class NeedsDrawTests : FakeDriverBase { [Fact] public void NeedsDraw_False_If_Width_Height_Zero () @@ -18,7 +20,7 @@ public void NeedsDraw_False_If_Width_Height_Zero () [Fact] public void NeedsDraw_True_Initially_If_Width_Height_Not_Zero () { - View superView = new () { Width = 1, Height = 1 }; + View superView = new () { Driver = CreateFakeDriver (), Width = 1, Height = 1 }; View view1 = new () { Width = 1, Height = 1 }; View view2 = new () { Width = 1, Height = 1 }; @@ -54,7 +56,7 @@ public void NeedsDraw_True_After_Constructor () var view = new View { Width = 2, Height = 2 }; Assert.True (view.NeedsDraw); - view = new() { Width = 2, Height = 2, BorderStyle = LineStyle.Single }; + view = new () { Width = 2, Height = 2, BorderStyle = LineStyle.Single }; Assert.True (view.NeedsDraw); } @@ -90,7 +92,7 @@ public void NeedsDraw_True_After_EndInit_Where_Call_Layout () view.EndInit (); Assert.True (view.NeedsDraw); - view = new() { Width = 2, Height = 2, BorderStyle = LineStyle.Single }; + view = new () { Width = 2, Height = 2, BorderStyle = LineStyle.Single }; view.BeginInit (); view.NeedsDraw = false; view.EndInit (); @@ -100,7 +102,7 @@ public void NeedsDraw_True_After_EndInit_Where_Call_Layout () [Fact] public void NeedsDraw_After_SetLayoutNeeded_And_Layout () { - var view = new View { Width = 2, Height = 2 }; + var view = new View { Driver = CreateFakeDriver (), Width = 2, Height = 2 }; Assert.True (view.NeedsDraw); Assert.False (view.NeedsLayout); @@ -120,7 +122,7 @@ public void NeedsDraw_After_SetLayoutNeeded_And_Layout () [Fact] public void NeedsDraw_False_After_SetRelativeLayout_Absolute_Dims () { - var view = new View { Width = 2, Height = 2 }; + var view = new View { Driver = CreateFakeDriver (), Width = 2, Height = 2 }; Assert.True (view.NeedsDraw); view.Draw (); @@ -224,7 +226,7 @@ public void NeedsDraw_True_After_LayoutSubViews () [Fact] public void NeedsDraw_False_After_Draw () { - var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single }; + var view = new View { Driver = CreateFakeDriver (), Width = 2, Height = 2, BorderStyle = LineStyle.Single }; Assert.True (view.NeedsDraw); view.BeginInit (); diff --git a/Tests/UnitTestsParallelizable/View/Draw/ViewClearViewportTests.cs b/Tests/UnitTestsParallelizable/View/Draw/ViewClearViewportTests.cs new file mode 100644 index 0000000000..1ef96e203b --- /dev/null +++ b/Tests/UnitTestsParallelizable/View/Draw/ViewClearViewportTests.cs @@ -0,0 +1,371 @@ +using System.Text; +using UnitTests; +using Xunit.Abstractions; + +namespace UnitTests_Parallelizable.ViewTests; + +public class ViewClearViewportTests () : FakeDriverBase +{ + [Fact] + public void ClearViewport_FillsViewportArea () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Clear the driver contents first + driver.FillRect (driver.Screen, new Rune ('X')); + + view.ClearViewport (); + + // The viewport area should be filled with spaces + Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) }); + + for (int y = viewportScreen.Y; y < viewportScreen.Y + viewportScreen.Height; y++) + { + for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++) + { + Assert.Equal (new Rune (' '), driver.Contents [y, x].Rune); + } + } + } + + [Fact] + public void ClearViewport_WithClearContentOnly_LimitsToVisibleContent () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver + }; + view.SetContentSize (new Size (100, 100)); // Content larger than viewport + view.ViewportSettings = ViewportSettingsFlags.ClearContentOnly; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Clear the driver contents first + driver.FillRect (driver.Screen, new Rune ('X')); + + view.ClearViewport (); + + // The visible content area should be cleared + Rectangle visibleContent = view.ViewportToScreen (new Rectangle (new (-view.Viewport.X, -view.Viewport.Y), view.GetContentSize ())); + Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) }); + Rectangle toClear = Rectangle.Intersect (viewportScreen, visibleContent); + + for (int y = toClear.Y; y < toClear.Y + toClear.Height; y++) + { + for (int x = toClear.X; x < toClear.X + toClear.Width; x++) + { + Assert.Equal (new Rune (' '), driver.Contents [y, x].Rune); + } + } + } + + [Fact] + public void ClearViewport_NullDriver_DoesNotThrow () + { + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20 + }; + view.BeginInit (); + view.EndInit (); + var exception = Record.Exception (() => view.ClearViewport ()); + Assert.Null (exception); + } + + [Fact] + public void ClearViewport_SetsNeedsDraw () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Clear NeedsDraw first + view.Draw (); + Assert.False (view.NeedsDraw); + + view.ClearViewport (); + + Assert.True (view.NeedsDraw); + } + + [Fact] + public void ClearViewport_WithTransparentFlag_DoesNotClear () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver, + ViewportSettings = ViewportSettingsFlags.Transparent + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Fill driver with a character + driver.FillRect (driver.Screen, new Rune ('X')); + + view.Draw (); + + // The viewport area should still have 'X' (not cleared) + Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) }); + + for (int y = viewportScreen.Y; y < viewportScreen.Y + viewportScreen.Height; y++) + { + for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++) + { + Assert.Equal (new Rune ('X'), driver.Contents [y, x].Rune); + } + } + } + + [Fact] + public void ClearingViewport_Event_Raised () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + bool eventRaised = false; + Rectangle? receivedRect = null; + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.ClearingViewport += (s, e) => + { + eventRaised = true; + receivedRect = e.NewViewport; + }; + + view.Draw (); + + Assert.True (eventRaised); + Assert.NotNull (receivedRect); + Assert.Equal (view.Viewport, receivedRect); + } + + [Fact] + public void ClearedViewport_Event_Raised () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + bool eventRaised = false; + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.ClearedViewport += (s, e) => eventRaised = true; + + view.Draw (); + + Assert.True (eventRaised); + } + + [Fact] + public void OnClearingViewport_CanPreventClear () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + bool clearedCalled = false; + + var view = new TestView + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + PreventClear = true + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.ClearedViewport += (s, e) => clearedCalled = true; + + view.Draw (); + + Assert.False (clearedCalled); + } + + [Fact] + public void ClearViewport_EmptyViewport_DoesNotThrow () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 1, + Height = 1, + Driver = driver + }; + view.Border!.Thickness = new Thickness (1); + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // With border of 1, viewport should be empty + Assert.True (view.Viewport.Width == 0 || view.Viewport.Height == 0); + + var exception = Record.Exception (() => view.ClearViewport ()); + + Assert.Null (exception); + } + + [Fact] + public void ClearViewport_WithScrolledViewport_ClearsCorrectArea () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver + }; + view.SetContentSize (new Size (100, 100)); + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Scroll the viewport + view.Viewport = view.Viewport with { X = 10, Y = 10 }; + + // Fill driver with a character + driver.FillRect (driver.Screen, new Rune ('X')); + + view.ClearViewport (); + + // The viewport area should be cleared (not the scrolled content area) + Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) }); + + for (int y = viewportScreen.Y; y < viewportScreen.Y + viewportScreen.Height; y++) + { + for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++) + { + Assert.Equal (new Rune (' '), driver.Contents [y, x].Rune); + } + } + } + + [Fact] + public void ClearViewport_WithClearContentOnly_AndScrolledViewport_ClearsOnlyVisibleContent () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + view.SetContentSize (new Size (15, 15)); // Content smaller than viewport + view.ViewportSettings = ViewportSettingsFlags.ClearContentOnly; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Scroll past the content + view.Viewport = view.Viewport with { X = 5, Y = 5 }; + + // Fill driver with a character + driver.FillRect (driver.Screen, new Rune ('X')); + + view.ClearViewport (); + + // Only the visible part of the content should be cleared + Rectangle visibleContent = view.ViewportToScreen (new Rectangle (new (-view.Viewport.X, -view.Viewport.Y), view.GetContentSize ())); + Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) }); + Rectangle toClear = Rectangle.Intersect (viewportScreen, visibleContent); + + if (toClear != Rectangle.Empty) + { + for (int y = toClear.Y; y < toClear.Y + toClear.Height; y++) + { + for (int x = toClear.X; x < toClear.X + toClear.Width; x++) + { + Assert.Equal (new Rune (' '), driver.Contents [y, x].Rune); + } + } + } + } + + private class TestView : View + { + public bool PreventClear { get; set; } + + protected override bool OnClearingViewport () + { + return PreventClear || base.OnClearingViewport (); + } + } +} diff --git a/Tests/UnitTestsParallelizable/View/Draw/ViewDrawTextAndLineCanvasTests.cs b/Tests/UnitTestsParallelizable/View/Draw/ViewDrawTextAndLineCanvasTests.cs new file mode 100644 index 0000000000..5f2af20550 --- /dev/null +++ b/Tests/UnitTestsParallelizable/View/Draw/ViewDrawTextAndLineCanvasTests.cs @@ -0,0 +1,495 @@ +using System.Text; +using UnitTests; +using Xunit.Abstractions; + +namespace UnitTests_Parallelizable.ViewTests; + +public class ViewDrawTextAndLineCanvasTests () : FakeDriverBase +{ + #region DrawText Tests + + [Fact] + public void DrawText_EmptyText_DoesNotThrow () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + Text = "" + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + var exception = Record.Exception (() => view.Draw ()); + + Assert.Null (exception); + } + + [Fact] + public void DrawText_NullText_DoesNotThrow () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + Text = null! + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + var exception = Record.Exception (() => view.Draw ()); + + Assert.Null (exception); + } + + [Fact] + public void DrawText_DrawsTextToDriver () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver, + Text = "Test" + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.Draw (); + + // Text should appear at the content location + Point screenPos = view.ContentToScreen (Point.Empty); + + Assert.Equal ('T', (char)driver.Contents [screenPos.Y, screenPos.X].Rune.Value); + Assert.Equal ('e', (char)driver.Contents [screenPos.Y, screenPos.X + 1].Rune.Value); + Assert.Equal ('s', (char)driver.Contents [screenPos.Y, screenPos.X + 2].Rune.Value); + Assert.Equal ('t', (char)driver.Contents [screenPos.Y, screenPos.X + 3].Rune.Value); + } + + [Fact] + public void DrawText_WithFocus_UsesFocusAttribute () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + Text = "Test", + CanFocus = true + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + view.SetFocus (); + + view.Draw (); + + // Text should use focus attribute + Point screenPos = view.ContentToScreen (Point.Empty); + Attribute expectedAttr = view.GetAttributeForRole (VisualRole.Focus); + + Assert.Equal (expectedAttr, driver.Contents [screenPos.Y, screenPos.X].Attribute); + } + + [Fact] + public void DrawText_WithoutFocus_UsesNormalAttribute () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + Text = "Test", + CanFocus = true + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.Draw (); + + // Text should use normal attribute + Point screenPos = view.ContentToScreen (Point.Empty); + Attribute expectedAttr = view.GetAttributeForRole (VisualRole.Normal); + + Assert.Equal (expectedAttr, driver.Contents [screenPos.Y, screenPos.X].Attribute); + } + + [Fact] + public void DrawText_SetsSubViewNeedsDraw () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + Text = "Test" + }; + var child = new View { X = 0, Y = 0, Width = 10, Height = 10 }; + view.Add (child); + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Clear SubViewNeedsDraw + view.Draw (); + Assert.False (view.SubViewNeedsDraw); + + // Call DrawText directly which should set SubViewNeedsDraw + view.DrawText (); + + // SubViews need to be redrawn since text was drawn over them + Assert.True (view.SubViewNeedsDraw); + } + + [Fact] + public void DrawingText_Event_Raised () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + bool eventRaised = false; + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + Text = "Test" + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.DrawingText += (s, e) => eventRaised = true; + + view.Draw (); + + Assert.True (eventRaised); + } + + [Fact] + public void DrewText_Event_Raised () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + bool eventRaised = false; + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + Text = "Test" + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.DrewText += (s, e) => eventRaised = true; + + view.Draw (); + + Assert.True (eventRaised); + } + + #endregion + + #region LineCanvas Tests + + [Fact] + public void LineCanvas_InitiallyEmpty () + { + var view = new View (); + + Assert.NotNull (view.LineCanvas); + Assert.Equal (Rectangle.Empty, view.LineCanvas.Bounds); + } + + [Fact] + public void RenderLineCanvas_DrawsLines () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Add a line to the canvas + Point screenPos = new Point (15, 15); + view.LineCanvas.AddLine (screenPos, 5, Orientation.Horizontal, LineStyle.Single); + + view.RenderLineCanvas (); + + // Verify the line was drawn (check for horizontal line character) + for (int i = 0; i < 5; i++) + { + Assert.NotEqual (new Rune (' '), driver.Contents [screenPos.Y, screenPos.X + i].Rune); + } + } + + [Fact] + public void RenderLineCanvas_ClearsAfterRendering () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Add a line to the canvas + view.LineCanvas.AddLine (new Point (15, 15), 5, Orientation.Horizontal, LineStyle.Single); + + Assert.NotEqual (Rectangle.Empty, view.LineCanvas.Bounds); + + view.RenderLineCanvas (); + + // LineCanvas should be cleared after rendering + Assert.Equal (Rectangle.Empty, view.LineCanvas.Bounds); + } + + [Fact] + public void RenderLineCanvas_WithSuperViewRendersLineCanvas_DoesNotClear () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + SuperViewRendersLineCanvas = true + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Add a line to the canvas + view.LineCanvas.AddLine (new Point (15, 15), 5, Orientation.Horizontal, LineStyle.Single); + + Rectangle boundsBefore = view.LineCanvas.Bounds; + + view.RenderLineCanvas (); + + // LineCanvas should NOT be cleared when SuperViewRendersLineCanvas is true + Assert.Equal (boundsBefore, view.LineCanvas.Bounds); + } + + [Fact] + public void SuperViewRendersLineCanvas_MergesWithParentCanvas () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var parent = new View + { + X = 10, + Y = 10, + Width = 50, + Height = 50, + Driver = driver + }; + var child = new View + { + X = 5, + Y = 5, + Width = 30, + Height = 30, + SuperViewRendersLineCanvas = true + }; + parent.Add (child); + parent.BeginInit (); + parent.EndInit (); + parent.LayoutSubViews (); + + // Add a line to child's canvas + child.LineCanvas.AddLine (new Point (20, 20), 5, Orientation.Horizontal, LineStyle.Single); + + Assert.NotEqual (Rectangle.Empty, child.LineCanvas.Bounds); + Assert.Equal (Rectangle.Empty, parent.LineCanvas.Bounds); + + parent.Draw (); + + // Child's canvas should have been merged into parent's + // and child's canvas should be cleared + Assert.Equal (Rectangle.Empty, child.LineCanvas.Bounds); + } + + [Fact] + public void OnRenderingLineCanvas_CanPreventRendering () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new TestView + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + PreventRenderLineCanvas = true + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // Add a line to the canvas + Point screenPos = new Point (15, 15); + view.LineCanvas.AddLine (screenPos, 5, Orientation.Horizontal, LineStyle.Single); + + view.Draw (); + + // When OnRenderingLineCanvas returns true, RenderLineCanvas is not called + // So the LineCanvas should still have lines (not cleared) + // BUT because SuperViewRendersLineCanvas is false (default), the LineCanvas + // gets cleared during the draw cycle anyway. We need to check that the + // line was NOT actually rendered to the driver. + bool lineRendered = true; + for (int i = 0; i < 5; i++) + { + if (driver.Contents [screenPos.Y, screenPos.X + i].Rune.Value == ' ') + { + lineRendered = false; + break; + } + } + + Assert.False (lineRendered); + } + + #endregion + + #region SuperViewRendersLineCanvas Tests + + [Fact] + public void SuperViewRendersLineCanvas_DefaultFalse () + { + var view = new View (); + + Assert.False (view.SuperViewRendersLineCanvas); + } + + [Fact] + public void SuperViewRendersLineCanvas_CanBeSet () + { + var view = new View { SuperViewRendersLineCanvas = true }; + + Assert.True (view.SuperViewRendersLineCanvas); + } + + [Fact] + public void Draw_WithSuperViewRendersLineCanvas_SetsNeedsDraw () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var parent = new View + { + X = 10, + Y = 10, + Width = 50, + Height = 50, + Driver = driver + }; + var child = new View + { + X = 5, + Y = 5, + Width = 30, + Height = 30, + SuperViewRendersLineCanvas = true + }; + parent.Add (child); + parent.BeginInit (); + parent.EndInit (); + parent.LayoutSubViews (); + + // Draw once to clear NeedsDraw + parent.Draw (); + Assert.False (child.NeedsDraw); + + // Draw again - child with SuperViewRendersLineCanvas should be redrawn + parent.Draw (); + + // The child should have been set to NeedsDraw during DrawSubViews + // This is verified by the fact that it was drawn (we can't check NeedsDraw after Draw) + } + + #endregion + + #region Helper Test View + + private class TestView : View + { + public bool PreventRenderLineCanvas { get; set; } + + protected override bool OnRenderingLineCanvas () + { + return PreventRenderLineCanvas || base.OnRenderingLineCanvas (); + } + } + + #endregion +} diff --git a/Tests/UnitTestsParallelizable/View/Draw/ViewDrawingClippingTests.cs b/Tests/UnitTestsParallelizable/View/Draw/ViewDrawingClippingTests.cs new file mode 100644 index 0000000000..ba5941efc3 --- /dev/null +++ b/Tests/UnitTestsParallelizable/View/Draw/ViewDrawingClippingTests.cs @@ -0,0 +1,754 @@ +#nullable enable +using UnitTests; +using Xunit.Abstractions; + +namespace UnitTests_Parallelizable.ViewTests; + +public class ViewDrawingClippingTests () : FakeDriverBase +{ + #region GetClip / SetClip Tests + + + [Fact] + public void GetClip_ReturnsDriverClip () + { + IDriver driver = CreateFakeDriver (80, 25); + var region = new Region (new Rectangle (10, 10, 20, 20)); + driver.Clip = region; + View view = new () { Driver = driver }; + + Region? result = view.GetClip (); + + Assert.NotNull (result); + Assert.Equal (region, result); + } + + [Fact] + public void SetClip_NullRegion_DoesNothing () + { + IDriver driver = CreateFakeDriver (80, 25); + var original = new Region (new Rectangle (5, 5, 10, 10)); + driver.Clip = original; + + View view = new () { Driver = driver }; + + view.SetClip (null); + + Assert.Equal (original, driver.Clip); + } + + [Fact] + public void SetClip_ValidRegion_SetsDriverClip () + { + IDriver driver = CreateFakeDriver (80, 25); + var region = new Region (new Rectangle (10, 10, 30, 30)); + View view = new () { Driver = driver }; + + view.SetClip (region); + + Assert.Equal (region, driver.Clip); + } + + #endregion + + #region SetClipToScreen Tests + + [Fact] + public void SetClipToScreen_ReturnsPreviousClip () + { + IDriver driver = CreateFakeDriver (80, 25); + var original = new Region (new Rectangle (5, 5, 10, 10)); + driver.Clip = original; + View view = new () { Driver = driver }; + + Region? previous = view.SetClipToScreen (); + + Assert.Equal (original, previous); + Assert.NotEqual (original, driver.Clip); + + Application.ResetState (true); + } + + [Fact] + public void SetClipToScreen_SetsClipToScreen () + { + IDriver driver = CreateFakeDriver (80, 25); + Application.Driver = driver; + View view = new () { Driver = driver }; + + view.SetClipToScreen (); + + Assert.NotNull (driver.Clip); + Assert.Equal (driver.Screen, driver.Clip.GetBounds ()); + + Application.ResetState (true); + } + + #endregion + + #region ExcludeFromClip Tests + + [Fact] + public void ExcludeFromClip_Rectangle_NullDriver_DoesNotThrow () + { + View view = new () { Driver = null }; + var exception = Record.Exception (() => view.ExcludeFromClip (new Rectangle (5, 5, 10, 10))); + Assert.Null (exception); + + Application.ResetState (true); + } + + [Fact] + public void ExcludeFromClip_Rectangle_ExcludesArea () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (new Rectangle (0, 0, 80, 25)); + Application.Driver = driver; + View view = new () { Driver = driver }; + + var toExclude = new Rectangle (10, 10, 20, 20); + view.ExcludeFromClip (toExclude); + + // Verify the region was excluded + Assert.NotNull (driver.Clip); + Assert.False (driver.Clip.Contains (15, 15)); + + Application.ResetState (true); + } + + [Fact] + public void ExcludeFromClip_Region_NullDriver_DoesNotThrow () + { + View view = new () { Driver = null }; + + var exception = Record.Exception (() => view.ExcludeFromClip (new Region (new Rectangle (5, 5, 10, 10)))); + Assert.Null (exception); + + Application.ResetState (true); + } + + [Fact] + public void ExcludeFromClip_Region_ExcludesArea () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (new Rectangle (0, 0, 80, 25)); + View view = new () { Driver = driver }; + + + var toExclude = new Region (new Rectangle (10, 10, 20, 20)); + view.ExcludeFromClip (toExclude); + + // Verify the region was excluded + Assert.NotNull (driver.Clip); + Assert.False (driver.Clip.Contains (15, 15)); + + Application.ResetState (true); + } + + #endregion + + #region AddFrameToClip Tests + + [Fact] + public void AddFrameToClip_NullDriver_ReturnsNull () + { + var view = new View { X = 0, Y = 0, Width = 10, Height = 10 }; + view.BeginInit (); + view.EndInit (); + + Region? result = view.AddFrameToClip (); + + Assert.Null (result); + } + + [Fact] + public void AddFrameToClip_IntersectsWithFrame () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + Region? previous = view.AddFrameToClip (); + + Assert.NotNull (previous); + Assert.NotNull (driver.Clip); + + // The clip should now be the intersection of the screen and the view's frame + Rectangle expectedBounds = new Rectangle (1, 1, 20, 20); + Assert.Equal (expectedBounds, driver.Clip.GetBounds ()); + } + + #endregion + + #region AddViewportToClip Tests + + [Fact] + public void AddViewportToClip_NullDriver_ReturnsNull () + { + var view = new View { X = 0, Y = 0, Width = 10, Height = 10 }; + view.BeginInit (); + view.EndInit (); + + Region? result = view.AddViewportToClip (); + + Assert.Null (result); + } + + [Fact] + public void AddViewportToClip_IntersectsWithViewport () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + Region? previous = view.AddViewportToClip (); + + Assert.NotNull (previous); + Assert.NotNull (driver.Clip); + + // The clip should be the viewport area + Rectangle viewportScreen = view.ViewportToScreen (new Rectangle (Point.Empty, view.Viewport.Size)); + Assert.Equal (viewportScreen, driver.Clip.GetBounds ()); + } + + [Fact] + public void AddViewportToClip_WithClipContentOnly_LimitsToVisibleContent () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver + }; + view.SetContentSize (new Size (100, 100)); + view.ViewportSettings = ViewportSettingsFlags.ClipContentOnly; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + Region? previous = view.AddViewportToClip (); + + Assert.NotNull (previous); + Assert.NotNull (driver.Clip); + + // The clip should be limited to visible content + Rectangle visibleContent = view.ViewportToScreen (new Rectangle (new (-view.Viewport.X, -view.Viewport.Y), view.GetContentSize ())); + Rectangle viewport = view.ViewportToScreen (new Rectangle (Point.Empty, view.Viewport.Size)); + Rectangle expected = Rectangle.Intersect (viewport, visibleContent); + + Assert.Equal (expected, driver.Clip.GetBounds ()); + } + + #endregion + + #region Clip Interaction Tests + + [Fact] + public void ClipRegions_StackCorrectly_WithNestedViews () + { + IDriver driver = CreateFakeDriver (100, 100); + driver.Clip = new Region (driver.Screen); + + var superView = new View + { + X = 1, + Y = 1, + Width = 50, + Height = 50, + Driver = driver + }; + superView.BeginInit (); + superView.EndInit (); + + var view = new View + { + X = 5, + Y = 5, + Width = 30, + Height = 30, + }; + superView.Add (view); + superView.LayoutSubViews (); + + // Set clip to superView's frame + Region? superViewClip = superView.AddFrameToClip (); + Rectangle superViewBounds = driver.Clip.GetBounds (); + + // Now set clip to view's frame + Region? viewClip = view.AddFrameToClip (); + Rectangle viewBounds = driver.Clip.GetBounds (); + + // Child clip should be within superView clip + Assert.True (superViewBounds.Contains (viewBounds.Location)); + + // Restore superView clip + view.SetClip (superViewClip); + // Assert.Equal (superViewBounds, driver.Clip.GetBounds ()); + } + + [Fact] + public void ClipRegions_RespectPreviousClip () + { + IDriver driver = CreateFakeDriver (80, 25); + var initialClip = new Region (new Rectangle (20, 20, 40, 40)); + driver.Clip = initialClip; + + var view = new View + { + X = 1, + Y = 1, + Width = 60, + Height = 60, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + Region? previous = view.AddFrameToClip (); + + // The new clip should be the intersection of the initial clip and the view's frame + Rectangle expected = Rectangle.Intersect ( + initialClip.GetBounds (), + view.FrameToScreen () + ); + + Assert.Equal (expected, driver.Clip.GetBounds ()); + + // Restore should give us back the original + view.SetClip (previous); + Assert.Equal (initialClip.GetBounds (), driver.Clip.GetBounds ()); + } + + #endregion + + #region Edge Cases + + [Fact] + public void AddFrameToClip_EmptyFrame_WorksCorrectly () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 0, + Height = 0, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + Region? previous = view.AddFrameToClip (); + + Assert.NotNull (previous); + Assert.NotNull (driver.Clip); + } + + [Fact] + public void AddViewportToClip_EmptyViewport_WorksCorrectly () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 1, // Minimal size to have adornments + Height = 1, + Driver = driver + }; + view.Border!.Thickness = new Thickness (1); + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // With border thickness of 1, the viewport should be empty + Assert.True (view.Viewport.Size.Width == 0 || view.Viewport.Size.Height == 0); + + Region? previous = view.AddViewportToClip (); + + Assert.NotNull (previous); + } + + [Fact] + public void ClipRegions_OutOfBounds_HandledCorrectly () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 100, // Outside screen bounds + Y = 100, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + Region? previous = view.AddFrameToClip (); + + Assert.NotNull (previous); + // The clip should be empty since the view is outside the screen + Assert.True (driver.Clip.IsEmpty () || !driver.Clip.Contains (100, 100)); + } + + #endregion + + #region Drawing Tests + + [Fact] + public void Clip_Set_BeforeDraw_ClipsDrawing () + { + IDriver driver = CreateFakeDriver (80, 25); + var clip = new Region (new Rectangle (10, 10, 10, 10)); + driver.Clip = clip; + + var view = new View + { + X = 0, + Y = 0, + Width = 50, + Height = 50, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.Draw (); + + // Verify clip was used + Assert.NotNull (driver.Clip); + } + + [Fact] + public void Draw_UpdatesDriverClip () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 1, + Y = 1, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.Draw (); + + // Clip should be updated to exclude the drawn view + Assert.NotNull (driver.Clip); + // Assert.False (driver.Clip.Contains (15, 15)); // Point inside the view should be excluded + } + + [Fact] + public void Draw_WithSubViews_ClipsCorrectly () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var superView = new View + { + X = 1, + Y = 1, + Width = 50, + Height = 50, + Driver = driver + }; + var view = new View { X = 5, Y = 5, Width = 20, Height = 20 }; + superView.Add (view); + superView.BeginInit (); + superView.EndInit (); + superView.LayoutSubViews (); + + superView.Draw (); + + // Both superView and view should be excluded from clip + Assert.NotNull (driver.Clip); + // Assert.False (driver.Clip.Contains (15, 15)); // Point in superView should be excluded + } + + [Fact] + public void Draw_NonVisibleView_DoesNotUpdateClip () + { + IDriver driver = CreateFakeDriver (80, 25); + var originalClip = new Region (driver.Screen); + driver.Clip = originalClip.Clone (); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Visible = false, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + + view.Draw (); + + // Clip should not be modified for invisible views + Assert.True (driver.Clip.Equals (originalClip)); + } + + [Fact] + public void ExcludeFromClip_ExcludesRegion () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + Application.Driver = driver; + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + var excludeRect = new Rectangle (15, 15, 10, 10); + view.ExcludeFromClip (excludeRect); + + Assert.NotNull (driver.Clip); + Assert.False (driver.Clip.Contains (20, 20)); // Point inside excluded rect should not be in clip + + Application.ResetState (true); + } + + [Fact] + public void ExcludeFromClip_WithNullClip_DoesNotThrow () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = null!; + Application.Driver = driver; + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + + var exception = Record.Exception (() => view.ExcludeFromClip (new Rectangle (15, 15, 10, 10))); + + Assert.Null (exception); + + Application.ResetState (true); + } + + #endregion + + #region Misc Tests + + [Fact] + public void SetClip_SetsDriverClip () + { + IDriver driver = CreateFakeDriver (80, 25); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + + var newClip = new Region (new Rectangle (5, 5, 30, 30)); + view.SetClip (newClip); + + Assert.Equal (newClip, driver.Clip); + } + + [Fact (Skip = "See BUGBUG in SetClip")] + public void SetClip_WithNullClip_ClearsClip () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (new Rectangle (10, 10, 20, 20)); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + + view.SetClip (null); + + Assert.Null (driver.Clip); + } + + [Fact] + public void Draw_Excludes_View_From_Clip () + { + IDriver driver = CreateFakeDriver (80, 25); + var originalClip = new Region (driver.Screen); + driver.Clip = originalClip.Clone (); + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + Region clipWithViewExcluded = originalClip.Clone (); + clipWithViewExcluded.Exclude (view.Frame); + + view.Draw (); + + Assert.Equal (clipWithViewExcluded, driver.Clip); + Assert.NotNull (driver.Clip); + } + + [Fact] + public void Draw_EmptyViewport_DoesNotCrash () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 10, + Y = 10, + Width = 1, + Height = 1, + Driver = driver + }; + view.Border!.Thickness = new Thickness (1); + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // With border of 1, viewport should be empty (0x0 or negative) + var exception = Record.Exception (() => view.Draw ()); + + Assert.Null (exception); + } + + [Fact] + public void Draw_VeryLargeView_HandlesClippingCorrectly () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 0, + Y = 0, + Width = 1000, + Height = 1000, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + var exception = Record.Exception (() => view.Draw ()); + + Assert.Null (exception); + } + + [Fact] + public void Draw_NegativeCoordinates_HandlesClippingCorrectly () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = -10, + Y = -10, + Width = 50, + Height = 50, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + var exception = Record.Exception (() => view.Draw ()); + + Assert.Null (exception); + } + + [Fact] + public void Draw_OutOfScreenBounds_HandlesClippingCorrectly () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 100, + Y = 100, + Width = 50, + Height = 50, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + var exception = Record.Exception (() => view.Draw ()); + + Assert.Null (exception); + } + + #endregion +} diff --git a/Tests/UnitTestsParallelizable/View/Draw/ViewDrawingFlowTests.cs b/Tests/UnitTestsParallelizable/View/Draw/ViewDrawingFlowTests.cs new file mode 100644 index 0000000000..58f2bbb184 --- /dev/null +++ b/Tests/UnitTestsParallelizable/View/Draw/ViewDrawingFlowTests.cs @@ -0,0 +1,701 @@ +#nullable enable +using UnitTests; +using Xunit.Abstractions; + +namespace UnitTests_Parallelizable.ViewTests; + +public class ViewDrawingFlowTests () : FakeDriverBase +{ + #region NeedsDraw Tests + + [Fact] + public void NeedsDraw_InitiallyFalse_WhenNotVisible () + { + var view = new View { Visible = false }; + view.BeginInit (); + view.EndInit (); + + Assert.False (view.NeedsDraw); + } + + [Fact] + public void NeedsDraw_TrueAfterSetNeedsDraw () + { + var view = new View { X = 0, Y = 0, Width = 10, Height = 10 }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.SetNeedsDraw (); + + Assert.True (view.NeedsDraw); + } + + [Fact] + public void NeedsDraw_ClearedAfterDraw () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 0, + Y = 0, + Width = 10, + Height = 10, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.SetNeedsDraw (); + Assert.True (view.NeedsDraw); + + view.Draw (); + + Assert.False (view.NeedsDraw); + } + + [Fact] + public void SetNeedsDraw_WithRectangle_UpdatesNeedsDrawRect () + { + var view = new View { Driver = CreateFakeDriver (), X = 0, Y = 0, Width = 20, Height = 20 }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // After layout, view will have NeedsDrawRect set to the viewport + // We need to clear it first + view.Draw (); + Assert.False (view.NeedsDraw); + Assert.Equal (Rectangle.Empty, view.NeedsDrawRect); + + var rect = new Rectangle (5, 5, 10, 10); + view.SetNeedsDraw (rect); + + Assert.True (view.NeedsDraw); + Assert.Equal (rect, view.NeedsDrawRect); + } + + [Fact] + public void SetNeedsDraw_MultipleRectangles_Expands () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View { X = 0, Y = 0, Width = 30, Height = 30, Driver = driver }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + // After layout, clear NeedsDraw + view.Draw (); + Assert.False (view.NeedsDraw); + + view.SetNeedsDraw (new Rectangle (5, 5, 10, 10)); + view.SetNeedsDraw (new Rectangle (15, 15, 10, 10)); + + // Should expand to cover the entire viewport when we have overlapping regions + // The current implementation expands to viewport size + Rectangle expected = new Rectangle (0, 0, 30, 30); + Assert.Equal (expected, view.NeedsDrawRect); + } + + [Fact] + public void SetNeedsDraw_NotVisible_DoesNotSet () + { + var view = new View + { + X = 0, + Y = 0, + Width = 10, + Height = 10, + Visible = false + }; + view.BeginInit (); + view.EndInit (); + + view.SetNeedsDraw (); + + Assert.False (view.NeedsDraw); + } + + [Fact] + public void SetNeedsDraw_PropagatesToSuperView () + { + var parent = new View { X = 0, Y = 0, Width = 50, Height = 50 }; + var child = new View { X = 10, Y = 10, Width = 20, Height = 20 }; + parent.Add (child); + parent.BeginInit (); + parent.EndInit (); + parent.LayoutSubViews (); + + child.SetNeedsDraw (); + + Assert.True (child.NeedsDraw); + Assert.True (parent.SubViewNeedsDraw); + } + + [Fact] + public void SetNeedsDraw_SetsAdornmentsNeedsDraw () + { + var view = new View { X = 0, Y = 0, Width = 20, Height = 20 }; + view.Border!.Thickness = new Thickness (1); + view.Padding!.Thickness = new Thickness (1); + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.SetNeedsDraw (); + + Assert.True (view.Border!.NeedsDraw); + Assert.True (view.Padding!.NeedsDraw); + } + + #endregion + + #region SubViewNeedsDraw Tests + + [Fact] + public void SubViewNeedsDraw_InitiallyFalse () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View { Width = 10, Height = 10, Driver = driver }; + view.BeginInit (); + view.EndInit (); + view.Draw (); // Draw once to clear initial NeedsDraw + + Assert.False (view.SubViewNeedsDraw); + } + + [Fact] + public void SetSubViewNeedsDraw_PropagatesUp () + { + var grandparent = new View { X = 0, Y = 0, Width = 100, Height = 100 }; + var parent = new View { X = 10, Y = 10, Width = 50, Height = 50 }; + var child = new View { X = 5, Y = 5, Width = 20, Height = 20 }; + + grandparent.Add (parent); + parent.Add (child); + grandparent.BeginInit (); + grandparent.EndInit (); + grandparent.LayoutSubViews (); + + child.SetSubViewNeedsDraw (); + + Assert.True (child.SubViewNeedsDraw); + Assert.True (parent.SubViewNeedsDraw); + Assert.True (grandparent.SubViewNeedsDraw); + } + + [Fact] + public void SubViewNeedsDraw_ClearedAfterDraw () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var parent = new View + { + X = 0, + Y = 0, + Width = 50, + Height = 50, + Driver = driver + }; + var child = new View { X = 10, Y = 10, Width = 20, Height = 20 }; + parent.Add (child); + parent.BeginInit (); + parent.EndInit (); + parent.LayoutSubViews (); + + child.SetNeedsDraw (); + Assert.True (parent.SubViewNeedsDraw); + + parent.Draw (); + + Assert.False (parent.SubViewNeedsDraw); + Assert.False (child.SubViewNeedsDraw); + } + + #endregion + + #region Draw Visibility Tests + + [Fact] + public void Draw_NotVisible_DoesNotDraw () + { + IDriver driver = CreateFakeDriver (80, 25); + + var view = new View + { + X = 0, + Y = 0, + Width = 10, + Height = 10, + Visible = false, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + + view.SetNeedsDraw (); + view.Draw (); + + // NeedsDraw should still be false (view wasn't drawn) + Assert.False (view.NeedsDraw); + } + + [Fact] + public void Draw_SuperViewNotVisible_DoesNotDraw () + { + IDriver driver = CreateFakeDriver (80, 25); + + var parent = new View + { + X = 0, + Y = 0, + Width = 50, + Height = 50, + Visible = false, + Driver = driver + }; + var child = new View { X = 10, Y = 10, Width = 20, Height = 20 }; + parent.Add (child); + parent.BeginInit (); + parent.EndInit (); + + child.SetNeedsDraw (); + child.Draw (); + + // Child should not have been drawn + Assert.True (child.NeedsDraw); // Still needs draw + } + + [Fact] + public void Draw_Enabled_False_UsesDisabledAttribute () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + bool drawingTextCalled = false; + Attribute? usedAttribute = null; + + var view = new TestView + { + X = 0, + Y = 0, + Width = 10, + Height = 10, + Enabled = false, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.DrawingText += (s, e) => + { + drawingTextCalled = true; + usedAttribute = driver.CurrentAttribute; + }; + + view.Draw (); + + Assert.True (drawingTextCalled); + Assert.NotNull (usedAttribute); + // The disabled attribute should have been used + Assert.Equal (view.GetAttributeForRole (VisualRole.Disabled), usedAttribute); + } + + #endregion + + #region Draw Order Tests + + [Fact] + public void Draw_CallsMethodsInCorrectOrder () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var callOrder = new List (); + + var view = new TestView + { + X = 0, + Y = 0, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.DrawingAdornmentsCallback = () => callOrder.Add ("DrawingAdornments"); + view.ClearingViewportCallback = () => callOrder.Add ("ClearingViewport"); + view.DrawingSubViewsCallback = () => callOrder.Add ("DrawingSubViews"); + view.DrawingTextCallback = () => callOrder.Add ("DrawingText"); + view.DrawingContentCallback = () => callOrder.Add ("DrawingContent"); + view.RenderingLineCanvasCallback = () => callOrder.Add ("RenderingLineCanvas"); + view.DrawCompleteCallback = () => callOrder.Add ("DrawComplete"); + + view.Draw (); + + Assert.Equal ( + new [] { "DrawingAdornments", "ClearingViewport", "DrawingSubViews", "DrawingText", "DrawingContent", "RenderingLineCanvas", "DrawComplete" }, + callOrder + ); + } + + [Fact] + public void Draw_WithSubViews_DrawsInReverseOrder () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var drawOrder = new List (); + + var parent = new View + { + X = 0, + Y = 0, + Width = 50, + Height = 50, + Driver = driver + }; + + var child1 = new TestView { X = 0, Y = 0, Width = 10, Height = 10, Id = "Child1" }; + var child2 = new TestView { X = 0, Y = 10, Width = 10, Height = 10, Id = "Child2" }; + var child3 = new TestView { X = 0, Y = 20, Width = 10, Height = 10, Id = "Child3" }; + + parent.Add (child1); + parent.Add (child2); + parent.Add (child3); + + parent.BeginInit (); + parent.EndInit (); + parent.LayoutSubViews (); + + child1.DrawingContentCallback = () => drawOrder.Add ("Child1"); + child2.DrawingContentCallback = () => drawOrder.Add ("Child2"); + child3.DrawingContentCallback = () => drawOrder.Add ("Child3"); + + parent.Draw (); + + // SubViews are drawn in reverse order for clipping optimization + Assert.Equal (new [] { "Child3", "Child2", "Child1" }, drawOrder); + } + + #endregion + + #region DrawContext Tests + + [Fact] + public void Draw_WithContext_PassesContext () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + DrawContext? receivedContext = null; + + var view = new TestView + { + X = 0, + Y = 0, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.DrawingContentCallback = () => { }; + view.DrawingContent += (s, e) => + { + receivedContext = e.DrawContext; + }; + + var context = new DrawContext (); + view.Draw (context); + + Assert.NotNull (receivedContext); + Assert.Equal (context, receivedContext); + } + + [Fact] + public void Draw_WithoutContext_CreatesContext () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + DrawContext? receivedContext = null; + + var view = new TestView + { + X = 0, + Y = 0, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.DrawingContentCallback = () => { }; + view.DrawingContent += (s, e) => + { + receivedContext = e.DrawContext; + }; + + view.Draw (); + + Assert.NotNull (receivedContext); + } + + #endregion + + #region Event Tests + + [Fact] + public void ClearingViewport_CanCancel () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 0, + Y = 0, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + bool clearedCalled = false; + + view.ClearingViewport += (s, e) => e.Cancel = true; + view.ClearedViewport += (s, e) => clearedCalled = true; + + view.Draw (); + + Assert.False (clearedCalled); + } + + [Fact] + public void DrawingText_CanCancel () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var view = new View + { + X = 0, + Y = 0, + Width = 20, + Height = 20, + Driver = driver, + Text = "Test" + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + bool drewTextCalled = false; + + view.DrawingText += (s, e) => e.Cancel = true; + view.DrewText += (s, e) => drewTextCalled = true; + + view.Draw (); + + Assert.False (drewTextCalled); + } + + [Fact] + public void DrawingSubViews_CanCancel () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + var parent = new TestView + { + X = 0, + Y = 0, + Width = 50, + Height = 50, + Driver = driver + }; + var child = new TestView { X = 10, Y = 10, Width = 20, Height = 20 }; + parent.Add (child); + parent.BeginInit (); + parent.EndInit (); + parent.LayoutSubViews (); + + bool childDrawn = false; + child.DrawingContentCallback = () => childDrawn = true; + + parent.DrawingSubViews += (s, e) => e.Cancel = true; + + parent.Draw (); + + Assert.False (childDrawn); + } + + [Fact] + public void DrawComplete_AlwaysCalled () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + bool drawCompleteCalled = false; + + var view = new View + { + X = 0, + Y = 0, + Width = 20, + Height = 20, + Driver = driver + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.DrawComplete += (s, e) => drawCompleteCalled = true; + + view.Draw (); + + Assert.True (drawCompleteCalled); + } + + #endregion + + #region Transparent View Tests + + [Fact] + public void Draw_TransparentView_DoesNotClearViewport () + { + IDriver driver = CreateFakeDriver (80, 25); + driver.Clip = new Region (driver.Screen); + + bool clearedViewport = false; + + var view = new View + { + X = 0, + Y = 0, + Width = 20, + Height = 20, + Driver = driver, + ViewportSettings = ViewportSettingsFlags.Transparent + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.ClearedViewport += (s, e) => clearedViewport = true; + + view.Draw (); + + Assert.False (clearedViewport); + } + + [Fact] + public void Draw_TransparentView_ExcludesDrawnRegionFromClip () + { + IDriver driver = CreateFakeDriver (80, 25); + var initialClip = new Region (driver.Screen); + driver.Clip = initialClip; + Application.Driver = driver; + + var view = new View + { + X = 10, + Y = 10, + Width = 20, + Height = 20, + Driver = driver, + ViewportSettings = ViewportSettingsFlags.Transparent + }; + view.BeginInit (); + view.EndInit (); + view.LayoutSubViews (); + + view.Draw (); + + // The drawn area should be excluded from the clip + Rectangle viewportScreen = view.ViewportToScreen (view.Viewport); + + // Points inside the view should be excluded + // Note: This test depends on the DrawContext tracking, which may not exclude if nothing was actually drawn + // We're verifying the mechanism exists, not that it necessarily excludes in this specific case + + Application.ResetState (true); + } + + #endregion + + #region Helper Test View + + private class TestView : View + { + public Action? DrawingAdornmentsCallback { get; set; } + public Action? ClearingViewportCallback { get; set; } + public Action? DrawingSubViewsCallback { get; set; } + public Action? DrawingTextCallback { get; set; } + public Action? DrawingContentCallback { get; set; } + public Action? RenderingLineCanvasCallback { get; set; } + public Action? DrawCompleteCallback { get; set; } + + protected override bool OnDrawingAdornments () + { + DrawingAdornmentsCallback?.Invoke (); + return base.OnDrawingAdornments (); + } + + protected override bool OnClearingViewport () + { + ClearingViewportCallback?.Invoke (); + return base.OnClearingViewport (); + } + + protected override bool OnDrawingSubViews (DrawContext? context) + { + DrawingSubViewsCallback?.Invoke (); + return base.OnDrawingSubViews (context); + } + + protected override bool OnDrawingText (DrawContext? context) + { + DrawingTextCallback?.Invoke (); + return base.OnDrawingText (context); + } + + protected override bool OnDrawingContent (DrawContext? context) + { + DrawingContentCallback?.Invoke (); + return base.OnDrawingContent (context); + } + + protected override bool OnRenderingLineCanvas () + { + RenderingLineCanvasCallback?.Invoke (); + return base.OnRenderingLineCanvas (); + } + + protected override void OnDrawComplete (DrawContext? context) + { + DrawCompleteCallback?.Invoke (); + base.OnDrawComplete (context); + } + } + + #endregion +} diff --git a/Tests/UnitTestsParallelizable/View/Layout/GetViewsUnderLocationTests.cs b/Tests/UnitTestsParallelizable/View/Layout/GetViewsUnderLocationTests.cs index 4bb0506c15..dd5cede33d 100644 --- a/Tests/UnitTestsParallelizable/View/Layout/GetViewsUnderLocationTests.cs +++ b/Tests/UnitTestsParallelizable/View/Layout/GetViewsUnderLocationTests.cs @@ -20,7 +20,7 @@ public void Returns_Null_If_No_SubViews_Coords_Outside (int testX, int testY) var location = new Point (testX, testY); // Act - List viewsUnderMouse = View.GetViewsUnderLocation (location, ViewportSettingsFlags.None); + List viewsUnderMouse = view.GetViewsUnderLocation (location, ViewportSettingsFlags.None); // Assert Assert.Empty (viewsUnderMouse); @@ -42,7 +42,7 @@ public void Returns_Null_If_Start_Not_Visible (int testX, int testY) var location = new Point (testX, testY); // Act - List viewsUnderMouse = View.GetViewsUnderLocation (location, ViewportSettingsFlags.None); + List viewsUnderMouse = view.GetViewsUnderLocation (location, ViewportSettingsFlags.None); // Assert Assert.Empty (viewsUnderMouse); diff --git a/Tests/UnitTestsParallelizable/Views/AllViewsDrawTests.cs b/Tests/UnitTestsParallelizable/Views/AllViewsDrawTests.cs index 35ebdf600f..1926538c82 100644 --- a/Tests/UnitTestsParallelizable/Views/AllViewsDrawTests.cs +++ b/Tests/UnitTestsParallelizable/Views/AllViewsDrawTests.cs @@ -21,6 +21,7 @@ public void AllViews_Draw_Does_Not_Layout (Type viewType) return; } + view.Driver = driver; output.WriteLine ($"Testing {viewType}"); if (view is IDesignable designable) diff --git a/Tests/UnitTestsParallelizable/Views/LineTests.cs b/Tests/UnitTestsParallelizable/Views/LineTests.cs index 8fa44f4a18..a0aa17ca9b 100644 --- a/Tests/UnitTestsParallelizable/Views/LineTests.cs +++ b/Tests/UnitTestsParallelizable/Views/LineTests.cs @@ -1,6 +1,8 @@ +using UnitTests; + namespace UnitTests_Parallelizable.ViewsTests; -public class LineTests +public class LineTests : FakeDriverBase { [Fact] public void Line_DefaultConstructor_Horizontal () @@ -87,7 +89,7 @@ public void Line_SupportsDifferentLineStyles (LineStyle style) [Fact] public void Line_DrawsCalled_Successfully () { - var app = new Window (); + var app = new Window () { Driver = CreateFakeDriver () }; var line = new Line { Y = 1, Width = 10 }; app.Add (line); @@ -103,7 +105,7 @@ public void Line_DrawsCalled_Successfully () [Fact] public void Line_WithBorder_DrawsSuccessfully () { - var app = new Window { Width = 20, Height = 10, BorderStyle = LineStyle.Single }; + var app = new Window { Driver = CreateFakeDriver (), Width = 20, Height = 10, BorderStyle = LineStyle.Single }; // Add a line that intersects with the window border var line = new Line { X = 5, Y = 0, Height = Dim.Fill (), Orientation = Orientation.Vertical }; @@ -121,7 +123,7 @@ public void Line_WithBorder_DrawsSuccessfully () [Fact] public void Line_MultipleIntersecting_DrawsSuccessfully () { - var app = new Window { Width = 30, Height = 15 }; + var app = new Window { Driver = CreateFakeDriver (), Width = 30, Height = 15 }; // Create intersecting lines var hLine = new Line { X = 5, Y = 5, Width = 15, Style = LineStyle.Single }; @@ -258,7 +260,7 @@ public void Line_Dimensions_WorkSameAsInitializers () // Test: new Line { Height = 9, Orientation = Orientation.Vertical } // Expected: Width=1, Height=9 - line = new() { Height = 9, Orientation = Orientation.Vertical }; + line = new () { Height = 9, Orientation = Orientation.Vertical }; Assert.Equal (1, line.Width.GetAnchor (0)); Assert.Equal (9, line.Height.GetAnchor (0)); diff --git a/Tests/UnitTestsParallelizable/Views/ScrollSliderTests.cs b/Tests/UnitTestsParallelizable/Views/ScrollSliderTests.cs index 89d9649c17..2f510d9a47 100644 --- a/Tests/UnitTestsParallelizable/Views/ScrollSliderTests.cs +++ b/Tests/UnitTestsParallelizable/Views/ScrollSliderTests.cs @@ -1,8 +1,9 @@ -using Xunit.Abstractions; +using UnitTests; +using Xunit.Abstractions; namespace UnitTests_Parallelizable.ViewsTests; -public class ScrollSliderTests +public class ScrollSliderTests (ITestOutputHelper output) : FakeDriverBase { [Fact] public void Constructor_Initializes_Correctly () @@ -675,4 +676,340 @@ Orientation orientation Assert.True (scrollSlider.Position <= 5); } + + + [Theory] + [SetupFakeApplication] + [InlineData ( + 3, + 10, + 1, + 0, + Orientation.Vertical, + @" +┌───┐ +│███│ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└───┘")] + [InlineData ( + 10, + 1, + 3, + 0, + Orientation.Horizontal, + @" +┌──────────┐ +│███ │ +└──────────┘")] + [InlineData ( + 3, + 10, + 3, + 0, + Orientation.Vertical, + @" +┌───┐ +│███│ +│███│ +│███│ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└───┘")] + + + + [InlineData ( + 3, + 10, + 5, + 0, + Orientation.Vertical, + @" +┌───┐ +│███│ +│███│ +│███│ +│███│ +│███│ +│ │ +│ │ +│ │ +│ │ +│ │ +└───┘")] + + [InlineData ( + 3, + 10, + 5, + 1, + Orientation.Vertical, + @" +┌───┐ +│ │ +│███│ +│███│ +│███│ +│███│ +│███│ +│ │ +│ │ +│ │ +│ │ +└───┘")] + [InlineData ( + 3, + 10, + 5, + 4, + Orientation.Vertical, + @" +┌───┐ +│ │ +│ │ +│ │ +│ │ +│███│ +│███│ +│███│ +│███│ +│███│ +│ │ +└───┘")] + [InlineData ( + 3, + 10, + 5, + 5, + Orientation.Vertical, + @" +┌───┐ +│ │ +│ │ +│ │ +│ │ +│ │ +│███│ +│███│ +│███│ +│███│ +│███│ +└───┘")] + [InlineData ( + 3, + 10, + 5, + 6, + Orientation.Vertical, + @" +┌───┐ +│ │ +│ │ +│ │ +│ │ +│ │ +│███│ +│███│ +│███│ +│███│ +│███│ +└───┘")] + + [InlineData ( + 3, + 10, + 10, + 0, + Orientation.Vertical, + @" +┌───┐ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +└───┘")] + + [InlineData ( + 3, + 10, + 10, + 5, + Orientation.Vertical, + @" +┌───┐ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +└───┘")] + [InlineData ( + 3, + 10, + 11, + 0, + Orientation.Vertical, + @" +┌───┐ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +│███│ +└───┘")] + + [InlineData ( + 10, + 3, + 5, + 0, + Orientation.Horizontal, + @" +┌──────────┐ +│█████ │ +│█████ │ +│█████ │ +└──────────┘")] + + [InlineData ( + 10, + 3, + 5, + 1, + Orientation.Horizontal, + @" +┌──────────┐ +│ █████ │ +│ █████ │ +│ █████ │ +└──────────┘")] + [InlineData ( + 10, + 3, + 5, + 4, + Orientation.Horizontal, + @" +┌──────────┐ +│ █████ │ +│ █████ │ +│ █████ │ +└──────────┘")] + [InlineData ( + 10, + 3, + 5, + 5, + Orientation.Horizontal, + @" +┌──────────┐ +│ █████│ +│ █████│ +│ █████│ +└──────────┘")] + [InlineData ( + 10, + 3, + 5, + 6, + Orientation.Horizontal, + @" +┌──────────┐ +│ █████│ +│ █████│ +│ █████│ +└──────────┘")] + + [InlineData ( + 10, + 3, + 10, + 0, + Orientation.Horizontal, + @" +┌──────────┐ +│██████████│ +│██████████│ +│██████████│ +└──────────┘")] + + [InlineData ( + 10, + 3, + 10, + 5, + Orientation.Horizontal, + @" +┌──────────┐ +│██████████│ +│██████████│ +│██████████│ +└──────────┘")] + [InlineData ( + 10, + 3, + 11, + 0, + Orientation.Horizontal, + @" +┌──────────┐ +│██████████│ +│██████████│ +│██████████│ +└──────────┘")] + public void Draws_Correctly (int superViewportWidth, int superViewportHeight, int sliderSize, int position, Orientation orientation, string expected) + { + IDriver driver = CreateFakeDriver (); + var super = new Window + { + Driver = driver, + Id = "super", + Width = superViewportWidth + 2, + Height = superViewportHeight + 2 + }; + + var scrollSlider = new ScrollSlider + { + Orientation = orientation, + Size = sliderSize, + //Position = position, + }; + Assert.Equal (sliderSize, scrollSlider.Size); + super.Add (scrollSlider); + scrollSlider.Position = position; + + super.Layout (); + super.Draw (); + + _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output, driver); + } } diff --git a/Tests/UnitTestsParallelizable/Views/ShortcutTests.cs b/Tests/UnitTestsParallelizable/Views/ShortcutTests.cs index 7866055ca9..fc740c63a7 100644 --- a/Tests/UnitTestsParallelizable/Views/ShortcutTests.cs +++ b/Tests/UnitTestsParallelizable/Views/ShortcutTests.cs @@ -108,7 +108,7 @@ public void NaturalSize (string command, string help, KeyCode key, int expectedW // | C H K | Assert.Equal (expectedWidth, shortcut.Frame.Width); - shortcut = new() + shortcut = new () { HelpText = help, Title = command, @@ -118,7 +118,7 @@ public void NaturalSize (string command, string help, KeyCode key, int expectedW shortcut.Layout (); Assert.Equal (expectedWidth, shortcut.Frame.Width); - shortcut = new() + shortcut = new () { HelpText = help, Key = key, @@ -128,7 +128,7 @@ public void NaturalSize (string command, string help, KeyCode key, int expectedW shortcut.Layout (); Assert.Equal (expectedWidth, shortcut.Frame.Width); - shortcut = new() + shortcut = new () { Key = key, HelpText = help, @@ -314,13 +314,16 @@ public void BindKeyToApplication_Changing_Adjusts_KeyBindings () shortcut.Key = Key.A; Assert.True (shortcut.HotKeyBindings.TryGet (Key.A, out _)); + shortcut.App = Application.Create (); shortcut.BindKeyToApplication = true; + shortcut.BeginInit (); + shortcut.EndInit (); Assert.False (shortcut.HotKeyBindings.TryGet (Key.A, out _)); - Assert.True (Application.KeyBindings.TryGet (Key.A, out _)); + Assert.True (shortcut.App?.Keyboard.KeyBindings.TryGet (Key.A, out _)); shortcut.BindKeyToApplication = false; Assert.True (shortcut.HotKeyBindings.TryGet (Key.A, out _)); - Assert.False (Application.KeyBindings.TryGet (Key.A, out _)); + Assert.False (shortcut.App?.Keyboard.KeyBindings.TryGet (Key.A, out _)); } [Theory] diff --git a/codecov.yml b/codecov.yml index ea178f7c12..b71daa0052 100644 --- a/codecov.yml +++ b/codecov.yml @@ -11,7 +11,7 @@ coverage: # Overall project coverage project: default: - target: 70% # Minimum target coverage + target: 75% # Minimum target coverage threshold: 1% # Allow 1% decrease without failing base: auto # Compare against base branch (v2_develop) if_ci_failed: error # Fail if CI fails diff --git a/docfx/docs/View.md b/docfx/docs/View.md index f65ab2de51..33ac685fd6 100644 --- a/docfx/docs/View.md +++ b/docfx/docs/View.md @@ -32,6 +32,8 @@ See the [Views Overview](views.md) for a catalog of all built-in View subclasses - [View.SuperView](~/api/Terminal.Gui.ViewBase.View.yml#Terminal_Gui_ViewBase_View_SuperView) - The View's container (null if the View has no container) - [View.Id](~/api/Terminal.Gui.ViewBase.View.yml#Terminal_Gui_ViewBase_View_Id) - Unique identifier for the View (should be unique among siblings) - [View.Data](~/api/Terminal.Gui.ViewBase.View.yml#Terminal_Gui_ViewBase_View_Data) - Arbitrary data attached to the View +- [View.App](~/api/Terminal.Gui.ViewBase.View.yml#Terminal_Gui_ViewBase_View_App) - The application context this View belongs to +- [View.Driver](~/api/Terminal.Gui.ViewBase.View.yml#Terminal_Gui_ViewBase_View_Driver) - The driver used for rendering (derived from App). This is a shortcut to `App.Driver` for convenience. --- @@ -103,6 +105,8 @@ Views implement [ISupportInitializeNotification](https://docs.microsoft.com/en-u 3. **[EndInit](~/api/Terminal.Gui.ViewBase.View.yml#Terminal_Gui_ViewBase_View_EndInit)** - Signals initialization is complete; raises [View.Initialized](~/api/Terminal.Gui.ViewBase.View.yml) event 4. **[IsInitialized](~/api/Terminal.Gui.ViewBase.View.yml#Terminal_Gui_ViewBase_View_IsInitialized)** - Property indicating if initialization is complete +During initialization, [View.App](~/api/Terminal.Gui.ViewBase.View.yml#Terminal_Gui_ViewBase_View_App) is set to reference the application context, enabling views to access application services like the driver and current session. + ### Disposal Views are [IDisposable](https://docs.microsoft.com/en-us/dotnet/api/system.idisposable): @@ -678,6 +682,7 @@ view.ShadowStyle = ShadowStyle.Transparent; ## See Also +- **[Application Deep Dive](application.md)** - Instance-based application architecture - **[Views Overview](views.md)** - Complete list of all built-in Views - **[Layout Deep Dive](layout.md)** - Detailed layout system documentation - **[Drawing Deep Dive](drawing.md)** - Drawing system and color management diff --git a/docfx/docs/application.md b/docfx/docs/application.md new file mode 100644 index 0000000000..b999b1fc03 --- /dev/null +++ b/docfx/docs/application.md @@ -0,0 +1,552 @@ +# Application Architecture + +Terminal.Gui v2 uses an instance-based application architecture that decouples views from the global application state, improving testability and enabling multiple application contexts. + +## View Hierarchy and Run Stack + +```mermaid +graph TB + subgraph ViewTree["View Hierarchy (SuperView/SubView)"] + direction TB + Top[Application.Current
Window] + Menu[MenuBar] + Status[StatusBar] + Content[Content View] + Button1[Button] + Button2[Button] + + Top --> Menu + Top --> Status + Top --> Content + Content --> Button1 + Content --> Button2 + end + + subgraph Stack["Application.SessionStack"] + direction TB + S1[Window
Currently Active] + S2[Previous Toplevel
Waiting] + S3[Base Toplevel
Waiting] + + S1 -.-> S2 -.-> S3 + end + + Top -.->|"same instance"| S1 + + style Top fill:#ccffcc,stroke:#339933,stroke-width:3px + style S1 fill:#ccffcc,stroke:#339933,stroke-width:3px +``` + +## Usage Example Flow + +```mermaid +sequenceDiagram + participant App as Application + participant Main as Main Window + participant Dialog as Dialog + + Note over App: Initially empty SessionStack + + App->>Main: Run(mainWindow) + activate Main + Note over App: SessionStack: [Main]
Current: Main + + Main->>Dialog: Run(dialog) + activate Dialog + Note over App: SessionStack: [Dialog, Main]
Current: Dialog + + Dialog->>App: RequestStop() + deactivate Dialog + Note over App: SessionStack: [Main]
Current: Main + + Main->>App: RequestStop() + deactivate Main + Note over App: SessionStack: []
Current: null +``` + +## Key Concepts + +### Instance-Based vs Static + +**Terminal.Gui v2** has transitioned from a static singleton pattern to an instance-based architecture: + +```csharp +// OLD (v1 / early v2 - now obsolete): +Application.Init(); +Application.Top.Add(myView); +Application.Run(); +Application.Shutdown(); + +// NEW (v2 instance-based): +var app = Application.Create (); +app.Init(); +var top = new Toplevel(); +top.Add(myView); +app.Run(top); +app.Shutdown(); +``` + +### View.App Property + +Every view now has an `App` property that references its application context: + +```csharp +public class View +{ + /// + /// Gets the application context for this view. + /// + public IApplication? App { get; internal set; } + + /// + /// Gets the application context, checking parent hierarchy if needed. + /// Override to customize application resolution. + /// + public virtual IApplication? GetApp() => App ?? SuperView?.GetApp(); +} +``` + +**Benefits:** +- Views can be tested without `Application.Init()` +- Multiple applications can coexist +- Clear ownership: views know their context +- Reduced global state dependencies + +### Accessing Application from Views + +**Recommended pattern:** + +```csharp +public class MyView : View +{ + public override void OnEnter(View view) + { + // Use View.App instead of static Application + App?.Current?.SetNeedsDraw(); + + // Access SessionStack + if (App?.SessionStack.Count > 0) + { + // Work with sessions + } + } +} +``` + +**Alternative - dependency injection:** + +```csharp +public class MyView : View +{ + private readonly IApplication _app; + + public MyView(IApplication app) + { + _app = app; + // Now completely decoupled from static Application + } + + public void DoWork() + { + _app.Current?.SetNeedsDraw(); + } +} +``` + +## IApplication Interface + +The `IApplication` interface defines the application contract: + +```csharp +public interface IApplication +{ + /// + /// Gets the currently running Toplevel (the "current session"). + /// Renamed from "Top" for clarity. + /// + Toplevel? Current { get; } + + /// + /// Gets the stack of running sessions. + /// Renamed from "TopLevels" to align with SessionToken terminology. + /// + ConcurrentStack SessionStack { get; } + + IDriver? Driver { get; } + IMainLoopCoordinator? MainLoop { get; } + + void Init(string? driverName = null); + void Shutdown(); + SessionToken? Begin(Toplevel toplevel); + void End(SessionToken sessionToken); + // ... other members +} +``` + +## Terminology Changes + +Terminal.Gui v2 modernized its terminology for clarity: + +### Application.Current (formerly "Top") + +The `Current` property represents the currently running Toplevel (the active session): + +```csharp +// Access the current session +Toplevel? current = app.Current; + +// From within a view +Toplevel? current = App?.Current; +``` + +**Why "Current" instead of "Top"?** +- Follows .NET patterns (`Thread.CurrentThread`, `HttpContext.Current`) +- Self-documenting: immediately clear it's the "current" active view +- Less confusing than "Top" which could mean "topmost in Z-order" + +### Application.SessionStack (formerly "TopLevels") + +The `SessionStack` property is the stack of running sessions: + +```csharp +// Access all running sessions +foreach (var toplevel in app.SessionStack) +{ + // Process each session +} + +// From within a view +int sessionCount = App?.SessionStack.Count ?? 0; +``` + +**Why "SessionStack" instead of "TopLevels"?** +- Describes both content (sessions) and structure (stack) +- Aligns with `SessionToken` terminology +- Follows .NET naming patterns (descriptive + collection type) + +## Migration from Static Application + +The static `Application` class now delegates to `ApplicationImpl.Instance` and is marked obsolete: + +```csharp +public static class Application +{ + [Obsolete("Use ApplicationImpl.Instance.Current or view.App?.Current")] + public static Toplevel? Current => Instance?.Current; + + [Obsolete("Use ApplicationImpl.Instance.SessionStack or view.App?.SessionStack")] + public static ConcurrentStack SessionStack => Instance?.SessionStack ?? new(); +} +``` + +### Migration Strategies + +**Strategy 1: Use View.App** + +```csharp +// OLD: +void MyMethod() +{ + Application.Current?.SetNeedsDraw(); +} + +// NEW: +void MyMethod(View view) +{ + view.App?.Current?.SetNeedsDraw(); +} +``` + +**Strategy 2: Pass IApplication** + +```csharp +// OLD: +void ProcessSessions() +{ + foreach (var toplevel in Application.SessionStack) + { + // Process + } +} + +// NEW: +void ProcessSessions(IApplication app) +{ + foreach (var toplevel in app.SessionStack) + { + // Process + } +} +``` + +**Strategy 3: Store IApplication Reference** + +```csharp +public class MyService +{ + private readonly IApplication _app; + + public MyService(IApplication app) + { + _app = app; + } + + public void DoWork() + { + _app.Current?.Title = "Processing..."; + } +} +``` + +## Session Management + +### Begin and End + +Applications manage sessions through `Begin()` and `End()`: + +```csharp +var app = Application.Create (); +app.Init(); + +var toplevel = new Toplevel(); + +// Begin a new session - pushes to SessionStack +SessionToken? token = app.Begin(toplevel); + +// Current now points to this toplevel +Debug.Assert(app.Current == toplevel); + +// End the session - pops from SessionStack +if (token != null) +{ + app.End(token); +} + +// Current restored to previous toplevel (if any) +``` + +### Nested Sessions + +Multiple sessions can run nested: + +```csharp +var app = Application.Create (); +app.Init(); + +// Session 1 +var main = new Toplevel { Title = "Main" }; +var token1 = app.Begin(main); +// app.Current == main, SessionStack.Count == 1 + +// Session 2 (nested) +var dialog = new Dialog { Title = "Dialog" }; +var token2 = app.Begin(dialog); +// app.Current == dialog, SessionStack.Count == 2 + +// End dialog +app.End(token2); +// app.Current == main, SessionStack.Count == 1 + +// End main +app.End(token1); +// app.Current == null, SessionStack.Count == 0 +``` + +## View.Driver Property + +Similar to `View.App`, views now have a `Driver` property: + +```csharp +public class View +{ + /// + /// Gets the driver for this view. + /// + public IDriver? Driver => GetDriver(); + + /// + /// Gets the driver, checking application context if needed. + /// Override to customize driver resolution. + /// + public virtual IDriver? GetDriver() => App?.Driver; +} +``` + +**Usage:** + +```csharp +public override void OnDrawContent(Rectangle viewport) +{ + // Use view's driver instead of Application.Driver + Driver?.Move(0, 0); + Driver?.AddStr("Hello"); +} +``` + +## Testing with the New Architecture + +The instance-based architecture dramatically improves testability: + +### Testing Views in Isolation + +```csharp +[Fact] +public void MyView_DisplaysCorrectly() +{ + // Create mock application + var mockApp = new Mock(); + mockApp.Setup(a => a.Current).Returns(new Toplevel()); + + // Create view with mock app + var view = new MyView { App = mockApp.Object }; + + // Test without Application.Init()! + view.SetNeedsDraw(); + Assert.True(view.NeedsDraw); + + // No Application.Shutdown() needed! +} +``` + +### Testing with Real ApplicationImpl + +```csharp +[Fact] +public void MyView_WorksWithRealApplication() +{ + var app = Application.Create (); + try + { + app.Init(new FakeDriver()); + + var view = new MyView(); + var top = new Toplevel(); + top.Add(view); + + app.Begin(top); + + // View.App automatically set + Assert.NotNull(view.App); + Assert.Same(app, view.App); + + // Test view behavior + view.DoSomething(); + + } + finally + { + app.Shutdown(); + } +} +``` + +## Best Practices + +### DO: Use View.App + +```csharp +✅ GOOD: +public void Refresh() +{ + App?.Current?.SetNeedsDraw(); +} +``` + +### DON'T: Use Static Application + +```csharp +❌ AVOID: +public void Refresh() +{ + Application.Current?.SetNeedsDraw(); // Obsolete! +} +``` + +### DO: Pass IApplication as Dependency + +```csharp +✅ GOOD: +public class Service +{ + public Service(IApplication app) { } +} +``` + +### DON'T: Assume Application.Instance Exists + +```csharp +❌ AVOID: +public class Service +{ + public void DoWork() + { + var app = Application.Instance; // Might be null! + } +} +``` + +### DO: Override GetApp() for Custom Resolution + +```csharp +✅ GOOD: +public class SpecialView : View +{ + private IApplication? _customApp; + + public override IApplication? GetApp() + { + return _customApp ?? base.GetApp(); + } +} +``` + +## Advanced Scenarios + +### Multiple Applications + +The instance-based architecture enables multiple applications: + +```csharp +// Application 1 +var app1 = Application.Create (); +app1.Init(new WindowsDriver()); +var top1 = new Toplevel { Title = "App 1" }; +// ... configure top1 + +// Application 2 (different driver!) +var app2 = Application.Create (); +app2.Init(new CursesDriver()); +var top2 = new Toplevel { Title = "App 2" }; +// ... configure top2 + +// Views in top1 use app1 +// Views in top2 use app2 +``` + +### Application-Agnostic Views + +Create views that work with any application: + +```csharp +public class UniversalView : View +{ + public void ShowMessage(string message) + { + // Works regardless of which application context + var app = GetApp(); + if (app != null) + { + var msg = new MessageBox(message); + app.Begin(msg); + } + } +} +``` + +## See Also + +- [Navigation](navigation.md) - Navigation with the instance-based architecture +- [Keyboard](keyboard.md) - Keyboard handling through View.App +- [Mouse](mouse.md) - Mouse handling through View.App +- [Drivers](drivers.md) - Driver access through View.Driver +- [Multitasking](multitasking.md) - Session management with SessionStack diff --git a/docfx/docs/config.md b/docfx/docs/config.md index 48a9930332..4a549d5ce9 100644 --- a/docfx/docs/config.md +++ b/docfx/docs/config.md @@ -459,7 +459,7 @@ ThemeManager.ThemeChanged += (sender, e) => { // Theme has changed // Refresh all views to use new theme - Application.Top?.SetNeedsDraw(); + Application.Current?.SetNeedsDraw(); }; ``` diff --git a/docfx/docs/drawing.md b/docfx/docs/drawing.md index 598ec7a386..a74edf4320 100644 --- a/docfx/docs/drawing.md +++ b/docfx/docs/drawing.md @@ -8,7 +8,7 @@ Terminal.Gui provides a set of APIs for formatting text, line drawing, and chara # View Drawing API -Terminal.Gui apps draw using the @Terminal.Gui.ViewBase.View.Move(System.Int32,System.Int32) and @Terminal.Gui.ViewBase.View.AddRune(System.Text.Rune) APIs. Move selects the column and row of the cell and AddRune places the specified glyph in that cell using the @Terminal.Gui.Drawing.Attribute that was most recently set via @Terminal.Gui.ViewBase.View.SetAttribute(Terminal.Gui.Drawing.Attribute). The @Terminal.Gui.Drivers.ConsoleDriver caches all changed Cells and efficiently outputs them to the terminal each iteration of the Application. In other words, Terminal.Gui uses deferred rendering. +Terminal.Gui apps draw using the @Terminal.Gui.ViewBase.View.Move(System.Int32,System.Int32) and @Terminal.Gui.ViewBase.View.AddRune(System.Text.Rune) APIs. Move selects the column and row of the cell and AddRune places the specified glyph in that cell using the @Terminal.Gui.Drawing.Attribute that was most recently set via @Terminal.Gui.ViewBase.View.SetAttribute(Terminal.Gui.Drawing.Attribute). The driver caches all changed Cells and efficiently outputs them to the terminal each iteration of the Application. In other words, Terminal.Gui uses deferred rendering. ## Coordinate System for Drawing @@ -26,7 +26,7 @@ See [Layout](layout.md) for more details of the Terminal.Gui coordinate system. 1) Adding the text to a @Terminal.Gui.Text.TextFormatter object. 2) Setting formatting options, such as @Terminal.Gui.Text.TextFormatter.Alignment. -3) Calling @Terminal.Gui.Text.TextFormatter.Draw(System.Drawing.Rectangle,Terminal.Gui.Drawing.Attribute,Terminal.Gui.Drawing.Attribute,System.Drawing.Rectangle,Terminal.Gui.Drivers.IConsoleDriver). +3) Calling @Terminal.Gui.Text.TextFormatter.Draw(Terminal.Gui.Drivers.IDriver, System.Drawing.Rectangle,Terminal.Gui.Drawing.Attribute,Terminal.Gui.Drawing.Attribute,System.Drawing.Rectangle). ## Line drawing @@ -62,19 +62,17 @@ If a View need to redraw because something changed within it's Content Area it c ## Clipping -Clipping enables better performance and features like transparent margins by ensuring regions of the terminal that need to be drawn actually get drawn by the @Terminal.Gui.Drivers.ConsoleDriver. Terminal.Gui supports non-rectangular clip regions with @Terminal.Gui.Drawing.Region. @Terminal.Gui.Drivers.ConsoleDriver.Clip is the application managed clip region and is managed by @Terminal.Gui.App.Application. Developers cannot change this directly, but can use @Terminal.Gui.ViewBase.View.SetClipToScreen, @Terminal.Gui.ViewBase.View.SetClip(Terminal.Gui.Drawing.Region), @Terminal.Gui.ViewBase.View.SetClipToFrame, etc... +Clipping enables better performance and features like transparent margins by ensuring regions of the terminal that need to be drawn actually get drawn by the driver. Terminal.Gui supports non-rectangular clip regions with @Terminal.Gui.Drawing.Region. The driver.Clip is the application managed clip region and is managed by @Terminal.Gui.App.Application. Developers cannot change this directly, but can use @Terminal.Gui.ViewBase.View.SetClipToScreen, @Terminal.Gui.ViewBase.View.SetClip(Terminal.Gui.Drawing.Region), @Terminal.Gui.ViewBase.View.SetClipToFrame, etc... ## Cell The @Terminal.Gui.Drawing.Cell class represents a single cell on the screen. It contains a character and an attribute. The character is of type `Rune` and the attribute is of type @Terminal.Gui.Drawing.Attribute. -`Cell` is not exposed directly to the developer. Instead, the @Terminal.Gui.Drivers.ConsoleDriver classes manage the `Cell` array that represents the screen. +`Cell` is not exposed directly to the developer. Instead, the driver classes manage the `Cell` array that represents the screen. To draw a `Cell` to the screen, use @Terminal.Gui.ViewBase.View.Move(System.Int32,System.Int32) to specify the row and column coordinates and then use the @Terminal.Gui.ViewBase.View.AddRune(System.Int32,System.Int32,System.Text.Rune) method to draw a single glyph. -// ... existing code ... - ## Attribute The @Terminal.Gui.Drawing.Attribute class represents the formatting attributes of a `Cell`. It exposes properties for the foreground and background colors as well as the text style. The foreground and background colors are of type @Terminal.Gui.Drawing.Color. Bold, underline, and other formatting attributes are supported via the @Terminal.Gui.Drawing.Attribute.Style property. @@ -100,8 +98,6 @@ SetAttributeForRole (VisualRole.Focus); AddStr ("Red on Black Underlined."); ``` -// ... existing code ... - ## VisualRole Represents the semantic visual role of a visual element rendered by a View (e.g., Normal text, Focused item, Active selection). @@ -141,4 +137,30 @@ See [View Deep Dive](View.md) for details. ## Diagnostics -The @Terminal.Gui.ViewBase.ViewDiagnosticFlags.DrawIndicator flag can be set on @Terminal.Gui.ViewBase.View.Diagnostics to cause an animated glyph to appear in the `Border` of each View. The glyph will animate each time that View's `Draw` method is called where either @Terminal.Gui.ViewBase.View.NeedsDraw or @Terminal.Gui.ViewBase.View.SubViewNeedsDraw is set. \ No newline at end of file +The @Terminal.Gui.ViewBase.ViewDiagnosticFlags.DrawIndicator flag can be set on @Terminal.Gui.ViewBase.View.Diagnostics to cause an animated glyph to appear in the `Border` of each View. The glyph will animate each time that View's `Draw` method is called where either @Terminal.Gui.ViewBase.View.NeedsDraw or @Terminal.Gui.ViewBase.View.SubViewNeedsDraw is set. + +## Accessing Application Drawing Context + +Views can access application-level drawing functionality through `View.App`: + +```csharp +public class CustomView : View +{ + protected override bool OnDrawingContent() + { + // Access driver capabilities through View.App + if (App?.Driver?.SupportsTrueColor == true) + { + // Use true color features + SetAttribute(new Attribute(Color.FromRgb(255, 0, 0), Color.FromRgb(0, 0, 255))); + } + else + { + // Fallback to 16-color mode + SetAttributeForRole(VisualRole.Normal); + } + + AddStr("Custom drawing with application context"); + return true; + } +} \ No newline at end of file diff --git a/docfx/docs/drivers.md b/docfx/docs/drivers.md index e8322ebf28..a7f5233479 100644 --- a/docfx/docs/drivers.md +++ b/docfx/docs/drivers.md @@ -34,7 +34,7 @@ Application.Init(); // Method 2: Pass driver name to Init Application.Init(driverName: "unix"); -// Method 3: Pass a custom IConsoleDriver instance +// Method 3: Pass a custom IDriver instance var customDriver = new MyCustomDriver(); Application.Init(driver: customDriver); ``` @@ -56,7 +56,7 @@ The v2 driver architecture uses the **Component Factory** pattern to create plat Each driver is composed of specialized components, each with a single responsibility: -#### IConsoleInput<T> +#### IInput<T> Reads raw console input events from the terminal. The generic type `T` represents the platform-specific input type: - `ConsoleKeyInfo` for DotNetDriver and FakeDriver - `WindowsConsole.InputRecord` for WindowsDriver @@ -64,7 +64,7 @@ Reads raw console input events from the terminal. The generic type `T` represent Runs on a dedicated input thread to avoid blocking the UI. -#### IConsoleOutput +#### IOutput Renders the output buffer to the terminal. Handles: - Writing text and ANSI escape sequences - Setting cursor position @@ -88,8 +88,8 @@ Manages the screen buffer and drawing operations: #### IWindowSizeMonitor Detects terminal size changes and raises `SizeChanged` events when the terminal is resized. -#### ConsoleDriverFacade<T> -A unified facade that implements `IConsoleDriver` and coordinates all the components. This is what gets assigned to `Application.Driver`. +#### DriverFacade<T> +A unified facade that implements `IDriver` and coordinates all the components. This is what gets assigned to `Application.Driver`. ### Threading Model @@ -105,22 +105,22 @@ The driver architecture employs a **multi-threaded design** for optimal responsi ├──────────────────┬───────────────────┐ │ │ │ ┌────────▼────────┐ ┌──────▼─────────┐ ┌──────▼──────────┐ - │ Input Thread │ │ Main UI Thread│ │ ConsoleDriver │ + │ Input Thread │ │ Main UI Thread│ │ Driver │ │ │ │ │ │ Facade │ - │ IConsoleInput │ │ ApplicationMain│ │ │ + │ IInput │ │ ApplicationMain│ │ │ │ reads console │ │ Loop processes │ │ Coordinates all │ │ input async │ │ events, layout,│ │ components │ │ into queue │ │ and rendering │ │ │ └─────────────────┘ └────────────────┘ └─────────────────┘ ``` -- **Input Thread**: Started by `MainLoopCoordinator`, runs `IConsoleInput.Run()` which continuously reads console input and queues it into a thread-safe `ConcurrentQueue`. +- **Input Thread**: Started by `MainLoopCoordinator`, runs `IInput.Run()` which continuously reads console input and queues it into a thread-safe `ConcurrentQueue`. - **Main UI Thread**: Runs `ApplicationMainLoop.Iteration()` which: 1. Processes input from the queue via `IInputProcessor` 2. Executes timeout callbacks 3. Checks for UI changes (layout/drawing) - 4. Renders updates via `IConsoleOutput` + 4. Renders updates via `IOutput` This separation ensures that input is never lost and the UI remains responsive during intensive operations. @@ -131,25 +131,25 @@ When you call `Application.Init()`: 1. **ApplicationImpl.Init()** is invoked 2. Creates a `MainLoopCoordinator` with the appropriate `ComponentFactory` 3. **MainLoopCoordinator.StartAsync()** begins: - - Starts the input thread which creates `IConsoleInput` - - Initializes the main UI loop which creates `IConsoleOutput` - - Creates `ConsoleDriverFacade` and assigns to `Application.Driver` + - Starts the input thread which creates `IInput` + - Initializes the main UI loop which creates `IOutput` + - Creates `DriverFacade` and assigns to `IApplication.Driver` - Waits for both threads to be ready 4. Returns control to the application ### Shutdown Flow -When `Application.Shutdown()` is called: +When `IApplication.Shutdown()` is called: 1. Cancellation token is triggered 2. Input thread exits its read loop -3. `IConsoleOutput` is disposed +3. `IOutput` is disposed 4. Main thread waits for input thread to complete 5. All resources are cleaned up ## Component Interfaces -### IConsoleDriver +### IDriver The main driver interface that the framework uses internally. Provides: @@ -167,16 +167,6 @@ The main driver interface that the framework uses internally. Provides: - Use @Terminal.Gui.ViewBase.View.AddRune and @Terminal.Gui.ViewBase.View.AddStr for drawing - ViewBase infrastructure classes (in `Terminal.Gui/ViewBase/`) can access Driver when needed for framework implementation -### IConsoleDriverFacade - -Extended interface for v2 drivers that exposes the internal components: - -- `IInputProcessor InputProcessor` -- `IOutputBuffer OutputBuffer` -- `IWindowSizeMonitor WindowSizeMonitor` - -This interface allows advanced scenarios and testing. - ## Platform-Specific Details ### DotNetDriver (NetComponentFactory) @@ -219,79 +209,13 @@ This ensures Terminal.Gui applications can be debugged directly in Visual Studio - Uses `FakeConsole` for all operations - Allows injection of predefined input - Captures output for verification -- Always used when `Application._forceFakeConsole` is true - -## Example: Checking Driver Capabilities - -```csharp -Application.Init(); - -// The driver is internal - access through Application properties -// Check screen dimensions -var screenWidth = Application.Screen.Width; -var screenHeight = Application.Screen.Height; - -// Check if 24-bit color is supported -bool supportsTrueColor = Application.Driver?.SupportsTrueColor ?? false; - -// Access advanced components (for framework/infrastructure code only) -if (Application.Driver is IConsoleDriverFacade facade) -{ - // Access individual components for advanced scenarios - IInputProcessor inputProcessor = facade.InputProcessor; - IOutputBuffer outputBuffer = facade.OutputBuffer; - IWindowSizeMonitor sizeMonitor = facade.WindowSizeMonitor; - - // Use components for advanced scenarios - sizeMonitor.SizeChanging += (s, e) => - { - Console.WriteLine($"Terminal resized to {e.Size}"); - }; -} -``` +- Always used when `IApplication.ForceDriver` is `fake` **Important:** View subclasses should not access `Application.Driver`. Use the View APIs instead: - `View.Move(col, row)` for positioning - `View.AddRune()` and `View.AddStr()` for drawing -- `Application.Screen` for screen dimensions - -## Custom Drivers - -To create a custom driver, implement `IComponentFactory`: - -```csharp -public class MyComponentFactory : ComponentFactory -{ - public override IConsoleInput CreateInput() - { - return new MyConsoleInput(); - } - - public override IConsoleOutput CreateOutput() - { - return new MyConsoleOutput(); - } - - public override IInputProcessor CreateInputProcessor( - ConcurrentQueue inputBuffer) - { - return new MyInputProcessor(inputBuffer); - } -} -``` - -Then use it: - -```csharp -ApplicationImpl.ChangeComponentFactory(new MyComponentFactory()); -Application.Init(); -``` - -## Legacy Drivers - -Terminal.Gui v1 drivers that implement `IConsoleDriver` but not `IConsoleDriverFacade` are still supported through a legacy compatibility layer. However, they do not benefit from the v2 architecture improvements (multi-threading, component separation, etc.). +- `View.App.Screen` for screen dimensions -**Note**: The legacy `MainLoop` infrastructure (including the `MainLoop` class, `IMainLoopDriver` interface, and `FakeMainLoop`) has been removed in favor of the modern architecture. All drivers now use the `MainLoopCoordinator` and `ApplicationMainLoop` system exclusively. ## See Also diff --git a/docfx/docs/keyboard.md b/docfx/docs/keyboard.md index 14071f69c0..6a08190fa7 100644 --- a/docfx/docs/keyboard.md +++ b/docfx/docs/keyboard.md @@ -91,7 +91,7 @@ The Command can be invoked even if the `View` that defines them is not focused o ### **Key Events** -Keyboard events are retrieved from [Console Drivers](drivers.md) each iteration of the [Application](~/api/Terminal.Gui.App.Application.yml) [Main Loop](multitasking.md). The console driver raises the @Terminal.Gui.Drivers.ConsoleDriver.KeyDown and @Terminal.Gui.Drivers.ConsoleDriver.KeyUp events which invoke @Terminal.Gui.App.Application.RaiseKeyDownEvent* and @Terminal.Gui.App.Application.RaiseKeyUpEvent(Terminal.Gui.Input.Key) respectively. +Keyboard events are retrieved from [Drivers](drivers.md) each iteration of the [Application](~/api/Terminal.Gui.App.Application.yml) [Main Loop](multitasking.md). The driver raises the @Terminal.Gui.Drivers.IDriver.KeyDown and @Terminal.Gui.Drivers.IDriver.KeyUp events which invoke @Terminal.Gui.App.Application.RaiseKeyDownEvent* and @Terminal.Gui.App.Application.RaiseKeyUpEvent(Terminal.Gui.Input.Key) respectively. > [!NOTE] > Not all drivers/platforms support sensing distinct KeyUp events. These drivers will simulate KeyUp events by raising KeyUp after KeyDown. @@ -113,12 +113,12 @@ To define application key handling logic for an entire application in cases wher ## **Key Down/Up Events** -*Terminal.Gui* supports key up/down events with @Terminal.Gui.ViewBase.View.OnKeyDown* and @Terminal.Gui.ViewBase.View.OnKeyUp*, but not all [Console Drivers](drivers.md) do. To receive these key down and key up events, you must use a driver that supports them (e.g. `WindowsDriver`). +*Terminal.Gui* supports key up/down events with @Terminal.Gui.ViewBase.View.OnKeyDown* and @Terminal.Gui.ViewBase.View.OnKeyUp*, but not all [Drivers](drivers.md) do. To receive these key down and key up events, you must use a driver that supports them (e.g. `WindowsDriver`). # General input model -- Key Down and Up events are generated by `ConsoleDriver`. -- `Application` subscribes to `ConsoleDriver.Down/Up` events and forwards them to the most-focused `TopLevel` view using `View.NewKeyDownEvent` and `View.NewKeyUpEvent`. +- Key Down and Up events are generated by the driver. +- `IApplication` implementations subscribe to driver KeyDown/Up events and forwards them to the most-focused `TopLevel` view using `View.NewKeyDownEvent` and `View.NewKeyUpEvent`. - The base (`View`) implementation of `NewKeyDownEvent` follows a pattern of "Before", "During", and "After" processing: - **Before** - If `Enabled == false` that view should *never* see keyboard (or mouse input). @@ -134,25 +134,19 @@ To define application key handling logic for an entire application in cases wher - Subclasses of `View` can (rarely) override `OnKeyDown` (or subscribe to `KeyDown`) to see keys before they are processed - Subclasses of `View` can (often) override `OnKeyDownNotHandled` to do key processing for keys that were not previously handled. `TextField` and `TextView` are examples. -## ConsoleDriver - -* No concept of `Command` or `KeyBindings` -* Use the low-level `KeyCode` enum. -* Exposes non-cancelable `KeyDown/Up` events. The `OnKey/Down/Up` methods are public and can be used to simulate keyboard input (in addition to SendKeys). - ## Application * Implements support for `KeyBindingScope.Application`. * Keyboard functionality is now encapsulated in the @Terminal.Gui.App.IKeyboard interface, accessed via @Terminal.Gui.App.Application.Keyboard. * @Terminal.Gui.App.Application.Keyboard provides access to @Terminal.Gui.Input.KeyBindings, key binding configuration (QuitKey, ArrangeKey, navigation keys), and keyboard event handling. -* For backward compatibility, @Terminal.Gui.App.Application still exposes static properties/methods that delegate to @Terminal.Gui.App.Application.Keyboard (e.g., `Application.KeyBindings`, `Application.RaiseKeyDownEvent`, `Application.QuitKey`). +* For backward compatibility, @Terminal.Gui.App.Application still exposes static properties/methods that delegate to @Terminal.Gui.App.Application.Keyboard (e.g., `IApplication.KeyBindings`, `IApplication.RaiseKeyDownEvent`, `IApplication.QuitKey`). * Exposes cancelable `KeyDown/Up` events (via `Handled = true`). The `RaiseKeyDownEvent` and `RaiseKeyUpEvent` methods are public and can be used to simulate keyboard input. * The @Terminal.Gui.App.IKeyboard interface enables testability with isolated keyboard instances that don't depend on static Application state. ## View * Implements support for `KeyBindings` and `HotKeyBindings`. -* Exposes cancelable non-virtual methods for a new key event: `NewKeyDownEvent` and `NewKeyUpEvent`. These methods are called by `Application` can be called to simulate keyboard input. +* Exposes cancelable non-virtual methods for a new key event: `NewKeyDownEvent` and `NewKeyUpEvent`. These methods are called by `IApplication` can be called to simulate keyboard input. * Exposes cancelable virtual methods for a new key event: `OnKeyDown` and `OnKeyUp`. These methods are called by `NewKeyDownEvent` and `NewKeyUpEvent` and can be overridden to handle keyboard input. ## IKeyboard Architecture @@ -175,9 +169,9 @@ The @Terminal.Gui.App.IKeyboard interface provides a decoupled, testable archite ```csharp // Modern approach - using IKeyboard -Application.Keyboard.KeyBindings.Add(Key.F1, Command.HotKey); -Application.Keyboard.RaiseKeyDownEvent(Key.Enter); -Application.Keyboard.QuitKey = Key.Q.WithCtrl; +App.Keyboard.KeyBindings.Add(Key.F1, Command.HotKey); +App.Keyboard.RaiseKeyDownEvent(Key.Enter); +App.Keyboard.QuitKey = Key.Q.WithCtrl; // Legacy approach - still works (delegates to Application.Keyboard) Application.KeyBindings.Add(Key.F1, Command.HotKey); @@ -202,6 +196,24 @@ Assert.Equal(Key.Q.WithCtrl, keyboard1.QuitKey); Assert.Equal(Key.X.WithCtrl, keyboard2.QuitKey); ``` +**Accessing application context from views:** + +```csharp +public class MyView : View +{ + protected override bool OnKeyDown(Key key) + { + // Use View.App instead of static Application + if (key == Key.F1) + { + App?.Keyboard?.KeyBindings.Add(Key.F2, Command.Accept); + return true; + } + return base.OnKeyDown(key); + } +} +``` + ### Architecture Benefits - **Parallel Testing**: Multiple test methods can create and use separate @Terminal.Gui.App.IKeyboard instances simultaneously without state interference. @@ -218,4 +230,10 @@ The @Terminal.Gui.App.Keyboard class implements @Terminal.Gui.App.IKeyboard and - **Events**: KeyDown, KeyUp events for application-level keyboard monitoring - **Command Implementations**: Handlers for Application-scoped commands (Quit, Suspend, Navigation, Refresh, Arrange) -The @Terminal.Gui.App.ApplicationImpl class creates and manages the @Terminal.Gui.App.IKeyboard instance, setting its `Application` property to `this` to provide the necessary @Terminal.Gui.App.IApplication reference. \ No newline at end of file +The @Terminal.Gui.App.ApplicationImpl class creates and manages the @Terminal.Gui.App.IKeyboard instance, setting its `IApplication` property to `this` to provide the necessary @Terminal.Gui.App.IApplication reference. + +## Driver + +* No concept of `Command` or `KeyBindings` +* Use the low-level `KeyCode` enum. +* Exposes non-cancelable `KeyDown/Up` events. The `OnKey/Down/Up` methods are public and can be used to simulate keyboard input (in addition to SendKeys) \ No newline at end of file diff --git a/docfx/docs/migratingfromv1.md b/docfx/docs/migratingfromv1.md index fbf121c2f5..8a459e0a0a 100644 --- a/docfx/docs/migratingfromv1.md +++ b/docfx/docs/migratingfromv1.md @@ -85,12 +85,12 @@ When measuring the screen space taken up by a `string` you can use the extension In v1, @Terminal.Gui.View was derived from `Responder` which supported `IDisposable`. In v2, `Responder` has been removed and @Terminal.Gui.View is the base-class supporting `IDisposable`. -In v1, @Terminal.Gui./Terminal.Gui.Application.Init) automatically created a toplevel view and set [Application.Top](~/api/Terminal.Gui.Application.Top. In v2, @Terminal.Gui.App.Application.Init no longer automatically creates a toplevel or sets @Terminal.Gui.App.Application.Top; app developers must explicitly create the toplevel view and pass it to @Terminal.Gui.App.Application.Run (or use `Application.Run`). Developers are responsible for calling `Dispose` on any toplevel they create before exiting. +In v1, @Terminal.Gui./Terminal.Gui.Application.Init) automatically created a toplevel view and set [Application.Current](~/api/Terminal.Gui.Application.Current. In v2, @Terminal.Gui.App.Application.Init no longer automatically creates a toplevel or sets @Terminal.Gui.App.Application.Current; app developers must explicitly create the toplevel view and pass it to @Terminal.Gui.App.Application.Run (or use `Application.Run`). Developers are responsible for calling `Dispose` on any toplevel they create before exiting. ### How to Fix * Replace `Responder` with @Terminal.Gui.View -* Update any code that assumes `Application.Init` automatically created a toplevel view and set `Application.Top`. +* Update any code that assumes `Application.Init` automatically created a toplevel view and set `Application.Current`. * Update any code that assumes `Application.Init` automatically disposed of the toplevel view when the application exited. ## @Terminal.Gui.Pos and @Terminal.Gui.Dim types now adhere to standard C# idioms @@ -523,6 +523,6 @@ new ( * To simplify programming, any `View` added as a SubView another `View` will have it's lifecycle owned by the Superview; when a `View` is disposed, it will call `Dispose` on all the items in the `SubViews` property. Note this behavior is the same as it was in v1, just clarified. -* In v1, `Application.End` called `Dispose ()` on @Terminal.Gui.App.Application.Top (via `Runstate.Toplevel`). This was incorrect as it meant that after `Application.Run` returned, `Application.Top` had been disposed, and any code that wanted to interrogate the results of `Run` by accessing `Application.Top` only worked by accident. This is because GC had not actually happened; if it had the application would have crashed. In v2 `Application.End` does NOT call `Dispose`, and it is the caller to `Application.Run` who is responsible for disposing the `Toplevel` that was either passed to `Application.Run (View)` or created by `Application.Run ()`. +* In v1, `Application.End` called `Dispose ()` on @Terminal.Gui.App.Application.Current (via `Runstate.Toplevel`). This was incorrect as it meant that after `Application.Run` returned, `Application.Current` had been disposed, and any code that wanted to interrogate the results of `Run` by accessing `Application.Current` only worked by accident. This is because GC had not actually happened; if it had the application would have crashed. In v2 `Application.End` does NOT call `Dispose`, and it is the caller to `Application.Run` who is responsible for disposing the `Toplevel` that was either passed to `Application.Run (View)` or created by `Application.Run ()`. * Any code that creates a `Toplevel`, either by using `top = new()` or by calling either `top = Application.Run ()` or `top = ApplicationRun()` must call `top.Dispose` when complete. The exception to this is if `top` is passed to `myView.Add(top)` making it a subview of `myView`. This is because the semantics of `Add` are that the `myView` takes over responsibility for the subviews lifetimes. Of course, if someone calls `myView.Remove(top)` to remove said subview, they then re-take responsbility for `top`'s lifetime and they must call `top.Dispose`. diff --git a/docfx/docs/mouse.md b/docfx/docs/mouse.md index 552a6c585d..56e8dff85d 100644 --- a/docfx/docs/mouse.md +++ b/docfx/docs/mouse.md @@ -66,14 +66,14 @@ Here are some common mouse binding patterns used throughout Terminal.Gui: At the core of *Terminal.Gui*'s mouse API is the @Terminal.Gui.Input.MouseEventArgs class. The @Terminal.Gui.Input.MouseEventArgs class provides a platform-independent abstraction for common mouse events. Every mouse event can be fully described in a @Terminal.Gui.Input.MouseEventArgs instance, and most of the mouse-related APIs are simply helper functions for decoding a @Terminal.Gui.Input.MouseEventArgs. -When the user does something with the mouse, the `ConsoleDriver` maps the platform-specific mouse event into a `MouseEventArgs` and calls `Application.RaiseMouseEvent`. Then, `Application.RaiseMouseEvent` determines which `View` the event should go to. The `View.OnMouseEvent` method can be overridden or the `View.MouseEvent` event can be subscribed to, to handle the low-level mouse event. If the low-level event is not handled by a view, `Application` will then call the appropriate high-level helper APIs. For example, if the user double-clicks the mouse, `View.OnMouseClick` will be called/`View.MouseClick` will be raised with the event arguments indicating which mouse button was double-clicked. +When the user does something with the mouse, the driver maps the platform-specific mouse event into a `MouseEventArgs` and calls `IApplication.Mouse.RaiseMouseEvent`. Then, `IApplication.Mouse.RaiseMouseEvent` determines which `View` the event should go to. The `View.OnMouseEvent` method can be overridden or the `View.MouseEvent` event can be subscribed to, to handle the low-level mouse event. If the low-level event is not handled by a view, `IApplication` will then call the appropriate high-level helper APIs. For example, if the user double-clicks the mouse, `View.OnMouseClick` will be called/`View.MouseClick` will be raised with the event arguments indicating which mouse button was double-clicked. ### Mouse Event Processing Flow Mouse events are processed through the following workflow using the [Cancellable Work Pattern](cancellable-work-pattern.md): -1. **Driver Level**: The ConsoleDriver captures platform-specific mouse events and converts them to `MouseEventArgs` -2. **Application Level**: `Application.RaiseMouseEvent` determines the target view and routes the event +1. **Driver Level**: The driver captures platform-specific mouse events and converts them to `MouseEventArgs` +2. **Application Level**: `IApplication.Mouse.RaiseMouseEvent` determines the target view and routes the event 3. **View Level**: The target view processes the event through: - `OnMouseEvent` (virtual method that can be overridden) - `MouseEvent` event (for event subscribers) @@ -157,8 +157,8 @@ view.MouseStateChanged += (sender, e) => The @Terminal.Gui.App.Application.MouseEvent event can be used if an application wishes to receive all mouse events before they are processed by individual views: -```cs -Application.MouseEvent += (sender, e) => +```csharp +App.Mouse.MouseEvent += (sender, e) => { // Handle application-wide mouse events if (e.Flags.HasFlag(MouseFlags.Button3Clicked)) @@ -169,6 +169,24 @@ Application.MouseEvent += (sender, e) => }; ``` +For view-specific mouse handling that needs access to application context, use `View.App`: + +```csharp +public class MyView : View +{ + protected override bool OnMouseEvent(MouseEventArgs mouseEvent) + { + if (mouseEvent.Flags.HasFlag(MouseFlags.Button3Clicked)) + { + // Access application mouse functionality through View.App + App?.MouseEvent?.Invoke(this, mouseEvent); + return true; + } + return base.OnMouseEvent(mouseEvent); + } +} +``` + ## Mouse Enter/Leave Events The @Terminal.Gui.ViewBase.View.MouseEnter and @Terminal.Gui.ViewBase.View.MouseLeave events enable a View to take action when the mouse enters or exits the view boundary. Internally, this is used to enable @Terminal.Gui.ViewBase.View.Highlight functionality: @@ -218,3 +236,4 @@ The `MouseEventArgs` provides both coordinate systems: + diff --git a/docfx/docs/navigation.md b/docfx/docs/navigation.md index c83f72d50b..7fde067950 100644 --- a/docfx/docs/navigation.md +++ b/docfx/docs/navigation.md @@ -374,7 +374,7 @@ In v1 `View` had `MostFocused` property that traversed up the view-hierarchy ret var focused = Application.Navigation.GetFocused(); // This replaces the v1 pattern: -// var focused = Application.Top.MostFocused; +// var focused = Application.Current.MostFocused; ``` ## How Does `View.Add/Remove` Work? diff --git a/docfx/docs/toc.yml b/docfx/docs/toc.yml index 66c9b5cbbd..8acb4573f2 100644 --- a/docfx/docs/toc.yml +++ b/docfx/docs/toc.yml @@ -26,6 +26,8 @@ href: events.md - name: Lexicon & Taxonomy href: lexicon.md +- name: Terminology Proposal + href: terminology-index.md - name: Keyboard href: keyboard.md - name: Layout Engine diff --git a/docfx/schemas/tui-config-schema.json b/docfx/schemas/tui-config-schema.json index 87fec828d3..52b7d7f38b 100644 --- a/docfx/schemas/tui-config-schema.json +++ b/docfx/schemas/tui-config-schema.json @@ -220,7 +220,7 @@ "additionalProperties": true, "definitions": { "Color": { - "description": "One be either one of the W3C standard color names (parsed case-insensitively), a ColorName16 (e.g. 'BrightBlue', parsed case-insensitively), an rgb(r,g,b) tuple, or a hex color string in the format #RRGGBB.", + "description": "One of the standard color names (parsed case-insensitively; (e.g. 'BrightBlue'), an rgb(r,g,b) tuple, or a hex color string in the format #RRGGBB.", "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", "oneOf": [