From c653dc37c3d17eb63228d06f9be99155be62ffa2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 21:18:08 +0000 Subject: [PATCH] Refactor ApplicationImpl to reduce dependency on Application static class - Remove Application.DefaultMaximumIterationsPerSecond dependency, use literal value - Move GetSupportedCultures() and GetAvailableCulturesFromEmbeddedResources() to ApplicationImpl - Add instance methods SubscribeDriverEvents() and UnsubscribeDriverEvents() in ApplicationImpl - Use instance _top field instead of Application.Top in Invoke() - Use instance _forceDriver field instead of Application.ForceDriver - ApplicationImpl now has minimal coupling to Application facade for static event management Co-authored-by: tig <585482+tig@users.noreply.github.com> --- Terminal.Gui/App/ApplicationImpl.cs | 88 +++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/App/ApplicationImpl.cs b/Terminal.Gui/App/ApplicationImpl.cs index 789832a4f..1e101fd15 100644 --- a/Terminal.Gui/App/ApplicationImpl.cs +++ b/Terminal.Gui/App/ApplicationImpl.cs @@ -3,6 +3,8 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Reflection; +using System.Resources; using Microsoft.Extensions.Logging; using Terminal.Gui.Drivers; @@ -31,7 +33,7 @@ public class ApplicationImpl : IApplication private readonly object _lockScreen = new (); private Rectangle? _screen; private bool _clearScreenNextIteration; - private ushort _maximumIterationsPerSecond = Application.DefaultMaximumIterationsPerSecond; + private ushort _maximumIterationsPerSecond = 25; // Default value for MaximumIterationsPerSecond private List? _supportedCultures; // When `End ()` is called, it is possible `RunState.Toplevel` is a different object than `Top`. @@ -198,7 +200,7 @@ public class ApplicationImpl : IApplication { if (_supportedCultures is null) { - _supportedCultures = Application.GetSupportedCultures (); + _supportedCultures = GetSupportedCultures (); } return _supportedCultures; } @@ -266,7 +268,7 @@ public class ApplicationImpl : IApplication if (string.IsNullOrWhiteSpace (_driverName)) { - _driverName = Application.ForceDriver; + _driverName = _forceDriver; } Debug.Assert(_navigation is null); @@ -303,7 +305,7 @@ public class ApplicationImpl : IApplication _initialized = true; Application.OnInitializedChanged (this, new (true)); - Application.SubscribeDriverEvents (); + SubscribeDriverEvents (); SynchronizationContext.SetSynchronizationContext (new ()); _mainThreadId = Thread.CurrentThread.ManagedThreadId; @@ -534,7 +536,7 @@ public class ApplicationImpl : IApplication public void Invoke (Action action) { // If we are already on the main UI thread - if (Application.Top is { Running: true } && _mainThreadId == Thread.CurrentThread.ManagedThreadId) + if (_top is { Running: true } && _mainThreadId == Thread.CurrentThread.ManagedThreadId) { action (); return; @@ -639,7 +641,7 @@ public class ApplicationImpl : IApplication // Driver stuff if (_driver is { }) { - Application.UnsubscribeDriverEvents (); + UnsubscribeDriverEvents (); _driver?.End (); _driver = null; } @@ -686,4 +688,78 @@ public class ApplicationImpl : IApplication _screen = null; } } + + private void SubscribeDriverEvents () + { + if (_driver is null) + { + throw new ArgumentNullException (nameof (_driver)); + } + + _driver.SizeChanged += Driver_SizeChanged; + _driver.KeyDown += Driver_KeyDown; + _driver.KeyUp += Driver_KeyUp; + _driver.MouseEvent += Driver_MouseEvent; + } + + private void UnsubscribeDriverEvents () + { + if (_driver is null) + { + throw new ArgumentNullException (nameof (_driver)); + } + + _driver.SizeChanged -= Driver_SizeChanged; + _driver.KeyDown -= Driver_KeyDown; + _driver.KeyUp -= Driver_KeyUp; + _driver.MouseEvent -= Driver_MouseEvent; + } + + private void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { Application.OnSizeChanging (e); } + private void Driver_KeyDown (object? sender, Key e) { Application.RaiseKeyDownEvent (e); } + private void Driver_KeyUp (object? sender, Key e) { Application.RaiseKeyUpEvent (e); } + private void Driver_MouseEvent (object? sender, MouseEventArgs e) { Application.RaiseMouseEvent (e); } + + private static List GetAvailableCulturesFromEmbeddedResources () + { + ResourceManager rm = new (typeof (Strings)); + + CultureInfo [] cultures = CultureInfo.GetCultures (CultureTypes.AllCultures); + + return cultures.Where ( + cultureInfo => + !cultureInfo.Equals (CultureInfo.InvariantCulture) + && rm.GetResourceSet (cultureInfo, true, false) is { } + ) + .ToList (); + } + + // BUGBUG: This does not return en-US even though it's supported by default + private static List GetSupportedCultures () + { + CultureInfo [] cultures = CultureInfo.GetCultures (CultureTypes.AllCultures); + + // Get the assembly + var assembly = Assembly.GetExecutingAssembly (); + + //Find the location of the assembly + string assemblyLocation = AppDomain.CurrentDomain.BaseDirectory; + + // Find the resource file name of the assembly + var resourceFilename = $"{assembly.GetName ().Name}.resources.dll"; + + 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 => + Directory.Exists (Path.Combine (assemblyLocation, cultureInfo.Name)) + && File.Exists (Path.Combine (assemblyLocation, cultureInfo.Name, resourceFilename)) + ) + .ToList (); + } + + // It's called from a self-contained single-file and get available cultures from the embedded resources strings. + return GetAvailableCulturesFromEmbeddedResources (); + } }