diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs
index 75a8a822d..b7038268f 100644
--- a/Terminal.Gui/Core/Toplevel.cs
+++ b/Terminal.Gui/Core/Toplevel.cs
@@ -210,7 +210,7 @@ namespace Terminal.Gui {
Application.GrabbingMouse += Application_GrabbingMouse;
Application.UnGrabbingMouse += Application_UnGrabbingMouse;
-
+
// TODO: v2 - ALL Views (Responders??!?!) should support the commands related to
// - Focus
// Move the appropriate AddCommand calls to `Responder`
@@ -378,8 +378,7 @@ namespace Terminal.Gui {
///
/// if was already loaded by the
- /// , otherwise. This is used to avoid the
- /// having wrong values while this was not yet loaded.
+ /// , otherwise.
///
public bool IsLoaded { get; private set; }
@@ -627,11 +626,15 @@ namespace Terminal.Gui {
l = top.SuperView.Frame.Width;
superView = top.SuperView;
}
- nx = Math.Max (x, 0);
- nx = nx + top.Frame.Width > l ? Math.Max (l - top.Frame.Width, 0) : nx;
var mfLength = top.Border?.DrawMarginFrame == true ? 2 : 1;
- if (nx + mfLength > top.Frame.X + top.Frame.Width) {
- nx = Math.Max (top.Frame.Right - mfLength, 0);
+ if (top.Frame.Width <= l) {
+ nx = Math.Max (x, 0);
+ nx = nx + top.Frame.Width > l ? Math.Max (l - top.Frame.Width, 0) : nx;
+ if (nx + mfLength > top.Frame.X + top.Frame.Width) {
+ nx = Math.Max (top.Frame.Right - mfLength, 0);
+ }
+ } else {
+ nx = x;
}
//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
bool m, s;
@@ -640,7 +643,7 @@ namespace Terminal.Gui {
mb = Application.Top.MenuBar;
} else {
var t = top.SuperView;
- while (!(t is Toplevel)) {
+ while (t is not Toplevel) {
t = t.SuperView;
}
m = ((Toplevel)t).MenuBar?.Visible == true;
@@ -657,7 +660,7 @@ namespace Terminal.Gui {
sb = Application.Top.StatusBar;
} else {
var t = top.SuperView;
- while (!(t is Toplevel)) {
+ while (t is not Toplevel) {
t = t.SuperView;
}
s = ((Toplevel)t).StatusBar?.Visible == true;
@@ -669,9 +672,11 @@ namespace Terminal.Gui {
l = s ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
}
ny = Math.Min (ny, l);
- ny = ny + top.Frame.Height >= l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
- if (ny + mfLength > top.Frame.Y + top.Frame.Height) {
- ny = Math.Max (top.Frame.Bottom - mfLength, 0);
+ if (top.Frame.Height <= l) {
+ ny = ny + top.Frame.Height >= l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
+ if (ny + mfLength > top.Frame.Y + top.Frame.Height) {
+ ny = Math.Max (top.Frame.Bottom - mfLength, 0);
+ }
}
//System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
@@ -698,15 +703,15 @@ namespace Terminal.Gui {
var superView = EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y,
out int nx, out int ny, out _, out StatusBar sb);
bool layoutSubviews = false;
- if ((top?.SuperView != null || (top != Application.Top && top.Modal)
+ if ((superView != top || top?.SuperView != null || (top != Application.Top && top.Modal)
|| (top?.SuperView == null && top.IsMdiChild))
- && (nx > top.Frame.X || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
+ && (top.Frame.X + top.Frame.Width > Driver.Cols || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
- if ((top.X == null || top.X is Pos.PosAbsolute) && top.Bounds.X != nx) {
+ if ((top.X == null || top.X is Pos.PosAbsolute) && top.Frame.X != nx) {
top.X = nx;
layoutSubviews = true;
}
- if ((top.Y == null || top.Y is Pos.PosAbsolute) && top.Bounds.Y != ny) {
+ if ((top.Y == null || top.Y is Pos.PosAbsolute) && top.Frame.Y != ny) {
top.Y = ny;
layoutSubviews = true;
}
@@ -994,6 +999,13 @@ namespace Terminal.Gui {
{
return MostFocused?.OnLeave (view) ?? base.OnLeave (view);
}
+
+ ///
+ protected override void Dispose (bool disposing)
+ {
+ dragPosition = null;
+ base.Dispose (disposing);
+ }
}
///
diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs
index c5a95a34e..559d90219 100644
--- a/UnitTests/TopLevels/ToplevelTests.cs
+++ b/UnitTests/TopLevels/ToplevelTests.cs
@@ -149,7 +149,6 @@ namespace Terminal.Gui.TopLevelTests {
[AutoInitShutdown]
public void Internal_Tests ()
{
- Toplevel.dragPosition = null; // dragPosition is `static` and must be reset for each instance or unit tests will fail?
var top = new Toplevel ();
var eventInvoked = "";
@@ -200,7 +199,7 @@ namespace Terminal.Gui.TopLevelTests {
Application.Begin (top);
Assert.Equal (top, Application.Top);
- // top is Application.Top without menu and status bar.
+ // Application.Top without menu and status bar.
var supView = top.EnsureVisibleBounds (top, 2, 2, out int nx, out int ny, out MenuBar mb, out StatusBar sb);
Assert.Equal (Application.Top, supView);
Assert.Equal (0, nx);
@@ -211,7 +210,7 @@ namespace Terminal.Gui.TopLevelTests {
top.AddMenuStatusBar (new MenuBar ());
Assert.NotNull (top.MenuBar);
- // top is Application.Top with a menu and without status bar.
+ // Application.Top with a menu and without status bar.
top.EnsureVisibleBounds (top, 2, 2, out nx, out ny, out mb, out sb);
Assert.Equal (0, nx);
Assert.Equal (1, ny);
@@ -221,20 +220,24 @@ namespace Terminal.Gui.TopLevelTests {
top.AddMenuStatusBar (new StatusBar ());
Assert.NotNull (top.StatusBar);
- // top is Application.Top with a menu and status bar.
+ // Application.Top with a menu and status bar.
top.EnsureVisibleBounds (top, 2, 2, out nx, out ny, out mb, out sb);
Assert.Equal (0, nx);
- Assert.Equal (1, ny);
+ // The available height is lower than the Application.Top height minus
+ // the menu bar and status bar, then the top can go beyond the bottom
+ Assert.Equal (2, ny);
Assert.NotNull (mb);
Assert.NotNull (sb);
top.RemoveMenuStatusBar (top.MenuBar);
Assert.Null (top.MenuBar);
- // top is Application.Top without a menu and with a status bar.
+ // Application.Top without a menu and with a status bar.
top.EnsureVisibleBounds (top, 2, 2, out nx, out ny, out mb, out sb);
Assert.Equal (0, nx);
- Assert.Equal (0, ny);
+ // The available height is lower than the Application.Top height minus
+ // the status bar, then the top can go beyond the bottom
+ Assert.Equal (2, ny);
Assert.Null (mb);
Assert.NotNull (sb);
@@ -252,7 +255,7 @@ namespace Terminal.Gui.TopLevelTests {
supView = win.EnsureVisibleBounds (win, 0, 0, out nx, out ny, out mb, out sb);
Assert.Equal (Application.Top, supView);
- // top is Application.Top without menu and status bar.
+ // Application.Top without menu and status bar.
top.EnsureVisibleBounds (win, 0, 0, out nx, out ny, out mb, out sb);
Assert.Equal (0, nx);
Assert.Equal (0, ny);
@@ -262,7 +265,7 @@ namespace Terminal.Gui.TopLevelTests {
top.AddMenuStatusBar (new MenuBar ());
Assert.NotNull (top.MenuBar);
- // top is Application.Top with a menu and without status bar.
+ // Application.Top with a menu and without status bar.
top.EnsureVisibleBounds (win, 2, 2, out nx, out ny, out mb, out sb);
Assert.Equal (0, nx);
Assert.Equal (1, ny);
@@ -272,10 +275,12 @@ namespace Terminal.Gui.TopLevelTests {
top.AddMenuStatusBar (new StatusBar ());
Assert.NotNull (top.StatusBar);
- // top is Application.Top with a menu and status bar.
+ // Application.Top with a menu and status bar.
top.EnsureVisibleBounds (win, 30, 20, out nx, out ny, out mb, out sb);
Assert.Equal (0, nx);
- Assert.Equal (1, ny);
+ // The available height is lower than the Application.Top height minus
+ // the menu bar and status bar, then the top can go beyond the bottom
+ Assert.Equal (20, ny);
Assert.NotNull (mb);
Assert.NotNull (sb);
@@ -289,7 +294,7 @@ namespace Terminal.Gui.TopLevelTests {
win = new Window () { Width = 60, Height = 15 };
top.Add (win);
- // top is Application.Top without menu and status bar.
+ // Application.Top without menu and status bar.
top.EnsureVisibleBounds (win, 0, 0, out nx, out ny, out mb, out sb);
Assert.Equal (0, nx);
Assert.Equal (0, ny);
@@ -299,7 +304,7 @@ namespace Terminal.Gui.TopLevelTests {
top.AddMenuStatusBar (new MenuBar ());
Assert.NotNull (top.MenuBar);
- // top is Application.Top with a menu and without status bar.
+ // Application.Top with a menu and without status bar.
top.EnsureVisibleBounds (win, 2, 2, out nx, out ny, out mb, out sb);
Assert.Equal (2, nx);
Assert.Equal (2, ny);
@@ -309,7 +314,7 @@ namespace Terminal.Gui.TopLevelTests {
top.AddMenuStatusBar (new StatusBar ());
Assert.NotNull (top.StatusBar);
- // top is Application.Top with a menu and status bar.
+ // Application.Top with a menu and status bar.
top.EnsureVisibleBounds (win, 30, 20, out nx, out ny, out mb, out sb);
Assert.Equal (20, nx); // 20+60=80
Assert.Equal (9, ny); // 9+15+1(mb)=25
@@ -1213,5 +1218,151 @@ namespace Terminal.Gui.TopLevelTests {
});
Assert.Equal (scrollView, Application.MouseGrabView);
}
+
+ [Fact, AutoInitShutdown]
+ public void Dialog_Bounds_Bigger_Than_Driver_Cols_And_Rows_Allow_Drag_Beyond_Left_Right_And_Bottom ()
+ {
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem("File", new MenuItem [] {
+ new MenuItem("New", "", null)
+ })
+ });
+
+ var sb = new StatusBar (new StatusItem [] {
+ new StatusItem(Key.N, "~CTRL-N~ New", null)
+ });
+ var top = Application.Top;
+ top.Add (menu, sb);
+ var dialog = new Dialog ("Dialog", 20, 3, new Button ("Ok"));
+ Application.Begin (top);
+ ((FakeDriver)Application.Driver).SetBufferSize (40, 10);
+ Application.Begin (dialog);
+ Application.Refresh ();
+ Assert.Equal (new Rect (0, 0, 40, 10), top.Frame);
+ Assert.Equal (new Rect (10, 3, 20, 3), dialog.Frame);
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+ File
+
+
+ ┌ Dialog ──────────┐
+ │ [ Ok ] │
+ └──────────────────┘
+
+
+
+ CTRL-N New ", output);
+
+ Assert.Null (Application.MouseGrabView);
+
+ ReflectionTools.InvokePrivate (
+ typeof (Application),
+ "ProcessMouseEvent",
+ new MouseEvent () {
+ X = 10,
+ Y = 3,
+ Flags = MouseFlags.Button1Pressed
+ });
+
+ Assert.Equal (dialog, Application.MouseGrabView);
+
+ ReflectionTools.InvokePrivate (
+ typeof (Application),
+ "ProcessMouseEvent",
+ new MouseEvent () {
+ X = -11,
+ Y = -4,
+ Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+ });
+
+ Application.Refresh ();
+ Assert.Equal (new Rect (0, 0, 40, 10), top.Frame);
+ Assert.Equal (new Rect (0, 1, 20, 3), dialog.Frame);
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+ File
+┌ Dialog ──────────┐
+│ [ Ok ] │
+└──────────────────┘
+
+
+
+
+
+ CTRL-N New ", output);
+
+ // Changes Top size to same size as Dialog more menu and scroll bar
+ ((FakeDriver)Application.Driver).SetBufferSize (20, 5);
+ ReflectionTools.InvokePrivate (
+ typeof (Application),
+ "ProcessMouseEvent",
+ new MouseEvent () {
+ X = -1,
+ Y = -1,
+ Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+ });
+
+ Application.Refresh ();
+ Assert.Equal (new Rect (0, 0, 20, 5), top.Frame);
+ Assert.Equal (new Rect (0, 1, 20, 3), dialog.Frame);
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+ File
+┌ Dialog ──────────┐
+│ [ Ok ] │
+└──────────────────┘
+ CTRL-N New ", output);
+
+ // Changes Top size smaller than Dialog size
+ ((FakeDriver)Application.Driver).SetBufferSize (19, 3);
+ ReflectionTools.InvokePrivate (
+ typeof (Application),
+ "ProcessMouseEvent",
+ new MouseEvent () {
+ X = -1,
+ Y = -1,
+ Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+ });
+
+ Application.Refresh ();
+ Assert.Equal (new Rect (0, 0, 19, 3), top.Frame);
+ Assert.Equal (new Rect (-1, 1, 20, 3), dialog.Frame);
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+ File
+ Dialog ──────────┐
+ [ Ok ] │", output);
+
+ ReflectionTools.InvokePrivate (
+ typeof (Application),
+ "ProcessMouseEvent",
+ new MouseEvent () {
+ X = 18,
+ Y = 3,
+ Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+ });
+
+ Application.Refresh ();
+ Assert.Equal (new Rect (0, 0, 19, 3), top.Frame);
+ Assert.Equal (new Rect (18, 2, 20, 3), dialog.Frame);
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+ File
+
+ CTRL-N New ┌", output);
+
+ // On a real app we can't go beyond the SuperView bounds
+ ReflectionTools.InvokePrivate (
+ typeof (Application),
+ "ProcessMouseEvent",
+ new MouseEvent () {
+ X = 19,
+ Y = 4,
+ Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+ });
+
+ Application.Refresh ();
+ Assert.Equal (new Rect (0, 0, 19, 3), top.Frame);
+ Assert.Equal (new Rect (19, 2, 20, 3), dialog.Frame);
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+ File
+
+ CTRL-N New", output);
+ }
}
}
\ No newline at end of file