From 3c74cc4845fcc236f7966e3ac67c7faaa7d8e9a0 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sat, 11 Dec 2021 00:36:50 +0100 Subject: [PATCH] Added PreventSiblingSpill to Element --- CHANGELOG.md | 1 + MLEM.Ui/Elements/Element.cs | 48 +++++++++++++++++++++++++++++++++++-- MLEM.Ui/UiSystem.cs | 6 +++++ Sandbox/GameImpl.cs | 30 +++++++++++++++++++---- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5679acc..22bdf78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 PreventSiblingSpill to Element Improvements - **Made Image ScaleToImage take ui scale into account** diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index 1398e05..7bcb555 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -241,9 +241,16 @@ namespace MLEM.Ui.Elements { /// Set this field to true to cause this element's final display area to never exceed that of its . /// If the resulting area is too large, the size of this element is shrunk to fit the target area. /// This can be useful if an element should fill the remaining area of a parent exactly. + /// When setting this value after this element has already been added to a ui, should be called. /// public bool PreventParentSpill; /// + /// Set this field to true to cause this element's final display ever to never overlap with any of its siblings (). + /// If the resulting area is too large, the size of this element is shrunk to best accomodate for the areas of its siblings. + /// When setting this value after this element has already been added to a ui, should be called. + /// + public bool PreventSiblingSpill; + /// /// The transparency (alpha value) that this element is rendered with. /// Note that, when is called, this alpha value is multiplied with the 's alpha value and passed down to this element's . /// @@ -333,6 +340,10 @@ namespace MLEM.Ui.Elements { /// public GenericCallback OnAreaUpdated; /// + /// Event that is called when this element's area is marked as dirty using . + /// + public GenericCallback OnAreaDirty; + /// /// Event that is called when the element that is currently being moused changes within the ui system. /// Note that the event fired doesn't necessarily correlate to this specific element. /// @@ -516,8 +527,17 @@ namespace MLEM.Ui.Elements { /// public void SetAreaDirty() { this.areaDirty = true; - if (this.Parent != null && (this.Anchor >= Anchor.AutoLeft || this.Parent.SetWidthBasedOnChildren || this.Parent.SetHeightBasedOnChildren)) - this.Parent.SetAreaDirty(); + if (this.Parent != null) { + // set parent dirty if the parent's layout depends on our area + if (this.Anchor >= Anchor.AutoLeft || this.Parent.SetWidthBasedOnChildren || this.Parent.SetHeightBasedOnChildren) + this.Parent.SetAreaDirty(); + // set siblings dirty that depend on our area + foreach (var sibling in this.GetSiblings()) { + if (sibling.PreventSiblingSpill) + sibling.SetAreaDirty(); + } + } + this.System?.InvokeOnElementAreaDirty(this); } /// @@ -644,6 +664,30 @@ namespace MLEM.Ui.Elements { newSize.Y = parentArea.Bottom - pos.Y; } + if (this.PreventSiblingSpill) { + foreach (var sibling in this.GetSiblings(e => !e.IsHidden)) { + var leftIntersect = sibling.Area.Right - pos.X; + var rightIntersect = pos.X + newSize.X - sibling.Area.Left; + var bottomIntersect = sibling.Area.Bottom - pos.Y; + var topIntersect = pos.Y + newSize.Y - sibling.Area.Top; + if (leftIntersect > 0 && rightIntersect > 0 && bottomIntersect > 0 && topIntersect > 0) { + if (rightIntersect + leftIntersect < topIntersect + bottomIntersect) { + if (rightIntersect > leftIntersect) { + pos.X = Math.Max(pos.X, sibling.Area.Right); + } else { + newSize.X = Math.Min(pos.X + newSize.X, sibling.Area.Left) - pos.X; + } + } else { + if (topIntersect > bottomIntersect) { + pos.Y = Math.Max(pos.Y, sibling.Area.Bottom); + } else { + newSize.Y = Math.Min(pos.Y + newSize.Y, sibling.Area.Top) - pos.Y; + } + } + } + } + } + this.area = new RectangleF(pos, newSize); this.System.InvokeOnElementAreaUpdated(this); diff --git a/MLEM.Ui/UiSystem.cs b/MLEM.Ui/UiSystem.cs index 77e56c0..36fbec4 100644 --- a/MLEM.Ui/UiSystem.cs +++ b/MLEM.Ui/UiSystem.cs @@ -152,6 +152,10 @@ namespace MLEM.Ui { /// public event Element.GenericCallback OnElementAreaUpdated; /// + /// Event that is invoked when an element's area is marked as dirty using . + /// + public event Element.GenericCallback OnElementAreaDirty; + /// /// Event that is invoked when the that the mouse is currently over changes /// public event Element.GenericCallback OnMousedElementChanged; @@ -193,6 +197,7 @@ namespace MLEM.Ui { this.OnElementTouchEnter += e => e.OnTouchEnter?.Invoke(e); this.OnElementTouchExit += e => e.OnTouchExit?.Invoke(e); this.OnElementAreaUpdated += e => e.OnAreaUpdated?.Invoke(e); + this.OnElementAreaDirty += e => e.OnAreaDirty?.Invoke(e); this.OnMousedElementChanged += e => this.ApplyToAll(t => t.OnMousedElementChanged?.Invoke(t, e)); this.OnTouchedElementChanged += e => this.ApplyToAll(t => t.OnTouchedElementChanged?.Invoke(t, e)); this.OnSelectedElementChanged += e => this.ApplyToAll(t => t.OnSelectedElementChanged?.Invoke(t, e)); @@ -357,6 +362,7 @@ namespace MLEM.Ui { internal void InvokeOnSelectedElementDrawn(Element element, GameTime time, SpriteBatch batch, float alpha) => this.OnSelectedElementDrawn?.Invoke(element, time, batch, alpha); internal void InvokeOnElementUpdated(Element element, GameTime time) => this.OnElementUpdated?.Invoke(element, time); internal void InvokeOnElementAreaUpdated(Element element) => this.OnElementAreaUpdated?.Invoke(element); + internal void InvokeOnElementAreaDirty(Element element) => this.OnElementAreaDirty?.Invoke(element); internal void InvokeOnElementPressed(Element element) => this.OnElementPressed?.Invoke(element); internal void InvokeOnElementSecondaryPressed(Element element) => this.OnElementSecondaryPressed?.Invoke(element); internal void InvokeOnElementSelected(Element element) => this.OnElementSelected?.Invoke(element); diff --git a/Sandbox/GameImpl.cs b/Sandbox/GameImpl.cs index 70e4f18..759a2f1 100644 --- a/Sandbox/GameImpl.cs +++ b/Sandbox/GameImpl.cs @@ -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,30 @@ 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); + spillPanel.AddChild(new Button(Anchor.TopLeft, new Vector2(30), "TL") { + OnUpdated = (e, time) => e.IsHidden = Input.IsKeyDown(Keys.D1) + }); + spillPanel.AddChild(new Button(Anchor.TopRight, new Vector2(30), "TR") { + OnUpdated = (e, time) => e.IsHidden = Input.IsKeyDown(Keys.D2) + }); + spillPanel.AddChild(new Button(Anchor.BottomLeft, new Vector2(30), "BL") { + OnUpdated = (e, time) => e.IsHidden = Input.IsKeyDown(Keys.D3) + }); + spillPanel.AddChild(new Button(Anchor.BottomRight, new Vector2(30), "BR") { + OnUpdated = (e, time) => e.IsHidden = Input.IsKeyDown(Keys.D4) + }); + spillPanel.AddChild(new Button(Anchor.Center, Vector2.Zero, "Spill Test") { + PositionOffset = new Vector2(-10, -5), + Size = new Vector2(60, 55), + OnPressed = e => { + e.PreventSiblingSpill = !e.PreventSiblingSpill; + e.SetAreaDirty(); + } + }); + this.UiSystem.Add("SpillTest", spillPanel); } protected override void DoUpdate(GameTime gameTime) {