Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osuTK;
using osuTK.Graphics;

Expand Down Expand Up @@ -89,6 +90,91 @@
AddUntilStep("container still autosized", () => container.Size == new Vector2(100));
}

[Test]
public void TestAutoSizeDuration()
{
Container parent = null;
Drawable child = null;

AddStep("create hierarchy", () =>
{
Child = parent = new Container
{
Masking = true,
AutoSizeAxes = Axes.Both,
AutoSizeDuration = 500,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Yellow,
},
new Container
{
Padding = new MarginPadding(50),
AutoSizeAxes = Axes.Both,
Child = child = new Box
{
Size = new Vector2(100),
Colour = Color4.Red,
}
}
}
};
});

AddSliderStep("AutoSizeDuration", 0f, 1500f, 500f, value =>
{
if (parent != null) parent.AutoSizeDuration = value;
});
AddSliderStep("Width", 0f, 300f, 100f, value =>
{
if (child != null) child.Width = value;
});
AddSliderStep("Height", 0f, 300f, 100f, value =>
{
if (child != null) child.Height = value;
});
}

[Test]
public void TestFinishAutoSizeTransforms()
{
Container parent = null;
Drawable child = null;

AddStep("create hierarchy", () =>
{
Child = parent = new Container
{
Masking = true,
AutoSizeAxes = Axes.Both,
AutoSizeDuration = 1000,
Name = "Parent",
Children = new Drawable[]

Check failure on line 155 in osu.Framework.Tests/Visual/Containers/TestSceneCompositeDrawable.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Redundant explicit array type specification in osu.Framework.Tests\Visual\Containers\TestSceneCompositeDrawable.cs on line 155
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Yellow,
},
child = new Box
{
Size = new Vector2(100),
Colour = Color4.Red,
Alpha = 0.5f,
}
}
};
});
AddAssert("size matches child", () => Precision.AlmostEquals(parent.ChildSize, child.LayoutSize));
AddStep("resize child", () => child.Size = new Vector2(200));
AddAssert("size doesn't match child", () => !Precision.AlmostEquals(parent.ChildSize, child.LayoutSize));
AddStep("finish autosize transform", () => parent.FinishAutoSizeTransforms());
AddAssert("size matches child", () => Precision.AlmostEquals(parent.ChildSize, child.LayoutSize));
}

private partial class SortableComposite : CompositeDrawable
{
public SortableComposite()
Expand Down
47 changes: 5 additions & 42 deletions osu.Framework.Tests/Visual/Layout/TestSceneLayoutDurations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ namespace osu.Framework.Tests.Visual.Layout
public partial class TestSceneLayoutDurations : FrameworkTestScene
{
private ManualClock manualClock;
private Container autoSizeContainer;
private FillFlowContainer fillFlowContainer;

private Box box1, box2;
private Box box;

private const float duration = 1000;

Expand All @@ -34,24 +33,6 @@ public void SetUp() => Schedule(() =>

Children = new Drawable[]
{
autoSizeContainer = new Container
{
Clock = new FramedClock(manualClock),
AutoSizeEasing = Easing.None,
Children = new[]
{
new Box
{
Colour = Color4.Red,
RelativeSizeAxes = Axes.Both
},
box1 = new Box
{
Colour = Color4.Transparent,
Size = Vector2.Zero,
},
}
},
fillFlowContainer = new FillFlowContainer
{
Clock = new FramedClock(manualClock),
Expand All @@ -60,27 +41,20 @@ public void SetUp() => Schedule(() =>
Children = new Drawable[]
{
new Box { Colour = Color4.Red, Size = new Vector2(100) },
box2 = new Box { Colour = Color4.Blue, Size = new Vector2(100) },
box = new Box { Colour = Color4.Blue, Size = new Vector2(100) },
}
}
};

paused = false;
autoSizeContainer.FinishTransforms();
fillFlowContainer.FinishTransforms();

autoSizeContainer.AutoSizeAxes = Axes.None;
autoSizeContainer.AutoSizeDuration = 0;
autoSizeContainer.Size = Vector2.Zero;
box1.Size = Vector2.Zero;

fillFlowContainer.LayoutDuration = 0;
fillFlowContainer.Size = new Vector2(200, 200);
});

private void check(float ratio) =>
AddAssert($"Check @{ratio}", () => Precision.AlmostEquals(autoSizeContainer.Size, new Vector2(changed_value * ratio)) &&
Precision.AlmostEquals(box2.Position, new Vector2(changed_value * (1 - ratio), changed_value * ratio)));
AddAssert($"Check @{ratio}", () => Precision.AlmostEquals(box.Position, new Vector2(changed_value * (1 - ratio), changed_value * ratio)));

private void skipTo(float ratio) => AddStep($"skip to {ratio}", () => { manualClock.CurrentTime = duration * ratio; });

Expand All @@ -91,13 +65,8 @@ public void TestChangeAfterDuration()
{
paused = true;
manualClock.CurrentTime = 0;
autoSizeContainer.FinishTransforms();
fillFlowContainer.FinishTransforms();

autoSizeContainer.AutoSizeAxes = Axes.Both;
autoSizeContainer.AutoSizeDuration = duration;
box1.Size = new Vector2(100);

fillFlowContainer.LayoutDuration = duration;
fillFlowContainer.Width = 100;
});
Expand All @@ -116,14 +85,11 @@ public void TestInterruptExistingDuration()
{
paused = true;
manualClock.CurrentTime = 0;
autoSizeContainer.FinishTransforms();
fillFlowContainer.FinishTransforms();

autoSizeContainer.AutoSizeAxes = Axes.Both;
autoSizeContainer.AutoSizeDuration = duration;
fillFlowContainer.LayoutDuration = duration;

box1.Size = new Vector2(changed_value);
box.Size = new Vector2(changed_value);
fillFlowContainer.Width = changed_value;
});

Expand All @@ -132,7 +98,6 @@ public void TestInterruptExistingDuration()

AddStep("set duration 0", () =>
{
autoSizeContainer.AutoSizeDuration = 0;
fillFlowContainer.LayoutDuration = 0;
});

Expand All @@ -146,7 +111,6 @@ public void TestInterruptExistingDuration()

AddStep("alter values", () =>
{
box1.Size = new Vector2(0);
fillFlowContainer.Width = 200;
});

Expand All @@ -162,11 +126,10 @@ public void TestInterruptExistingDuration()

protected override void Update()
{
if (autoSizeContainer != null)
if (fillFlowContainer != null)
{
if (!paused) manualClock.CurrentTime = Clock.CurrentTime;

autoSizeContainer.Children[0].Invalidate();
fillFlowContainer.Invalidate();
}

Expand Down
89 changes: 56 additions & 33 deletions osu.Framework/Graphics/Containers/CompositeDrawable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using osu.Framework.Statistics;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Caching;
using osu.Framework.Development;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Extensions.ExceptionExtensions;
Expand Down Expand Up @@ -945,6 +946,7 @@ public override bool UpdateSubTree()
UpdateAfterChildren();

updateChildrenSizeDependencies();
applyAutoSize();
UpdateAfterAutoSize();
return true;
}
Expand Down Expand Up @@ -1813,17 +1815,11 @@ protected set
}

/// <summary>
/// The duration which automatic sizing should take. If zero, then it is instantaneous.
/// Otherwise, this is equivalent to applying an automatic size via a resize transform.
/// The duration which automatic sizing should approximately take. If zero, then it is instantaneous.
/// AutoSize is being applied continuously so the actual amount of time taken depends on the overall change in value.
/// </summary>
public float AutoSizeDuration { get; protected set; }

/// <summary>
/// The type of easing which should be used for smooth automatic sizing when <see cref="AutoSizeDuration"/>
/// is non-zero.
/// </summary>
public Easing AutoSizeEasing { get; protected set; }

/// <summary>
/// Fired after this <see cref="CompositeDrawable"/>'s <see cref="Size"/> is updated through autosize.
/// </summary>
Expand Down Expand Up @@ -1938,14 +1934,15 @@ private Vector2 computeAutoSize()
private void updateAutoSize()
{
if (AutoSizeAxes == Axes.None)
{
targetAutoSize.Invalidate();
return;
}

Vector2 b = computeAutoSize() + Padding.Total;
targetAutoSize.Value = computeAutoSize() + Padding.Total;

autoSizeResizeTo(new Vector2(
AutoSizeAxes.HasFlagFast(Axes.X) ? b.X : base.Width,
AutoSizeAxes.HasFlagFast(Axes.Y) ? b.Y : base.Height
), AutoSizeDuration, AutoSizeEasing);
if (!didInitialAutoSize || AutoSizeDuration <= 0)
autoSizeResizeTo(targetAutoSize.Value, 0);

//note that this is called before autoSize becomes valid. may be something to consider down the line.
//might work better to add an OnRefresh event in Cached<> and invoke there.
Expand All @@ -1970,25 +1967,59 @@ private void updateChildrenSizeDependencies()
}
}

private void autoSizeResizeTo(Vector2 newSize, double duration = 0, Easing easing = Easing.None)
private void applyAutoSize()
{
if (targetAutoSize.IsValid)
autoSizeResizeTo(targetAutoSize.Value, AutoSizeDuration);

didInitialAutoSize = true;
}

private void autoSizeResizeTo(Vector2 targetSize, double duration)
{
var currentTransform = TransformsForTargetMember(nameof(baseSize)).FirstOrDefault() as AutoSizeTransform;
targetSize = new Vector2(
AutoSizeAxes.HasFlagFast(Axes.X) ? targetSize.X : base.Width,
AutoSizeAxes.HasFlagFast(Axes.Y) ? targetSize.Y : base.Height
);

if ((currentTransform?.EndValue ?? Size) != newSize)
if (duration <= 0)
{
if (duration == 0)
{
if (currentTransform != null)
ClearTransforms(false, nameof(baseSize));
baseSize = newSize;
}
else
this.TransformTo(this.PopulateTransform(new AutoSizeTransform { Rewindable = false }, newSize, duration, easing));
baseSize = targetSize;
targetAutoSize.Invalidate();
return;
}

Vector2 newSize = Interpolation.DampContinuously(baseSize, targetSize, duration / 4, Time.Elapsed);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duration / 4 value is kind of eyeballed here, it will bring the current value within 0.4% of the target value after the given duration has passed. When testing this the resulting animation roughly landed in the right ballpark for most cases.


if (Precision.AlmostEquals(newSize, targetSize, 0.5f))
{
newSize = targetSize;
targetAutoSize.Invalidate();
}

baseSize = newSize;
}

/// <summary>
/// A helper property for <see cref="autoSizeResizeTo(Vector2, double, Easing)"/> to change the size of <see cref="CompositeDrawable"/>s with <see cref="AutoSizeAxes"/>.
/// Immediately resizes to the current target size if <see cref="AutoSizeDuration"/> is non-zero.
/// </summary>
protected void FinishAutoSizeTransforms()
{
updateChildrenSizeDependencies();

if (targetAutoSize.IsValid)
autoSizeResizeTo(targetAutoSize.Value, 0);
}

/// <summary>
/// When valid, holds the current target size that should be approached when using automatic sizing and <see cref="AutoSizeDuration"/> is non-zero.
/// </summary>
private readonly Cached<Vector2> targetAutoSize = new Cached<Vector2>();

private bool didInitialAutoSize;

/// <summary>
/// A helper property for <see cref="autoSizeResizeTo(Vector2, double)"/> to change the size of <see cref="CompositeDrawable"/>s with <see cref="AutoSizeAxes"/>.
/// </summary>
private Vector2 baseSize
{
Expand All @@ -2000,14 +2031,6 @@ private Vector2 baseSize
}
}

private class AutoSizeTransform : TransformCustom<Vector2, CompositeDrawable>
{
public AutoSizeTransform()
: base(nameof(baseSize))
{
}
}

#endregion
}
}
13 changes: 4 additions & 9 deletions osu.Framework/Graphics/Containers/Container.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,8 +492,8 @@ public void ChangeChildDepth(T child, float newDepth)
}

/// <summary>
/// The duration which automatic sizing should take. If zero, then it is instantaneous.
/// Otherwise, this is equivalent to applying an automatic size via a resize transform.
/// The duration which automatic sizing should approximately take. If zero, then it is instantaneous.
/// AutoSize is being applied continuously so the actual amount of time taken depends on the overall change in value.
/// </summary>
public new float AutoSizeDuration
{
Expand All @@ -502,14 +502,9 @@ public void ChangeChildDepth(T child, float newDepth)
}

/// <summary>
/// The type of easing which should be used for smooth automatic sizing when <see cref="AutoSizeDuration"/>
/// is non-zero.
/// Immediately resizes to the current target size if <see cref="AutoSizeDuration"/> is non-zero.
/// </summary>
public new Easing AutoSizeEasing
{
get => base.AutoSizeEasing;
set => base.AutoSizeEasing = value;
}
public new void FinishAutoSizeTransforms() => base.FinishAutoSizeTransforms();

public struct Enumerator : IEnumerator<T>
{
Expand Down
Loading
Loading