diff --git a/Terminal.Gui/App/ApplicationImpl.Screen.cs b/Terminal.Gui/App/ApplicationImpl.Screen.cs index 98c4492769..98f9c40ea8 100644 --- a/Terminal.Gui/App/ApplicationImpl.Screen.cs +++ b/Terminal.Gui/App/ApplicationImpl.Screen.cs @@ -26,15 +26,24 @@ public Rectangle Screen // Resize the output buffer to match the inline region dimensions. _screen = value; (Driver as DriverImpl)?.ResizeOutputBuffer (value.Width, value.Height); + + RaiseScreenChangedEvent (value); + + return; } - else + + _screen = null; + + if (Driver is null) { - // Fullscreen: sync with Driver.Screen (resizes both terminal tracking and buffer). - _screen = null; - Driver?.SetScreenSize (value.Width, value.Height); + RaiseScreenChangedEvent (Screen); + + return; } - RaiseScreenChangedEvent (Screen); + // Fullscreen: sync with Driver.Screen (resizes both terminal tracking and buffer). + // Driver_SizeChanged will raise ScreenChanged and invalidate layout. + Driver.SetScreenSize (value.Width, value.Height); } } diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs index 3a9da5a3b4..5c55d3ab84 100644 --- a/Terminal.Gui/App/IApplication.cs +++ b/Terminal.Gui/App/IApplication.cs @@ -596,6 +596,13 @@ public interface IApplication : IDisposable /// If the has not been initialized, this will return a default size of 2048x2048; useful /// for unit tests. /// + /// + /// In mode, setting this property delegates to + /// when the Driver has been initialized. If the Driver + /// is (before or after disposal), + /// the setter raises with the current getter value but does not call the Driver. + /// To simulate a terminal resize in tests, prefer calling directly. + /// /// Rectangle Screen { get; set; } diff --git a/Tests/UnitTestsParallelizable/Application/ScreenTests.cs b/Tests/UnitTestsParallelizable/Application/ScreenTests.cs index d8e7396106..9f7b040e82 100644 --- a/Tests/UnitTestsParallelizable/Application/ScreenTests.cs +++ b/Tests/UnitTestsParallelizable/Application/ScreenTests.cs @@ -411,6 +411,41 @@ public void Screen_Property_Allows_Setting_With_Zero_Origin () Assert.Equal (new (0, 0, 100, 50), app.Screen); } + // Copilot + [Fact] + public void Screen_Property_Setting_Before_Begin_Raises_ScreenChanged_Once () + { + // Arrange + using IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + + int eventCount = 0; + Rectangle? newScreen = null; + + EventHandler> handler = (_, args) => + { + eventCount++; + newScreen = args.Value; + }; + + app.ScreenChanged += handler; + + try + { + // Act + app.Screen = new (0, 0, 100, 50); + + // Assert + Assert.Equal (1, eventCount); + Assert.Equal (new (0, 0, 100, 50), newScreen); + Assert.Equal (new (0, 0, 100, 50), app.Screen); + } + finally + { + app.ScreenChanged -= handler; + } + } + [Fact] public void Screen_Property_Setting_Raises_ScreenChanged_Event () { @@ -479,6 +514,44 @@ public void Screen_Property_Thread_Safe_Access () #endregion Screen Property Tests + // Copilot + [Fact] + public void Screen_Setter_Before_Driver_Init_Fires_ScreenChanged_Without_Delegating_To_Driver () + { + // Arrange — Create app but do NOT call Init, so Driver remains null. + using IApplication app = Application.Create (); + + // Verify precondition: Driver is null before Init. + Assert.Null (app.Driver); + + var eventCount = 0; + Rectangle? reported = null; + + EventHandler> handler = (_, args) => + { + eventCount++; + reported = args.Value; + }; + + app.ScreenChanged += handler; + + try + { + // Act — setting Screen with no Driver should still raise ScreenChanged. + app.Screen = new (0, 0, 120, 40); + + // Assert — event fired exactly once with the default getter value (2048x2048) + // because _screen is reset to null in fullscreen mode and Driver is null. + Assert.Equal (1, eventCount); + Assert.NotNull (reported); + Assert.Equal (new (0, 0, 2048, 2048), reported.Value); + } + finally + { + app.ScreenChanged -= handler; + } + } + #region Inline Mode Screen Tests // Copilot