1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-22 06:02:54 +08:00

Rewrite the way that cursor overrides are done game-wide

This commit is contained in:
smoogipoo 2018-01-12 18:13:17 +09:00
parent 2e235660ad
commit 512e4d2c9f
15 changed files with 132 additions and 60 deletions

View File

@ -1,15 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Cursor;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
namespace osu.Game.Rulesets.Osu.Edit namespace osu.Game.Rulesets.Osu.Edit
{ {
public class OsuEditPlayfield : OsuPlayfield public class OsuEditPlayfield : OsuPlayfield
{ {
protected override CursorContainer CreateCursor() => null;
protected override bool ProxyApproachCircles => false; protected override bool ProxyApproachCircles => false;
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -15,5 +16,7 @@ namespace osu.Game.Rulesets.Osu.Edit
} }
protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
protected override CursorContainer CreateCursor() => null;
} }
} }

View File

@ -12,8 +12,6 @@ using osu.Game.Rulesets.UI;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Framework.Graphics.Cursor;
namespace osu.Game.Rulesets.Osu.UI namespace osu.Game.Rulesets.Osu.UI
{ {
@ -23,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.UI
private readonly Container judgementLayer; private readonly Container judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer; private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
public override bool ProvidingUserCursor => true;
// Todo: This should not be a thing, but is currently required for the editor // Todo: This should not be a thing, but is currently required for the editor
// https://github.com/ppy/osu-framework/issues/1283 // https://github.com/ppy/osu-framework/issues/1283
protected virtual bool ProxyApproachCircles => true; protected virtual bool ProxyApproachCircles => true;
@ -70,15 +66,6 @@ namespace osu.Game.Rulesets.Osu.UI
}); });
} }
protected override void LoadComplete()
{
base.LoadComplete();
var cursor = CreateCursor();
if (cursor != null)
AddInternal(cursor);
}
public override void Add(DrawableHitObject h) public override void Add(DrawableHitObject h)
{ {
h.Depth = (float)h.HitObject.StartTime; h.Depth = (float)h.HitObject.StartTime;
@ -113,7 +100,5 @@ namespace osu.Game.Rulesets.Osu.UI
judgementLayer.Add(explosion); judgementLayer.Add(explosion);
} }
protected virtual CursorContainer CreateCursor() => new GameplayCursor();
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input; using osu.Framework.Input;
using OpenTK; using OpenTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -10,6 +11,7 @@ using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
@ -49,5 +51,7 @@ namespace osu.Game.Rulesets.Osu.UI
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay); protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
protected override CursorContainer CreateCursor() => new GameplayCursor();
} }
} }

View File

@ -0,0 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor;
namespace osu.Game.Graphics.Cursor
{
public interface IProvideLocalCursor : IDrawable
{
/// <summary>
/// The cursor provided by this <see cref="Drawable"/>.
/// </summary>
CursorContainer LocalCursor { get; }
/// <summary>
/// Whether the cursor provided by this <see cref="Drawable"/> should be displayed.
/// If this is false, a cursor occurring earlier in the draw hierarchy will be displayed instead.
/// </summary>
bool ProvidesUserCursor { get; }
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
namespace osu.Game.Graphics.Cursor
{
public class OsuCursorContainer : Container, IProvideLocalCursor
{
protected override Container<Drawable> Content => content;
private readonly Container content;
public CursorContainer LocalCursor { get; }
public bool ProvidesUserCursor => true;
public OsuCursorContainer()
{
AddRangeInternal(new Drawable[]
{
LocalCursor = new MenuCursor { State = Visibility.Hidden },
content = new Container { RelativeSizeAxes = Axes.Both }
});
}
private InputManager inputManager;
protected override void LoadComplete()
{
base.LoadComplete();
inputManager = GetContainingInputManager();
}
private IProvideLocalCursor currentTarget;
protected override void Update()
{
base.Update();
var newTarget = inputManager.HoveredDrawables.OfType<IProvideLocalCursor>().FirstOrDefault(t => t.ProvidesUserCursor) ?? this;
if (currentTarget == newTarget)
return;
currentTarget?.LocalCursor?.Hide();
newTarget.LocalCursor?.Show();
currentTarget = newTarget;
}
}
}

View File

@ -297,8 +297,6 @@ namespace osu.Game
else else
Toolbar.State = Visibility.Visible; Toolbar.State = Visibility.Visible;
}; };
Cursor.State = Visibility.Hidden;
} }
private void forwardLoggedErrorsToNotifications() private void forwardLoggedErrorsToNotifications()
@ -446,8 +444,6 @@ namespace osu.Game
Beatmap.Disabled = applyRestrictions; Beatmap.Disabled = applyRestrictions;
mainContent.Padding = new MarginPadding { Top = ToolbarOffset }; mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden;
} }
private void screenAdded(Screen newScreen) private void screenAdded(Screen newScreen)

View File

@ -52,8 +52,6 @@ namespace osu.Game
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
protected MenuCursor Cursor;
public Bindable<WorkingBeatmap> Beatmap { get; private set; } public Bindable<WorkingBeatmap> Beatmap { get; private set; }
private Bindable<bool> fpsDisplayVisible; private Bindable<bool> fpsDisplayVisible;
@ -211,21 +209,14 @@ namespace osu.Game
GlobalKeyBindingInputManager globalBinding; GlobalKeyBindingInputManager globalBinding;
base.Content.Add(new DrawSizePreservingFillContainer var cursorContainer = new OsuCursorContainer { RelativeSizeAxes = Axes.Both };
{ cursorContainer.Child = globalBinding = new GlobalKeyBindingInputManager(this)
Children = new Drawable[]
{
Cursor = new MenuCursor(),
globalBinding = new GlobalKeyBindingInputManager(this)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(Cursor) Child = content = new OsuTooltipContainer(cursorContainer.LocalCursor) { RelativeSizeAxes = Axes.Both }
{ };
RelativeSizeAxes = Axes.Both,
} base.Content.Add(new DrawSizePreservingFillContainer { Child = cursorContainer });
}
}
});
KeyBindingStore.Register(globalBinding); KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding); dependencies.Cache(globalBinding);

View File

@ -22,11 +22,6 @@ namespace osu.Game.Rulesets.UI
public Container<Drawable> ScaledContent; public Container<Drawable> ScaledContent;
/// <summary>
/// Whether we are currently providing the local user a gameplay cursor.
/// </summary>
public virtual bool ProvidingUserCursor => false;
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content; private readonly Container<Drawable> content;

View File

@ -13,6 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -43,11 +44,6 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public PassThroughInputManager KeyBindingInputManager; public PassThroughInputManager KeyBindingInputManager;
/// <summary>
/// Whether we are currently providing the local user a gameplay cursor.
/// </summary>
public virtual bool ProvidingUserCursor => false;
/// <summary> /// <summary>
/// Whether we have a replay loaded currently. /// Whether we have a replay loaded currently.
/// </summary> /// </summary>
@ -61,6 +57,11 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public Playfield Playfield => playfield.Value; public Playfield Playfield => playfield.Value;
/// <summary>
/// The cursor provided by this <see cref="RulesetContainer"/>. May be null if no cursor is provided.
/// </summary>
public readonly CursorContainer Cursor;
protected readonly Ruleset Ruleset; protected readonly Ruleset Ruleset;
/// <summary> /// <summary>
@ -71,6 +72,8 @@ namespace osu.Game.Rulesets.UI
{ {
Ruleset = ruleset; Ruleset = ruleset;
playfield = new Lazy<Playfield>(CreatePlayfield); playfield = new Lazy<Playfield>(CreatePlayfield);
Cursor = CreateCursor();
} }
public abstract ScoreProcessor CreateScoreProcessor(); public abstract ScoreProcessor CreateScoreProcessor();
@ -98,6 +101,12 @@ namespace osu.Game.Rulesets.UI
ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
} }
/// <summary>
/// Creates the cursor. May be null if the <see cref="RulesetContainer"/> doesn't provide a custom cursor.
/// </summary>
protected virtual CursorContainer CreateCursor() => null;
/// <summary> /// <summary>
/// Creates a Playfield. /// Creates a Playfield.
/// </summary> /// </summary>
@ -144,8 +153,6 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
protected readonly bool IsForCurrentRuleset; protected readonly bool IsForCurrentRuleset;
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this); public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
@ -212,6 +219,9 @@ namespace osu.Game.Rulesets.UI
AddInternal(KeyBindingInputManager); AddInternal(KeyBindingInputManager);
KeyBindingInputManager.Add(Playfield); KeyBindingInputManager.Add(Playfield);
if (Cursor != null)
KeyBindingInputManager.Add(Cursor);
loadObjects(); loadObjects();
} }

View File

@ -9,10 +9,12 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Framework.Graphics.Cursor;
namespace osu.Game.Screens.Menu namespace osu.Game.Screens.Menu
{ {
public class Disclaimer : OsuScreen public class Disclaimer : OsuScreen, IProvideLocalCursor
{ {
private Intro intro; private Intro intro;
private readonly SpriteIcon icon; private readonly SpriteIcon icon;
@ -20,7 +22,8 @@ namespace osu.Game.Screens.Menu
public override bool ShowOverlaysOnEnter => false; public override bool ShowOverlaysOnEnter => false;
public override bool HasLocalCursorDisplayed => true; public CursorContainer LocalCursor => null;
public bool ProvidesUserCursor => true;
public Disclaimer() public Disclaimer()
{ {

View File

@ -15,10 +15,12 @@ using osu.Game.Configuration;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Framework.Graphics.Cursor;
namespace osu.Game.Screens.Menu namespace osu.Game.Screens.Menu
{ {
public class Intro : OsuScreen public class Intro : OsuScreen, IProvideLocalCursor
{ {
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83"; private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
@ -31,10 +33,11 @@ namespace osu.Game.Screens.Menu
private SampleChannel welcome; private SampleChannel welcome;
private SampleChannel seeya; private SampleChannel seeya;
public override bool HasLocalCursorDisplayed => true;
public override bool ShowOverlaysOnEnter => false; public override bool ShowOverlaysOnEnter => false;
public CursorContainer LocalCursor => null;
public bool ProvidesUserCursor => true;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty(); protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty();
private Bindable<bool> menuVoice; private Bindable<bool> menuVoice;

View File

@ -37,8 +37,6 @@ namespace osu.Game.Screens
protected new OsuGameBase Game => base.Game as OsuGameBase; protected new OsuGameBase Game => base.Game as OsuGameBase;
public virtual bool HasLocalCursorDisplayed => false;
private OsuLogo logo; private OsuLogo logo;
/// <summary> /// <summary>

View File

@ -23,22 +23,22 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Screens.Play.BreaksOverlay; using osu.Game.Screens.Play.BreaksOverlay;
using osu.Game.Storyboards.Drawables; using osu.Game.Storyboards.Drawables;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
public class Player : OsuScreen public class Player : OsuScreen, IProvideLocalCursor
{ {
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
public override bool ShowOverlaysOnEnter => false; public override bool ShowOverlaysOnEnter => false;
public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor;
public Action RestartRequested; public Action RestartRequested;
public override bool AllowBeatmapRulesetChange => false; public override bool AllowBeatmapRulesetChange => false;
@ -51,6 +51,9 @@ namespace osu.Game.Screens.Play
public int RestartCount; public int RestartCount;
public CursorContainer LocalCursor => RulesetContainer.Cursor;
public bool ProvidesUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded;
private IAdjustableClock adjustableSourceClock; private IAdjustableClock adjustableSourceClock;
private FramedOffsetClock offsetClock; private FramedOffsetClock offsetClock;
private DecoupleableInterpolatingFramedClock decoupledClock; private DecoupleableInterpolatingFramedClock decoupledClock;
@ -152,6 +155,12 @@ namespace osu.Game.Screens.Play
userAudioOffset.ValueChanged += v => offsetClock.Offset = v; userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
userAudioOffset.TriggerChange(); userAudioOffset.TriggerChange();
// We want the cursor to be above everything (including the skip button), but still be able to be controlled
// by the ruleset's input manager and replay, so we need to proxy it out from the ruleset container
var cursorProxyContainer = new Container { RelativeSizeAxes = Axes.Both };
if (RulesetContainer.Cursor != null)
cursorProxyContainer.Add(RulesetContainer.Cursor.CreateProxy());
Children = new Drawable[] Children = new Drawable[]
{ {
storyboardContainer = new Container storyboardContainer = new Container
@ -195,6 +204,7 @@ namespace osu.Game.Screens.Play
Clock = decoupledClock, Clock = decoupledClock,
Breaks = beatmap.Breaks Breaks = beatmap.Breaks
}, },
cursorProxyContainer
} }
}, },
failOverlay = new FailOverlay failOverlay = new FailOverlay

View File

@ -374,8 +374,10 @@
<Compile Include="Graphics\Containers\ParallaxContainer.cs" /> <Compile Include="Graphics\Containers\ParallaxContainer.cs" />
<Compile Include="Graphics\Containers\ReverseChildIDFillFlowContainer.cs" /> <Compile Include="Graphics\Containers\ReverseChildIDFillFlowContainer.cs" />
<Compile Include="Graphics\Containers\SectionsContainer.cs" /> <Compile Include="Graphics\Containers\SectionsContainer.cs" />
<Compile Include="Graphics\Cursor\IProvideLocalCursor.cs" />
<Compile Include="Graphics\Cursor\MenuCursor.cs" /> <Compile Include="Graphics\Cursor\MenuCursor.cs" />
<Compile Include="Graphics\Cursor\OsuContextMenuContainer.cs" /> <Compile Include="Graphics\Cursor\OsuContextMenuContainer.cs" />
<Compile Include="Graphics\Cursor\OsuCursorContainer.cs" />
<Compile Include="Graphics\Cursor\OsuTooltipContainer.cs" /> <Compile Include="Graphics\Cursor\OsuTooltipContainer.cs" />
<Compile Include="Graphics\IHasAccentColour.cs" /> <Compile Include="Graphics\IHasAccentColour.cs" />
<Compile Include="Graphics\OsuColour.cs" /> <Compile Include="Graphics\OsuColour.cs" />