diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6d5d64..58f86fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,12 @@ Jump to version:
- [5.0.0](#500)
## 6.1.0
+### MLEM.Ui
+Additions
+- Added some extension methods for querying Anchor types
+
+Improvements
+- Allow elements to auto-adjust their size even when their children are aligned oddly
## 6.0.0
### MLEM
diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs
index c0ef4a7..b0a84b9 100644
--- a/MLEM.Ui/Elements/Element.cs
+++ b/MLEM.Ui/Elements/Element.cs
@@ -633,7 +633,7 @@ namespace MLEM.Ui.Elements {
break;
}
- if (this.Anchor >= Anchor.AutoLeft) {
+ if (this.Anchor.IsAuto()) {
Element previousChild;
if (this.Anchor == Anchor.AutoInline || this.Anchor == Anchor.AutoInlineIgnoreOverflow) {
previousChild = this.GetOlderSibling(e => !e.IsHidden && e.CanAutoAnchorsAttach);
@@ -687,11 +687,13 @@ namespace MLEM.Ui.Elements {
if (this.SetHeightBasedOnChildren) {
var lowest = this.GetLowestChild(e => !e.IsHidden);
if (lowest != null) {
- autoSize.Y = lowest.UnscrolledArea.Bottom - pos.Y + this.ScaledChildPadding.Bottom;
+ if (lowest.Anchor.IsTopAligned()) {
+ autoSize.Y = lowest.UnscrolledArea.Bottom - pos.Y + this.ScaledChildPadding.Bottom;
+ } else {
+ autoSize.Y = lowest.UnscrolledArea.Height + this.ScaledChildPadding.Height;
+ }
foundChild = lowest;
} else {
- if (this.Children.Any(e => !e.IsHidden))
- throw new InvalidOperationException($"{this} with root {this.Root.Name} sets its height based on children but it only has visible children anchored too low ({string.Join(", ", this.Children.Where(c => !c.IsHidden).Select(c => c.Anchor))})");
autoSize.Y = 0;
}
}
@@ -699,11 +701,13 @@ namespace MLEM.Ui.Elements {
if (this.SetWidthBasedOnChildren) {
var rightmost = this.GetRightmostChild(e => !e.IsHidden);
if (rightmost != null) {
- autoSize.X = rightmost.UnscrolledArea.Right - pos.X + this.ScaledChildPadding.Right;
+ if (rightmost.Anchor.IsLeftAligned()) {
+ autoSize.X = rightmost.UnscrolledArea.Right - pos.X + this.ScaledChildPadding.Right;
+ } else {
+ autoSize.X = rightmost.UnscrolledArea.Width + this.ScaledChildPadding.Width;
+ }
foundChild = rightmost;
} else {
- if (this.Children.Any(e => !e.IsHidden))
- throw new InvalidOperationException($"{this} with root {this.Root.Name} sets its width based on children but it only has visible children anchored too far right ({string.Join(", ", this.Children.Where(c => !c.IsHidden).Select(c => c.Anchor))})");
autoSize.X = 0;
}
}
@@ -717,11 +721,9 @@ namespace MLEM.Ui.Elements {
// we want to leave some leeway to prevent float rounding causing an infinite loop
if (!autoSize.Equals(this.UnscrolledArea.Size, Element.Epsilon)) {
recursion++;
- if (recursion >= 16) {
+ 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?");
- } else {
- UpdateDisplayArea(autoSize);
- }
+ UpdateDisplayArea(autoSize);
}
}
}
@@ -773,13 +775,15 @@ namespace MLEM.Ui.Elements {
/// The lowest element, or null if no such element exists
public Element GetLowestChild(Func condition = null) {
Element lowest = null;
+ var lowestX = float.MinValue;
foreach (var child in this.Children) {
if (condition != null && !condition(child))
continue;
- if (child.Anchor > Anchor.TopRight && child.Anchor < Anchor.AutoLeft)
- continue;
- if (lowest == null || child.UnscrolledArea.Bottom >= lowest.UnscrolledArea.Bottom)
+ var x = !child.Anchor.IsTopAligned() ? child.UnscrolledArea.Height : child.UnscrolledArea.Bottom;
+ if (x >= lowestX) {
lowest = child;
+ lowestX = x;
+ }
}
return lowest;
}
@@ -791,13 +795,15 @@ namespace MLEM.Ui.Elements {
/// The rightmost element, or null if no such element exists
public Element GetRightmostChild(Func condition = null) {
Element rightmost = null;
+ var rightmostX = float.MinValue;
foreach (var child in this.Children) {
if (condition != null && !condition(child))
continue;
- if (child.Anchor < Anchor.AutoLeft && child.Anchor != Anchor.TopLeft && child.Anchor != Anchor.CenterLeft && child.Anchor != Anchor.BottomLeft)
- continue;
- if (rightmost == null || child.UnscrolledArea.Right >= rightmost.UnscrolledArea.Right)
+ var x = !child.Anchor.IsLeftAligned() ? child.UnscrolledArea.Width : child.UnscrolledArea.Right;
+ if (child.UnscrolledArea.Right >= rightmostX) {
rightmost = child;
+ rightmostX = x;
+ }
}
return rightmost;
}
@@ -1119,7 +1125,7 @@ namespace MLEM.Ui.Elements {
/// Whether the is a grandchild of this element, rather than a direct child.
protected virtual void OnChildAreaDirty(Element child, bool grandchild) {
if (!grandchild) {
- if (child.Anchor >= Anchor.AutoLeft || this.SetWidthBasedOnChildren || this.SetHeightBasedOnChildren)
+ if (child.Anchor.IsAuto() || this.SetWidthBasedOnChildren || this.SetHeightBasedOnChildren)
this.SetAreaDirty();
}
this.Parent?.OnChildAreaDirty(child, true);
diff --git a/MLEM.Ui/Elements/ElementHelper.cs b/MLEM.Ui/Elements/ElementHelper.cs
index e919beb..47d565d 100644
--- a/MLEM.Ui/Elements/ElementHelper.cs
+++ b/MLEM.Ui/Elements/ElementHelper.cs
@@ -214,5 +214,32 @@ namespace MLEM.Ui.Elements {
return tooltip;
}
+ ///
+ /// Returns whether the given is automatic. The anchors , , , and will return true.
+ ///
+ /// The anchor to query.
+ /// Whether the given anchor is automatic.
+ public static bool IsAuto(this Anchor anchor) {
+ return anchor == Anchor.AutoLeft || anchor == Anchor.AutoCenter || anchor == Anchor.AutoRight || anchor == Anchor.AutoInline || anchor == Anchor.AutoInlineIgnoreOverflow;
+ }
+
+ ///
+ /// Returns whether the given is left-aligned for the purpose of . The anchors , , , , and will return true.
+ ///
+ /// The anchor to query.
+ /// Whether the given anchor is left-aligned.
+ public static bool IsLeftAligned(this Anchor anchor) {
+ return anchor == Anchor.TopLeft || anchor == Anchor.CenterLeft || anchor == Anchor.BottomLeft || anchor == Anchor.AutoLeft || anchor == Anchor.AutoInline || anchor == Anchor.AutoInlineIgnoreOverflow;
+ }
+
+ ///
+ /// Returns whether the given is top-aligned for the purpose of . The anchors , , , , , , and will return true.
+ ///
+ /// The anchor to query.
+ /// Whether the given anchor is top-aligned.
+ public static bool IsTopAligned(this Anchor anchor) {
+ return anchor == Anchor.TopLeft || anchor == Anchor.TopCenter || anchor == Anchor.TopRight || anchor == Anchor.AutoLeft || anchor == Anchor.AutoCenter || anchor == Anchor.AutoRight || anchor == Anchor.AutoInline || anchor == Anchor.AutoInlineIgnoreOverflow;
+ }
+
}
}
diff --git a/MLEM.Ui/Elements/Panel.cs b/MLEM.Ui/Elements/Panel.cs
index 75cb704..2d015f3 100644
--- a/MLEM.Ui/Elements/Panel.cs
+++ b/MLEM.Ui/Elements/Panel.cs
@@ -98,7 +98,7 @@ namespace MLEM.Ui.Elements {
if (this.SetHeightBasedOnChildren)
throw new NotSupportedException("A panel can't both set height based on children and scroll overflow");
foreach (var child in this.Children) {
- if (child != this.ScrollBar && child.Anchor < Anchor.AutoLeft)
+ if (child != this.ScrollBar && !child.Anchor.IsAuto())
throw new NotSupportedException($"A panel that handles overflow can't contain non-automatic anchors ({child})");
if (child is Panel panel && panel.scrollOverflow)
throw new NotSupportedException($"A panel that scrolls overflow cannot contain another panel that scrolls overflow ({child})");
@@ -161,7 +161,7 @@ namespace MLEM.Ui.Elements {
///
protected override void OnChildAreaDirty(Element child, bool grandchild) {
base.OnChildAreaDirty(child, grandchild);
- // we only need to scroll when a grandchild changes, since all of our children are forced
+ // we only need to scroll when a grandchild changes, since all of our children are forced
// to be auto-anchored and so will automatically propagate their changes up to us
if (grandchild)
this.ScrollChildren();
diff --git a/Sandbox/GameImpl.cs b/Sandbox/GameImpl.cs
index d83f808..738af60 100644
--- a/Sandbox/GameImpl.cs
+++ b/Sandbox/GameImpl.cs
@@ -27,14 +27,14 @@ using MonoGame.Extended;
using MonoGame.Extended.Tiled;
using MonoGame.Extended.ViewportAdapters;
-namespace Sandbox;
+namespace Sandbox;
public class GameImpl : MlemGame {
private Camera camera;
- private TiledMap map;
+ /*private TiledMap map;
private IndividualTiledMapRenderer mapRenderer;
- private TiledMapCollisions collisions;
+ private TiledMapCollisions collisions;*/
private RawContentManager rawContent;
private TokenizedString tokenized;
@@ -50,14 +50,14 @@ public class GameImpl : MlemGame {
this.Components.Add(this.rawContent = new RawContentManager(this.Services));
- this.map = MlemGame.LoadContent("Tiled/Map");
+ /*this.map = MlemGame.LoadContent("Tiled/Map");
this.mapRenderer = new IndividualTiledMapRenderer(this.map);
- this.collisions = new TiledMapCollisions(this.map);
+ this.collisions = new TiledMapCollisions(this.map);*/
this.camera = new Camera(this.GraphicsDevice) {
AutoScaleWithScreen = true,
Scale = 2,
- LookingPosition = new Vector2(25, 25) * this.map.GetTileSize(),
+ /*LookingPosition = new Vector2(25, 25) * this.map.GetTileSize(),*/
MinScale = 0.25F,
MaxScale = 4
};
@@ -296,7 +296,7 @@ public class GameImpl : MlemGame {
this.SpriteBatch.End();
};
- var viewport = new BoxingViewportAdapter(this.Window, this.GraphicsDevice, 1280, 720);
+ /*var viewport = new BoxingViewportAdapter(this.Window, this.GraphicsDevice, 1280, 720);
var newPanel = new Panel(Anchor.TopLeft, new Vector2(200, 100), new Vector2(10, 10));
newPanel.AddChild(new Button(Anchor.TopLeft, new Vector2(100, 20), "Text", "Tooltip text"));
this.UiSystem.Add("Panel", newPanel);
@@ -341,7 +341,19 @@ public class GameImpl : MlemGame {
}
}
this.SpriteBatch.End();
- };
+ };*/
+
+ var widthPanel = new Panel(Anchor.Center, Vector2.One, Vector2.Zero, true) {SetWidthBasedOnChildren = true};
+ for (var i = 0; i < 5; i++)
+ widthPanel.AddChild(new Paragraph(Anchor.AutoCenter, 100000, "Test String " + Math.Pow(10, i), true) {
+ OnUpdated = (e, time) => {
+ if (Input.IsPressed(Keys.A)) {
+ e.Anchor = (Anchor) (((int) e.Anchor + 1) % EnumHelper.GetValues().Count());
+ Console.WriteLine(e.Anchor);
+ }
+ }
+ });
+ this.UiSystem.Add("WidthTest", widthPanel);
}
protected override void DoUpdate(GameTime gameTime) {
@@ -356,12 +368,12 @@ public class GameImpl : MlemGame {
/*if (Input.InputsDown.Length > 0)
Console.WriteLine("Down: " + string.Join(", ", Input.InputsDown));*/
- if (MlemGame.Input.InputsPressed.Length > 0)
+ /*if (MlemGame.Input.InputsPressed.Length > 0)
Console.WriteLine("Pressed: " + string.Join(", ", MlemGame.Input.InputsPressed));
MlemGame.Input.HandleKeyboardRepeats = false;
Console.WriteLine("Down time: " + MlemGame.Input.GetDownTime(Keys.A));
Console.WriteLine("Time since press: " + MlemGame.Input.GetTimeSincePress(Keys.A));
- Console.WriteLine("Up time: " + MlemGame.Input.GetUpTime(Keys.A));
+ Console.WriteLine("Up time: " + MlemGame.Input.GetUpTime(Keys.A));*/
}
protected override void DoDraw(GameTime gameTime) {
@@ -396,4 +408,4 @@ public class GameImpl : MlemGame {
}
-}
\ No newline at end of file
+}