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>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Cursor;
using osu.Game.Rulesets.Osu.UI;
namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuEditPlayfield : OsuPlayfield
{
protected override CursorContainer CreateCursor() => null;
protected override bool ProxyApproachCircles => false;
}
}

View File

@ -1,6 +1,7 @@
// 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.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
@ -15,5 +16,7 @@ public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isF
}
protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
protected override CursorContainer CreateCursor() => null;
}
}

View File

@ -12,8 +12,6 @@
using System.Linq;
using osu.Game.Rulesets.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
{
@ -23,8 +21,6 @@ public class OsuPlayfield : Playfield
private readonly Container judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
public override bool ProvidingUserCursor => true;
// Todo: This should not be a thing, but is currently required for the editor
// https://github.com/ppy/osu-framework/issues/1283
protected virtual bool ProxyApproachCircles => true;
@ -70,15 +66,6 @@ public OsuPlayfield() : base(BASE_SIZE.X)
});
}
protected override void LoadComplete()
{
base.LoadComplete();
var cursor = CreateCursor();
if (cursor != null)
AddInternal(cursor);
}
public override void Add(DrawableHitObject h)
{
h.Depth = (float)h.HitObject.StartTime;
@ -113,7 +100,5 @@ public override void OnJudgement(DrawableHitObject judgedObject, Judgement judge
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>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using OpenTK;
using osu.Game.Beatmaps;
@ -10,6 +11,7 @@
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Replays;
@ -49,5 +51,7 @@ protected override DrawableHitObject<OsuHitObject> GetVisualRepresentation(OsuHi
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
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 @@ void updateScreenOffset()
else
Toolbar.State = Visibility.Visible;
};
Cursor.State = Visibility.Hidden;
}
private void forwardLoggedErrorsToNotifications()
@ -446,8 +444,6 @@ protected override void UpdateAfterChildren()
Beatmap.Disabled = applyRestrictions;
mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden;
}
private void screenAdded(Screen newScreen)

View File

@ -52,8 +52,6 @@ public class OsuGameBase : Framework.Game, IOnlineComponent
protected override Container<Drawable> Content => content;
protected MenuCursor Cursor;
public Bindable<WorkingBeatmap> Beatmap { get; private set; }
private Bindable<bool> fpsDisplayVisible;
@ -211,21 +209,14 @@ protected override void LoadComplete()
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,
Child = content = new OsuTooltipContainer(Cursor)
{
RelativeSizeAxes = Axes.Both,
}
}
}
});
RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(cursorContainer.LocalCursor) { RelativeSizeAxes = Axes.Both }
};
base.Content.Add(new DrawSizePreservingFillContainer { Child = cursorContainer });
KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding);

View File

@ -22,11 +22,6 @@ public abstract class Playfield : Container
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;
private readonly Container<Drawable> content;

View File

@ -13,6 +13,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
@ -43,11 +44,6 @@ public abstract class RulesetContainer : Container
/// </summary>
public PassThroughInputManager KeyBindingInputManager;
/// <summary>
/// Whether we are currently providing the local user a gameplay cursor.
/// </summary>
public virtual bool ProvidingUserCursor => false;
/// <summary>
/// Whether we have a replay loaded currently.
/// </summary>
@ -61,6 +57,11 @@ public abstract class RulesetContainer : Container
/// </summary>
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;
/// <summary>
@ -71,6 +72,8 @@ protected RulesetContainer(Ruleset ruleset)
{
Ruleset = ruleset;
playfield = new Lazy<Playfield>(CreatePlayfield);
Cursor = CreateCursor();
}
public abstract ScoreProcessor CreateScoreProcessor();
@ -98,6 +101,12 @@ public virtual void SetReplay(Replay replay)
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>
/// Creates a Playfield.
/// </summary>
@ -144,8 +153,6 @@ public abstract class RulesetContainer<TObject> : RulesetContainer
/// </summary>
protected readonly bool IsForCurrentRuleset;
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
protected override Container<Drawable> Content => content;
@ -212,6 +219,9 @@ private void load()
AddInternal(KeyBindingInputManager);
KeyBindingInputManager.Add(Playfield);
if (Cursor != null)
KeyBindingInputManager.Add(Cursor);
loadObjects();
}

View File

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

View File

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

View File

@ -37,8 +37,6 @@ public abstract class OsuScreen : Screen
protected new OsuGameBase Game => base.Game as OsuGameBase;
public virtual bool HasLocalCursorDisplayed => false;
private OsuLogo logo;
/// <summary>

View File

@ -23,22 +23,22 @@
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
using osu.Game.Screens.Play.BreaksOverlay;
using osu.Game.Storyboards.Drawables;
namespace osu.Game.Screens.Play
{
public class Player : OsuScreen
public class Player : OsuScreen, IProvideLocalCursor
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
public override bool ShowOverlaysOnEnter => false;
public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor;
public Action RestartRequested;
public override bool AllowBeatmapRulesetChange => false;
@ -51,6 +51,9 @@ public class Player : OsuScreen
public int RestartCount;
public CursorContainer LocalCursor => RulesetContainer.Cursor;
public bool ProvidesUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded;
private IAdjustableClock adjustableSourceClock;
private FramedOffsetClock offsetClock;
private DecoupleableInterpolatingFramedClock decoupledClock;
@ -152,6 +155,12 @@ private void load(AudioManager audio, OsuConfigManager config, APIAccess api)
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
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[]
{
storyboardContainer = new Container
@ -195,6 +204,7 @@ private void load(AudioManager audio, OsuConfigManager config, APIAccess api)
Clock = decoupledClock,
Breaks = beatmap.Breaks
},
cursorProxyContainer
}
},
failOverlay = new FailOverlay

View File

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