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:
commit
4efe09e7c2
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user