mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-01 08:50:25 +01:00
Rewrote SetRelativeLayout to move logic in to the Pos & Dim classes. Massively beefed up unit tests.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
namespace Terminal.Gui;
|
||||
using static Terminal.Gui.Pos;
|
||||
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the position of a <see cref="View"/> which can be an absolute value, a percentage, centered, or
|
||||
@@ -319,6 +321,12 @@ public class Pos
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual int GetLocation (int superviewDimension, Dim dim, int autosize, bool autoSize)
|
||||
{
|
||||
return this.Anchor (superviewDimension);
|
||||
}
|
||||
|
||||
|
||||
internal class PosAbsolute (int n) : Pos
|
||||
{
|
||||
private readonly int _n = n;
|
||||
@@ -354,12 +362,29 @@ public class Pos
|
||||
}
|
||||
return width - _offset;
|
||||
}
|
||||
|
||||
internal override int GetLocation (int superviewDimension, Dim dim, int autosize, bool autoSize)
|
||||
{
|
||||
int newLocation = this.Anchor (superviewDimension);
|
||||
if (this.UseDimForOffset)
|
||||
{
|
||||
newLocation -= dim.Anchor (superviewDimension);
|
||||
}
|
||||
return newLocation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class PosCenter : Pos
|
||||
{
|
||||
public override string ToString () { return "Center"; }
|
||||
internal override int Anchor (int width) { return width / 2; }
|
||||
internal override int GetLocation (int superviewDimension, Dim dim, int autosize, bool autoSize)
|
||||
{
|
||||
var newDimension = Math.Max (dim.GetDimension (0, superviewDimension, autosize, autoSize), 0);
|
||||
return Anchor (superviewDimension - newDimension);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class PosCombine (bool add, Pos left, Pos right) : Pos
|
||||
@@ -381,6 +406,19 @@ public class Pos
|
||||
|
||||
return la - ra;
|
||||
}
|
||||
|
||||
internal override int GetLocation (int superviewDimension, Dim dim, int autosize, bool autoSize)
|
||||
{
|
||||
int newDimension = dim.GetDimension (0, superviewDimension, autosize, autoSize);
|
||||
int left = _left.GetLocation (superviewDimension, dim, autosize, autoSize);
|
||||
int right = _right.GetLocation (superviewDimension, dim, autosize, autoSize);
|
||||
if (_add)
|
||||
{
|
||||
return left + right;
|
||||
}
|
||||
|
||||
return left - right;
|
||||
}
|
||||
}
|
||||
|
||||
internal class PosFactor (float n) : Pos
|
||||
@@ -645,6 +683,13 @@ public class Dim
|
||||
|
||||
internal virtual int Anchor (int width) { return 0; }
|
||||
|
||||
internal virtual int GetDimension (int location, int dimension, int autosize, bool autoSize)
|
||||
{
|
||||
int newDimension = Math.Max (Anchor (dimension - location), 0);
|
||||
return autoSize && autosize > newDimension ? autosize : newDimension;
|
||||
}
|
||||
|
||||
|
||||
// BUGBUG: newPos is never used.
|
||||
private static void SetDimCombine (Dim left, DimCombine newPos) { (left as DimView)?.Target.SetNeedsLayout (); }
|
||||
|
||||
@@ -655,6 +700,15 @@ public class Dim
|
||||
public override int GetHashCode () { return _n.GetHashCode (); }
|
||||
public override string ToString () { return $"Absolute({_n})"; }
|
||||
internal override int Anchor (int width) { return _n; }
|
||||
|
||||
internal override int GetDimension (int location, int dimension, int autosize, bool autoSize)
|
||||
{
|
||||
// DimAbsolute.Anchor (int width) ignores width and returns n
|
||||
int newDimension = Math.Max (Anchor (0), 0);
|
||||
|
||||
// BUGBUG: AutoSize does two things: makes text fit AND changes the view's dimensions
|
||||
return autoSize && autosize > newDimension ? autosize : newDimension;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DimCombine (bool add, Dim left, Dim right) : Dim
|
||||
@@ -676,6 +730,24 @@ public class Dim
|
||||
|
||||
return la - ra;
|
||||
}
|
||||
|
||||
internal override int GetDimension (int location, int dimension, int autosize, bool autoSize)
|
||||
{
|
||||
int leftNewDim = _left.GetDimension (location, dimension, autosize, autoSize);
|
||||
int rightNewDim = _right.GetDimension (location, dimension, autosize, autoSize);
|
||||
|
||||
int newDimension;
|
||||
if (_add)
|
||||
{
|
||||
newDimension = leftNewDim + rightNewDim;
|
||||
}
|
||||
else
|
||||
{
|
||||
newDimension = Math.Max (0, leftNewDim - rightNewDim);
|
||||
}
|
||||
|
||||
return autoSize && autosize > newDimension ? autosize : newDimension;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DimFactor (float n, bool r = false) : Dim
|
||||
@@ -688,6 +760,21 @@ public class Dim
|
||||
public bool IsFromRemaining () { return _remaining; }
|
||||
public override string ToString () { return $"Factor({_factor},{_remaining})"; }
|
||||
internal override int Anchor (int width) { return (int)(width * _factor); }
|
||||
|
||||
internal override int GetDimension (int location, int dimension, int autosize, bool autoSize)
|
||||
{
|
||||
int newDimension;
|
||||
if (_remaining)
|
||||
{
|
||||
newDimension = Math.Max (Anchor (dimension - location), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
newDimension = Anchor (dimension);
|
||||
}
|
||||
|
||||
return autoSize && autosize > newDimension ? autosize : newDimension;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DimFill (int margin) : Dim
|
||||
@@ -723,11 +810,11 @@ public class Dim
|
||||
}
|
||||
|
||||
string tside = side switch
|
||||
{
|
||||
0 => "Height",
|
||||
1 => "Width",
|
||||
_ => "unknown"
|
||||
};
|
||||
{
|
||||
0 => "Height",
|
||||
1 => "Width",
|
||||
_ => "unknown"
|
||||
};
|
||||
|
||||
return $"View({tside},{Target})";
|
||||
}
|
||||
@@ -735,11 +822,11 @@ public class Dim
|
||||
internal override int Anchor (int width)
|
||||
{
|
||||
return side switch
|
||||
{
|
||||
0 => Target.Frame.Height,
|
||||
1 => Target.Frame.Width,
|
||||
_ => 0
|
||||
};
|
||||
{
|
||||
0 => Target.Frame.Height,
|
||||
1 => Target.Frame.Width,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1011,7 +1011,6 @@ public partial class View
|
||||
Debug.Assert (_width is { });
|
||||
Debug.Assert (_height is { });
|
||||
|
||||
int newX, newW, newY, newH;
|
||||
var autosize = Size.Empty;
|
||||
|
||||
if (AutoSize)
|
||||
@@ -1021,176 +1020,19 @@ public partial class View
|
||||
autosize = GetAutoSize ();
|
||||
}
|
||||
|
||||
// TODO: Since GetNewLocationAndDimension does not depend on View, it can be moved into PosDim.cs
|
||||
// TODO: to make architecture more clean. Do this after DimAuto is implemented and the
|
||||
// TODO: View.AutoSize stuff is removed.
|
||||
int newX, newW, newY, newH;
|
||||
newX = _x.GetLocation (superviewContentSize.Width, _width, autosize.Width, AutoSize);
|
||||
newW = _width.GetDimension (newX, superviewContentSize.Width, autosize.Width, AutoSize);
|
||||
newY = _y.GetLocation (superviewContentSize.Height, _height, autosize.Height, AutoSize);
|
||||
newH = _height.GetDimension (newY, superviewContentSize.Height, autosize.Height, AutoSize);
|
||||
|
||||
// Returns the new dimension (width or height) and location (x or y) for the View given
|
||||
// the superview's Viewport
|
||||
// the current Pos (View.X or View.Y)
|
||||
// the current Dim (View.Width or View.Height)
|
||||
// This method is called recursively if pos is Pos.PosCombine
|
||||
(int newLocation, int newDimension) GetNewLocationAndDimension (
|
||||
bool width,
|
||||
Size superviewContentSize,
|
||||
Pos pos,
|
||||
Dim dim,
|
||||
int autosizeDimension
|
||||
)
|
||||
{
|
||||
// Gets the new dimension (width or height, dependent on `width`) of the given Dim given:
|
||||
// location: the current location (x or y)
|
||||
// dimension: the new dimension (width or height) (if relevant for Dim type)
|
||||
// autosize: the size to use if autosize = true
|
||||
// This method is recursive if d is Dim.DimCombine
|
||||
int GetNewDimension (Dim d, int location, int dimension, int autosize)
|
||||
{
|
||||
int newDimension;
|
||||
Rectangle newFrame = new (newX, newY, newW, newH);
|
||||
|
||||
switch (d)
|
||||
{
|
||||
case Dim.DimCombine combine:
|
||||
// TODO: Move combine logic into DimCombine?
|
||||
int leftNewDim = GetNewDimension (combine._left, location, dimension, autosize);
|
||||
int rightNewDim = GetNewDimension (combine._right, location, dimension, autosize);
|
||||
|
||||
if (combine._add)
|
||||
{
|
||||
newDimension = leftNewDim + rightNewDim;
|
||||
}
|
||||
else
|
||||
{
|
||||
newDimension = leftNewDim - rightNewDim;
|
||||
}
|
||||
|
||||
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
|
||||
|
||||
break;
|
||||
|
||||
case Dim.DimFactor factor when !factor.IsFromRemaining ():
|
||||
newDimension = d.Anchor (dimension);
|
||||
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
|
||||
|
||||
break;
|
||||
|
||||
case Dim.DimAbsolute:
|
||||
// DimAbsolute.Anchor (int width) ignores width and returns n
|
||||
newDimension = Math.Max (d.Anchor (0), 0);
|
||||
|
||||
// BUGBUG: AutoSize does two things: makes text fit AND changes the view's dimensions
|
||||
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
|
||||
|
||||
break;
|
||||
|
||||
case Dim.DimFill:
|
||||
default:
|
||||
newDimension = Math.Max (d.Anchor (dimension - location), 0);
|
||||
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return newDimension;
|
||||
}
|
||||
|
||||
int newDimension, newLocation;
|
||||
int superviewDimension = width ? superviewContentSize.Width : superviewContentSize.Height;
|
||||
|
||||
// Determine new location
|
||||
switch (pos)
|
||||
{
|
||||
case Pos.PosCenter posCenter:
|
||||
// For Center, the dimension is dependent on location, but we need to force getting the dimension first
|
||||
// using a location of 0
|
||||
newDimension = Math.Max (GetNewDimension (dim, 0, superviewDimension, autosizeDimension), 0);
|
||||
newLocation = posCenter.Anchor (superviewDimension - newDimension);
|
||||
|
||||
newDimension = Math.Max (
|
||||
GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
|
||||
0
|
||||
);
|
||||
break;
|
||||
|
||||
case Pos.PosCombine combine:
|
||||
// TODO: Move combine logic into PosCombine?
|
||||
int left, right;
|
||||
|
||||
(left, newDimension) = GetNewLocationAndDimension (
|
||||
width,
|
||||
superviewContentSize,
|
||||
combine._left,
|
||||
dim,
|
||||
autosizeDimension
|
||||
);
|
||||
|
||||
(right, newDimension) = GetNewLocationAndDimension (
|
||||
width,
|
||||
superviewContentSize,
|
||||
combine._right,
|
||||
dim,
|
||||
autosizeDimension
|
||||
);
|
||||
|
||||
if (combine._add)
|
||||
{
|
||||
newLocation = left + right;
|
||||
}
|
||||
else
|
||||
{
|
||||
newLocation = left - right;
|
||||
}
|
||||
|
||||
newDimension = Math.Max (
|
||||
GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
|
||||
0
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case Pos.PosAnchorEnd anchorEnd:
|
||||
newLocation = anchorEnd.Anchor (superviewDimension);
|
||||
if (anchorEnd.UseDimForOffset)
|
||||
{
|
||||
newLocation -= dim.Anchor (superviewDimension);
|
||||
}
|
||||
|
||||
newDimension = Math.Max (
|
||||
GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
|
||||
0
|
||||
);
|
||||
break;
|
||||
|
||||
case Pos.PosAbsolute:
|
||||
case Pos.PosFactor:
|
||||
case Pos.PosFunc:
|
||||
case Pos.PosView:
|
||||
default:
|
||||
newLocation = pos?.Anchor (superviewDimension) ?? 0;
|
||||
|
||||
newDimension = Math.Max (
|
||||
GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
|
||||
0
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return (newLocation, newDimension);
|
||||
}
|
||||
|
||||
// horizontal/width
|
||||
(newX, newW) = GetNewLocationAndDimension (true, superviewContentSize, _x, _width, autosize.Width);
|
||||
|
||||
// vertical/height
|
||||
(newY, newH) = GetNewLocationAndDimension (false, superviewContentSize, _y, _height, autosize.Height);
|
||||
|
||||
Rectangle r = new (newX, newY, newW, newH);
|
||||
|
||||
if (Frame != r)
|
||||
if (Frame != newFrame)
|
||||
{
|
||||
// Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height, making
|
||||
// the view LayoutStyle.Absolute.
|
||||
SetFrame (r);
|
||||
SetFrame (newFrame);
|
||||
|
||||
if (_x is Pos.PosAbsolute)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Dim;
|
||||
|
||||
|
||||
// Alias Console to MockConsole so we don't accidentally use Console
|
||||
using Console = Terminal.Gui.FakeConsole;
|
||||
@@ -22,6 +24,57 @@ public class DimTests
|
||||
Thread.CurrentThread.CurrentUICulture = culture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DimAbsolute_GetDimension_ReturnsCorrectValue ()
|
||||
{
|
||||
var dim = new DimAbsolute (10);
|
||||
var result = dim.GetDimension (0, 100, 50, false);
|
||||
Assert.Equal (10, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DimCombine_GetDimension_ReturnsCorrectValue ()
|
||||
{
|
||||
var dim1 = new DimAbsolute (10);
|
||||
var dim2 = new DimAbsolute (20);
|
||||
var dim = dim1 + dim2;
|
||||
var result = dim.GetDimension (0, 100, 50, false);
|
||||
Assert.Equal (30, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DimFactor_GetDimension_ReturnsCorrectValue ()
|
||||
{
|
||||
var dim = new DimFactor (0.5f);
|
||||
var result = dim.GetDimension (0, 100, 50, false);
|
||||
Assert.Equal (50, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DimFill_GetDimension_ReturnsCorrectValue ()
|
||||
{
|
||||
var dim = Dim.Fill ();
|
||||
var result = dim.GetDimension (0, 100, 50, false);
|
||||
Assert.Equal (100, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DimFunc_GetDimension_ReturnsCorrectValue ()
|
||||
{
|
||||
var dim = new DimFunc (() => 10);
|
||||
var result = dim.GetDimension (0, 100, 50, false);
|
||||
Assert.Equal (10, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DimView_GetDimension_ReturnsCorrectValue ()
|
||||
{
|
||||
var view = new View { Width = 10 };
|
||||
var dim = new DimView (view, 1);
|
||||
var result = dim.GetDimension (0, 100, 50, false);
|
||||
Assert.Equal (10, result);
|
||||
}
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// A new test that does not depend on Application is needed.
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,67 @@
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Dim;
|
||||
using static Terminal.Gui.Pos;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class PosTests (ITestOutputHelper output)
|
||||
{
|
||||
[Fact]
|
||||
public void PosAbsolute_GetLocation_ReturnsExpectedValue ()
|
||||
{
|
||||
var posAbsolute = new PosAbsolute (5);
|
||||
var result = posAbsolute.GetLocation (10, new DimAbsolute (2), 1, false);
|
||||
Assert.Equal (5, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosAnchorEnd_GetLocation_ReturnsExpectedValue ()
|
||||
{
|
||||
var posAnchorEnd = new PosAnchorEnd (5);
|
||||
var result = posAnchorEnd.GetLocation (10, new DimAbsolute (2), 1, false);
|
||||
Assert.Equal (5, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosCenter_GetLocation_ReturnsExpectedValue ()
|
||||
{
|
||||
var posCenter = new PosCenter ();
|
||||
var result = posCenter.GetLocation (10, new DimAbsolute (2), 1, false);
|
||||
Assert.Equal (4, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosCombine_GetLocation_ReturnsExpectedValue ()
|
||||
{
|
||||
var posCombine = new PosCombine (true, new PosAbsolute (5), new PosAbsolute (3));
|
||||
var result = posCombine.GetLocation (10, new DimAbsolute (2), 1, false);
|
||||
Assert.Equal (8, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosFactor_GetLocation_ReturnsExpectedValue ()
|
||||
{
|
||||
var posFactor = new PosFactor (0.5f);
|
||||
var result = posFactor.GetLocation (10, new DimAbsolute (2), 1, false);
|
||||
Assert.Equal (5, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosFunc_GetLocation_ReturnsExpectedValue ()
|
||||
{
|
||||
var posFunc = new PosFunc (() => 5);
|
||||
var result = posFunc.GetLocation (10, new DimAbsolute (2), 1, false);
|
||||
Assert.Equal (5, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosView_GetLocation_ReturnsExpectedValue ()
|
||||
{
|
||||
var posView = new PosView (new View { Frame = new Rectangle (5, 5, 10, 10) }, 0);
|
||||
var result = posView.GetLocation (10, new DimAbsolute (2), 1, false);
|
||||
Assert.Equal (5, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void At_Equal ()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user