From 92f9164256064109ee36bbd60f569cd0e34ae283 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sat, 24 Sep 2022 18:46:33 +0200 Subject: [PATCH] Added Panel.ScrollToElement --- CHANGELOG.md | 1 + MLEM.Ui/Elements/Panel.cs | 109 ++++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22fc6bd..c75b094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Additions - Added Element.AutoSizeAddedAbsolute to allow for more granular control of auto-sizing - Added Element.OnAddedToUi and Element.OnRemovedFromUi - Added ScrollBar.MouseDragScrolling +- Added Panel.ScrollToElement Improvements - Allow elements to auto-adjust their size even when their children are aligned oddly diff --git a/MLEM.Ui/Elements/Panel.cs b/MLEM.Ui/Elements/Panel.cs index 6ccf880..dafda63 100644 --- a/MLEM.Ui/Elements/Panel.cs +++ b/MLEM.Ui/Elements/Panel.cs @@ -84,9 +84,7 @@ namespace MLEM.Ui.Elements { return; if (e == null || !e.GetParentTree().Contains(this)) return; - var firstChild = this.Children.FirstOrDefault(c => c != this.ScrollBar); - if (firstChild != null) - this.ScrollBar.CurrentValue = (e.Area.Center.Y - this.Area.Height / 2 - firstChild.Area.Top) / e.Scale + this.ChildPadding.Value.Height / 2; + this.ScrollToElement(e); }; this.AddChild(this.ScrollBar); } @@ -116,15 +114,6 @@ namespace MLEM.Ui.Elements { this.ScrollSetup(); } - private void ScrollChildren() { - if (!this.scrollOverflow) - return; - // we ignore false grandchildren so that the children of the scroll bar stay in place - foreach (var child in this.GetChildren(c => c != this.ScrollBar, true, true)) - child.ScrollOffset.Y = -this.ScrollBar.CurrentValue; - this.relevantChildrenDirty = true; - } - /// public override void ForceUpdateSortedChildren() { base.ForceUpdateSortedChildren(); @@ -144,26 +133,6 @@ namespace MLEM.Ui.Elements { base.RemoveChildren(e => e != this.ScrollBar && (condition == null || condition(e))); } - /// - protected override IList GetRelevantChildren() { - var relevant = base.GetRelevantChildren(); - if (this.scrollOverflow) { - if (this.relevantChildrenDirty) - this.ForceUpdateRelevantChildren(); - relevant = this.relevantChildren; - } - return relevant; - } - - /// - 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 - // to be auto-anchored and so will automatically propagate their changes up to us - if (grandchild) - this.ScrollChildren(); - } - /// public override void Draw(GameTime time, SpriteBatch batch, float alpha, SpriteBatchContext context) { // draw children onto the render target if we have one @@ -208,11 +177,32 @@ namespace MLEM.Ui.Elements { return base.GetElementUnderPos(position); } - private RectangleF GetRenderTargetArea() { - var area = this.ChildPaddedArea; - area.X = this.DisplayArea.X; - area.Width = this.DisplayArea.Width; - return area; + /// + public override void Dispose() { + if (this.renderTarget != null) { + this.renderTarget.Dispose(); + this.renderTarget = null; + } + base.Dispose(); + } + + /// + /// Scrolls this panel's to the given in such a way that its center is positioned in the center of this panel. + /// + /// The element to scroll to. + public void ScrollToElement(Element element) { + this.ScrollToElement(element.Area.Center.Y); + } + + /// + /// Scrolls this panel's to the given coordinate in such a way that the coordinate is positioned in the center of this panel. + /// + /// The y coordinate to scroll to, which should have this element's applied. + public void ScrollToElement(float elementY) { + var firstChild = this.Children.FirstOrDefault(c => c != this.ScrollBar); + if (firstChild == null) + return; + this.ScrollBar.CurrentValue = (elementY - this.Area.Height / 2 - firstChild.Area.Top) / this.Scale + this.ChildPadding.Value.Height / 2; } /// @@ -226,6 +216,26 @@ namespace MLEM.Ui.Elements { this.SetScrollBarStyle(); } + /// + protected override IList GetRelevantChildren() { + var relevant = base.GetRelevantChildren(); + if (this.scrollOverflow) { + if (this.relevantChildrenDirty) + this.ForceUpdateRelevantChildren(); + relevant = this.relevantChildren; + } + return relevant; + } + + /// + 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 + // to be auto-anchored and so will automatically propagate their changes up to us + if (grandchild) + this.ScrollChildren(); + } + /// /// Prepares the panel for auto-scrolling, creating the render target and setting up the scroll bar's maximum value. /// @@ -274,15 +284,6 @@ namespace MLEM.Ui.Elements { } } - /// - public override void Dispose() { - if (this.renderTarget != null) { - this.renderTarget.Dispose(); - this.renderTarget = null; - } - base.Dispose(); - } - private void SetScrollBarStyle() { if (this.ScrollBar == null) return; @@ -309,5 +310,21 @@ namespace MLEM.Ui.Elements { } } + private RectangleF GetRenderTargetArea() { + var area = this.ChildPaddedArea; + area.X = this.DisplayArea.X; + area.Width = this.DisplayArea.Width; + return area; + } + + private void ScrollChildren() { + if (!this.scrollOverflow) + return; + // we ignore false grandchildren so that the children of the scroll bar stay in place + foreach (var child in this.GetChildren(c => c != this.ScrollBar, true, true)) + child.ScrollOffset.Y = -this.ScrollBar.CurrentValue; + this.relevantChildrenDirty = true; + } + } }