1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-25 22:18:34 +01:00

Allow elements with larger children to influence a panel's scrollable area

This commit is contained in:
Ell 2023-05-21 11:11:52 +02:00
parent f1740b7b32
commit 3127ad5b74
3 changed files with 33 additions and 9 deletions

View file

@ -43,6 +43,7 @@ Improvements
- Increased Element area calculation recursion limit to 64 - Increased Element area calculation recursion limit to 64
- Improved the SquishingGroup algorithm by prioritizing each element's final size - Improved the SquishingGroup algorithm by prioritizing each element's final size
- Allow specifying start and end indices when drawing a Paragraph - Allow specifying start and end indices when drawing a Paragraph
- Allow elements with larger children to influence a panel's scrollable area
Fixes Fixes
- Fixed images not updating their hidden state properly when the displayed texture changes - Fixed images not updating their hidden state properly when the displayed texture changes

View file

@ -859,14 +859,16 @@ namespace MLEM.Ui.Elements {
/// Returns this element's lowest child element (in terms of y position) that matches the given condition. /// Returns this element's lowest child element (in terms of y position) that matches the given condition.
/// </summary> /// </summary>
/// <param name="condition">The condition to match</param> /// <param name="condition">The condition to match</param>
/// <param name="total">Whether to evaluate based on the child's <see cref="GetTotalCoveredArea"/>, rather than its <see cref="UnscrolledArea"/>.</param>
/// <returns>The lowest element, or null if no such element exists</returns> /// <returns>The lowest element, or null if no such element exists</returns>
public Element GetLowestChild(Func<Element, bool> condition = null) { public Element GetLowestChild(Func<Element, bool> condition = null, bool total = false) {
Element lowest = null; Element lowest = null;
var lowestX = float.MinValue; var lowestX = float.MinValue;
foreach (var child in this.Children) { foreach (var child in this.Children) {
if (condition != null && !condition(child)) if (condition != null && !condition(child))
continue; continue;
var x = !child.Anchor.IsTopAligned() ? child.UnscrolledArea.Height : child.UnscrolledArea.Bottom; var covered = total ? child.GetTotalCoveredArea(true) : child.UnscrolledArea;
var x = !child.Anchor.IsTopAligned() ? covered.Height : covered.Bottom;
if (x >= lowestX) { if (x >= lowestX) {
lowest = child; lowest = child;
lowestX = x; lowestX = x;
@ -879,14 +881,16 @@ namespace MLEM.Ui.Elements {
/// Returns this element's rightmost child (in terms of x position) that matches the given condition. /// Returns this element's rightmost child (in terms of x position) that matches the given condition.
/// </summary> /// </summary>
/// <param name="condition">The condition to match</param> /// <param name="condition">The condition to match</param>
/// <param name="total">Whether to evaluate based on the child's <see cref="GetTotalCoveredArea"/>, rather than its <see cref="UnscrolledArea"/>.</param>
/// <returns>The rightmost element, or null if no such element exists</returns> /// <returns>The rightmost element, or null if no such element exists</returns>
public Element GetRightmostChild(Func<Element, bool> condition = null) { public Element GetRightmostChild(Func<Element, bool> condition = null, bool total = false) {
Element rightmost = null; Element rightmost = null;
var rightmostX = float.MinValue; var rightmostX = float.MinValue;
foreach (var child in this.Children) { foreach (var child in this.Children) {
if (condition != null && !condition(child)) if (condition != null && !condition(child))
continue; continue;
var x = !child.Anchor.IsLeftAligned() ? child.UnscrolledArea.Width : child.UnscrolledArea.Right; var covered = total ? child.GetTotalCoveredArea(true) : child.UnscrolledArea;
var x = !child.Anchor.IsLeftAligned() ? covered.Width : covered.Right;
if (x >= rightmostX) { if (x >= rightmostX) {
rightmost = child; rightmost = child;
rightmostX = x; rightmostX = x;
@ -900,8 +904,9 @@ namespace MLEM.Ui.Elements {
/// The returned element's <see cref="Parent"/> will always be equal to this element's <see cref="Parent"/>. /// The returned element's <see cref="Parent"/> will always be equal to this element's <see cref="Parent"/>.
/// </summary> /// </summary>
/// <param name="condition">The condition to match</param> /// <param name="condition">The condition to match</param>
/// <param name="total">Whether to evaluate based on the child's <see cref="GetTotalCoveredArea"/>, rather than its <see cref="UnscrolledArea"/>.</param>
/// <returns>The lowest older sibling of this element, or null if no such element exists</returns> /// <returns>The lowest older sibling of this element, or null if no such element exists</returns>
public Element GetLowestOlderSibling(Func<Element, bool> condition = null) { public Element GetLowestOlderSibling(Func<Element, bool> condition = null, bool total = false) {
if (this.Parent == null) if (this.Parent == null)
return null; return null;
Element lowest = null; Element lowest = null;
@ -910,7 +915,7 @@ namespace MLEM.Ui.Elements {
break; break;
if (condition != null && !condition(child)) if (condition != null && !condition(child))
continue; continue;
if (lowest == null || child.UnscrolledArea.Bottom >= lowest.UnscrolledArea.Bottom) if (lowest == null || (total ? child.GetTotalCoveredArea(true) : child.UnscrolledArea).Bottom >= lowest.UnscrolledArea.Bottom)
lowest = child; lowest = child;
} }
return lowest; return lowest;
@ -992,6 +997,21 @@ namespace MLEM.Ui.Elements {
yield return parent; yield return parent;
} }
/// <summary>
/// Returns the total covered area of this element, which is its <see cref="Area"/> (or <see cref="UnscrolledArea"/>), unioned with all of the total covered areas of its <see cref="Children"/>.
/// The returned area is only different from this element's <see cref="Area"/> (or <see cref="UnscrolledArea"/>) if it has any <see cref="Children"/> that are outside of this element's area, or are bigger than this element.
/// </summary>
/// <param name="unscrolled">Whether to use elements' <see cref="UnscrolledArea"/> (instead of their <see cref="Area"/>).</param>
/// <returns>This element's total covered area.</returns>
public RectangleF GetTotalCoveredArea(bool unscrolled) {
var ret = unscrolled ? this.UnscrolledArea : this.Area;
foreach (var child in this.Children) {
if (!child.IsHidden)
ret = RectangleF.Union(ret, child.GetTotalCoveredArea(unscrolled));
}
return ret;
}
/// <summary> /// <summary>
/// Returns a subset of <see cref="Children"/> that are currently relevant in terms of drawing and input querying. /// Returns a subset of <see cref="Children"/> that are currently relevant in terms of drawing and input querying.
/// A <see cref="Panel"/> only returns elements that are currently in view here. /// A <see cref="Panel"/> only returns elements that are currently in view here.

View file

@ -230,8 +230,11 @@ namespace MLEM.Ui.Elements {
base.OnChildAreaDirty(child, 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 // to be auto-anchored and so will automatically propagate their changes up to us
if (grandchild) if (grandchild) {
this.ScrollChildren(); this.ScrollChildren();
// we also need to re-setup here in case the child is involved in a special GetTotalCoveredArea
this.ScrollSetup();
}
} }
/// <inheritdoc /> /// <inheritdoc />
@ -253,8 +256,8 @@ namespace MLEM.Ui.Elements {
float childrenHeight; float childrenHeight;
if (this.Children.Count > 1) { if (this.Children.Count > 1) {
var firstChild = this.Children.FirstOrDefault(c => c != this.ScrollBar); var firstChild = this.Children.FirstOrDefault(c => c != this.ScrollBar);
var lowestChild = this.GetLowestChild(c => c != this.ScrollBar && !c.IsHidden); var lowestChild = this.GetLowestChild(c => c != this.ScrollBar && !c.IsHidden, true);
childrenHeight = lowestChild.Area.Bottom - firstChild.Area.Top; childrenHeight = lowestChild.GetTotalCoveredArea(false).Bottom - firstChild.Area.Top;
} else { } else {
// if we only have one child (the scroll bar), then the children take up no visual height // if we only have one child (the scroll bar), then the children take up no visual height
childrenHeight = 0; childrenHeight = 0;