diff --git a/Terminal.Gui/App/ApplicationImpl.Screen.cs b/Terminal.Gui/App/ApplicationImpl.Screen.cs index 4f3f06af1e..b0f986d460 100644 --- a/Terminal.Gui/App/ApplicationImpl.Screen.cs +++ b/Terminal.Gui/App/ApplicationImpl.Screen.cs @@ -65,6 +65,11 @@ private void RaiseScreenChangedEvent (Rectangle screen) runnableView.SetNeedsLayout (); } } + + if (Popovers?.GetActivePopover () is View { Visible: true } visiblePopover) + { + visiblePopover.SetNeedsLayout (); + } } private void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) @@ -117,16 +122,9 @@ public void LayoutAndDraw (bool forceRedraw = false) List views = [.. SessionStack.Select (r => r.Runnable! as View)!]; - if (Popovers?.GetActivePopover () is { Visible: true } visiblePopover) + if (Popovers?.GetActivePopover () is View { Visible: true, NeedsLayout: true } visiblePopoverNeedingLayout) { - visiblePopover.SetNeedsDraw (); - visiblePopover.SetNeedsLayout (); - - // Need View for views.Insert - if (visiblePopover is View popoverView) - { - views.Insert (0, popoverView); - } + views.Insert (0, visiblePopoverNeedingLayout); } // Layout @@ -240,6 +238,30 @@ public void LayoutAndDraw (bool forceRedraw = false) // Draw bool needsDraw = forceRedraw || views.Any (v => v is { NeedsDraw: true } or { SubViewNeedsDraw: true }); + if (Popovers?.GetActivePopover () is View { Visible: true } visiblePopover) + { + if (needsDraw) + { + visiblePopover.SetNeedsDraw (); + + if (!views.Contains (visiblePopover)) + { + views.Insert (0, visiblePopover); + } + } + else if (visiblePopover.NeedsDraw || visiblePopover.SubViewNeedsDraw) + { + visiblePopover.SetNeedsDraw (); + + if (!views.Contains (visiblePopover)) + { + views.Insert (0, visiblePopover); + } + + needsDraw = true; + } + } + if (Driver is { } && (neededLayout || needsDraw)) { Logging.Redraws.Add (1); diff --git a/Tests/UnitTestsParallelizable/Application/Popover/Application.PopoverTests.cs b/Tests/UnitTestsParallelizable/Application/Popover/Application.PopoverTests.cs index efd9d15537..c3a0d5de47 100644 --- a/Tests/UnitTestsParallelizable/Application/Popover/Application.PopoverTests.cs +++ b/Tests/UnitTestsParallelizable/Application/Popover/Application.PopoverTests.cs @@ -286,12 +286,184 @@ public void LayoutAndDraw_ScreenResize_LayoutsVisiblePopover () top.Dispose (); } + // CoPilot - ChatGPT v4 + [Fact] + public void LayoutAndDraw_VisibleIdlePopover_DoesNotRaiseLayoutAndDrawComplete () + { + // Arrange + using IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + app.Driver!.SetScreenSize (80, 25); + + Runnable top = new () { App = app }; + SessionToken? token = app.Begin (top); + + PopoverTestClass popover = new () + { + App = app, + Width = 10, + Height = 5 + }; + app.Popovers!.Register (popover); + app.Popovers.Show (popover); + + // Clear the initial invalidation caused by showing the popover. + app.LayoutAndDraw (); + + int layoutAndDrawCompleteCount = 0; + app.LayoutAndDrawComplete += CountLayoutAndDrawComplete; + + // Act + app.LayoutAndDraw (); + + // Assert + Assert.Equal (0, layoutAndDrawCompleteCount); + + app.LayoutAndDrawComplete -= CountLayoutAndDrawComplete; + app.End (token!); + top.Dispose (); + + return; + + void CountLayoutAndDrawComplete (object? _, EventArgs __) => layoutAndDrawCompleteCount++; + } + + // CoPilot - ChatGPT v4 + [Fact] + public void LayoutAndDraw_VisiblePopoverNeedingDraw_RaisesLayoutAndDrawComplete () + { + // Arrange + using IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + app.Driver!.SetScreenSize (80, 25); + + Runnable top = new () { App = app }; + SessionToken? token = app.Begin (top); + + PopoverTestClass popover = new () + { + App = app, + Width = 10, + Height = 5 + }; + app.Popovers!.Register (popover); + app.Popovers.Show (popover); + app.LayoutAndDraw (); + + int layoutAndDrawCompleteCount = 0; + app.LayoutAndDrawComplete += CountLayoutAndDrawComplete; + popover.SetNeedsDraw (); + + // Act + app.LayoutAndDraw (); + + // Assert + Assert.Equal (1, layoutAndDrawCompleteCount); + + app.LayoutAndDrawComplete -= CountLayoutAndDrawComplete; + app.End (token!); + top.Dispose (); + + return; + + void CountLayoutAndDrawComplete (object? _, EventArgs __) => layoutAndDrawCompleteCount++; + } + + // CoPilot - ChatGPT v4 + [Fact] + public void LayoutAndDraw_VisiblePopoverNeedingLayout_RaisesLayoutAndDrawComplete () + { + // Arrange + using IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + app.Driver!.SetScreenSize (80, 25); + + Runnable top = new () { App = app }; + SessionToken? token = app.Begin (top); + + PopoverTestClass popover = new () + { + App = app, + Width = 10, + Height = 5 + }; + app.Popovers!.Register (popover); + app.Popovers.Show (popover); + app.LayoutAndDraw (); + + int layoutAndDrawCompleteCount = 0; + app.LayoutAndDrawComplete += CountLayoutAndDrawComplete; + popover.SetNeedsLayout (); + + // Act + app.LayoutAndDraw (); + + // Assert + Assert.Equal (1, layoutAndDrawCompleteCount); + + app.LayoutAndDrawComplete -= CountLayoutAndDrawComplete; + app.End (token!); + top.Dispose (); + + return; + + void CountLayoutAndDrawComplete (object? _, EventArgs __) => layoutAndDrawCompleteCount++; + } + + // CoPilot - ChatGPT v4 + [Fact] + public void LayoutAndDraw_UnderlyingViewNeedsDraw_RedrawsPopoverWithoutLayout () + { + // Arrange + using IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + app.Driver!.SetScreenSize (80, 25); + + Runnable top = new () { App = app }; + + View content = new () + { + Width = 10, + Height = 5 + }; + top.Add (content); + + SessionToken? token = app.Begin (top); + + PopoverTestClass popover = new () + { + App = app, + Width = 10, + Height = 5 + }; + app.Popovers!.Register (popover); + app.Popovers.Show (popover); + + app.LayoutAndDraw (); + popover.DrawCompleteCount = 0; + popover.LayoutPassCount = 0; + + content.SetNeedsDraw (); + + // Act + app.LayoutAndDraw (); + + // Assert + Assert.Equal (1, popover.DrawCompleteCount); + Assert.Equal (0, popover.LayoutPassCount); + + app.End (token!); + top.Dispose (); + } + public class PopoverTestClass : View, IPopoverView { public List HandledKeys { get; } = []; public int NewCommandInvokeCount { get; private set; } + public int DrawCompleteCount { get; set; } + public int LayoutPassCount { get; set; } public bool HandleNewCommand { get; set; } @@ -380,6 +552,12 @@ protected override bool OnKeyDown (Key key) return false; } + protected override void OnDrawComplete (DrawContext? context) + { + DrawCompleteCount++; + base.OnDrawComplete (context); + } + protected override void OnSubViewLayout (LayoutEventArgs args) { LayoutPassCount++;