1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-14 05:47:20 +08:00

Merge pull request #19377 from frenzibyte/fix-timeline-tests

Fix intermittent timeline zoom test failures
This commit is contained in:
Dean Herbert 2022-07-26 15:18:35 +09:00 committed by GitHub
commit 4efe09e7c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 79 deletions

View File

@ -8,26 +8,17 @@ using osu.Framework.Graphics;
namespace osu.Game.Tests.Visual.Editing
{
[Ignore("Timeline initialisation is kinda broken.")] // Initial work to rectify this was done in https://github.com/ppy/osu/pull/19297, but needs more massaging to work.
public class TestSceneTimelineZoom : TimelineTestScene
{
public override Drawable CreateTestComponent() => Empty();
[Test]
[FlakyTest]
/*
* Fail rate around 0.3%
*
* TearDown : osu.Framework.Testing.Drawables.Steps.AssertButton+TracedException : range halved
* --TearDown
* at osu.Framework.Threading.ScheduledDelegate.RunTaskInternal()
* at osu.Framework.Threading.Scheduler.Update()
* at osu.Framework.Graphics.Drawable.UpdateSubTree()
*/
public void TestVisibleRangeUpdatesOnZoomChange()
{
double initialVisibleRange = 0;
AddUntilStep("wait for load", () => MusicController.TrackLoaded);
AddStep("reset zoom", () => TimelineArea.Timeline.Zoom = 100);
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);
@ -45,6 +36,8 @@ namespace osu.Game.Tests.Visual.Editing
{
double initialVisibleRange = 0;
AddUntilStep("wait for load", () => MusicController.TrackLoaded);
AddStep("reset timeline size", () => TimelineArea.Timeline.Width = 1);
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);

View File

@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Editing
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(30)
},
scrollContainer = new ZoomableScrollContainer
scrollContainer = new ZoomableScrollContainer(1, 60, 1)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -80,21 +80,6 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
}
[Test]
public void TestZoomRangeUpdate()
{
AddStep("set zoom to 2", () => scrollContainer.Zoom = 2);
AddStep("set min zoom to 5", () => scrollContainer.MinZoom = 5);
AddAssert("zoom = 5", () => scrollContainer.Zoom == 5);
AddStep("set max zoom to 10", () => scrollContainer.MaxZoom = 10);
AddAssert("zoom = 5", () => scrollContainer.Zoom == 5);
AddStep("set min zoom to 20", () => scrollContainer.MinZoom = 20);
AddStep("set max zoom to 40", () => scrollContainer.MaxZoom = 40);
AddAssert("zoom = 20", () => scrollContainer.Zoom == 20);
}
[Test]
public void TestZoom0()
{

View File

@ -146,13 +146,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
waveform.Waveform = b.NewValue.Waveform;
track = b.NewValue.Track;
// todo: i don't think this is safe, the track may not be loaded yet.
if (track.Length > 0)
{
MaxZoom = getZoomLevelForVisibleMilliseconds(500);
MinZoom = getZoomLevelForVisibleMilliseconds(10000);
defaultTimelineZoom = getZoomLevelForVisibleMilliseconds(6000);
}
setupTimelineZoom();
}, true);
Zoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom);
@ -205,6 +199,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
scrollToTrackTime();
}
private void setupTimelineZoom()
{
if (!track.IsLoaded)
{
Scheduler.AddOnce(setupTimelineZoom);
return;
}
defaultTimelineZoom = getZoomLevelForVisibleMilliseconds(6000);
float initialZoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom);
SetupZoom(initialZoom, getZoomLevelForVisibleMilliseconds(10000), getZoomLevelForVisibleMilliseconds(500));
}
protected override bool OnScroll(ScrollEvent e)
{
// if this is not a precision scroll event, let the editor handle the seek itself (for snapping support)

View File

@ -32,20 +32,28 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private readonly Container zoomedContent;
protected override Container<Drawable> Content => zoomedContent;
private float currentZoom = 1;
/// <summary>
/// The current zoom level of <see cref="ZoomableScrollContainer" />.
/// It may differ from <see cref="Zoom" /> during transitions.
/// The current zoom level of <see cref="ZoomableScrollContainer"/>.
/// It may differ from <see cref="Zoom"/> during transitions.
/// </summary>
public float CurrentZoom => currentZoom;
public float CurrentZoom { get; private set; } = 1;
private bool isZoomSetUp;
[Resolved(canBeNull: true)]
private IFrameBasedClock editorClock { get; set; }
private readonly LayoutValue zoomedContentWidthCache = new LayoutValue(Invalidation.DrawSize);
public ZoomableScrollContainer()
private float minZoom;
private float maxZoom;
/// <summary>
/// Creates a <see cref="ZoomableScrollContainer"/> with no zoom range.
/// Functionality will be disabled until zoom is set up via <see cref="SetupZoom"/>.
/// </summary>
protected ZoomableScrollContainer()
: base(Direction.Horizontal)
{
base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y });
@ -53,46 +61,36 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
AddLayout(zoomedContentWidthCache);
}
private float minZoom = 1;
/// <summary>
/// The minimum zoom level allowed.
/// Creates a <see cref="ZoomableScrollContainer"/> with a defined zoom range.
/// </summary>
public float MinZoom
public ZoomableScrollContainer(float minimum, float maximum, float initial)
: this()
{
get => minZoom;
set
{
if (value < 1)
throw new ArgumentException($"{nameof(MinZoom)} must be >= 1.", nameof(value));
minZoom = value;
// ensure zoom range is in valid state before updating zoom.
if (MinZoom < MaxZoom)
updateZoom();
}
SetupZoom(initial, minimum, maximum);
}
private float maxZoom = 60;
/// <summary>
/// The maximum zoom level allowed.
/// Sets up the minimum and maximum range of this zoomable scroll container, along with the initial zoom value.
/// </summary>
public float MaxZoom
/// <param name="initial">The initial zoom value, applied immediately.</param>
/// <param name="minimum">The minimum zoom value.</param>
/// <param name="maximum">The maximum zoom value.</param>
protected void SetupZoom(float initial, float minimum, float maximum)
{
get => maxZoom;
set
{
if (value < 1)
throw new ArgumentException($"{nameof(MaxZoom)} must be >= 1.", nameof(value));
if (minimum < 1)
throw new ArgumentException($"{nameof(minimum)} ({minimum}) must be >= 1.", nameof(maximum));
maxZoom = value;
if (maximum < 1)
throw new ArgumentException($"{nameof(maximum)} ({maximum}) must be >= 1.", nameof(maximum));
// ensure zoom range is in valid state before updating zoom.
if (MaxZoom > MinZoom)
updateZoom();
}
if (minimum > maximum)
throw new ArgumentException($"{nameof(minimum)} ({minimum}) must be less than {nameof(maximum)} ({maximum})");
minZoom = minimum;
maxZoom = maximum;
CurrentZoom = zoomTarget = initial;
isZoomSetUp = true;
}
/// <summary>
@ -104,14 +102,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
set => updateZoom(value);
}
private void updateZoom(float? value = null)
private void updateZoom(float value)
{
float newZoom = Math.Clamp(value ?? Zoom, MinZoom, MaxZoom);
if (!isZoomSetUp)
return;
float newZoom = Math.Clamp(value, minZoom, maxZoom);
if (IsLoaded)
setZoomTarget(newZoom, ToSpaceOfOtherDrawable(new Vector2(DrawWidth / 2, 0), zoomedContent).X);
else
currentZoom = zoomTarget = newZoom;
CurrentZoom = zoomTarget = newZoom;
}
protected override void Update()
@ -141,22 +142,25 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private void updateZoomedContentWidth()
{
zoomedContent.Width = DrawWidth * currentZoom;
zoomedContent.Width = DrawWidth * CurrentZoom;
zoomedContentWidthCache.Validate();
}
public void AdjustZoomRelatively(float change, float? focusPoint = null)
{
if (!isZoomSetUp)
return;
const float zoom_change_sensitivity = 0.02f;
setZoomTarget(zoomTarget + change * (MaxZoom - minZoom) * zoom_change_sensitivity, focusPoint);
setZoomTarget(zoomTarget + change * (maxZoom - minZoom) * zoom_change_sensitivity, focusPoint);
}
private float zoomTarget = 1;
private void setZoomTarget(float newZoom, float? focusPoint = null)
{
zoomTarget = Math.Clamp(newZoom, MinZoom, MaxZoom);
zoomTarget = Math.Clamp(newZoom, minZoom, maxZoom);
focusPoint ??= zoomedContent.ToLocalSpace(ToScreenSpace(new Vector2(DrawWidth / 2, 0))).X;
transformZoomTo(zoomTarget, focusPoint.Value, ZoomDuration, ZoomEasing);
@ -192,7 +196,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private readonly float scrollOffset;
/// <summary>
/// Transforms <see cref="ZoomableScrollContainer.currentZoom"/> to a new value.
/// Transforms <see cref="ZoomableScrollContainer.CurrentZoom"/> to a new value.
/// </summary>
/// <param name="focusPoint">The focus point in absolute coordinates local to the content.</param>
/// <param name="contentSize">The size of the content.</param>
@ -204,7 +208,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
this.scrollOffset = scrollOffset;
}
public override string TargetMember => nameof(currentZoom);
public override string TargetMember => nameof(CurrentZoom);
private float valueAt(double time)
{
@ -222,7 +226,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
float expectedWidth = d.DrawWidth * newZoom;
float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset;
d.currentZoom = newZoom;
d.CurrentZoom = newZoom;
d.updateZoomedContentWidth();
// Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area.
@ -231,7 +235,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
d.ScrollTo(targetOffset, false);
}
protected override void ReadIntoStartValue(ZoomableScrollContainer d) => StartValue = d.currentZoom;
protected override void ReadIntoStartValue(ZoomableScrollContainer d) => StartValue = d.CurrentZoom;
}
}
}