mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-12-25 01:39:23 +01:00
Added a SquishingGroup element to MLEM.Ui
This commit is contained in:
parent
28a928ec2c
commit
103d7c7503
5 changed files with 146 additions and 22 deletions
|
@ -37,6 +37,7 @@ Additions
|
|||
- Allow specifying a maximum amount of characters for a TextField
|
||||
- Added a multiline editing mode to TextField
|
||||
- Added a formatting code to allow for inline font changes
|
||||
- Added a SquishingGroup element
|
||||
|
||||
Improvements
|
||||
- **Made Image ScaleToImage take ui scale into account**
|
||||
|
|
|
@ -176,7 +176,7 @@ namespace MLEM.Ui.Elements {
|
|||
}
|
||||
/// <summary>
|
||||
/// The priority of this element as part of its <see cref="Parent"/> element.
|
||||
/// A higher priority means the element will be drawn first and, if auto-anchoring is used, anchored higher up within its parent.
|
||||
/// A higher priority means the element will be drawn first, but not anchored higher up if auto-anchoring is used.
|
||||
/// </summary>
|
||||
public int Priority {
|
||||
get => this.priority;
|
||||
|
@ -256,6 +256,10 @@ namespace MLEM.Ui.Elements {
|
|||
/// Stores whether this element is its <see cref="Root"/>'s <see cref="RootElement.SelectedElement"/>.
|
||||
/// </summary>
|
||||
public bool IsSelected { get; protected set; }
|
||||
/// <summary>
|
||||
/// Returns whether this element's <see cref="SetAreaDirty"/> method has been recently called and its area has not been updated since then using <see cref="UpdateAreaIfDirty"/> or <see cref="ForceUpdateArea"/>.
|
||||
/// </summary>
|
||||
public bool AreaDirty { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A style property that contains the selection indicator that is displayed on this element if it is the <see cref="RootElement.SelectedElement"/>
|
||||
|
@ -398,7 +402,6 @@ namespace MLEM.Ui.Elements {
|
|||
private Vector2 size;
|
||||
private Vector2 offset;
|
||||
private RectangleF area;
|
||||
private bool areaDirty;
|
||||
private bool isHidden;
|
||||
private int priority;
|
||||
private UiStyle style;
|
||||
|
@ -515,17 +518,16 @@ namespace MLEM.Ui.Elements {
|
|||
/// If this element is auto-anchored or its parent automatically changes its size based on its children, this element's parent's area is also marked dirty.
|
||||
/// </summary>
|
||||
public void SetAreaDirty() {
|
||||
this.areaDirty = true;
|
||||
if (this.Parent != null && (this.Anchor >= Anchor.AutoLeft || this.Parent.SetWidthBasedOnChildren || this.Parent.SetHeightBasedOnChildren))
|
||||
this.Parent.SetAreaDirty();
|
||||
this.AreaDirty = true;
|
||||
this.Parent?.OnChildAreaDirty(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this element's <see cref="Area"/> and all of its <see cref="Children"/> by calling <see cref="ForceUpdateArea"/> if <see cref="areaDirty"/> is true.
|
||||
/// Updates this element's <see cref="Area"/> and all of its <see cref="Children"/> by calling <see cref="ForceUpdateArea"/> if <see cref="AreaDirty"/> is true.
|
||||
/// </summary>
|
||||
/// <returns>Whether <see cref="areaDirty"/> was true and <see cref="ForceUpdateArea"/> was called</returns>
|
||||
/// <returns>Whether <see cref="AreaDirty"/> was true and <see cref="ForceUpdateArea"/> was called</returns>
|
||||
public bool UpdateAreaIfDirty() {
|
||||
if (this.areaDirty) {
|
||||
if (this.AreaDirty) {
|
||||
this.ForceUpdateArea();
|
||||
return true;
|
||||
}
|
||||
|
@ -537,7 +539,7 @@ namespace MLEM.Ui.Elements {
|
|||
/// This method also updates all of this element's <see cref="Children"/>'s areas.
|
||||
/// </summary>
|
||||
public virtual void ForceUpdateArea() {
|
||||
this.areaDirty = false;
|
||||
this.AreaDirty = false;
|
||||
if (this.IsHidden)
|
||||
return;
|
||||
// if the parent's area is dirty, it would get updated anyway when querying its ChildPaddedArea,
|
||||
|
@ -644,11 +646,7 @@ namespace MLEM.Ui.Elements {
|
|||
newSize.Y = parentArea.Bottom - pos.Y;
|
||||
}
|
||||
|
||||
this.area = new RectangleF(pos, newSize);
|
||||
this.System.InvokeOnElementAreaUpdated(this);
|
||||
|
||||
foreach (var child in this.Children)
|
||||
child.ForceUpdateArea();
|
||||
this.SetAreaDirectlyAndUpdateChildren(new RectangleF(pos, newSize));
|
||||
|
||||
if (this.SetWidthBasedOnChildren || this.SetHeightBasedOnChildren) {
|
||||
Element foundChild = null;
|
||||
|
@ -697,6 +695,19 @@ namespace MLEM.Ui.Elements {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets this element's <see cref="Area"/> to the given <see cref="RectangleF"/> and invokes the <see cref="UiSystem.OnElementAreaUpdated"/> event.
|
||||
/// This method also updates all of this element's <see cref="Children"/>'s areas.
|
||||
/// Note that this method does not take into account any auto-sizing, anchoring or positioning, and so it should be used sparingly, if at all.
|
||||
/// </summary>
|
||||
/// <seealso cref="ForceUpdateArea"/>
|
||||
public void SetAreaDirectlyAndUpdateChildren(RectangleF area) {
|
||||
this.area = area;
|
||||
this.System.InvokeOnElementAreaUpdated(this);
|
||||
foreach (var child in this.Children)
|
||||
child.ForceUpdateArea();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the actual size that this element should take up, based on the area that its parent encompasses.
|
||||
/// By default, this is based on the information specified in <see cref="Size"/>'s documentation.
|
||||
|
@ -1024,6 +1035,16 @@ namespace MLEM.Ui.Elements {
|
|||
this.SecondActionSound.SetFromStyle(style.ActionSound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method that gets called by this element's <see cref="Children"/> when their <see cref="SetAreaDirty"/> methods get called.
|
||||
/// Note that the element's area might already be dirty, which will not stop this method from being called.
|
||||
/// </summary>
|
||||
/// <param name="child">The child whose area is being set dirty</param>
|
||||
protected virtual void OnChildAreaDirty(Element child) {
|
||||
if (child.Anchor >= Anchor.AutoLeft || this.SetWidthBasedOnChildren || this.SetHeightBasedOnChildren)
|
||||
this.SetAreaDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the given <paramref name="position"/> by the inverse of this element's <see cref="Transform"/> matrix.
|
||||
/// </summary>
|
||||
|
|
77
MLEM.Ui/Elements/SquishingGroup.cs
Normal file
77
MLEM.Ui/Elements/SquishingGroup.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MLEM.Extensions;
|
||||
using MLEM.Misc;
|
||||
|
||||
namespace MLEM.Ui.Elements {
|
||||
/// <summary>
|
||||
/// A squishing group is a <see cref="Group"/> whose <see cref="Element.Children"/> automatically get resized so that they do not overlap each other.
|
||||
/// The order in which elements are squished depends on their <see cref="Element.Priority"/>, where elements with a lower priority will move out of the way of elements with a higher priority.
|
||||
/// If all elements have the same priority, their addition order (their order in <see cref="Element.Children"/>) determines squish order.
|
||||
/// </summary>
|
||||
public class SquishingGroup : Group {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new squishing group with the given settings.
|
||||
/// </summary>
|
||||
/// <param name="anchor">The group's anchor.</param>
|
||||
/// <param name="size">The group's size.</param>
|
||||
public SquishingGroup(Anchor anchor, Vector2 size) : base(anchor, size, false) {
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ForceUpdateArea() {
|
||||
base.ForceUpdateArea();
|
||||
// we squish children in order of priority, since auto-anchoring is based on addition order
|
||||
for (var i = 0; i < this.SortedChildren.Count; i++) {
|
||||
var child = this.SortedChildren[i];
|
||||
if (SquishChild(child, out var squished))
|
||||
child.SetAreaDirectlyAndUpdateChildren(squished);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnChildAreaDirty(Element child) {
|
||||
base.OnChildAreaDirty(child);
|
||||
this.SetAreaDirty();
|
||||
}
|
||||
|
||||
private static bool SquishChild(Element element, out RectangleF squishedArea) {
|
||||
squishedArea = default;
|
||||
if (element.IsHidden)
|
||||
return false;
|
||||
var pos = element.Area.Location;
|
||||
var size = element.Area.Size;
|
||||
foreach (var sibling in element.GetSiblings(e => !e.IsHidden)) {
|
||||
var siblingArea = sibling.Area;
|
||||
var leftIntersect = siblingArea.Right - pos.X;
|
||||
var rightIntersect = pos.X + size.X - siblingArea.Left;
|
||||
var bottomIntersect = siblingArea.Bottom - pos.Y;
|
||||
var topIntersect = pos.Y + size.Y - siblingArea.Top;
|
||||
if (leftIntersect > 0 && rightIntersect > 0 && bottomIntersect > 0 && topIntersect > 0) {
|
||||
if (rightIntersect + leftIntersect < topIntersect + bottomIntersect) {
|
||||
if (rightIntersect > leftIntersect) {
|
||||
size.X -= siblingArea.Right - pos.X;
|
||||
pos.X = Math.Max(pos.X, siblingArea.Right);
|
||||
} else {
|
||||
size.X = Math.Min(pos.X + size.X, siblingArea.Left) - pos.X;
|
||||
}
|
||||
} else {
|
||||
if (topIntersect > bottomIntersect) {
|
||||
size.Y -= siblingArea.Bottom - pos.Y;
|
||||
pos.Y = Math.Max(pos.Y, siblingArea.Bottom);
|
||||
} else {
|
||||
size.Y = Math.Min(pos.Y + size.Y, siblingArea.Top) - pos.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!pos.Equals(element.Area.Location, Epsilon) || !size.Equals(element.Area.Size, Epsilon)) {
|
||||
squishedArea = new RectangleF(pos, size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ using Microsoft.Xna.Framework.Input;
|
|||
using MLEM.Cameras;
|
||||
using MLEM.Data;
|
||||
using MLEM.Data.Content;
|
||||
using MLEM.Extended.Extensions;
|
||||
using MLEM.Extended.Font;
|
||||
using MLEM.Extended.Tiled;
|
||||
using MLEM.Extensions;
|
||||
|
@ -22,9 +21,7 @@ using MLEM.Textures;
|
|||
using MLEM.Ui;
|
||||
using MLEM.Ui.Elements;
|
||||
using MLEM.Ui.Style;
|
||||
using MonoGame.Extended;
|
||||
using MonoGame.Extended.Tiled;
|
||||
using Group = MLEM.Ui.Elements.Group;
|
||||
|
||||
namespace Sandbox {
|
||||
public class GameImpl : MlemGame {
|
||||
|
@ -216,7 +213,7 @@ namespace Sandbox {
|
|||
invalidPanel.AddChild(new VerticalSpace(1));
|
||||
this.UiSystem.Add("Invalid", invalidPanel);*/
|
||||
|
||||
var loadGroup = new Group(Anchor.TopLeft, Vector2.One, false);
|
||||
/*var loadGroup = new Group(Anchor.TopLeft, Vector2.One, false);
|
||||
var loadPanel = loadGroup.AddChild(new Panel(Anchor.Center, new Vector2(150, 150), Vector2.Zero, false, true, false) {
|
||||
ChildPadding = new Padding(5, 10, 5, 5)
|
||||
});
|
||||
|
@ -240,7 +237,36 @@ namespace Sandbox {
|
|||
}
|
||||
};
|
||||
par.OnDrawn = (e, time, batch, a) => batch.DrawRectangle(e.DisplayArea.ToExtended(), Color.Red);
|
||||
this.UiSystem.Add("Load", loadGroup);
|
||||
this.UiSystem.Add("Load", loadGroup);*/
|
||||
|
||||
var spillPanel = new Panel(Anchor.Center, new Vector2(100), Vector2.Zero);
|
||||
var squishingGroup = spillPanel.AddChild(new SquishingGroup(Anchor.TopLeft, Vector2.One));
|
||||
squishingGroup.AddChild(new Button(Anchor.TopLeft, new Vector2(30), "TL") {
|
||||
OnUpdated = (e, time) => e.IsHidden = Input.IsKeyDown(Keys.D1),
|
||||
Priority = 10
|
||||
}).SetData("Ref", "TL");
|
||||
squishingGroup.AddChild(new Button(Anchor.TopRight, new Vector2(30), "TR") {
|
||||
OnUpdated = (e, time) => e.IsHidden = Input.IsKeyDown(Keys.D2),
|
||||
Priority = 20
|
||||
}).SetData("Ref", "TR");
|
||||
squishingGroup.AddChild(new Button(Anchor.BottomLeft, new Vector2(30), "BL") {
|
||||
OnUpdated = (e, time) => e.IsHidden = Input.IsKeyDown(Keys.D3),
|
||||
Priority = 30
|
||||
}).SetData("Ref", "BL");
|
||||
squishingGroup.AddChild(new Button(Anchor.BottomRight, new Vector2(30), "BR") {
|
||||
OnUpdated = (e, time) => e.IsHidden = Input.IsKeyDown(Keys.D4),
|
||||
Priority = 40
|
||||
}).SetData("Ref", "BR");
|
||||
squishingGroup.AddChild(new Button(Anchor.Center, Vector2.Zero, "0") {
|
||||
PositionOffset = new Vector2(-10, -5),
|
||||
Size = new Vector2(60, 55),
|
||||
OnPressed = e => {
|
||||
e.Priority = 100 - e.Priority;
|
||||
((Button) e).Text.Text = e.Priority.ToString();
|
||||
e.SetAreaDirty();
|
||||
}
|
||||
}).SetData("Ref", "Main");
|
||||
this.UiSystem.Add("SpillTest", spillPanel);
|
||||
}
|
||||
|
||||
protected override void DoUpdate(GameTime gameTime) {
|
||||
|
@ -253,10 +279,10 @@ namespace Sandbox {
|
|||
this.camera.Zoom(0.1F * Math.Sign(delta), this.InputHandler.MousePosition.ToVector2());
|
||||
}
|
||||
|
||||
if (Input.InputsDown.Length > 0)
|
||||
/*if (Input.InputsDown.Length > 0)
|
||||
Console.WriteLine("Down: " + string.Join(", ", Input.InputsDown));
|
||||
if (Input.InputsPressed.Length > 0)
|
||||
Console.WriteLine("Pressed: " + string.Join(", ", Input.InputsPressed));
|
||||
Console.WriteLine("Pressed: " + string.Join(", ", Input.InputsPressed));*/
|
||||
}
|
||||
|
||||
protected override void DoDraw(GameTime gameTime) {
|
||||
|
|
Loading…
Reference in a new issue