Merge pull request #2438 from BDisp/v2_dialog-bigger-fix_2434

Fixes #2434. Dialog bounds bigger than Cols and Rows must be allowed to drag beyond left, right and bottom.
This commit is contained in:
Tig
2023-03-21 08:34:49 -07:00
committed by GitHub
2 changed files with 193 additions and 30 deletions

View File

@@ -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 {
/// <summary>
/// <see langword="true"/> if was already loaded by the <see cref="Application.Begin(Toplevel)"/>
/// <see langword="false"/>, otherwise. This is used to avoid the <see cref="View._needsDisplay"/>
/// having wrong values while this was not yet loaded.
/// <see langword="false"/>, otherwise.
/// </summary>
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);
}
///<inheritdoc/>
protected override void Dispose (bool disposing)
{
dragPosition = null;
base.Dispose (disposing);
}
}
/// <summary>

View File

@@ -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);
}
}
}