1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-18 14:43:22 +08:00

Merge pull request #31364 from bdach/editor-storyboard-display-2

Display storyboard in editor background
This commit is contained in:
Dean Herbert 2025-01-10 15:09:44 +09:00 committed by GitHub
commit 73adc4dfd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 142 additions and 55 deletions

View File

@ -7,16 +7,15 @@ using System;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components;
@ -43,14 +42,6 @@ namespace osu.Game.Tests.Visual.Editing
private BeatmapSetInfo importedBeatmapSet; private BeatmapSetInfo importedBeatmapSet;
private Bindable<float> editorDim;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
editorDim = config.GetBindable<float>(OsuSetting.EditorDim);
}
public override void SetUpSteps() public override void SetUpSteps()
{ {
AddStep("import test beatmap", () => importedBeatmapSet = BeatmapImportHelper.LoadOszIntoOsu(game).GetResultSafely()); AddStep("import test beatmap", () => importedBeatmapSet = BeatmapImportHelper.LoadOszIntoOsu(game).GetResultSafely());
@ -81,15 +72,7 @@ namespace osu.Game.Tests.Visual.Editing
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null); AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
AddStep("exit player", () => editorPlayer.Exit()); AddStep("exit player", () => editorPlayer.Exit());
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor); AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
AddUntilStep("background has correct params", () => AddUntilStep("background is correct", () => this.ChildrenOfType<BackgroundScreenStack>().Single().CurrentScreen is EditorBackgroundScreen);
{
// the test gameplay player's beatmap may be the "same" beatmap as the one being edited, *but* the `BeatmapInfo` references may differ
// due to the beatmap refetch logic ran on editor suspend.
// this test cares about checking the background belonging to the editor specifically, so check that using reference equality
// (as `.Equals()` cannot discern between the two, as they technically share the same database GUID).
var background = this.ChildrenOfType<BackgroundScreenBeatmap>().Single(b => ReferenceEquals(b.Beatmap.BeatmapInfo, EditorBeatmap.BeatmapInfo));
return background.DimWhenUserSettingsIgnored.Value == editorDim.Value && background.BlurAmount.Value == 0;
});
AddAssert("no mods selected", () => SelectedMods.Value.Count == 0); AddAssert("no mods selected", () => SelectedMods.Value.Count == 0);
} }
@ -114,15 +97,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("exit player", () => editorPlayer.Exit()); AddStep("exit player", () => editorPlayer.Exit());
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor); AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
AddUntilStep("background has correct params", () => AddUntilStep("background is correct", () => this.ChildrenOfType<BackgroundScreenStack>().Single().CurrentScreen is EditorBackgroundScreen);
{
// the test gameplay player's beatmap may be the "same" beatmap as the one being edited, *but* the `BeatmapInfo` references may differ
// due to the beatmap refetch logic ran on editor suspend.
// this test cares about checking the background belonging to the editor specifically, so check that using reference equality
// (as `.Equals()` cannot discern between the two, as they technically share the same database GUID).
var background = this.ChildrenOfType<BackgroundScreenBeatmap>().Single(b => ReferenceEquals(b.Beatmap.BeatmapInfo, EditorBeatmap.BeatmapInfo));
return background.DimWhenUserSettingsIgnored.Value == editorDim.Value && background.BlurAmount.Value == 0;
});
AddStep("start track", () => EditorClock.Start()); AddStep("start track", () => EditorClock.Start());
AddAssert("sample playback re-enabled", () => !Editor.SamplePlaybackDisabled.Value); AddAssert("sample playback re-enabled", () => !Editor.SamplePlaybackDisabled.Value);

View File

@ -220,6 +220,7 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.AlwaysShowHoldForMenuButton, false); SetDefault(OsuSetting.AlwaysShowHoldForMenuButton, false);
SetDefault(OsuSetting.AlwaysRequireHoldingForPause, false); SetDefault(OsuSetting.AlwaysRequireHoldingForPause, false);
SetDefault(OsuSetting.EditorShowStoryboard, true);
} }
protected override bool CheckLookupContainsPrivateInformation(OsuSetting lookup) protected override bool CheckLookupContainsPrivateInformation(OsuSetting lookup)
@ -455,5 +456,6 @@ namespace osu.Game.Configuration
MultiplayerShowInProgressFilter, MultiplayerShowInProgressFilter,
BeatmapListingFeaturedArtistFilter, BeatmapListingFeaturedArtistFilter,
ShowMobileDisclaimer, ShowMobileDisclaimer,
EditorShowStoryboard,
} }
} }

View File

@ -101,18 +101,6 @@ namespace osu.Game.Screens.Backgrounds
} }
} }
/// <summary>
/// Reloads beatmap's background.
/// </summary>
public void RefreshBackground()
{
Schedule(() =>
{
cancellationSource?.Cancel();
LoadComponentAsync(new BeatmapBackground(beatmap), switchBackground, (cancellationSource = new CancellationTokenSource()).Token);
});
}
private void switchBackground(BeatmapBackground b) private void switchBackground(BeatmapBackground b)
{ {
float newDepth = 0; float newDepth = 0;

View File

@ -0,0 +1,117 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Storyboards.Drawables;
namespace osu.Game.Screens.Backgrounds
{
public partial class EditorBackgroundScreen : BackgroundScreen
{
private readonly WorkingBeatmap beatmap;
private readonly Container dimContainer;
private CancellationTokenSource? cancellationTokenSource;
private Bindable<float> dimLevel = null!;
private Bindable<bool> showStoryboard = null!;
private BeatmapBackground background = null!;
private Container storyboardContainer = null!;
private IFrameBasedClock? clockSource;
public EditorBackgroundScreen(WorkingBeatmap beatmap)
{
this.beatmap = beatmap;
InternalChild = dimContainer = new Container
{
RelativeSizeAxes = Axes.Both,
};
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
dimContainer.AddRange(createContent());
background = dimContainer.OfType<BeatmapBackground>().Single();
storyboardContainer = dimContainer.OfType<Container>().Single();
dimLevel = config.GetBindable<float>(OsuSetting.EditorDim);
showStoryboard = config.GetBindable<bool>(OsuSetting.EditorShowStoryboard);
}
private IEnumerable<Drawable> createContent() =>
[
new BeatmapBackground(beatmap) { RelativeSizeAxes = Axes.Both, },
// this kooky container nesting is here because the storyboard needs a custom clock
// but also needs it on an isolated-enough level that doesn't break screen stack expiry logic (which happens if the clock was put on `this`),
// or doesn't make it literally impossible to fade the storyboard in/out in real time (which happens if the fade transforms were to be applied directly to the storyboard).
new Container
{
RelativeSizeAxes = Axes.Both,
Child = new DrawableStoryboard(beatmap.Storyboard)
{
Clock = clockSource ?? Clock,
}
}
];
protected override void LoadComplete()
{
base.LoadComplete();
dimLevel.BindValueChanged(_ => dimContainer.FadeColour(OsuColour.Gray(1 - dimLevel.Value), 500, Easing.OutQuint), true);
showStoryboard.BindValueChanged(_ => updateState());
updateState(0);
}
private void updateState(double duration = 500)
{
storyboardContainer.FadeTo(showStoryboard.Value ? 1 : 0, duration, Easing.OutQuint);
// yes, this causes overdraw, but is also a (crude) fix for bad-looking transitions on screen entry
// caused by the previous background on the background stack poking out from under this one and then instantly fading out
background.FadeColour(beatmap.Storyboard.ReplacesBackground && showStoryboard.Value ? Colour4.Black : Colour4.White, duration, Easing.OutQuint);
}
public void ChangeClockSource(IFrameBasedClock frameBasedClock)
{
clockSource = frameBasedClock;
if (IsLoaded)
storyboardContainer.Child.Clock = frameBasedClock;
}
public void RefreshBackground()
{
cancellationTokenSource?.Cancel();
LoadComponentsAsync(createContent(), loaded =>
{
dimContainer.Clear();
dimContainer.AddRange(loaded);
background = dimContainer.OfType<BeatmapBackground>().Single();
storyboardContainer = dimContainer.OfType<Container>().Single();
updateState(0);
}, (cancellationTokenSource ??= new CancellationTokenSource()).Token);
}
public override bool Equals(BackgroundScreen? other)
{
if (other is not EditorBackgroundScreen otherBeatmapBackground)
return false;
return base.Equals(other) && beatmap == otherBeatmapBackground.beatmap;
}
}
}

View File

@ -45,6 +45,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Screens.Edit.Components.Menus;
using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Screens.Edit.Compose.Components.Timeline;
@ -54,7 +55,6 @@ using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Edit.Timing; using osu.Game.Screens.Edit.Timing;
using osu.Game.Screens.Edit.Verify; using osu.Game.Screens.Edit.Verify;
using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay;
using osu.Game.Screens.Play;
using osu.Game.Users; using osu.Game.Users;
using osuTK.Input; using osuTK.Input;
using WebCommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings; using WebCommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings;
@ -63,7 +63,7 @@ namespace osu.Game.Screens.Edit
{ {
[Cached(typeof(IBeatSnapProvider))] [Cached(typeof(IBeatSnapProvider))]
[Cached] [Cached]
public partial class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>, IKeyBindingHandler<PlatformAction>, IBeatSnapProvider, ISamplePlaybackDisabler, IBeatSyncProvider public partial class Editor : OsuScreen, IKeyBindingHandler<GlobalAction>, IKeyBindingHandler<PlatformAction>, IBeatSnapProvider, ISamplePlaybackDisabler, IBeatSyncProvider
{ {
/// <summary> /// <summary>
/// An offset applied to waveform visuals to align them with expectations. /// An offset applied to waveform visuals to align them with expectations.
@ -210,6 +210,7 @@ namespace osu.Game.Screens.Edit
private OnScreenDisplay onScreenDisplay { get; set; } private OnScreenDisplay onScreenDisplay { get; set; }
private Bindable<float> editorBackgroundDim; private Bindable<float> editorBackgroundDim;
private Bindable<bool> editorShowStoryboard;
private Bindable<bool> editorHitMarkers; private Bindable<bool> editorHitMarkers;
private Bindable<bool> editorAutoSeekOnPlacement; private Bindable<bool> editorAutoSeekOnPlacement;
private Bindable<bool> editorLimitedDistanceSnap; private Bindable<bool> editorLimitedDistanceSnap;
@ -320,6 +321,7 @@ namespace osu.Game.Screens.Edit
OsuMenuItem redoMenuItem; OsuMenuItem redoMenuItem;
editorBackgroundDim = config.GetBindable<float>(OsuSetting.EditorDim); editorBackgroundDim = config.GetBindable<float>(OsuSetting.EditorDim);
editorShowStoryboard = config.GetBindable<bool>(OsuSetting.EditorShowStoryboard);
editorHitMarkers = config.GetBindable<bool>(OsuSetting.EditorShowHitMarkers); editorHitMarkers = config.GetBindable<bool>(OsuSetting.EditorShowHitMarkers);
editorAutoSeekOnPlacement = config.GetBindable<bool>(OsuSetting.EditorAutoSeekOnPlacement); editorAutoSeekOnPlacement = config.GetBindable<bool>(OsuSetting.EditorAutoSeekOnPlacement);
editorLimitedDistanceSnap = config.GetBindable<bool>(OsuSetting.EditorLimitedDistanceSnap); editorLimitedDistanceSnap = config.GetBindable<bool>(OsuSetting.EditorLimitedDistanceSnap);
@ -398,7 +400,13 @@ namespace osu.Game.Screens.Edit
}, },
] ]
}, },
new OsuMenuItemSpacer(),
new BackgroundDimMenuItem(editorBackgroundDim), new BackgroundDimMenuItem(editorBackgroundDim),
new ToggleMenuItem("Show storyboard")
{
State = { BindTarget = editorShowStoryboard },
},
new OsuMenuItemSpacer(),
new ToggleMenuItem(EditorStrings.ShowHitMarkers) new ToggleMenuItem(EditorStrings.ShowHitMarkers)
{ {
State = { BindTarget = editorHitMarkers }, State = { BindTarget = editorHitMarkers },
@ -466,12 +474,14 @@ namespace osu.Game.Screens.Edit
changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true); changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
changeHandler?.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true); changeHandler?.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
editorBackgroundDim.BindValueChanged(_ => dimBackground()); editorBackgroundDim.BindValueChanged(_ => setUpBackground());
} }
[Resolved] [Resolved]
private MusicController musicController { get; set; } private MusicController musicController { get; set; }
protected override BackgroundScreen CreateBackground() => new EditorBackgroundScreen(Beatmap.Value);
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -853,24 +863,23 @@ namespace osu.Game.Screens.Edit
public override void OnEntering(ScreenTransitionEvent e) public override void OnEntering(ScreenTransitionEvent e)
{ {
base.OnEntering(e); base.OnEntering(e);
dimBackground(); setUpBackground();
resetTrack(true); resetTrack(true);
} }
public override void OnResuming(ScreenTransitionEvent e) public override void OnResuming(ScreenTransitionEvent e)
{ {
base.OnResuming(e); base.OnResuming(e);
dimBackground(); setUpBackground();
clock.BindAdjustments(); clock.BindAdjustments();
} }
private void dimBackground() private void setUpBackground()
{ {
ApplyToBackground(b => ApplyToBackground(b =>
{ {
b.IgnoreUserSettings.Value = true; var editorBackground = (EditorBackgroundScreen)b;
b.DimWhenUserSettingsIgnored.Value = editorBackgroundDim.Value; editorBackground.ChangeClockSource(clock);
b.BlurAmount.Value = 0;
}); });
} }
@ -909,11 +918,6 @@ namespace osu.Game.Screens.Edit
beatmap.EditorTimestamp = clock.CurrentTime; beatmap.EditorTimestamp = clock.CurrentTime;
}); });
ApplyToBackground(b =>
{
b.DimWhenUserSettingsIgnored.Value = 0;
});
resetTrack(); resetTrack();
refetchBeatmap(); refetchBeatmap();

View File

@ -12,6 +12,7 @@ using osu.Game.Beatmaps;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Models; using osu.Game.Models;
using osu.Game.Screens.Backgrounds;
using osu.Game.Utils; using osu.Game.Utils;
namespace osu.Game.Screens.Edit.Setup namespace osu.Game.Screens.Edit.Setup
@ -87,7 +88,7 @@ namespace osu.Game.Screens.Edit.Setup
(metadata, name) => metadata.BackgroundFile = name); (metadata, name) => metadata.BackgroundFile = name);
headerBackground.UpdateBackground(); headerBackground.UpdateBackground();
editor?.ApplyToBackground(bg => bg.RefreshBackground()); editor?.ApplyToBackground(bg => ((EditorBackgroundScreen)bg).RefreshBackground());
return true; return true;
} }