Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Terminal.Gui/Core/PosDim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ public static Dim Sized (int n)

internal class DimCombine : Dim {
internal Dim left, right;
bool add;
internal bool add;
public DimCombine (bool add, Dim left, Dim right)
{
this.left = left;
Expand Down
134 changes: 80 additions & 54 deletions Terminal.Gui/Core/View.cs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ public override bool CanFocus {
}
if (base.CanFocus != value) {
base.CanFocus = value;

switch (value) {
case false when tabIndex > -1:
TabIndex = -1;
Expand All @@ -357,7 +357,7 @@ public override bool CanFocus {
SuperView.CanFocus = true;
break;
}

if (value && tabIndex == -1) {
TabIndex = SuperView != null ? SuperView.tabIndexes.IndexOf (this) : -1;
}
Expand Down Expand Up @@ -881,10 +881,10 @@ public void SetNeedsDisplay (Rect region)
NeedDisplay = new Rect (x, y, w, h);
}
container?.SetChildNeedsDisplay ();

if (subviews == null)
return;

foreach (var view in subviews)
if (view.Frame.IntersectsWith (region)) {
var childRegion = Rect.Intersect (view.Frame, region);
Expand Down Expand Up @@ -1132,7 +1132,7 @@ internal void ViewToScreen (int col, int row, out int rcol, out int rrow, bool c
// Computes the real row, col relative to the screen.
rrow = row + frame.Y;
rcol = col + frame.X;

var curContainer = container;
while (curContainer != null) {
rrow += curContainer.frame.Y;
Expand Down Expand Up @@ -1303,7 +1303,7 @@ public virtual void PositionCursor ()
}

bool hasFocus;

/// <inheritdoc/>
public override bool HasFocus => hasFocus;

Expand Down Expand Up @@ -1694,7 +1694,7 @@ public override bool ProcessKey (KeyEvent keyEvent)
if (args.Handled)
return true;
}

return Focused?.Enabled == true && Focused?.ProcessKey (keyEvent) == true;
}

Expand Down Expand Up @@ -1870,7 +1870,7 @@ public override bool ProcessHotKey (KeyEvent keyEvent)
return true;
if (subviews == null || subviews.Count == 0)
return false;

foreach (var view in subviews)
if (view.Enabled && view.ProcessHotKey (keyEvent))
return true;
Expand All @@ -1897,7 +1897,7 @@ public override bool ProcessColdKey (KeyEvent keyEvent)
return true;
if (subviews == null || subviews.Count == 0)
return false;

foreach (var view in subviews)
if (view.Enabled && view.ProcessColdKey (keyEvent))
return true;
Expand Down Expand Up @@ -2043,7 +2043,7 @@ public bool FocusPrev ()
FocusLast ();
return focused != null;
}

var focusedIdx = -1;
for (var i = tabIndexes.Count; i > 0;) {
i--;
Expand Down Expand Up @@ -2153,20 +2153,8 @@ internal void SetRelativeLayout (Rect hostFrame)
actX = x.Anchor (hostFrame.Width - actW);
} else {
actX = x?.Anchor (hostFrame.Width) ?? 0;

switch (width) {
case null:
actW = AutoSize ? s.Width : hostFrame.Width;
break;
case Dim.DimFactor factor when !factor.IsFromRemaining ():
actW = width.Anchor (hostFrame.Width);
actW = AutoSize && s.Width > actW ? s.Width : actW;
break;
default:
actW = Math.Max (width.Anchor (hostFrame.Width - actX), 0);
actW = AutoSize && s.Width > actW ? s.Width : actW;
break;
}

actW = Math.Max (CalculateActualWidth (width, hostFrame, actX, s), 0);
}

if (y is Pos.PosCenter) {
Expand All @@ -2179,30 +2167,78 @@ internal void SetRelativeLayout (Rect hostFrame)
actY = y.Anchor (hostFrame.Height - actH);
} else {
actY = y?.Anchor (hostFrame.Height) ?? 0;

switch (height) {
case null:
actH = AutoSize ? s.Height : hostFrame.Height;
break;
case Dim.DimFactor factor when !factor.IsFromRemaining ():
actH = height.Anchor (hostFrame.Height);
actH = AutoSize && s.Height > actH ? s.Height : actH;
break;
default:
actH = Math.Max (height.Anchor (hostFrame.Height - actY), 0);
actH = AutoSize && s.Height > actH ? s.Height : actH;
break;
}

actH = Math.Max (CalculateActualHight (height, hostFrame, actY, s), 0);
}

var r = new Rect (actX, actY, actW, actH);
if (Frame != r) {
Frame = new Rect (actX, actY, actW, actH);
Frame = r;
if (!SetMinWidthHeight ())
TextFormatter.Size = GetBoundsTextFormatterSize ();
}
}

private int CalculateActualWidth (Dim width, Rect hostFrame, int actX, Size s)
{
int actW;
switch (width) {
case null:
actW = AutoSize ? s.Width : hostFrame.Width;
break;
case Dim.DimCombine combine:
int leftActW = CalculateActualWidth (combine.left, hostFrame, actX, s);
int rightActW = CalculateActualWidth (combine.right, hostFrame, actX, s);
if (combine.add) {
actW = leftActW + rightActW;
} else {
actW = leftActW - rightActW;
}
actW = AutoSize && s.Width > actW ? s.Width : actW;
break;
case Dim.DimFactor factor when !factor.IsFromRemaining ():
actW = width.Anchor (hostFrame.Width);
actW = AutoSize && s.Width > actW ? s.Width : actW;
break;
default:
actW = Math.Max (width.Anchor (hostFrame.Width - actX), 0);
actW = AutoSize && s.Width > actW ? s.Width : actW;
break;
}

return actW;
}

private int CalculateActualHight (Dim height, Rect hostFrame, int actY, Size s)
{
int actH;
switch (height) {
case null:
actH = AutoSize ? s.Height : hostFrame.Height;
break;
case Dim.DimCombine combine:
int leftActH = CalculateActualHight (combine.left, hostFrame, actY, s);
int rightActH = CalculateActualHight (combine.right, hostFrame, actY, s);
if (combine.add) {
actH = leftActH + rightActH;
} else {
actH = leftActH - rightActH;
}
actH = AutoSize && s.Height > actH ? s.Height : actH;
break;
case Dim.DimFactor factor when !factor.IsFromRemaining ():
actH = height.Anchor (hostFrame.Height);
actH = AutoSize && s.Height > actH ? s.Height : actH;
break;
default:
actH = Math.Max (height.Anchor (hostFrame.Height - actY), 0);
actH = AutoSize && s.Height > actH ? s.Height : actH;
break;
}

return actH;
}

// https://en.wikipedia.org/wiki/Topological_sorting
List<View> TopologicalSort (IEnumerable<View> nodes, ICollection<(View From, View To)> edges)
{
Expand Down Expand Up @@ -2326,48 +2362,40 @@ void CollectPos (Pos pos, View from, ref HashSet<View> nNodes, ref HashSet<(View
{
switch (pos) {
case Pos.PosView pv:
{
if (pv.Target != this) {
nEdges.Add ((pv.Target, from));
}
foreach (var v in from.InternalSubviews) {
CollectAll (v, ref nNodes, ref nEdges);
}
return;
}
case Pos.PosCombine pc:
{
foreach (var v in from.InternalSubviews) {
CollectPos (pc.left, from, ref nNodes, ref nEdges);
CollectPos (pc.right, from, ref nNodes, ref nEdges);
}
break;
}
}
}

void CollectDim (Dim dim, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
{
switch (dim) {
case Dim.DimView dv:
{
if (dv.Target != this) {
nEdges.Add ((dv.Target, from));
}
foreach (var v in from.InternalSubviews) {
CollectAll (v, ref nNodes, ref nEdges);
}
return;
}
case Dim.DimCombine dc:
{
foreach (var v in from.InternalSubviews) {
CollectDim (dc.left, from, ref nNodes, ref nEdges);
CollectDim (dc.right, from, ref nNodes, ref nEdges);
}
break;
}
}
}

void CollectAll (View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
Expand Down Expand Up @@ -2759,14 +2787,14 @@ public class MouseEventArgs : EventArgs {
/// The <see cref="MouseEvent"/> for the event.
/// </summary>
public MouseEvent MouseEvent { get; set; }

/// <summary>
/// Indicates if the current mouse event has already been processed and the driver should stop notifying any other event subscriber.
/// Its important to set this value to true specially when updating any View's layout from inside the subscriber method.
/// </summary>
/// <remarks>This property forwards to the <see cref="MouseEvent.Handled"/> property and is provided as a convenience and for
/// backwards compatibility</remarks>
public bool Handled {
public bool Handled {
get => MouseEvent.Handled;
set => MouseEvent.Handled = value;
}
Expand All @@ -2785,7 +2813,7 @@ public override bool OnMouseEnter (MouseEvent mouseEvent)

var args = new MouseEventArgs (mouseEvent);
MouseEnter?.Invoke (args);

return args.Handled || base.OnMouseEnter (mouseEvent);
}

Expand All @@ -2802,7 +2830,7 @@ public override bool OnMouseLeave (MouseEvent mouseEvent)

var args = new MouseEventArgs (mouseEvent);
MouseLeave?.Invoke (args);

return args.Handled || base.OnMouseLeave (mouseEvent);
}

Expand Down Expand Up @@ -2956,7 +2984,6 @@ bool CanSetHeight (int desiredHeight, out int resultHeight)
canSetHeight = !ForceValidatePosDim;
break;
case Dim.DimFactor factor:
{
// Tries to get the SuperView height otherwise the view height.
var sh = SuperView != null ? SuperView.Frame.Height : h;
if (factor.IsFromRemaining ()) {
Expand All @@ -2965,7 +2992,6 @@ bool CanSetHeight (int desiredHeight, out int resultHeight)
h = Height.Anchor (sh);
canSetHeight = !ForceValidatePosDim;
break;
}
default:
canSetHeight = true;
break;
Expand Down
37 changes: 37 additions & 0 deletions UnitTests/DimTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,5 +1246,42 @@ public void Function_Equal ()
dim2 = Dim.Function (f2);
Assert.NotEqual (dim1, dim2);
}

[Theory, AutoInitShutdown]
[InlineData (0, true)]
[InlineData (0, false)]
[InlineData (50, true)]
[InlineData (50, false)]

public void DimPercentPlusOne (int startingDistance, bool testHorizontal)
{
var container = new View {
Width = 100,
Height = 100,
};

var label = new Label {
X = testHorizontal ? startingDistance : 0,
Y = testHorizontal ? 0 : startingDistance,
Width = testHorizontal ? Dim.Percent (50) + 1 : 1,
Height = testHorizontal ? 1 : Dim.Percent (50) + 1,
};

container.Add (label);
Application.Top.Add (container);
Application.Top.LayoutSubviews ();


Assert.Equal (100, container.Frame.Width);
Assert.Equal (100, container.Frame.Height);

if (testHorizontal) {
Assert.Equal (51, label.Frame.Width);
Assert.Equal (1, label.Frame.Height);
} else {
Assert.Equal (1, label.Frame.Width);
Assert.Equal (51, label.Frame.Height);
}
}
}
}
35 changes: 35 additions & 0 deletions UnitTests/PosTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,5 +1032,40 @@ public void Function_Equal ()
pos2 = Pos.Function (f2);
Assert.NotEqual (pos1, pos2);
}

[Theory, AutoInitShutdown]
[InlineData (true)]
[InlineData (false)]

public void PosPercentPlusOne (bool testHorizontal)
{
var container = new View {
Width = 100,
Height = 100,
};

var label = new Label {
X = testHorizontal ? Pos.Percent (50) + Pos.Percent (10) + 1 : 1,
Y = testHorizontal ? 1 : Pos.Percent (50) + Pos.Percent (10) + 1,
Width = 10,
Height = 10,
};

container.Add (label);
Application.Top.Add (container);
Application.Top.LayoutSubviews ();


Assert.Equal (100, container.Frame.Width);
Assert.Equal (100, container.Frame.Height);

if (testHorizontal) {
Assert.Equal (61, label.Frame.X);
Assert.Equal (1, label.Frame.Y);
} else {
Assert.Equal (1, label.Frame.X);
Assert.Equal (61, label.Frame.Y);
}
}
}
}