Refactor Force16Colors and ForceDriver according to requirements

- Created new public Driver class in Terminal.Gui.Drivers namespace to hold Force16Colors static property
- Removed Force16Colors from Application and IApplication
- Modified DriverImpl to read Force16Colors at construction time and store in instance field
- Added GetForce16Colors() method to IDriver interface
- Made ForceDriver get-only on IApplication (setter remains on Application static class)
- Updated all references to use Driver.Force16Colors instead of Application.Force16Colors
- Updated Output classes (FakeOutput, NetOutput, UnixOutput) to use Driver.Force16Colors
- Updated ApplicationImpl to reset Driver.Force16Colors instead of Application.Force16Colors
- Fixed tests to use Application.ForceDriver setter instead of IApplication.ForceDriver

Co-authored-by: tig <585482+tig@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-05 04:48:24 +00:00
parent f69082ab90
commit f1394366b7
20 changed files with 92 additions and 79 deletions

View File

@@ -186,11 +186,11 @@ public class ColorPickers : Scenario
{
X = Pos.Right (cbSupportsTrueColor) + 1,
Y = Pos.Top (lblDriverName),
CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
CheckedState = Terminal.Gui.Drivers.Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
Enabled = canTrueColor,
Text = "Force16Colors"
};
cbUseTrueColor.CheckedStateChanging += (_, evt) => { Application.Force16Colors = evt.Result == CheckState.Checked; };
cbUseTrueColor.CheckedStateChanging += (_, evt) => { Terminal.Gui.Drivers.Driver.Force16Colors = evt.Result == CheckState.Checked; };
app.Add (lblDriverName, cbSupportsTrueColor, cbUseTrueColor);
// Set default colors.

View File

@@ -122,11 +122,11 @@ public class Images : Scenario
{
X = Pos.Right (cbSupportsTrueColor) + 2,
Y = 0,
CheckedState = !Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
CheckedState = !Terminal.Gui.Drivers.Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
Enabled = canTrueColor,
Text = "Use true color"
};
cbUseTrueColor.CheckedStateChanging += (_, evt) => Application.Force16Colors = evt.Result == CheckState.UnChecked;
cbUseTrueColor.CheckedStateChanging += (_, evt) => Terminal.Gui.Drivers.Driver.Force16Colors = evt.Result == CheckState.UnChecked;
_win.Add (cbUseTrueColor);
var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" };

View File

@@ -133,14 +133,14 @@ public class LineDrawing : Scenario
var d = new Dialog
{
Title = title,
Width = Application.Force16Colors ? 35 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
Width = Terminal.Gui.Drivers.Driver.Force16Colors ? 35 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
Height = 10
};
var btnOk = new Button
{
X = Pos.Center () - 5,
Y = Application.Force16Colors ? 6 : 4,
Y = Terminal.Gui.Drivers.Driver.Force16Colors ? 6 : 4,
Text = "Ok",
Width = Dim.Auto (),
IsDefault = true
@@ -174,7 +174,7 @@ public class LineDrawing : Scenario
d.AddButton (btnCancel);
View cp;
if (Application.Force16Colors)
if (Terminal.Gui.Drivers.Driver.Force16Colors)
{
cp = new ColorPicker16
{
@@ -197,7 +197,7 @@ public class LineDrawing : Scenario
Application.Run (d);
d.Dispose ();
newColor = Application.Force16Colors ? ((ColorPicker16)cp).SelectedColor : ((ColorPicker)cp).SelectedColor;
newColor = Terminal.Gui.Drivers.Driver.Force16Colors ? ((ColorPicker16)cp).SelectedColor : ((ColorPicker)cp).SelectedColor;
return accept;
}

View File

@@ -176,7 +176,7 @@ public class UICatalogRunnable : Runnable
_force16ColorsMenuItemCb = new ()
{
Title = "Force _16 Colors",
CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
CheckedState = Terminal.Gui.Drivers.Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
// Best practice for CheckBoxes in menus is to disable focus and highlight states
CanFocus = false,
HighlightStates = MouseState.None
@@ -184,7 +184,7 @@ public class UICatalogRunnable : Runnable
_force16ColorsMenuItemCb.CheckedStateChanging += (sender, args) =>
{
if (Application.Force16Colors
if (Terminal.Gui.Drivers.Driver.Force16Colors
&& args.Result == CheckState.UnChecked
&& !Application.Driver!.SupportsTrueColor)
{
@@ -194,7 +194,7 @@ public class UICatalogRunnable : Runnable
_force16ColorsMenuItemCb.CheckedStateChanged += (sender, args) =>
{
Application.Force16Colors = args.Value == CheckState.Checked;
Terminal.Gui.Drivers.Driver.Force16Colors = args.Value == CheckState.Checked;
_force16ColorsShortcutCb!.CheckedState = args.Value;
Application.LayoutAndDraw ();
@@ -646,13 +646,13 @@ public class UICatalogRunnable : Runnable
_force16ColorsShortcutCb = new ()
{
Title = "16 color mode",
CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
CheckedState = Terminal.Gui.Drivers.Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
CanFocus = false
};
_force16ColorsShortcutCb.CheckedStateChanging += (sender, args) =>
{
if (Application.Force16Colors
if (Terminal.Gui.Drivers.Driver.Force16Colors
&& args.Result == CheckState.UnChecked
&& !Application.Driver!.SupportsTrueColor)
{
@@ -663,7 +663,7 @@ public class UICatalogRunnable : Runnable
_force16ColorsShortcutCb.CheckedStateChanged += (sender, args) =>
{
Application.Force16Colors = args.Value == CheckState.Checked;
Terminal.Gui.Drivers.Driver.Force16Colors = args.Value == CheckState.Checked;
_force16ColorsMenuItemCb!.CheckedState = args.Value;
Application.LayoutAndDraw ();
};
@@ -714,7 +714,7 @@ public class UICatalogRunnable : Runnable
}
_disableMouseCb!.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked;
_force16ColorsShortcutCb!.CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
_force16ColorsShortcutCb!.CheckedState = Terminal.Gui.Drivers.Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
Application.TopRunnableView?.SetNeedsDraw ();
}

View File

@@ -13,25 +13,6 @@ public static partial class Application // Driver abstractions
internal set => ApplicationImpl.Instance.Driver = value;
}
private static bool _force16Colors = false; // Resources/config.json overrides
/// <inheritdoc cref="IApplication.Force16Colors"/>
[ConfigurationProperty (Scope = typeof (SettingsScope))]
[Obsolete ("The legacy static Application object is going away.")]
public static bool Force16Colors
{
get => _force16Colors;
set
{
bool oldValue = _force16Colors;
_force16Colors = value;
Force16ColorsChanged?.Invoke (null, new ValueChangedEventArgs<bool> (oldValue, _force16Colors));
}
}
/// <summary>Raised when <see cref="Force16Colors"/> changes.</summary>
public static event EventHandler<ValueChangedEventArgs<bool>>? Force16ColorsChanged;
private static string _forceDriver = string.Empty; // Resources/config.json overrides
/// <inheritdoc cref="IApplication.ForceDriver"/>

View File

@@ -8,10 +8,7 @@ internal partial class ApplicationImpl
public IDriver? Driver { get; set; }
/// <inheritdoc/>
public bool Force16Colors { get; set; }
/// <inheritdoc/>
public string ForceDriver { get; set; } = string.Empty;
public string ForceDriver { get; internal set; } = string.Empty;
/// <inheritdoc/>
public List<SixelToRender> Sixel { get; } = new ();

View File

@@ -313,7 +313,7 @@ internal partial class ApplicationImpl
// Note: ForceDriver and Force16Colors are reset
// If they need to persist across Init/Shutdown cycles
// then the user of the library should manage that state
Force16Colors = false;
Terminal.Gui.Drivers.Driver.Force16Colors = false;
ForceDriver = string.Empty;
// === 11. Reset synchronization context ===
@@ -365,8 +365,6 @@ internal partial class ApplicationImpl
#endif
// Event handlers for Application static property changes
private void OnForce16ColorsChanged (object? sender, ValueChangedEventArgs<bool> e) { Force16Colors = e.NewValue; }
private void OnForceDriverChanged (object? sender, ValueChangedEventArgs<string> e) { ForceDriver = e.NewValue; }
/// <summary>
@@ -374,7 +372,6 @@ internal partial class ApplicationImpl
/// </summary>
private void UnsubscribeApplicationEvents ()
{
Application.Force16ColorsChanged -= OnForce16ColorsChanged;
Application.ForceDriverChanged -= OnForceDriverChanged;
}
}

View File

@@ -15,7 +15,6 @@ internal partial class ApplicationImpl : IApplication
internal ApplicationImpl ()
{
// Subscribe to Application static property change events
Application.Force16ColorsChanged += OnForce16ColorsChanged;
Application.ForceDriverChanged += OnForceDriverChanged;
}
@@ -146,7 +145,7 @@ internal partial class ApplicationImpl : IApplication
// Reset Application static properties to their defaults
// This ensures tests start with clean state
Application.ForceDriver = string.Empty;
Application.Force16Colors = false;
Drivers.Driver.Force16Colors = false;
Application.IsMouseDisabled = false;
Application.QuitKey = Key.Esc;
Application.ArrangeKey = Key.F5.WithCtrl;

View File

@@ -450,17 +450,10 @@ public interface IApplication : IDisposable
IClipboard? Clipboard { get; }
/// <summary>
/// Gets or sets whether <see cref="Driver"/> will be forced to output only the 16 colors defined in
/// <see cref="ColorName16"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be
/// output as long as the selected <see cref="IDriver"/> supports TrueColor.
/// Gets the name of the driver to force. One of "fake", "dotnet", "windows", or "unix".
/// If not specified, the driver is selected based on the platform.
/// </summary>
bool Force16Colors { get; set; }
/// <summary>
/// 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.
/// </summary>
string ForceDriver { get; set; }
string ForceDriver { get; }
/// <summary>
/// Gets or location and size of the application in the terminal. By default, the location is (0, 0) and the size

View File

@@ -81,7 +81,7 @@ public class NetOutput : OutputBase, IOutput
/// <inheritdoc/>
protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
{
if (Application.Force16Colors)
if (Terminal.Gui.Drivers.Driver.Force16Colors)
{
output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));

View File

@@ -0,0 +1,29 @@
namespace Terminal.Gui.Drivers;
/// <summary>
/// Provides driver-wide configuration settings.
/// </summary>
public static class Driver
{
private static bool _force16Colors = false; // Resources/config.json overrides
/// <summary>
/// Gets or sets whether drivers should use 16 colors instead of the default TrueColors.
/// This is a process-wide setting that is read by each driver instance at construction time.
/// </summary>
/// <remarks>
/// <para>
/// This setting is read by driver instances when they are created. Changing this value after
/// a driver has been initialized will not affect existing driver instances.
/// </para>
/// <para>
/// Individual drivers may override this if they do not support TrueColor output.
/// </para>
/// </remarks>
[ConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = false)]
public static bool Force16Colors
{
get => _force16Colors;
set => _force16Colors = value;
}
}

View File

@@ -31,6 +31,7 @@ internal class DriverImpl : IDriver
private readonly IOutput _output;
private readonly AnsiRequestScheduler _ansiRequestScheduler;
private CursorVisibility _lastCursor = CursorVisibility.Default;
private readonly bool _instanceForce16Colors;
/// <summary>
/// Initializes a new instance of the <see cref="DriverImpl"/> class.
@@ -53,6 +54,9 @@ internal class DriverImpl : IDriver
OutputBuffer = outputBuffer;
_ansiRequestScheduler = ansiRequestScheduler;
// Read the static Force16Colors value at construction time and store in instance field
_instanceForce16Colors = Terminal.Gui.Drivers.Driver.Force16Colors;
InputProcessor.KeyDown += (s, e) => KeyDown?.Invoke (s, e);
InputProcessor.KeyUp += (s, e) => KeyUp?.Invoke (s, e);
@@ -202,13 +206,15 @@ internal class DriverImpl : IDriver
public bool SupportsTrueColor => true;
/// <inheritdoc/>
public bool Force16Colors
bool IDriver.Force16Colors
{
get => Application.Force16Colors || !SupportsTrueColor;
set => Application.Force16Colors = value || !SupportsTrueColor;
get => _instanceForce16Colors || !SupportsTrueColor;
set => throw new InvalidOperationException ("Force16Colors is read-only per driver instance. Set Terminal.Gui.Drivers.Driver.Force16Colors static property before driver creation.");
}
/// <inheritdoc/>
public bool GetForce16Colors () => _instanceForce16Colors || !SupportsTrueColor;
/// <inheritdoc/>
public Attribute CurrentAttribute

View File

@@ -86,7 +86,7 @@ public class FakeOutput : OutputBase, IOutput
/// <inheritdoc/>
protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
{
if (Application.Force16Colors)
if (Terminal.Gui.Drivers.Driver.Force16Colors)
{
output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));

View File

@@ -84,8 +84,9 @@ public interface IDriver
bool SupportsTrueColor { get; }
/// <summary>
/// Gets or sets whether the <see cref="IDriver"/> should use 16 colors instead of the default TrueColors.
/// See <see cref="Application.Force16Colors"/> to change this setting via <see cref="ConfigurationManager"/>.
/// Gets whether the <see cref="IDriver"/> should use 16 colors instead of the default TrueColors.
/// This value is set at driver construction time from the static <c>Terminal.Gui.Drivers.Driver.Force16Colors</c> property.
/// Use <see cref="GetForce16Colors"/> to retrieve this value.
/// </summary>
/// <remarks>
/// <para>
@@ -95,6 +96,14 @@ public interface IDriver
/// </remarks>
bool Force16Colors { get; set; }
/// <summary>
/// Gets whether the <see cref="IDriver"/> is using 16 colors instead of TrueColors.
/// </summary>
/// <returns>
/// <see langword="true"/> if the driver is using 16 colors; otherwise, <see langword="false"/>.
/// </returns>
bool GetForce16Colors ();
/// <summary>
/// The <see cref="System.Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or
/// <see cref="AddStr"/>

View File

@@ -39,7 +39,7 @@ internal class UnixOutput : OutputBase, IOutput
/// <inheritdoc />
protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
{
if (Application.Force16Colors)
if (Terminal.Gui.Drivers.Driver.Force16Colors)
{
output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));

View File

@@ -1,5 +1,7 @@
#nullable disable

using DriverConfig = Terminal.Gui.Drivers.Driver;
namespace Terminal.Gui.Views;
public partial class ColorPicker
@@ -21,14 +23,14 @@ public partial class ColorPicker
var d = new Dialog
{
Title = title,
Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
Width = DriverConfig.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
Height = 20
};
var btnOk = new Button
{
X = Pos.Center () - 5,
Y = Application.Force16Colors ? 6 : 4,
Y = DriverConfig.Force16Colors ? 6 : 4,
Text = "Ok",
Width = Dim.Auto (),
IsDefault = true
@@ -63,7 +65,7 @@ public partial class ColorPicker
View cpForeground;
if (Application.Force16Colors)
if (DriverConfig.Force16Colors)
{
cpForeground = new ColorPicker16
{
@@ -88,7 +90,7 @@ public partial class ColorPicker
View cpBackground;
if (Application.Force16Colors)
if (DriverConfig.Force16Colors)
{
cpBackground = new ColorPicker16
{
@@ -117,8 +119,8 @@ public partial class ColorPicker
app.Run (d);
d.Dispose ();
Color newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor;
Color newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor;
Color newForeColor = DriverConfig.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor;
Color newBackColor = DriverConfig.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor;
newAttribute = new (newForeColor, newBackColor);
app.Dispose ();
return accept;

View File

@@ -391,7 +391,7 @@ public class Shortcut : View, IOrientation, IDesignable
/// <example>
/// <para>
/// This example illustrates how to add a <see cref="Shortcut"/> to a <see cref="StatusBar"/> that toggles the
/// <see cref="IApplication.Force16Colors"/> property.
/// <see cref="IDriver.Force16Colors"/> property.
/// </para>
/// <code>
/// var force16ColorsShortcut = new Shortcut
@@ -401,12 +401,12 @@ public class Shortcut : View, IOrientation, IDesignable
/// CommandView = new CheckBox { Text = "Force 16 Colors" }
/// };
/// var cb = force16ColorsShortcut.CommandView as CheckBox;
/// cb.Checked = Application.Force16Colors;
/// cb.Checked = Terminal.Gui.Drivers.Driver.Force16Colors;
///
/// cb.Toggled += (s, e) =>
/// {
/// var cb = s as CheckBox;
/// Application.Force16Colors = cb!.Checked == true;
/// Terminal.Gui.Drivers.Driver.Force16Colors = cb!.Checked == true;
/// Application.Refresh();
/// };
/// StatusBar.Add(force16ColorsShortcut);

View File

@@ -132,7 +132,7 @@ public class ApplicationTests (ITestOutputHelper output)
{
using IApplication app = Application.Create ();
app.ForceDriver = "fake";
Application.ForceDriver = "fake";
// Note: Init() without params picks up driver configuration
app.Init ();

View File

@@ -73,7 +73,7 @@ public class ToAnsiTests : FakeDriverBase
public void ToAnsi_With_Background_Colors (bool force16Colors, string expected)
{
IDriver driver = CreateFakeDriver (10, 2);
Application.Force16Colors = force16Colors;
Terminal.Gui.Drivers.Driver.Force16Colors = force16Colors;
// Set background color
driver.CurrentAttribute = new (Color.White, Color.Red);
@@ -210,7 +210,7 @@ public class ToAnsiTests : FakeDriverBase
IDriver driver = CreateFakeDriver (10, 1);
// Use RGB colors (when not forcing 16 colors)
Application.Force16Colors = false;
Terminal.Gui.Drivers.Driver.Force16Colors = false;
try
{
driver.CurrentAttribute = new Attribute (new Color (255, 0, 0), new Color (0, 255, 0));
@@ -224,7 +224,7 @@ public class ToAnsiTests : FakeDriverBase
}
finally
{
Application.Force16Colors = true; // Reset
Terminal.Gui.Drivers.Driver.Force16Colors = true; // Reset
}
}
@@ -234,7 +234,7 @@ public class ToAnsiTests : FakeDriverBase
IDriver driver = CreateFakeDriver (10, 1);
// Force 16 colors
Application.Force16Colors = true;
Terminal.Gui.Drivers.Driver.Force16Colors = true;
driver.CurrentAttribute = new Attribute (Color.Red, Color.Blue);
driver.AddStr ("16Color");

View File

@@ -44,8 +44,8 @@ public class GlobalTestSetup : IDisposable
//// Don't check Application.ForceDriver
//Assert.Empty (Application.ForceDriver);
//// Don't check Application.Force16Colors
////Assert.False (Application.Force16Colors);
//// Don't check Terminal.Gui.Drivers.Driver.Force16Colors
////Assert.False (Terminal.Gui.Drivers.Driver.Force16Colors);
//Assert.Null (Application.Driver);
//Assert.False (Application.StopAfterFirstIteration);
Assert.Equal (Key.Tab.WithShift, Application.PrevTabKey);