diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineZoom.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineZoom.cs new file mode 100644 index 0000000000..d726bd004e --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineZoom.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Utils; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneTimelineZoom : TimelineTestScene + { + public override Drawable CreateTestComponent() => Empty(); + + [Test] + public void TestVisibleRangeUpdatesOnZoomChange() + { + double initialVisibleRange = 0; + + AddStep("reset zoom", () => TimelineArea.Timeline.Zoom = 100); + AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange); + + AddStep("scale zoom", () => TimelineArea.Timeline.Zoom = 200); + AddAssert("range halved", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange / 2, 1)); + AddStep("descale zoom", () => TimelineArea.Timeline.Zoom = 50); + AddAssert("range doubled", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange * 2, 1)); + + AddStep("restore zoom", () => TimelineArea.Timeline.Zoom = 100); + AddAssert("range restored", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange, 1)); + } + + [Test] + public void TestVisibleRangeConstantOnSizeChange() + { + double initialVisibleRange = 0; + + AddStep("reset timeline size", () => TimelineArea.Timeline.Width = 1); + AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange); + + AddStep("scale timeline size", () => TimelineArea.Timeline.Width = 2); + AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange); + AddStep("descale timeline size", () => TimelineArea.Timeline.Width = 0.5f); + AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange); + + AddStep("restore timeline size", () => TimelineArea.Timeline.Width = 1); + AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange); + } + } +} diff --git a/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs b/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs index 95d11d6909..2d056bafdd 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs @@ -44,7 +44,12 @@ namespace osu.Game.Tests.Visual.Editing RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(30) }, - scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both } + scrollContainer = new ZoomableScrollContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + } } }, new MenuCursor() @@ -62,7 +67,15 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestWidthInitialization() { - AddAssert("Inner container width was initialized", () => innerBox.DrawWidth > 0); + AddAssert("Inner container width was initialized", () => innerBox.DrawWidth == scrollContainer.DrawWidth); + } + + [Test] + public void TestWidthUpdatesOnDrawSizeChanges() + { + AddStep("Shrink scroll container", () => scrollContainer.Width = 0.5f); + AddAssert("Scroll container width shrunk", () => scrollContainer.DrawWidth == scrollContainer.Parent.DrawWidth / 2); + AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth); } [Test] diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs index 35d103ddf1..d008368b69 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Transforms; using osu.Framework.Input.Events; +using osu.Framework.Layout; using osu.Framework.Timing; using osu.Framework.Utils; using osu.Game.Graphics.Containers; @@ -40,10 +41,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved(canBeNull: true)] private IFrameBasedClock editorClock { get; set; } + private readonly LayoutValue zoomedContentWidthCache = new LayoutValue(Invalidation.DrawSize); + public ZoomableScrollContainer() : base(Direction.Horizontal) { base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y }); + + AddLayout(zoomedContentWidthCache); } private float minZoom = 1; @@ -103,12 +108,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - protected override void LoadComplete() + protected override void Update() { - base.LoadComplete(); + base.Update(); - // This width only gets updated on the application of a transform, so this needs to be initialized here. - updateZoomedContentWidth(); + if (!zoomedContentWidthCache.IsValid) + updateZoomedContentWidth(); } protected override bool OnScroll(ScrollEvent e) @@ -128,7 +133,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return base.OnScroll(e); } - private void updateZoomedContentWidth() => zoomedContent.Width = DrawWidth * currentZoom; + private void updateZoomedContentWidth() + { + zoomedContent.Width = DrawWidth * currentZoom; + zoomedContentWidthCache.Validate(); + } private float zoomTarget = 1; @@ -199,8 +208,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset; d.currentZoom = newZoom; - d.updateZoomedContentWidth(); + // Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area. // TODO: Make sure draw size gets invalidated properly on the framework side, and remove this once it is. d.Invalidate(Invalidation.DrawSize);