diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 66db439c82..aa8848c55f 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -26,9 +26,9 @@
-
-
-
+
+
+
diff --git a/osu.Game.Tests/Visual/Editor/TestCaseZoomableScrollContainer.cs b/osu.Game.Tests/Visual/Editor/TestCaseZoomableScrollContainer.cs
index e2cf1ef28a..55c978ae06 100644
--- a/osu.Game.Tests/Visual/Editor/TestCaseZoomableScrollContainer.cs
+++ b/osu.Game.Tests/Visual/Editor/TestCaseZoomableScrollContainer.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
+using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
@@ -18,38 +19,49 @@ namespace osu.Game.Tests.Visual.Editor
{
public class TestCaseZoomableScrollContainer : ManualInputManagerTestCase
{
- private readonly ZoomableScrollContainer scrollContainer;
- private readonly Drawable innerBox;
+ private ZoomableScrollContainer scrollContainer;
+ private Drawable innerBox;
- public TestCaseZoomableScrollContainer()
+ [SetUpSteps]
+ public void SetUpSteps()
{
- Children = new Drawable[]
+ AddStep("Add new scroll container", () =>
{
- new Container
+ Children = new Drawable[]
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.X,
- Height = 250,
- Width = 0.75f,
- Children = new Drawable[]
+ new Container
{
- new Box
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ Height = 250,
+ Width = 0.75f,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = OsuColour.Gray(30)
- },
- scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both }
- }
- },
- new MenuCursor()
- };
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(30)
+ },
+ scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both }
+ }
+ },
+ new MenuCursor()
+ };
- scrollContainer.Add(innerBox = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientHorizontal(new Color4(0.8f, 0.6f, 0.4f, 1f), new Color4(0.4f, 0.6f, 0.8f, 1f))
+ scrollContainer.Add(innerBox = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientHorizontal(new Color4(0.8f, 0.6f, 0.4f, 1f), new Color4(0.4f, 0.6f, 0.8f, 1f))
+ });
});
+ AddUntilStep("Scroll container is loaded", () => scrollContainer.LoadState >= LoadState.Loaded);
+ }
+
+ [Test]
+ public void TestWidthInitialization()
+ {
+ AddAssert("Inner container width was initialized", () => innerBox.DrawWidth > 0);
}
[Test]
diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePause.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePause.cs
index b9c7fd14bd..14be10b65c 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCasePause.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCasePause.cs
@@ -157,6 +157,8 @@ namespace osu.Game.Tests.Visual.Gameplay
private void confirmPaused()
{
confirmClockRunning(false);
+ AddAssert("player not exited", () => Player.IsCurrentScreen());
+ AddAssert("player not failed", () => !Player.HasFailed);
AddAssert("pause overlay shown", () => Player.PauseOverlayVisible);
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseUpdateableBeatmapBackgroundSprite.cs
index a4dd7b83e2..6185fbd34e 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestCaseUpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestCaseUpdateableBeatmapBackgroundSprite.cs
@@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
- Padding = new MarginPadding { Bottom = 250 }
+ Padding = new MarginPadding { Bottom = 550 }
}
};
@@ -132,11 +132,9 @@ namespace osu.Game.Tests.Visual.UserInterface
var loadedBackgrounds = backgrounds.Where(b => b.ContentLoaded);
- int initialLoadCount = 0;
-
- AddUntilStep("some loaded", () => (initialLoadCount = loadedBackgrounds.Count()) > 0);
+ AddUntilStep("some loaded", () => loadedBackgrounds.Any());
AddStep("scroll to bottom", () => scrollContainer.ScrollToEnd());
- AddUntilStep("some unloaded", () => loadedBackgrounds.Count() < initialLoadCount);
+ AddUntilStep("all unloaded", () => !loadedBackgrounds.Any());
}
private class TestUpdateableBeatmapBackgroundSprite : UpdateableBeatmapBackgroundSprite
diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs
index d62b53088a..594bc1e3ca 100644
--- a/osu.Game/Online/API/APIAccess.cs
+++ b/osu.Game/Online/API/APIAccess.cs
@@ -77,13 +77,13 @@ namespace osu.Game.Online.API
///
public void Register(IOnlineComponent component)
{
- Scheduler.Add(delegate { components.Add(component); });
+ Schedule(() => components.Add(component));
component.APIStateChanged(this, state);
}
public void Unregister(IOnlineComponent component)
{
- Scheduler.Add(delegate { components.Remove(component); });
+ Schedule(() => components.Remove(component));
}
public string AccessToken => authentication.RequestAccessToken();
@@ -274,7 +274,7 @@ namespace osu.Game.Online.API
state = value;
log.Add($@"We just went {state}!");
- Scheduler.Add(delegate
+ Schedule(() =>
{
components.ForEach(c => c.APIStateChanged(this, state));
OnStateChange?.Invoke(oldState, state);
@@ -352,9 +352,13 @@ namespace osu.Game.Online.API
public void Logout()
{
flushQueue();
+
password = null;
authentication.Clear();
- LocalUser.Value = createGuestUser();
+
+ // Scheduled prior to state change such that the state changed event is invoked with the correct user present
+ Schedule(() => LocalUser.Value = createGuestUser());
+
State = APIState.Offline;
}
diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs
index de710c5fcd..e41c90be45 100644
--- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs
+++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs
@@ -4,14 +4,12 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Scoring;
using osu.Game.Users;
@@ -185,8 +183,6 @@ namespace osu.Game.Overlays.Profile.Header
private class ScoreRankInfo : CompositeDrawable
{
- private readonly ScoreRank rank;
- private readonly Sprite rankSprite;
private readonly OsuSpriteText rankCount;
public int RankCount
@@ -196,8 +192,6 @@ namespace osu.Game.Overlays.Profile.Header
public ScoreRankInfo(ScoreRank rank)
{
- this.rank = rank;
-
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
@@ -206,10 +200,10 @@ namespace osu.Game.Overlays.Profile.Header
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- rankSprite = new Sprite
+ new DrawableRank(rank)
{
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit
+ RelativeSizeAxes = Axes.X,
+ Height = 30,
},
rankCount = new OsuSpriteText
{
@@ -220,12 +214,6 @@ namespace osu.Game.Overlays.Profile.Header
}
};
}
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- rankSprite.Texture = textures.Get($"Grades/{rank.GetDescription()}");
- }
}
}
}
diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs
index 4ac866fc90..9a5a4d4acd 100644
--- a/osu.Game/Rulesets/RulesetConfigCache.cs
+++ b/osu.Game/Rulesets/RulesetConfigCache.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
+using System.Collections.Concurrent;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets
///
public class RulesetConfigCache : Component
{
- private readonly Dictionary configCache = new Dictionary();
+ private readonly ConcurrentDictionary configCache = new ConcurrentDictionary();
private readonly SettingsStore settingsStore;
public RulesetConfigCache(SettingsStore settingsStore)
@@ -34,10 +34,7 @@ namespace osu.Game.Rulesets
if (ruleset.RulesetInfo.ID == null)
throw new InvalidOperationException("The provided ruleset doesn't have a valid id.");
- if (configCache.TryGetValue(ruleset.RulesetInfo.ID.Value, out var existing))
- return existing;
-
- return configCache[ruleset.RulesetInfo.ID.Value] = ruleset.CreateConfig(settingsStore);
+ return configCache.GetOrAdd(ruleset.RulesetInfo.ID.Value, _ => ruleset.CreateConfig(settingsStore));
}
}
}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 0ca92a8861..cf42a70640 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -360,6 +360,9 @@ namespace osu.Game.Rulesets.Scoring
JudgedHits--;
+ if (result.Type != HitResult.None)
+ scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1;
+
if (result.Judgement.IsBonus)
{
if (result.IsHit)
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
index 9b00a3998d..829437d599 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
@@ -92,6 +92,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // This width only gets updated on the application of a transform, so this needs to be initialized here.
+ updateZoomedContentWidth();
+ }
+
protected override bool OnScroll(ScrollEvent e)
{
if (e.IsPrecise)
@@ -102,6 +110,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
return true;
}
+ private void updateZoomedContentWidth() => zoomedContent.Width = DrawWidth * currentZoom;
+
private float zoomTarget = 1;
private void setZoomTarget(float newZoom, float focusPoint)
@@ -163,7 +173,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
d.currentZoom = newZoom;
- d.zoomedContent.Width = d.DrawWidth * d.currentZoom;
+ d.updateZoomedContentWidth();
// Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area.
// TODO: Make sure draw size gets invalidated properly on the framework side, and remove this once it is.
d.Invalidate(Invalidation.DrawSize);
diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs
index d7fc90e36d..0f3c92a962 100644
--- a/osu.Game/Screens/Play/HUD/ModDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs
@@ -24,6 +24,8 @@ namespace osu.Game.Screens.Play.HUD
public bool DisplayUnrankedText = true;
+ public bool AllowExpand = true;
+
private readonly Bindable> current = new Bindable>();
public Bindable> Current
@@ -88,7 +90,9 @@ namespace osu.Game.Screens.Play.HUD
protected override void LoadComplete()
{
base.LoadComplete();
+
appearTransform();
+ iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
}
private void appearTransform()
@@ -98,14 +102,17 @@ namespace osu.Game.Screens.Play.HUD
else
unrankedText.Hide();
- iconsContainer.FinishTransforms();
- iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
expand();
+
using (iconsContainer.BeginDelayedSequence(1200))
contract();
}
- private void expand() => iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
+ private void expand()
+ {
+ if (AllowExpand)
+ iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
+ }
private void contract() => iconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint);
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index 3c1b33297a..f8e092c8b1 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -129,6 +129,7 @@ namespace osu.Game.Screens.Play
private void replayLoadedValueChanged(ValueChangedEvent e)
{
PlayerSettingsOverlay.ReplayLoaded = e.NewValue;
+ HoldToQuit.PauseOnFocusLost = !e.NewValue;
if (e.NewValue)
{
diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs
index 03b9826e9b..6ba29751b0 100644
--- a/osu.Game/Screens/Select/Footer.cs
+++ b/osu.Game/Screens/Select/Footer.cs
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using osuTK;
using osuTK.Graphics;
-using osuTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -20,9 +19,6 @@ namespace osu.Game.Screens.Select
{
private readonly Box modeLight;
- private const float play_song_select_button_width = 100;
- private const float play_song_select_button_height = 50;
-
public const float HEIGHT = 50;
public const int TRANSITION_LENGTH = 300;
@@ -33,57 +29,36 @@ namespace osu.Game.Screens.Select
private readonly FillFlowContainer buttons;
- /// Text on the button.
- /// Colour of the button.
- /// Hotkey of the button.
- /// Action the button does.
- ///
- /// Higher depth to be put on the left, and lower to be put on the right.
- /// Notice this is different to !
- ///
- public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0)
- {
- var button = new FooterButton
- {
- Text = text,
- Height = play_song_select_button_height,
- Width = play_song_select_button_width,
- Depth = depth,
- SelectedColour = colour,
- DeselectedColour = colour.Opacity(0.5f),
- Hotkey = hotkey,
- Hovered = updateModeLight,
- HoverLost = updateModeLight,
- Action = action,
- };
-
- buttons.Add(button);
- buttons.SetLayoutPosition(button, -depth);
- }
-
private readonly List overlays = new List();
- /// Text on the button.
- /// Colour of the button.
- /// Hotkey of the button.
+ /// THe button to be added.
/// The to be toggled by this button.
- ///
- /// Higher depth to be put on the left, and lower to be put on the right.
- /// Notice this is different to !
- ///
- public void AddButton(string text, Color4 colour, OverlayContainer overlay, Key? hotkey = null, float depth = 0)
+ public void AddButton(FooterButton button, OverlayContainer overlay)
{
overlays.Add(overlay);
- AddButton(text, colour, () =>
+ button.Action = () => showOverlay(overlay);
+
+ AddButton(button);
+ }
+
+ /// Button to be added.
+ public void AddButton(FooterButton button)
+ {
+ button.Hovered = updateModeLight;
+ button.HoverLost = updateModeLight;
+
+ buttons.Add(button);
+ }
+
+ private void showOverlay(OverlayContainer overlay)
+ {
+ foreach (var o in overlays)
{
- foreach (var o in overlays)
- {
- if (o == overlay)
- o.ToggleVisibility();
- else
- o.Hide();
- }
- }, hotkey, depth);
+ if (o == overlay)
+ o.ToggleVisibility();
+ else
+ o.Hide();
+ }
}
private void updateModeLight() => modeLight.FadeColour(buttons.FirstOrDefault(b => b.IsHovered)?.SelectedColour ?? Color4.Transparent, TRANSITION_LENGTH, Easing.OutQuint);
diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs
index 9b98e344ce..e18a086a10 100644
--- a/osu.Game/Screens/Select/FooterButton.cs
+++ b/osu.Game/Screens/Select/FooterButton.cs
@@ -6,6 +6,7 @@ using osuTK;
using osuTK.Graphics;
using osuTK.Input;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
@@ -20,11 +21,11 @@ namespace osu.Game.Screens.Select
public string Text
{
- get => spriteText?.Text;
+ get => SpriteText?.Text;
set
{
- if (spriteText != null)
- spriteText.Text = value;
+ if (SpriteText != null)
+ SpriteText.Text = value;
}
}
@@ -53,7 +54,8 @@ namespace osu.Game.Screens.Select
}
}
- private readonly SpriteText spriteText;
+ protected readonly Container TextContainer;
+ protected readonly SpriteText SpriteText;
private readonly Box box;
private readonly Box light;
@@ -61,8 +63,18 @@ namespace osu.Game.Screens.Select
public FooterButton()
{
+ AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
+ TextContainer = new Container
+ {
+ Size = new Vector2(100, 50),
+ Child = SpriteText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }
+ },
box = new Box
{
RelativeSizeAxes = Axes.Both,
@@ -78,11 +90,6 @@ namespace osu.Game.Screens.Select
EdgeSmoothness = new Vector2(2, 0),
RelativeSizeAxes = Axes.X,
},
- spriteText = new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- }
};
}
diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs
new file mode 100644
index 0000000000..c96c5022c0
--- /dev/null
+++ b/osu.Game/Screens/Select/FooterButtonMods.cs
@@ -0,0 +1,60 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Screens.Play.HUD;
+using osu.Game.Rulesets.Mods;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Game.Graphics;
+using osuTK;
+using osuTK.Input;
+
+namespace osu.Game.Screens.Select
+{
+ public class FooterButtonMods : FooterButton
+ {
+ public FooterButtonMods(Bindable> mods)
+ {
+ FooterModDisplay modDisplay;
+
+ Add(new Container
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Child = modDisplay = new FooterModDisplay
+ {
+ DisplayUnrankedText = false,
+ Scale = new Vector2(0.8f)
+ },
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Left = 70 }
+ });
+
+ if (mods != null)
+ modDisplay.Current = mods;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ SelectedColour = colours.Yellow;
+ DeselectedColour = SelectedColour.Opacity(0.5f);
+ Text = @"mods";
+ Hotkey = Key.F1;
+ }
+
+ private class FooterModDisplay : ModDisplay
+ {
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.Parent?.ReceivePositionalInputAt(screenSpacePos) ?? false;
+
+ public FooterModDisplay()
+ {
+ AllowExpand = false;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/FooterButtonOptions.cs b/osu.Game/Screens/Select/FooterButtonOptions.cs
new file mode 100644
index 0000000000..c000d8a8c8
--- /dev/null
+++ b/osu.Game/Screens/Select/FooterButtonOptions.cs
@@ -0,0 +1,22 @@
+// 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.Extensions.Color4Extensions;
+using osu.Game.Graphics;
+using osuTK.Input;
+
+namespace osu.Game.Screens.Select
+{
+ public class FooterButtonOptions : FooterButton
+ {
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ SelectedColour = colours.Blue;
+ DeselectedColour = SelectedColour.Opacity(0.5f);
+ Text = @"options";
+ Hotkey = Key.F3;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs
new file mode 100644
index 0000000000..14c9eb2035
--- /dev/null
+++ b/osu.Game/Screens/Select/FooterButtonRandom.cs
@@ -0,0 +1,68 @@
+// 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.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osuTK.Input;
+
+namespace osu.Game.Screens.Select
+{
+ public class FooterButtonRandom : FooterButton
+ {
+ private readonly SpriteText secondaryText;
+ private bool secondaryActive;
+
+ public FooterButtonRandom()
+ {
+ TextContainer.Add(secondaryText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = @"rewind",
+ Alpha = 0
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ SelectedColour = colours.Green;
+ DeselectedColour = SelectedColour.Opacity(0.5f);
+ Text = @"random";
+ Hotkey = Key.F2;
+ }
+
+ protected override bool OnKeyDown(KeyDownEvent e)
+ {
+ secondaryActive = e.ShiftPressed;
+ updateText();
+ return base.OnKeyDown(e);
+ }
+
+ protected override bool OnKeyUp(KeyUpEvent e)
+ {
+ secondaryActive = e.ShiftPressed;
+ updateText();
+ return base.OnKeyUp(e);
+ }
+
+ private void updateText()
+ {
+ if (secondaryActive)
+ {
+ SpriteText.FadeOut(120, Easing.InQuad);
+ secondaryText.FadeIn(120, Easing.InQuad);
+ }
+ else
+ {
+ SpriteText.FadeIn(120, Easing.InQuad);
+ secondaryText.FadeOut(120, Easing.InQuad);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 14c362b8ca..d5301116a9 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -223,9 +223,9 @@ namespace osu.Game.Screens.Select
if (Footer != null)
{
- Footer.AddButton(@"mods", colours.Yellow, ModSelect, Key.F1);
- Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
- Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3);
+ Footer.AddButton(new FooterButtonMods(mods), ModSelect);
+ Footer.AddButton(new FooterButtonRandom { Action = triggerRandom });
+ Footer.AddButton(new FooterButtonOptions(), BeatmapOptions);
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null, Key.Number1);
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index b6b4896658..8f733a5c55 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -11,11 +11,11 @@
-
-
-
-
-
+
+
+
+
+