diff --git a/Terminal.Gui/App/Application.Driver.cs b/Terminal.Gui/App/Application.Driver.cs index e7a3243e2..aa2ee80fe 100644 --- a/Terminal.Gui/App/Application.Driver.cs +++ b/Terminal.Gui/App/Application.Driver.cs @@ -6,7 +6,6 @@ public static partial class Application // Driver abstractions { internal static bool _forceFakeConsole; - // TODO: Add to IApplication /// Gets the that has been selected. See also . public static IConsoleDriver? Driver { @@ -14,18 +13,18 @@ public static partial class Application // Driver abstractions internal set => ApplicationImpl.Instance.Driver = value; } - // TODO: Add to IApplication - // BUGBUG: Force16Colors should be nullable. /// /// Gets or sets whether will be forced to output only the 16 colors defined in /// . The default is , meaning 24-bit (TrueColor) colors will be output /// as long as the selected supports TrueColor. /// [ConfigurationProperty (Scope = typeof (SettingsScope))] - public static bool Force16Colors { get; set; } + public static bool Force16Colors + { + get => ApplicationImpl.Instance.Force16Colors; + set => ApplicationImpl.Instance.Force16Colors = value; + } - // TODO: Add to IApplication - // BUGBUG: ForceDriver should be nullable. /// /// Forces the use of the specified driver (one of "fake", "dotnet", "windows", or "unix"). If not /// specified, the driver is selected based on the platform. @@ -35,12 +34,15 @@ public static partial class Application // Driver abstractions /// with either `driver` or `driverName` specified. /// [ConfigurationProperty (Scope = typeof (SettingsScope))] - public static string ForceDriver { get; set; } = string.Empty; + public static string ForceDriver + { + get => ApplicationImpl.Instance.ForceDriver; + set => ApplicationImpl.Instance.ForceDriver = value; + } - // TODO: Add to IApplication /// /// Collection of sixel images to write out to screen when updating. /// Only add to this collection if you are sure terminal supports sixel format. /// - public static List Sixel { get; } = new List (); + public static List Sixel => ApplicationImpl.Instance.Sixel; } diff --git a/Terminal.Gui/App/Application.Initialization.cs b/Terminal.Gui/App/Application.Lifecycle.cs similarity index 88% rename from Terminal.Gui/App/Application.Initialization.cs rename to Terminal.Gui/App/Application.Lifecycle.cs index 188545366..c69ad06f6 100644 --- a/Terminal.Gui/App/Application.Initialization.cs +++ b/Terminal.Gui/App/Application.Lifecycle.cs @@ -5,7 +5,7 @@ using System.Reflection; namespace Terminal.Gui.App; -public static partial class Application // Initialization (Init/Shutdown) +public static partial class Application // Lifecycle (Init/Shutdown) { /// Initializes a new instance of a Terminal.Gui Application. must be called when the application is closing. @@ -24,7 +24,7 @@ public static partial class Application // Initialization (Init/Shutdown) /// The function combines /// and /// into a single - /// call. An application cam use without explicitly calling + /// call. An application can use without explicitly calling /// . /// /// @@ -118,28 +118,9 @@ public static partial class Application // Initialization (Init/Shutdown) // or go through the modern application architecture if (Driver is null) { - //// Try to find a legacy IConsoleDriver type that matches the driver name - //bool useLegacyDriver = false; - //if (!string.IsNullOrEmpty (ForceDriver)) - //{ - // (List drivers, List driverTypeNames) = GetDriverTypes (); - // Type? driverType = drivers.FirstOrDefault (t => t!.Name.Equals (ForceDriver, StringComparison.InvariantCultureIgnoreCase)); - - // if (driverType is { } && !typeof (IConsoleDriverFacade).IsAssignableFrom (driverType)) - // { - // // This is a legacy driver (not a ConsoleDriverFacade) - // Driver = (IConsoleDriver)Activator.CreateInstance (driverType)!; - // useLegacyDriver = true; - // } - //} - - //// Use the modern application architecture - //if (!useLegacyDriver) - { - ApplicationImpl.Instance.Init (driver, driverName); - Debug.Assert (Driver is { }); - return; - } + ApplicationImpl.Instance.Init (driver, driverName); + Debug.Assert (Driver is { }); + return; } Debug.Assert (Navigation is null); @@ -203,7 +184,7 @@ public static partial class Application // Initialization (Init/Shutdown) private static void Driver_KeyUp (object? sender, Key e) { RaiseKeyUpEvent (e); } private static void Driver_MouseEvent (object? sender, MouseEventArgs e) { RaiseMouseEvent (e); } - /// Gets of list of types and type names that are available. + /// Gets a list of types and type names that are available. /// [RequiresUnreferencedCode ("AOT")] public static (List, List) GetDriverTypes () @@ -229,8 +210,6 @@ public static partial class Application // Initialization (Init/Shutdown) .Union (["dotnet", "windows", "unix", "fake"]) .ToList ()!; - - return (driverTypes, driverTypeNames); } diff --git a/Terminal.Gui/App/Application.Screen.cs b/Terminal.Gui/App/Application.Screen.cs index 0d9a932c5..879141412 100644 --- a/Terminal.Gui/App/Application.Screen.cs +++ b/Terminal.Gui/App/Application.Screen.cs @@ -4,9 +4,6 @@ namespace Terminal.Gui.App; public static partial class Application // Screen related stuff; intended to hide Driver details { - private static readonly object _lockScreen = new (); - private static Rectangle? _screen; - /// /// Gets or sets the size of the screen. By default, this is the size of the screen as reported by the . /// @@ -17,30 +14,8 @@ public static partial class Application // Screen related stuff; intended to hid /// public static Rectangle Screen { - get - { - lock (_lockScreen) - { - if (_screen == null) - { - _screen = Driver?.Screen ?? new (new (0, 0), new (2048, 2048)); - } - - return _screen.Value; - } - } - set - { - if (value is {} && (value.X != 0 || value.Y != 0)) - { - throw new NotImplementedException ($"Screen locations other than 0, 0 are not yet supported"); - } - - lock (_lockScreen) - { - _screen = value; - } - } + get => ApplicationImpl.Instance.Screen; + set => ApplicationImpl.Instance.Screen = value; } /// Invoked when the terminal's size changed. The new size of the terminal is provided. @@ -85,5 +60,9 @@ public static partial class Application // Screen related stuff; intended to hid /// This is typical set to true when a View's changes and that view has no /// SuperView (e.g. when is moved or resized. /// - internal static bool ClearScreenNextIteration { get; set; } + internal static bool ClearScreenNextIteration + { + get => ApplicationImpl.Instance.ClearScreenNextIteration; + set => ApplicationImpl.Instance.ClearScreenNextIteration = value; + } } diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs index 10c57beed..718c24772 100644 --- a/Terminal.Gui/App/Application.cs +++ b/Terminal.Gui/App/Application.cs @@ -232,7 +232,13 @@ public static partial class Application Driver = null; } - _screen = null; + // Reset Screen to null so it will be recalculated on next access + // Note: ApplicationImpl.Shutdown() also calls ResetScreen() before calling this method + // to avoid potential circular reference issues. Calling it twice is harmless. + if (ApplicationImpl.Instance is ApplicationImpl impl) + { + impl.ResetScreen (); + } // Don't reset ForceDriver; it needs to be set before Init is called. //ForceDriver = string.Empty; diff --git a/Terminal.Gui/App/ApplicationImpl.cs b/Terminal.Gui/App/ApplicationImpl.cs index 5668d9450..426d7d238 100644 --- a/Terminal.Gui/App/ApplicationImpl.cs +++ b/Terminal.Gui/App/ApplicationImpl.cs @@ -24,6 +24,12 @@ public class ApplicationImpl : IApplication private Toplevel? _top; private readonly ConcurrentStack _topLevels = new (); private int _mainThreadId = -1; + private bool _force16Colors; + private string _forceDriver = string.Empty; + private readonly List _sixel = new (); + private readonly object _lockScreen = new (); + private Rectangle? _screen; + private bool _clearScreenNextIteration; // Private static readonly Lazy instance of Application private static Lazy _lazyInstance = new (() => new ApplicationImpl ()); @@ -94,6 +100,59 @@ public class ApplicationImpl : IApplication set => _initialized = value; } + /// + public bool Force16Colors + { + get => _force16Colors; + set => _force16Colors = value; + } + + /// + public string ForceDriver + { + get => _forceDriver; + set => _forceDriver = value; + } + + /// + public List Sixel => _sixel; + + /// + public Rectangle Screen + { + get + { + lock (_lockScreen) + { + if (_screen == null) + { + _screen = Driver?.Screen ?? new (new (0, 0), new (2048, 2048)); + } + + return _screen.Value; + } + } + set + { + if (value is {} && (value.X != 0 || value.Y != 0)) + { + throw new NotImplementedException ($"Screen locations other than 0, 0 are not yet supported"); + } + + lock (_lockScreen) + { + _screen = value; + } + } + } + + /// + public bool ClearScreenNextIteration + { + get => _clearScreenNextIteration; + set => _clearScreenNextIteration = value; + } + /// public ApplicationPopover? Popover { @@ -381,6 +440,9 @@ public class ApplicationImpl : IApplication bool wasInitialized = _initialized; + // Reset Screen before calling Application.ResetState to avoid circular reference + ResetScreen (); + // Call ResetState FIRST so it can properly dispose Popover and other resources // that are accessed via Application.* static properties that now delegate to instance fields Application.ResetState (); @@ -396,6 +458,10 @@ public class ApplicationImpl : IApplication _top = null; _topLevels.Clear (); _mainThreadId = -1; + _screen = null; + _clearScreenNextIteration = false; + _sixel.Clear (); + // Don't reset ForceDriver and Force16Colors; they need to be set before Init is called if (wasInitialized) { @@ -469,15 +535,12 @@ public class ApplicationImpl : IApplication tops.Insert (0, visiblePopover); } - // BUGBUG: Application.Screen needs to be moved to IApplication - bool neededLayout = View.Layout (tops.ToArray ().Reverse (), Application.Screen.Size); + bool neededLayout = View.Layout (tops.ToArray ().Reverse (), Screen.Size); - // BUGBUG: Application.ClearScreenNextIteration needs to be moved to IApplication - if (Application.ClearScreenNextIteration) + if (ClearScreenNextIteration) { forceRedraw = true; - // BUGBUG: Application.Screen needs to be moved to IApplication - Application.ClearScreenNextIteration = false; + ClearScreenNextIteration = false; } if (forceRedraw) @@ -490,4 +553,15 @@ public class ApplicationImpl : IApplication View.SetClipToScreen (); _driver?.Refresh (); } + + /// + /// Resets the Screen field to null so it will be recalculated on next access. + /// + internal void ResetScreen () + { + lock (_lockScreen) + { + _screen = null; + } + } } diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs index 44f058855..93a4c7c22 100644 --- a/Terminal.Gui/App/IApplication.cs +++ b/Terminal.Gui/App/IApplication.cs @@ -33,6 +33,35 @@ public interface IApplication /// Gets or sets whether the application has been initialized. bool Initialized { get; set; } + /// + /// Gets or sets whether will be forced to output only the 16 colors defined in + /// . The default is , meaning 24-bit (TrueColor) colors will be output + /// as long as the selected supports TrueColor. + /// + bool Force16Colors { get; set; } + + /// + /// Forces the use of the specified driver (one of "fake", "dotnet", "windows", or "unix"). If not + /// specified, the driver is selected based on the platform. + /// + string ForceDriver { get; set; } + + /// + /// Collection of sixel images to write out to screen when updating. + /// Only add to this collection if you are sure terminal supports sixel format. + /// + List Sixel { get; } + + /// + /// Gets or sets the size of the screen. By default, this is the size of the screen as reported by the . + /// + Rectangle Screen { get; set; } + + /// + /// Gets or sets whether the screen will be cleared, and all Views redrawn, during the next Application iteration. + /// + bool ClearScreenNextIteration { get; set; } + /// Gets or sets the popover manager. ApplicationPopover? Popover { get; set; }