diff --git a/osu.Android.props b/osu.Android.props
index dc3e14c141..afc5d4ec52 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
index f7909071ea..9e78185272 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
@@ -7,7 +7,6 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
-using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
using osu.Framework.Timing;
@@ -194,13 +193,7 @@ namespace osu.Game.Rulesets.Osu.Tests
addSeekStep(0);
- AddStep("adjust track rate", () => MusicController.CurrentTrack.AddAdjustment(AdjustableProperty.Tempo, new BindableDouble(rate)));
- // autoplay replay frames use track time;
- // if a spin takes 1000ms in track time and we're playing with a 2x rate adjustment, the spin will take 500ms of *real* time.
- // therefore we need to apply the rate adjustment to the replay itself to change from track time to real time,
- // as real time is what we care about for spinners
- // (so we're making the spin take 1000ms in real time *always*, regardless of the track clock's rate).
- transformReplay(replay => applyRateAdjustment(replay, rate));
+ AddStep("adjust track rate", () => Player.GameplayClockContainer.UserPlaybackRate.Value = rate);
addSeekStep(1000);
AddAssert("progress almost same", () => Precision.AlmostEquals(expectedProgress, drawableSpinner.Progress, 0.05));
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 07f40f763b..b73ae5eeb9 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Tracking.BindValueChanged(updateSlidingSample);
}
- private SkinnableSound slidingSample;
+ private PausableSkinnableSound slidingSample;
protected override void LoadSamples()
{
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
clone.Name = "sliderslide";
- AddInternal(slidingSample = new SkinnableSound(clone)
+ AddInternal(slidingSample = new PausableSkinnableSound(clone)
{
Looping = true
});
@@ -112,10 +112,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private void updateSlidingSample(ValueChangedEvent tracking)
{
- // note that samples will not start playing if exiting a seek operation in the middle of a slider.
- // may be something we want to address at a later point, but not so easy to make happen right now
- // (SkinnableSound would need to expose whether the sample is already playing and this logic would need to run in Update).
- if (tracking.NewValue && ShouldPlaySamples)
+ if (tracking.NewValue)
slidingSample?.Play();
else
slidingSample?.Stop();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index a57bb466c7..6488c60acf 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
isSpinning.BindValueChanged(updateSpinningSample);
}
- private SkinnableSound spinningSample;
+ private PausableSkinnableSound spinningSample;
private const float spinning_sample_initial_frequency = 1.0f;
private const float spinning_sample_modulated_base_frequency = 0.5f;
@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
clone.Name = "spinnerspin";
- AddInternal(spinningSample = new SkinnableSound(clone)
+ AddInternal(spinningSample = new PausableSkinnableSound(clone)
{
Volume = { Value = 0 },
Looping = true,
@@ -113,10 +113,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private void updateSpinningSample(ValueChangedEvent tracking)
{
- // note that samples will not start playing if exiting a seek operation in the middle of a spinner.
- // may be something we want to address at a later point, but not so easy to make happen right now
- // (SkinnableSound would need to expose whether the sample is already playing and this logic would need to run in Update).
- if (tracking.NewValue && ShouldPlaySamples)
+ if (tracking.NewValue)
{
spinningSample?.Play();
spinningSample?.VolumeTo(1, 200);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
index f1a782cbb5..05ed38d241 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
@@ -2,11 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
+using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@@ -77,6 +79,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private bool rotationTransferred;
+ [Resolved(canBeNull: true)]
+ private GameplayClock gameplayClock { get; set; }
+
protected override void Update()
{
base.Update();
@@ -126,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
currentRotation += angle;
// rate has to be applied each frame, because it's not guaranteed to be constant throughout playback
// (see: ModTimeRamp)
- RateAdjustedRotation += (float)(Math.Abs(angle) * Clock.Rate);
+ RateAdjustedRotation += (float)(Math.Abs(angle) * (gameplayClock?.TrueGameplayRate ?? Clock.Rate));
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs
index 7c39c040b1..fcf7c529f5 100644
--- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs
+++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs
@@ -42,9 +42,9 @@ namespace osu.Game.Rulesets.Taiko.Audio
}
}
- private SkinnableSound addSound(HitSampleInfo hitSampleInfo, double lifetimeStart, double lifetimeEnd)
+ private PausableSkinnableSound addSound(HitSampleInfo hitSampleInfo, double lifetimeStart, double lifetimeEnd)
{
- var drawable = new SkinnableSound(hitSampleInfo)
+ var drawable = new PausableSkinnableSound(hitSampleInfo)
{
LifetimeStart = lifetimeStart,
LifetimeEnd = lifetimeEnd
@@ -57,8 +57,8 @@ namespace osu.Game.Rulesets.Taiko.Audio
public class DrumSample
{
- public SkinnableSound Centre;
- public SkinnableSound Rim;
+ public PausableSkinnableSound Centre;
+ public PausableSkinnableSound Rim;
}
}
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
index 09f5ac2224..b82e776164 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
@@ -4,7 +4,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Edit;
-using osu.Game.Rulesets.Osu.Beatmaps;
+using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Timing;
@@ -17,16 +17,26 @@ namespace osu.Game.Tests.Visual.Editing
[Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap;
+ protected override bool ScrollUsingMouseWheel => false;
+
public TestSceneTimingScreen()
{
- editorBeatmap = new EditorBeatmap(new OsuBeatmap());
+ editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
}
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
+ Beatmap.Disabled = true;
+
Child = new TimingScreen();
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ Beatmap.Disabled = false;
+ base.Dispose(isDisposing);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs
index ed75d83151..8f2011e5dd 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs
@@ -22,11 +22,11 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneSkinnableSound : OsuTestScene
{
- [Cached]
+ [Cached(typeof(ISamplePlaybackDisabler))]
private GameplayClock gameplayClock = new GameplayClock(new FramedClock());
private TestSkinSourceContainer skinSource;
- private SkinnableSound skinnableSound;
+ private PausableSkinnableSound skinnableSound;
[SetUp]
public void SetUp() => Schedule(() =>
@@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
Clock = gameplayClock,
RelativeSizeAxes = Axes.Both,
- Child = skinnableSound = new SkinnableSound(new SampleInfo("normal-sliderslide"))
+ Child = skinnableSound = new PausableSkinnableSound(new SampleInfo("normal-sliderslide"))
},
};
});
diff --git a/osu.Game/Extensions/EditorDisplayExtensions.cs b/osu.Game/Extensions/EditorDisplayExtensions.cs
new file mode 100644
index 0000000000..f749b88b46
--- /dev/null
+++ b/osu.Game/Extensions/EditorDisplayExtensions.cs
@@ -0,0 +1,26 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+
+namespace osu.Game.Extensions
+{
+ public static class EditorDisplayExtensions
+ {
+ ///
+ /// Get an editor formatted string (mm:ss:mss)
+ ///
+ /// A time value in milliseconds.
+ /// An editor formatted display string.
+ public static string ToEditorFormattedString(this double milliseconds) =>
+ ToEditorFormattedString(TimeSpan.FromMilliseconds(milliseconds));
+
+ ///
+ /// Get an editor formatted string (mm:ss:mss)
+ ///
+ /// A time value.
+ /// An editor formatted display string.
+ public static string ToEditorFormattedString(this TimeSpan timeSpan) =>
+ $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}";
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs
index 4004953cd1..282de3a8e1 100644
--- a/osu.Game/Rulesets/Mods/ModNightcore.cs
+++ b/osu.Game/Rulesets/Mods/ModNightcore.cs
@@ -52,10 +52,10 @@ namespace osu.Game.Rulesets.Mods
public class NightcoreBeatContainer : BeatSyncedContainer
{
- private SkinnableSound hatSample;
- private SkinnableSound clapSample;
- private SkinnableSound kickSample;
- private SkinnableSound finishSample;
+ private PausableSkinnableSound hatSample;
+ private PausableSkinnableSound clapSample;
+ private PausableSkinnableSound kickSample;
+ private PausableSkinnableSound finishSample;
private int? firstBeat;
@@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mods
{
InternalChildren = new Drawable[]
{
- hatSample = new SkinnableSound(new SampleInfo("nightcore-hat")),
- clapSample = new SkinnableSound(new SampleInfo("nightcore-clap")),
- kickSample = new SkinnableSound(new SampleInfo("nightcore-kick")),
- finishSample = new SkinnableSound(new SampleInfo("nightcore-finish")),
+ hatSample = new PausableSkinnableSound(new SampleInfo("nightcore-hat")),
+ clapSample = new PausableSkinnableSound(new SampleInfo("nightcore-clap")),
+ kickSample = new PausableSkinnableSound(new SampleInfo("nightcore-kick")),
+ finishSample = new PausableSkinnableSound(new SampleInfo("nightcore-finish")),
};
}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 7c05bc9aa7..eb12c1cdfc 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -17,7 +17,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Configuration;
-using osu.Game.Screens.Play;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Objects.Drawables
@@ -34,7 +33,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
///
public readonly Bindable AccentColour = new Bindable(Color4.Gray);
- protected SkinnableSound Samples { get; private set; }
+ protected PausableSkinnableSound Samples { get; private set; }
public virtual IEnumerable GetSamples() => HitObject.Samples;
@@ -179,7 +178,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
}
- Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)));
+ Samples = new PausableSkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)));
AddInternal(Samples);
}
@@ -359,9 +358,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
}
- [Resolved(canBeNull: true)]
- private GameplayClock gameplayClock { get; set; }
-
///
/// Calculate the position to be used for sample playback at a specified X position (0..1).
///
@@ -374,18 +370,13 @@ namespace osu.Game.Rulesets.Objects.Drawables
return balance_adjust_amount * (userPositionalHitSounds.Value ? position - 0.5f : 0);
}
- ///
- /// Whether samples should currently be playing. Will be false during seek operations.
- ///
- protected bool ShouldPlaySamples => gameplayClock?.IsSeeking != true;
-
///
/// Plays all the hit sounds for this .
/// This is invoked automatically when this is hit.
///
public virtual void PlaySamples()
{
- if (Samples != null && ShouldPlaySamples)
+ if (Samples != null)
{
Samples.Balance.Value = CalculateSamplePlaybackBalance(SamplePlaybackPosition);
Samples.Play();
diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
index a4af92749f..55c4edfbd1 100644
--- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
+++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
@@ -2,7 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
@@ -59,7 +62,7 @@ namespace osu.Game.Rulesets.UI
{
if (clock != null)
{
- stabilityGameplayClock.ParentGameplayClock = parentGameplayClock = clock;
+ parentGameplayClock = stabilityGameplayClock.ParentGameplayClock = clock;
GameplayClock.IsPaused.BindTo(clock.IsPaused);
}
}
@@ -215,7 +218,9 @@ namespace osu.Game.Rulesets.UI
private class StabilityGameplayClock : GameplayClock
{
- public IFrameBasedClock ParentGameplayClock;
+ public GameplayClock ParentGameplayClock;
+
+ public override IEnumerable> NonGameplayAdjustments => ParentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty>();
public StabilityGameplayClock(FramedClock underlyingClock)
: base(underlyingClock)
diff --git a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs
index c68eeeb4f9..0a8c339559 100644
--- a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs
+++ b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs
@@ -3,8 +3,8 @@
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
-using System;
using osu.Framework.Allocation;
+using osu.Game.Extensions;
using osu.Game.Graphics;
namespace osu.Game.Screens.Edit.Components
@@ -35,9 +35,7 @@ namespace osu.Game.Screens.Edit.Components
protected override void Update()
{
base.Update();
-
- var timespan = TimeSpan.FromMilliseconds(editorClock.CurrentTime);
- trackTimer.Text = $"{(timespan < TimeSpan.Zero ? "-" : string.Empty)}{timespan:mm\\:ss\\:fff}";
+ trackTimer.Text = editorClock.CurrentTime.ToEditorFormattedString();
}
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs
index b99a053859..d870eb5279 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs
@@ -14,9 +14,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
public class TimelineArea : Container
{
- private readonly Timeline timeline = new Timeline { RelativeSizeAxes = Axes.Both };
+ public readonly Timeline Timeline = new Timeline { RelativeSizeAxes = Axes.Both };
- protected override Container Content => timeline;
+ protected override Container Content => Timeline;
[BackgroundDependencyLoader]
private void load()
@@ -107,7 +107,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}
}
},
- timeline
+ Timeline
},
},
ColumnDimensions = new[]
@@ -121,9 +121,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
waveformCheckbox.Current.Value = true;
- timeline.WaveformVisible.BindTo(waveformCheckbox.Current);
+ Timeline.WaveformVisible.BindTo(waveformCheckbox.Current);
}
- private void changeZoom(float change) => timeline.Zoom += change;
+ private void changeZoom(float change) => Timeline.Zoom += change;
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
index b95b3842b3..bc2ccfc605 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
@@ -3,18 +3,23 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osuTK;
using osuTK.Graphics;
@@ -34,11 +39,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private readonly List shadowComponents = new List();
+ private DrawableHitObject drawableHitObject;
+
+ private Bindable comboColour;
+
+ private readonly Container mainComponents;
+
+ private readonly OsuSpriteText comboIndexText;
+
+ private Bindable comboIndex;
+
private const float thickness = 5;
private const float shadow_radius = 5;
- private const float circle_size = 16;
+ private const float circle_size = 24;
public TimelineHitObjectBlueprint(HitObject hitObject)
: base(hitObject)
@@ -54,14 +69,28 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
+ AddRangeInternal(new Drawable[]
+ {
+ mainComponents = new Container
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ },
+ comboIndexText = new OsuSpriteText
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.Centre,
+ Font = OsuFont.Numeric.With(size: circle_size / 2, weight: FontWeight.Black),
+ },
+ });
+
circle = new Circle
{
Size = new Vector2(circle_size),
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
- RelativePositionAxes = Axes.X,
- AlwaysPresent = true,
- Colour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
@@ -77,7 +106,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
DragBar dragBarUnderlay;
Container extensionBar;
- AddRangeInternal(new Drawable[]
+ mainComponents.AddRange(new Drawable[]
{
extensionBar = new Container
{
@@ -117,12 +146,54 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}
else
{
- AddInternal(circle);
+ mainComponents.Add(circle);
}
updateShadows();
}
+ [BackgroundDependencyLoader(true)]
+ private void load(HitObjectComposer composer)
+ {
+ if (composer != null)
+ {
+ // best effort to get the drawable representation for grabbing colour and what not.
+ drawableHitObject = composer.HitObjects.FirstOrDefault(d => d.HitObject == HitObject);
+ }
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ if (HitObject is IHasComboInformation comboInfo)
+ {
+ comboIndex = comboInfo.IndexInCurrentComboBindable.GetBoundCopy();
+ comboIndex.BindValueChanged(combo =>
+ {
+ comboIndexText.Text = (combo.NewValue + 1).ToString();
+ }, true);
+ }
+
+ if (drawableHitObject != null)
+ {
+ comboColour = drawableHitObject.AccentColour.GetBoundCopy();
+ comboColour.BindValueChanged(colour =>
+ {
+ if (HitObject is IHasDuration)
+ mainComponents.Colour = ColourInfo.GradientHorizontal(drawableHitObject.AccentColour.Value, Color4.White);
+ else
+ mainComponents.Colour = drawableHitObject.AccentColour.Value;
+
+ var col = mainComponents.Colour.TopLeft.Linear;
+ float brightness = col.R + col.G + col.B;
+
+ // decide the combo index colour based on brightness?
+ comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White;
+ }, true);
+ }
+ }
+
protected override void Update()
{
base.Update();
diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
index d7a4661fa0..5282b4d998 100644
--- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
+++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
@@ -1,8 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osu.Game.Skinning;
@@ -18,11 +22,23 @@ namespace osu.Game.Screens.Edit.Compose
{
}
- protected override Drawable CreateMainContent()
+ private Ruleset ruleset;
+
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance();
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+
+ ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance();
composer = ruleset?.CreateHitObjectComposer();
+ // make the composer available to the timeline and other components in this screen.
+ dependencies.CacheAs(composer);
+
+ return dependencies;
+ }
+
+ protected override Drawable CreateMainContent()
+ {
if (ruleset == null || composer == null)
return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer");
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index fd090e0959..a0692d94e6 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -107,6 +107,7 @@ namespace osu.Game.Screens.Edit
UpdateClockSource();
dependencies.CacheAs(clock);
+ dependencies.CacheAs(clock);
AddInternal(clock);
// todo: remove caching of this and consume via editorBeatmap?
diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs
index ec203df064..4b7cd82637 100644
--- a/osu.Game/Screens/Edit/EditorClock.cs
+++ b/osu.Game/Screens/Edit/EditorClock.cs
@@ -7,17 +7,18 @@ using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
-using osu.Framework.Utils;
using osu.Framework.Timing;
+using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Screens.Play;
namespace osu.Game.Screens.Edit
{
///
/// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor.
///
- public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
+ public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock, ISamplePlaybackDisabler
{
public IBindable