From 82b8c0ab49ec25bc1cfd37bb18c1168523467cfd Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 29 Mar 2021 08:28:49 +0200 Subject: [PATCH] fixed a rare stack overflow where scroll bars could get stuck in an auto-hide loop --- MLEM.Ui/Elements/Element.cs | 24 ++++++++++++++---------- MLEM.Ui/Elements/Panel.cs | 2 +- MLEM.Ui/Elements/ScrollBar.cs | 6 ++++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index 449fe42..86e8615 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -626,26 +626,30 @@ namespace MLEM.Ui.Elements { if (this.Children.Count > 0) { Element foundChild = null; - var autoSize = this.Area.Size; + var autoSize = this.UnscrolledArea.Size; if (this.SetHeightBasedOnChildren) { var lowest = this.GetLowestChild(e => !e.IsHidden); - var newY = lowest?.UnscrolledArea.Bottom - pos.Y + this.ScaledChildPadding.Bottom; - if (newY != null && autoSize.Y != newY) { - autoSize.Y = newY.Value; - foundChild = lowest; + if (lowest != null) { + var newHeight = lowest.UnscrolledArea.Bottom - pos.Y + this.ScaledChildPadding.Bottom; + if (!newHeight.Equals(autoSize.Y, 0.01F)) { + autoSize.Y = newHeight; + foundChild = lowest; + } } } if (this.SetWidthBasedOnChildren) { var rightmost = this.GetRightmostChild(e => !e.IsHidden); - var newX = rightmost?.UnscrolledArea.Right - pos.X + this.ScaledChildPadding.Right; - if (newX != null && autoSize.X != newX) { - autoSize.X = newX.Value; - foundChild = rightmost; + if (rightmost != null) { + var newWidth = rightmost.UnscrolledArea.Right - pos.X + this.ScaledChildPadding.Right; + if (!newWidth.Equals(autoSize.X, 0.01F)) { + autoSize.X = newWidth; + foundChild = rightmost; + } } } if (this.TreatSizeAsMinimum) autoSize = Vector2.Max(autoSize, actualSize); - if (!this.Area.Size.Equals(autoSize, 0.01F)) { + if (foundChild != null) { recursion++; if (recursion >= 16) { throw new ArithmeticException($"The area of {this} with root {this.Root?.Name} has recursively updated too often. Does its child {foundChild} contain any conflicting auto-sizing settings?"); diff --git a/MLEM.Ui/Elements/Panel.cs b/MLEM.Ui/Elements/Panel.cs index c9bda1a..101444f 100644 --- a/MLEM.Ui/Elements/Panel.cs +++ b/MLEM.Ui/Elements/Panel.cs @@ -218,7 +218,7 @@ namespace MLEM.Ui.Elements { return; // the "real" first child is the scroll bar, which we want to ignore var firstChild = this.Children.First(c => c != this.ScrollBar); - var lowestChild = this.GetLowestChild(e => !e.IsHidden); + var lowestChild = this.GetLowestChild(c => c != this.ScrollBar && !c.IsHidden); // the max value of the scrollbar is the amount of non-scaled pixels taken up by overflowing components var childrenHeight = lowestChild.Area.Bottom - firstChild.Area.Top; this.ScrollBar.MaxValue = (childrenHeight - this.Area.Height) / this.Scale + this.ChildPadding.Height; diff --git a/MLEM.Ui/Elements/ScrollBar.cs b/MLEM.Ui/Elements/ScrollBar.cs index 556cc73..a3b2b06 100644 --- a/MLEM.Ui/Elements/ScrollBar.cs +++ b/MLEM.Ui/Elements/ScrollBar.cs @@ -44,8 +44,10 @@ namespace MLEM.Ui.Elements { this.maxValue = Math.Max(0, value); // force current value to be clamped this.CurrentValue = this.CurrentValue; - if (this.AutoHideWhenEmpty && this.IsHidden != this.maxValue <= 0) { - this.IsHidden = this.maxValue <= 0; + // auto-hide if necessary + var shouldHide = this.maxValue <= 0.01F; + if (this.AutoHideWhenEmpty && this.IsHidden != shouldHide) { + this.IsHidden = shouldHide; this.OnAutoHide?.Invoke(this); } }