diff --git a/Terminal.Gui/App/Popovers/Popover.cs b/Terminal.Gui/App/Popovers/Popover.cs index 0a2f323445..eec4d7ff4f 100644 --- a/Terminal.Gui/App/Popovers/Popover.cs +++ b/Terminal.Gui/App/Popovers/Popover.cs @@ -350,6 +350,19 @@ protected override void OnVisibleChanged () base.OnVisibleChanged (); // PopoverImpl handles Hide } + /// + protected override void OnFrameChanged (in Rectangle frame) + { + base.OnFrameChanged (in frame); + + if (!Visible || Anchor is null || ContentView is null) + { + return; + } + + SetPosition (anchor: Anchor ()); + } + /// /// Extracts the result from the content view using or . /// diff --git a/Tests/UnitTestsParallelizable/Application/Popover/Application.PopoverTests.cs b/Tests/UnitTestsParallelizable/Application/Popover/Application.PopoverTests.cs index dc4cd38a3c..efd9d15537 100644 --- a/Tests/UnitTestsParallelizable/Application/Popover/Application.PopoverTests.cs +++ b/Tests/UnitTestsParallelizable/Application/Popover/Application.PopoverTests.cs @@ -250,12 +250,50 @@ public void Hide_NonActivePopover_DoesNotAffectActivePopover () Assert.Equal (initialVisibleState, popover2.Visible); } + // CoPilot - ChatGPT v4 + [Fact] + public void LayoutAndDraw_ScreenResize_LayoutsVisiblePopover () + { + // 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 (); + popover.LayoutPassCount = 0; + + app.Driver.SetScreenSize (100, 30); + + // Act + app.LayoutAndDraw (); + + // Assert + Assert.True (popover.LayoutPassCount > 0); + + app.End (token!); + top.Dispose (); + } + public class PopoverTestClass : View, IPopoverView { public List HandledKeys { get; } = []; public int NewCommandInvokeCount { get; private set; } + public int LayoutPassCount { get; set; } + public bool HandleNewCommand { get; set; } /// @@ -342,6 +380,12 @@ protected override bool OnKeyDown (Key key) return false; } + protected override void OnSubViewLayout (LayoutEventArgs args) + { + LayoutPassCount++; + base.OnSubViewLayout (args); + } + /// public IRunnable? Owner { get; set; } diff --git a/Tests/UnitTestsParallelizable/Views/DropDownListTests.cs b/Tests/UnitTestsParallelizable/Views/DropDownListTests.cs index 45012aea99..680a132fd7 100644 --- a/Tests/UnitTestsParallelizable/Views/DropDownListTests.cs +++ b/Tests/UnitTestsParallelizable/Views/DropDownListTests.cs @@ -900,6 +900,120 @@ public void Scrolling_TallDropdown_TopItemsDraw () app.End (token!); } + // CoPilot - ChatGPT v4 + [Fact] + public void VisiblePopover_Repositions_WhenTerminalIsResized () + { + // Arrange + using IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + app.Driver!.SetScreenSize (40, 15); + + using Runnable top = new (); + SessionToken? token = app.Begin (top); + + ObservableCollection items = ["Alpha", "Beta", "Gamma"]; + + DropDownList dropdown = new () + { + X = Pos.Center (), + Y = Pos.Center (), + Width = 12, + Source = new ListWrapper (items), + ReadOnly = true + }; + + top.Add (dropdown); + app.LayoutAndDraw (); + + dropdown.SetFocus (); + app.InjectKey (Key.F4); + app.LayoutAndDraw (); + + Popover? popover = FindDropDownPopover (app) as Popover; + Assert.NotNull (popover); + Assert.True (popover.Visible); + + Rectangle initialListFrame = popover.ContentView!.FrameToScreen (); + Rectangle initialDropDownFrame = dropdown.FrameToScreen (); + + // Act + app.Driver.SetScreenSize (60, 25); + app.LayoutAndDraw (); + + // Assert + Rectangle resizedListFrame = popover.ContentView.FrameToScreen (); + Rectangle resizedDropDownFrame = dropdown.FrameToScreen (); + + Assert.NotEqual (initialDropDownFrame.Location, resizedDropDownFrame.Location); + Assert.Equal (resizedDropDownFrame.X, resizedListFrame.X); + Assert.Equal (resizedDropDownFrame.Bottom, resizedListFrame.Y); + Assert.NotEqual (initialListFrame.Location, resizedListFrame.Location); + + app.End (token!); + } + + // CoPilot - ChatGPT v4 + [Fact] + public void VisiblePopover_LayoutsHeight_WhenTerminalIsResized () + { + // Arrange + using IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + app.Driver!.SetScreenSize (30, 10); + + using Runnable top = new (); + SessionToken? token = app.Begin (top); + + ObservableCollection items = + [ + "Item_00", + "Item_01", + "Item_02", + "Item_03", + "Item_04", + "Item_05", + "Item_06", + "Item_07", + "Item_08", + "Item_09" + ]; + + DropDownList dropdown = new () + { + X = 0, + Y = 0, + Width = 12, + Source = new ListWrapper (items), + ReadOnly = true + }; + + top.Add (dropdown); + app.LayoutAndDraw (); + + dropdown.SetFocus (); + app.InjectKey (Key.F4); + app.LayoutAndDraw (); + + Popover? popover = FindDropDownPopover (app) as Popover; + Assert.NotNull (popover); + Assert.True (popover.Visible); + + int initialHeight = popover.ContentView!.Frame.Height; + Assert.Equal (9, initialHeight); + + // Act + app.Driver.SetScreenSize (30, 5); + app.LayoutAndDraw (); + + // Assert + int resizedHeight = popover.ContentView.Frame.Height; + Assert.Equal (4, resizedHeight); + Assert.NotEqual (initialHeight, resizedHeight); + + app.End (token!); + } + // Helper to find the DropDownList popover (excludes the context menu popover) private static IPopoverView? FindDropDownPopover (IApplication app) => app.Popovers?.Popovers.OfType> ().FirstOrDefault (); }