From 7f0cb0bbf61cb10b61ec768ec1504ecf128bf4e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 May 2018 12:06:50 +0900 Subject: [PATCH 01/13] Add key bindings for scroll speed Closes #2689. - [ ] Depends on ppy/osu-framework#1569 being fixed. --- .../Input/Bindings/GlobalActionContainer.cs | 12 +++++-- .../UI/Scrolling/ScrollingPlayfield.cs | 32 ++++++++++--------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index fced2e5d95..83ffd415ae 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -45,7 +45,9 @@ namespace osu.Game.Input.Bindings public IEnumerable InGameKeyBindings => new[] { new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene), - new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry) + new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry), + new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed), + new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed), }; protected override IEnumerable KeyBindingInputQueue => @@ -85,6 +87,12 @@ namespace osu.Game.Input.Bindings ToggleGameplayMouseButtons, [Description("Go back")] - Back + Back, + + [Description("Increase scroll speed")] + IncreaseScrollSpeed, + + [Description("Decrease scroll speed")] + DecreaseScrollSpeed, } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 6f86d20295..e8b0a53f92 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -4,30 +4,33 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Objects.Drawables; -using OpenTK.Input; namespace osu.Game.Rulesets.UI.Scrolling { /// /// A type of specialized towards scrolling s. /// - public abstract class ScrollingPlayfield : Playfield + public abstract class ScrollingPlayfield : Playfield, IKeyBindingHandler { /// /// The default span of time visible by the length of the scrolling axes. /// This is clamped between and . /// private const double time_span_default = 1500; + /// /// The minimum span of time that may be visible by the length of the scrolling axes. /// private const double time_span_min = 50; + /// /// The maximum span of time that may be visible by the length of the scrolling axes. /// private const double time_span_max = 10000; + /// /// The step increase/decrease of the span of time visible by the length of the scrolling axes. /// @@ -78,27 +81,26 @@ namespace osu.Game.Rulesets.UI.Scrolling HitObjects.TimeRange.BindTo(VisibleTimeRange); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction); + + public bool OnPressed(GlobalAction action) { if (!UserScrollSpeedAdjustment) return false; - if (state.Keyboard.ControlPressed) + switch (action) { - switch (args.Key) - { - case Key.Minus: - this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange + time_span_step, 200, Easing.OutQuint); - break; - case Key.Plus: - this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange - time_span_step, 200, Easing.OutQuint); - break; - } + case GlobalAction.IncreaseScrollSpeed: + this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange - time_span_step, 200, Easing.OutQuint); + return true; + case GlobalAction.DecreaseScrollSpeed: + this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange + time_span_step, 200, Easing.OutQuint); + return true; } return false; } - protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction); + public bool OnReleased(GlobalAction action) => false; } } From 4bcc05a7fcc3917228eeaeddea72b0ca9ce701bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jun 2018 18:24:34 +0900 Subject: [PATCH 02/13] Remove null checks on ruleset Add a default ruleset to `OsuTestCase` to cover testing scenarios. --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 13 ++++--------- osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs | 8 ++------ osu.Game/Screens/OsuScreen.cs | 7 ++----- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 3 +-- osu.Game/Screens/Select/FilterControl.cs | 8 +++++--- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 5 ++--- osu.Game/Tests/Visual/OsuTestCase.cs | 11 ++++++++++- 7 files changed, 26 insertions(+), 29 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 48b7907572..7e4c986810 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -51,8 +51,8 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } - [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, Bindable ruleset, RulesetStore rulesets, AudioManager audio) + [BackgroundDependencyLoader] + private void load(OsuColour colours, Bindable ruleset, AudioManager audio) { SelectedMods.ValueChanged += selectedModsChanged; @@ -60,13 +60,8 @@ namespace osu.Game.Overlays.Mods HighMultiplierColour = colours.Green; UnrankedLabel.Colour = colours.Blue; - if (ruleset != null) - Ruleset.BindTo(ruleset); - else - Ruleset.Value = rulesets.AvailableRulesets.First(); - - Ruleset.ValueChanged += rulesetChanged; - Ruleset.TriggerChange(); + Ruleset.BindTo(ruleset); + Ruleset.BindValueChanged(rulesetChanged, true); sampleOn = audio.Sample.Get(@"UI/check-on"); sampleOff = audio.Sample.Get(@"UI/check-off"); diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 6926502d9b..dae4f84b1a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Toolbar }; } - [BackgroundDependencyLoader(true)] + [BackgroundDependencyLoader] private void load(RulesetStore rulesets, Bindable parentRuleset) { this.rulesets = rulesets; @@ -82,11 +82,7 @@ namespace osu.Game.Overlays.Toolbar ruleset.ValueChanged += rulesetChanged; ruleset.DisabledChanged += disabledChanged; - - if (parentRuleset != null) - ruleset.BindTo(parentRuleset); - else - ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); + ruleset.BindTo(parentRuleset); } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index bf650dd514..8f7ab14937 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -84,11 +84,8 @@ namespace osu.Game.Screens [BackgroundDependencyLoader(true)] private void load(BindableBeatmap beatmap, OsuGame osu, AudioManager audio, Bindable ruleset) { - if (beatmap != null) - Beatmap.BindTo(beatmap); - - if (ruleset != null) - Ruleset.BindTo(ruleset); + Beatmap.BindTo(beatmap); + Ruleset.BindTo(ruleset); if (osu != null) { diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 27089311f2..d26702fcf9 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -55,8 +55,7 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader(true)] private void load([CanBeNull] Bindable parentRuleset) { - if (parentRuleset != null) - ruleset.BindTo(parentRuleset); + ruleset.BindTo(parentRuleset); ruleset.ValueChanged += _ => updateDisplay(); } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index abf14a653b..fca6ee23d1 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -29,6 +29,7 @@ namespace osu.Game.Screens.Select private readonly TabControl groupTabs; private SortMode sort = SortMode.Title; + public SortMode Sort { get { return sort; } @@ -43,6 +44,7 @@ namespace osu.Game.Screens.Select } private GroupMode group = GroupMode.All; + public GroupMode Group { get { return group; } @@ -69,7 +71,8 @@ namespace osu.Game.Screens.Select private readonly SearchTextBox searchTextBox; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || groupTabs.ReceiveMouseInputAt(screenSpacePos) || sortTabs.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => + base.ReceiveMouseInputAt(screenSpacePos) || groupTabs.ReceiveMouseInputAt(screenSpacePos) || sortTabs.ReceiveMouseInputAt(screenSpacePos); public FilterControl() { @@ -177,8 +180,7 @@ namespace osu.Game.Screens.Select showConverted = config.GetBindable(OsuSetting.ShowConvertedBeatmaps); showConverted.ValueChanged += val => updateCriteria(); - if (parentRuleset != null) - ruleset.BindTo(parentRuleset); + ruleset.BindTo(parentRuleset); ruleset.ValueChanged += val => updateCriteria(); ruleset.TriggerChange(); } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index c2a76c14c9..560fd4e375 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -41,6 +41,7 @@ namespace osu.Game.Screens.Select.Leaderboards private ScheduledDelegate showScoresDelegate; private IEnumerable scores; + public IEnumerable Scores { get { return scores; } @@ -196,9 +197,7 @@ namespace osu.Game.Screens.Select.Leaderboards { this.api = api; - if (parentRuleset != null) - ruleset.BindTo(parentRuleset); - + ruleset.BindTo(parentRuleset); ruleset.ValueChanged += _ => updateScores(); if (api != null) diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 1740658da6..a877ce2333 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -1,10 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Configuration; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Rulesets; namespace osu.Game.Tests.Visual { @@ -13,6 +16,8 @@ namespace osu.Game.Tests.Visual private readonly OsuTestBeatmap beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap()); protected BindableBeatmap Beatmap => beatmap; + private readonly Bindable ruleset = new Bindable(); + protected DependencyContainer Dependencies { get; private set; } protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) @@ -22,13 +27,17 @@ namespace osu.Game.Tests.Visual Dependencies.CacheAs(beatmap); Dependencies.CacheAs(beatmap); + Dependencies.CacheAs(ruleset); + return Dependencies; } [BackgroundDependencyLoader] - private void load(AudioManager audioManager) + private void load(AudioManager audioManager, RulesetStore rulesets) { beatmap.SetAudioManager(audioManager); + + ruleset.Value = rulesets.AvailableRulesets.First(); } protected override void Dispose(bool isDisposing) From 018dcc98b40304437a9440c925b233679a65ad40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jun 2018 18:59:13 +0900 Subject: [PATCH 03/13] Fix failing tests --- osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs | 8 +++++--- osu.Game/Tests/Visual/OsuTestCase.cs | 6 +++--- osu.Game/Tests/Visual/TestCasePlayer.cs | 6 +++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index f52fecfd02..ee66f53ddc 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -65,17 +65,19 @@ namespace osu.Game.Tests.Visual foreach (var rulesetInfo in rulesets.AvailableRulesets) { - var ruleset = rulesetInfo.CreateInstance(); + var instance = rulesetInfo.CreateInstance(); var testBeatmap = createTestBeatmap(rulesetInfo); beatmaps.Add(testBeatmap); + AddStep("set ruleset", () => Ruleset.Value = rulesetInfo); + selectBeatmap(testBeatmap); - testBeatmapLabels(ruleset); + testBeatmapLabels(instance); // TODO: adjust cases once more info is shown for other gamemodes - switch (ruleset) + switch (instance) { case OsuRuleset _: testInfoLabels(5); diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index a877ce2333..b3e523b63c 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual private readonly OsuTestBeatmap beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap()); protected BindableBeatmap Beatmap => beatmap; - private readonly Bindable ruleset = new Bindable(); + protected readonly Bindable Ruleset = new Bindable(); protected DependencyContainer Dependencies { get; private set; } @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual Dependencies.CacheAs(beatmap); Dependencies.CacheAs(beatmap); - Dependencies.CacheAs(ruleset); + Dependencies.CacheAs(Ruleset); return Dependencies; } @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual { beatmap.SetAudioManager(audioManager); - ruleset.Value = rulesets.AvailableRulesets.First(); + Ruleset.Value = rulesets.AvailableRulesets.First(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 20c9646aa3..9afb1dd6cd 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -86,7 +86,11 @@ namespace osu.Game.Tests.Visual private readonly WeakList workingWeakReferences = new WeakList(); private readonly WeakList playerWeakReferences = new WeakList(); - private Player loadPlayerFor(RulesetInfo ri) => loadPlayerFor(ri.CreateInstance()); + private Player loadPlayerFor(RulesetInfo ri) + { + Ruleset.Value = ri; + return loadPlayerFor(ri.CreateInstance()); + } private Player loadPlayerFor(Ruleset r) { From 2c597874bf8340cde91856f931855a08d092b10b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 1 Jul 2018 18:17:02 +0900 Subject: [PATCH 04/13] Fix leaderboard showing placeholder briefly when entering song select --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index ac6154369d..a0d447afed 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -40,6 +40,8 @@ namespace osu.Game.Screens.Select.Leaderboards private ScheduledDelegate showScoresDelegate; + private bool scoresLoadedOnce; + private IEnumerable scores; public IEnumerable Scores { @@ -48,6 +50,8 @@ namespace osu.Game.Screens.Select.Leaderboards { scores = value; + scoresLoadedOnce = true; + scrollFlow?.FadeOut(fade_duration, Easing.OutQuint).Expire(); scrollFlow = null; @@ -227,6 +231,10 @@ namespace osu.Game.Screens.Select.Leaderboards private void updateScores() { + // don't display any scores or placeholder until the first Scores_Set has been called. + // this avoids scope changes flickering a "no scores" placeholder before initialisation of song select is finished. + if (!scoresLoadedOnce) return; + getScoresRequest?.Cancel(); getScoresRequest = null; From 596787c94123ead5839c41379ff52cb3a5555b4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Jul 2018 14:22:37 +0900 Subject: [PATCH 05/13] Standardise input handling of popup dialogs --- osu.Game/Overlays/Dialog/PopupDialog.cs | 147 +++++++++++++----------- osu.Game/Overlays/DialogOverlay.cs | 49 ++++---- 2 files changed, 100 insertions(+), 96 deletions(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 5f90ec67ad..68e4481446 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -6,16 +6,16 @@ using System.Linq; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Dialog { @@ -23,6 +23,9 @@ namespace osu.Game.Overlays.Dialog { public static readonly float ENTER_DURATION = 500; public static readonly float EXIT_DURATION = 200; + + protected override bool BlockPassThroughMouse => false; + private readonly Vector2 ringSize = new Vector2(100f); private readonly Vector2 ringMinifiedSize = new Vector2(20f); private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f); @@ -34,26 +37,28 @@ namespace osu.Game.Overlays.Dialog private readonly SpriteText header; private readonly TextFlowContainer body; + private bool actionInvoked; + public FontAwesome Icon { - get { return icon.Icon; } - set { icon.Icon = value; } + get => icon.Icon; + set => icon.Icon = value; } public string HeaderText { - get { return header.Text; } - set { header.Text = value; } + get => header.Text; + set => header.Text = value; } public string BodyText { - set { body.Text = value; } + set => body.Text = value; } public IEnumerable Buttons { - get { return buttonsContainer.Children; } + get => buttonsContainer.Children; set { buttonsContainer.ChildrenEnumerable = value; @@ -62,71 +67,17 @@ namespace osu.Game.Overlays.Dialog var action = b.Action; b.Action = () => { + if (actionInvoked) return; + Hide(); + + actionInvoked = true; action?.Invoke(); }; } } } - private void pressButtonAtIndex(int index) - { - if (index < Buttons.Count()) - Buttons.Skip(index).First().TriggerOnClick(); - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (args.Repeat) return false; - - if (args.Key == Key.Enter || args.Key == Key.KeypadEnter) - { - Buttons.OfType().FirstOrDefault()?.TriggerOnClick(); - return true; - } - - // press button at number if 1-9 on number row or keypad are pressed - var k = args.Key; - if (k >= Key.Number1 && k <= Key.Number9) - { - pressButtonAtIndex(k - Key.Number1); - return true; - } - - if (k >= Key.Keypad1 && k <= Key.Keypad9) - { - pressButtonAtIndex(k - Key.Keypad1); - return true; - } - - return base.OnKeyDown(state, args); - } - - protected override void PopIn() - { - base.PopIn(); - - // Reset various animations but only if the dialog animation fully completed - if (content.Alpha == 0) - { - buttonsContainer.TransformSpacingTo(buttonsEnterSpacing); - buttonsContainer.MoveToY(buttonsEnterSpacing.Y); - ring.ResizeTo(ringMinifiedSize); - } - - content.FadeIn(ENTER_DURATION, Easing.OutQuint); - ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint); - buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint); - buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint); - } - - protected override void PopOut() - { - base.PopOut(); - - content.FadeOut(EXIT_DURATION, Easing.InSine); - } - public PopupDialog() { RelativeSizeAxes = Axes.Both; @@ -136,9 +87,6 @@ namespace osu.Game.Overlays.Dialog content = new Container { RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Width = 0.4f, Alpha = 0f, Children = new Drawable[] { @@ -243,5 +191,66 @@ namespace osu.Game.Overlays.Dialog }, }; } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) return false; + + if (args.Key == Key.Enter || args.Key == Key.KeypadEnter) + { + Buttons.OfType().FirstOrDefault()?.TriggerOnClick(); + return true; + } + + // press button at number if 1-9 on number row or keypad are pressed + var k = args.Key; + if (k >= Key.Number1 && k <= Key.Number9) + { + pressButtonAtIndex(k - Key.Number1); + return true; + } + + if (k >= Key.Keypad1 && k <= Key.Keypad9) + { + pressButtonAtIndex(k - Key.Keypad1); + return true; + } + + return base.OnKeyDown(state, args); + } + + protected override void PopIn() + { + base.PopIn(); + + actionInvoked = false; + + // Reset various animations but only if the dialog animation fully completed + if (content.Alpha == 0) + { + buttonsContainer.TransformSpacingTo(buttonsEnterSpacing); + buttonsContainer.MoveToY(buttonsEnterSpacing.Y); + ring.ResizeTo(ringMinifiedSize); + } + + content.FadeIn(ENTER_DURATION, Easing.OutQuint); + ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint); + buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint); + buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint); + } + + protected override void PopOut() + { + if (!actionInvoked) buttonsContainer.Last().TriggerOnClick(); + + base.PopOut(); + content.FadeOut(EXIT_DURATION, Easing.InSine); + } + + private void pressButtonAtIndex(int index) + { + if (index < Buttons.Count()) + Buttons.Skip(index).First().TriggerOnClick(); + } } } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index e26a3cba2f..c40f517023 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -1,12 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Dialog; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays @@ -16,6 +13,20 @@ namespace osu.Game.Overlays private readonly Container dialogContainer; private PopupDialog currentDialog; + public DialogOverlay() + { + RelativeSizeAxes = Axes.Both; + + Child = dialogContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }; + + Width = 0.4f; + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + } + public void Push(PopupDialog dialog) { if (dialog == currentDialog) return; @@ -32,6 +43,8 @@ namespace osu.Game.Overlays protected override bool PlaySamplesOnStateChange => false; + protected override bool BlockPassThroughKeyboard => true; + private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) { if (v != Visibility.Hidden) return; @@ -52,32 +65,14 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); - this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); - } - public DialogOverlay() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] + if (currentDialog?.State == Visibility.Visible) { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, - }, - }, - dialogContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }, - }; + currentDialog.Hide(); + return; + } + + this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); } } } From b61fe10f62fd53cf2f63fcfc5532f260bc030b78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Jul 2018 15:07:13 +0900 Subject: [PATCH 06/13] Adapt to IBindables --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- osu.Game/Tests/Visual/OsuTestCase.cs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 7e4c986810..c584a32a82 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Mods public readonly Bindable> SelectedMods = new Bindable>(); - public readonly Bindable Ruleset = new Bindable(); + public readonly IBindable Ruleset = new Bindable(); private void rulesetChanged(RulesetInfo newRuleset) { @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader] - private void load(OsuColour colours, Bindable ruleset, AudioManager audio) + private void load(OsuColour colours, IBindable ruleset, AudioManager audio) { SelectedMods.ValueChanged += selectedModsChanged; diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index b3e523b63c..5f70055021 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -28,6 +28,7 @@ namespace osu.Game.Tests.Visual Dependencies.CacheAs(beatmap); Dependencies.CacheAs(Ruleset); + Dependencies.CacheAs>(Ruleset); return Dependencies; } From 90d90370c2afc7964fc90574ece2599b1bcf399b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Jul 2018 15:25:37 +0900 Subject: [PATCH 07/13] Use testcase ruleset --- osu.Game.Tests/Visual/TestCaseMods.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs index 3255478bea..73c37348d5 100644 --- a/osu.Game.Tests/Visual/TestCaseMods.cs +++ b/osu.Game.Tests/Visual/TestCaseMods.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual foreach (var rulesetInfo in rulesets.AvailableRulesets) { Ruleset ruleset = rulesetInfo.CreateInstance(); - AddStep($"switch to {ruleset.Description}", () => modSelect.Ruleset.Value = rulesetInfo); + AddStep($"switch to {ruleset.Description}", () => Ruleset.Value = rulesetInfo); switch (ruleset) { From 341ffa46672e5b2665b5a68c2a371424e3e9efe7 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 2 Jul 2018 16:07:52 +0900 Subject: [PATCH 08/13] Fix MenuCursor crash --- osu.Game/Graphics/Cursor/MenuCursor.cs | 54 +++++++++++++++----------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 5f57fb76b0..47a3d5c998 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -11,9 +11,9 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Configuration; using System; -using System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Graphics.Textures; +using OpenTK.Input; namespace osu.Game.Graphics.Cursor { @@ -25,9 +25,8 @@ namespace osu.Game.Graphics.Cursor protected override Drawable CreateCursor() => new Cursor(); private Bindable cursorRotate; - private bool dragging; - - private bool startRotation; + private DragRotationState dragRotationState; + private Vector2 positionMouseDown; [BackgroundDependencyLoader(true)] private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager) @@ -40,18 +39,18 @@ namespace osu.Game.Graphics.Cursor protected override bool OnMouseMove(InputState state) { - if (cursorRotate && dragging) + if (dragRotationState != DragRotationState.NotDragging) { - Debug.Assert(state.Mouse.PositionMouseDown != null); - + var position = state.Mouse.Position; + var distance = Vector2Extensions.Distance(position, positionMouseDown); // don't start rotating until we're moved a minimum distance away from the mouse down location, // else it can have an annoying effect. - // ReSharper disable once PossibleInvalidOperationException - startRotation |= Vector2Extensions.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30; - - if (startRotation) + if (dragRotationState == DragRotationState.DragStarted && distance > 30) + dragRotationState = DragRotationState.Rotating; + // don't rotate when distance if zero to avoid NaN + if (dragRotationState == DragRotationState.Rotating && distance > 0) { - Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown.Value; + Vector2 offset = state.Mouse.Position - positionMouseDown; float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f; // Always rotate in the direction of least distance @@ -66,13 +65,7 @@ namespace osu.Game.Graphics.Cursor return base.OnMouseMove(state); } - - protected override bool OnDragStart(InputState state) - { - dragging = true; - return base.OnDragStart(state); - } - + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { ActiveCursor.Scale = new Vector2(1); @@ -80,6 +73,12 @@ namespace osu.Game.Graphics.Cursor ((Cursor)ActiveCursor).AdditiveLayer.Alpha = 0; ((Cursor)ActiveCursor).AdditiveLayer.FadeInFromZero(800, Easing.OutQuint); + + if (args.Button == MouseButton.Left && cursorRotate) + { + dragRotationState = DragRotationState.DragStarted; + positionMouseDown = state.Mouse.Position; + } return base.OnMouseDown(state, args); } @@ -87,14 +86,16 @@ namespace osu.Game.Graphics.Cursor { if (!state.Mouse.HasMainButtonPressed) { - dragging = false; - startRotation = false; - ((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint); - ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf); ActiveCursor.ScaleTo(1, 500, Easing.OutElastic); } + if (args.Button == MouseButton.Left) + { + if (dragRotationState == DragRotationState.Rotating) + ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf); + dragRotationState = DragRotationState.NotDragging; + } return base.OnMouseUp(state, args); } @@ -160,5 +161,12 @@ namespace osu.Game.Graphics.Cursor cursorScale.TriggerChange(); } } + + private enum DragRotationState + { + NotDragging, + DragStarted, + Rotating, + } } } From 63746876c2cbcdeb3dd02c81d5ab750e192b88d1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 2 Jul 2018 16:09:33 +0900 Subject: [PATCH 09/13] typo --- osu.Game/Graphics/Cursor/MenuCursor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 47a3d5c998..15fd554d5b 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -47,7 +47,7 @@ namespace osu.Game.Graphics.Cursor // else it can have an annoying effect. if (dragRotationState == DragRotationState.DragStarted && distance > 30) dragRotationState = DragRotationState.Rotating; - // don't rotate when distance if zero to avoid NaN + // don't rotate when distance is zero to avoid NaN if (dragRotationState == DragRotationState.Rotating && distance > 0) { Vector2 offset = state.Mouse.Position - positionMouseDown; From ae6fdd8e29abfbda7394c16d4d30ea60be1de103 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 2 Jul 2018 16:20:44 +0900 Subject: [PATCH 10/13] whitespace --- osu.Game/Graphics/Cursor/MenuCursor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 15fd554d5b..0bb33930b0 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -65,7 +65,7 @@ namespace osu.Game.Graphics.Cursor return base.OnMouseMove(state); } - + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { ActiveCursor.Scale = new Vector2(1); From 9f405b0894910d02716545b9b28e39ea52ceca07 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Jul 2018 16:49:07 +0900 Subject: [PATCH 11/13] Don't hard-bail if config skin doesn't exist anymore --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 930b3c1eaa..18a371e904 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -56,7 +56,13 @@ namespace osu.Game.Overlays.Settings.Sections reloadSkins(); - skinDropdown.Bindable = config.GetBindable(OsuSetting.Skin); + var skinBindable = config.GetBindable(OsuSetting.Skin); + + // Todo: This should not be necessary when OsuConfigManager is databased + if (skinDropdown.Items.All(s => s.Value != skinBindable.Value)) + skinBindable.Value = 0; + + skinDropdown.Bindable = skinBindable; } private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); From 6c848f135c70b67cc9d17bdb851518c79a1269af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Jul 2018 17:48:16 +0900 Subject: [PATCH 12/13] Add comment about last button invocation --- osu.Game/Overlays/Dialog/PopupDialog.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 68e4481446..d90a850f4a 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -241,7 +241,10 @@ namespace osu.Game.Overlays.Dialog protected override void PopOut() { - if (!actionInvoked) buttonsContainer.Last().TriggerOnClick(); + if (!actionInvoked) + // In the case a user did not choose an action before a hide was triggered, press the last button. + // This is presumed to always be a sane default "cancel" action. + buttonsContainer.Last().TriggerOnClick(); base.PopOut(); content.FadeOut(EXIT_DURATION, Easing.InSine); From 73851fb1945a847b0e2108901c8845f4bcb439ca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Jul 2018 15:57:05 +0900 Subject: [PATCH 13/13] Fix beatmap details not working for unranked beatmaps --- osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 3b6bb565b0..bbaaa0c756 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -49,7 +49,7 @@ namespace osu.Game.Online.API.Requests.Responses private DateTimeOffset submitted { get; set; } [JsonProperty(@"ranked_date")] - private DateTimeOffset ranked { get; set; } + private DateTimeOffset? ranked { get; set; } [JsonProperty(@"last_updated")] private DateTimeOffset lastUpdated { get; set; }