mirror of
https://github.com/ppy/osu.git
synced 2026-05-23 02:21:19 +08:00
Hook up footer beatmap options via new ISongSelectBeatmapActions class
This commit is contained in:
@@ -12,11 +12,10 @@ using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.Footer;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Select;
|
||||
using osuTK.Input;
|
||||
using FooterButtonMods = osu.Game.Screens.SelectV2.FooterButtonMods;
|
||||
using FooterButtonOptions = osu.Game.Screens.SelectV2.FooterButtonOptions;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
@@ -382,17 +381,20 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
// }
|
||||
|
||||
[Test]
|
||||
public void TestFooterShowOptions()
|
||||
public void TestFooterOptions()
|
||||
{
|
||||
LoadSongSelect();
|
||||
|
||||
AddStep("enable options", () =>
|
||||
{
|
||||
var optionsButton = this.ChildrenOfType<ScreenFooterButton>().Last();
|
||||
ImportBeatmapForRuleset(0);
|
||||
|
||||
optionsButton.Enabled.Value = true;
|
||||
optionsButton.TriggerClick();
|
||||
});
|
||||
// song select should automatically select the beatmap for us but this is not implemented yet.
|
||||
// todo: remove when that's the case.
|
||||
AddAssert("no beatmap selected", () => Beatmap.IsDefault);
|
||||
AddStep("select beatmap", () => Beatmap.Value = Beatmaps.GetWorkingBeatmap(Beatmaps.GetAllUsableBeatmapSets().Single().Beatmaps.First()));
|
||||
AddAssert("options enabled", () => this.ChildrenOfType<FooterButtonOptions>().Single().Enabled.Value);
|
||||
|
||||
AddStep("click", () => this.ChildrenOfType<FooterButtonOptions>().Single().TriggerClick());
|
||||
AddUntilStep("popover displayed", () => this.ChildrenOfType<FooterButtonOptions.Popover>().Any(p => p.IsPresent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -400,7 +402,23 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
LoadSongSelect();
|
||||
|
||||
AddToggleStep("set options enabled state", state => this.ChildrenOfType<ScreenFooterButton>().Last().Enabled.Value = state);
|
||||
ImportBeatmapForRuleset(0);
|
||||
|
||||
// song select should automatically select the beatmap for us but this is not implemented yet.
|
||||
// todo: remove when that's the case.
|
||||
AddAssert("no beatmap selected", () => Beatmap.IsDefault);
|
||||
AddStep("select beatmap", () => Beatmap.Value = Beatmaps.GetWorkingBeatmap(Beatmaps.GetAllUsableBeatmapSets().Single().Beatmaps.First()));
|
||||
|
||||
AddAssert("options enabled", () => this.ChildrenOfType<FooterButtonOptions>().Single().Enabled.Value);
|
||||
AddStep("delete all beatmaps", () => Beatmaps.Delete());
|
||||
|
||||
// song select should automatically select the beatmap for us but this is not implemented yet.
|
||||
// todo: remove when that's the case.
|
||||
AddAssert("beatmap selected", () => !Beatmap.IsDefault);
|
||||
AddStep("select no beatmap", () => Beatmap.SetDefault());
|
||||
|
||||
AddUntilStep("wait for no beatmap", () => Beatmap.IsDefault);
|
||||
AddAssert("options disabled", () => !this.ChildrenOfType<FooterButtonOptions>().Single().Enabled.Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
@@ -17,6 +19,12 @@ namespace osu.Game.Screens.SelectV2
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private ISongSelectBeatmapActions? beatmapActions { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour)
|
||||
{
|
||||
@@ -28,6 +36,22 @@ namespace osu.Game.Screens.SelectV2
|
||||
Action = this.ShowPopover;
|
||||
}
|
||||
|
||||
public Framework.Graphics.UserInterface.Popover GetPopover() => new Popover(this, colourProvider);
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
beatmap.BindValueChanged(_ => beatmapChanged(), true);
|
||||
}
|
||||
|
||||
private void beatmapChanged()
|
||||
{
|
||||
this.HidePopover();
|
||||
Enabled.Value = !beatmap.IsDefault;
|
||||
}
|
||||
|
||||
public Framework.Graphics.UserInterface.Popover GetPopover() => new Popover(this, beatmap.Value)
|
||||
{
|
||||
ColourProvider = colourProvider,
|
||||
BeatmapActions = beatmapActions
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Collections;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@@ -34,22 +33,21 @@ namespace osu.Game.Screens.SelectV2
|
||||
private FillFlowContainer buttonFlow = null!;
|
||||
private readonly FooterButtonOptions footerButton;
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider;
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
|
||||
private WorkingBeatmap beatmapWhenOpening = null!;
|
||||
// Can't use DI for these due to popover being initialised from a footer button which ends up being on the global
|
||||
// PopoverContainer.
|
||||
public ISongSelectBeatmapActions? BeatmapActions { get; init; }
|
||||
public required OverlayColourProvider ColourProvider { get; init; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||
|
||||
public Popover(FooterButtonOptions footerButton, OverlayColourProvider colourProvider)
|
||||
public Popover(FooterButtonOptions footerButton, WorkingBeatmap beatmap)
|
||||
{
|
||||
this.footerButton = footerButton;
|
||||
this.colourProvider = colourProvider;
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ManageCollectionsDialog? manageCollectionsDialog, OsuColour colours, BeatmapManager? beatmapManager)
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Content.Padding = new MarginPadding(5);
|
||||
|
||||
@@ -60,23 +58,21 @@ namespace osu.Game.Screens.SelectV2
|
||||
Spacing = new Vector2(3),
|
||||
};
|
||||
|
||||
beatmapWhenOpening = beatmap.Value;
|
||||
|
||||
addHeader(CommonStrings.General);
|
||||
addButton(SongSelectStrings.ManageCollections, FontAwesome.Solid.Book, () => manageCollectionsDialog?.Show());
|
||||
addButton(SongSelectStrings.ManageCollections, FontAwesome.Solid.Book, () => BeatmapActions?.ManageCollections());
|
||||
|
||||
addHeader(SongSelectStrings.ForAllDifficulties, beatmapWhenOpening.BeatmapSetInfo.ToString());
|
||||
addButton(SongSelectStrings.DeleteBeatmap, FontAwesome.Solid.Trash, () => { }, colours.Red1); // songSelect?.DeleteBeatmap(beatmapWhenOpening.BeatmapSetInfo);
|
||||
addHeader(SongSelectStrings.ForAllDifficulties, beatmap.BeatmapSetInfo.ToString());
|
||||
addButton(SongSelectStrings.DeleteBeatmap, FontAwesome.Solid.Trash, () => BeatmapActions?.Delete(beatmap.BeatmapSetInfo), colours.Red1);
|
||||
|
||||
addHeader(SongSelectStrings.ForSelectedDifficulty, beatmapWhenOpening.BeatmapInfo.DifficultyName);
|
||||
// TODO: make work, and make show "unplayed" or "played" based on status.
|
||||
addButton(SongSelectStrings.MarkAsPlayed, FontAwesome.Regular.TimesCircle, null);
|
||||
addButton(SongSelectStrings.ClearAllLocalScores, FontAwesome.Solid.Eraser, () => { }, colours.Red1); // songSelect?.ClearScores(beatmapWhenOpening.BeatmapInfo);
|
||||
addHeader(SongSelectStrings.ForSelectedDifficulty, beatmap.BeatmapInfo.DifficultyName);
|
||||
// TODO: replace with "remove from played" button when beatmap is already played.
|
||||
addButton(SongSelectStrings.MarkAsPlayed, FontAwesome.Regular.TimesCircle, () => BeatmapActions?.MarkPlayed(beatmap.BeatmapInfo));
|
||||
addButton(SongSelectStrings.ClearAllLocalScores, FontAwesome.Solid.Eraser, () => BeatmapActions?.ClearScores(beatmap.BeatmapInfo), colours.Red1);
|
||||
|
||||
// if (songSelect != null && songSelect.AllowEditing)
|
||||
addButton(SongSelectStrings.EditBeatmap, FontAwesome.Solid.PencilAlt, () => { }); // songSelect.Edit(beatmapWhenOpening.BeatmapInfo);
|
||||
if (BeatmapActions?.EditingAllowed == true)
|
||||
addButton(SongSelectStrings.EditBeatmap, FontAwesome.Solid.PencilAlt, () => BeatmapActions.Edit(beatmap.BeatmapInfo));
|
||||
|
||||
addButton(WebCommonStrings.ButtonsHide.ToSentence(), FontAwesome.Solid.Magic, () => beatmapManager?.Hide(beatmapWhenOpening.BeatmapInfo));
|
||||
addButton(WebCommonStrings.ButtonsHide.ToSentence(), FontAwesome.Solid.Magic, () => BeatmapActions?.Hide(beatmap.BeatmapInfo));
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -84,8 +80,12 @@ namespace osu.Game.Screens.SelectV2
|
||||
base.LoadComplete();
|
||||
|
||||
ScheduleAfterChildren(() => GetContainingFocusManager()!.ChangeFocus(this));
|
||||
}
|
||||
|
||||
beatmap.BindValueChanged(_ => Hide());
|
||||
protected override void UpdateState(ValueChangedEvent<Visibility> state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
footerButton.OverlayState.Value = state.NewValue;
|
||||
}
|
||||
|
||||
private void addHeader(LocalisableString text, string? context = null)
|
||||
@@ -104,7 +104,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
textFlow.NewLine();
|
||||
textFlow.AddText(context, t =>
|
||||
{
|
||||
t.Colour = colourProvider.Content2;
|
||||
t.Colour = ColourProvider.Content2;
|
||||
t.Font = t.Font.With(size: 13);
|
||||
});
|
||||
}
|
||||
@@ -118,6 +118,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
Text = text,
|
||||
Icon = icon,
|
||||
BackgroundColour = ColourProvider.Background3,
|
||||
TextColour = colour,
|
||||
Action = () =>
|
||||
{
|
||||
@@ -129,44 +130,6 @@ namespace osu.Game.Screens.SelectV2
|
||||
buttonFlow.Add(button);
|
||||
}
|
||||
|
||||
private partial class OptionButton : OsuButton
|
||||
{
|
||||
public IconUsage Icon { get; init; }
|
||||
public Color4? TextColour { get; init; }
|
||||
|
||||
public OptionButton()
|
||||
{
|
||||
Size = new Vector2(265, 50);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
BackgroundColour = colourProvider.Background3;
|
||||
|
||||
SpriteText.Colour = TextColour ?? Color4.White;
|
||||
Content.CornerRadius = 10;
|
||||
|
||||
Add(new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = new Vector2(17),
|
||||
X = 15,
|
||||
Icon = Icon,
|
||||
Colour = TextColour ?? Color4.White,
|
||||
});
|
||||
}
|
||||
|
||||
protected override SpriteText CreateText() => new OsuSpriteText
|
||||
{
|
||||
Depth = -1,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
X = 40
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
// don't absorb control as ToolbarRulesetSelector uses control + number to navigate
|
||||
@@ -188,10 +151,40 @@ namespace osu.Game.Screens.SelectV2
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
protected override void UpdateState(ValueChangedEvent<Visibility> state)
|
||||
private partial class OptionButton : OsuButton
|
||||
{
|
||||
base.UpdateState(state);
|
||||
footerButton.OverlayState.Value = state.NewValue;
|
||||
public IconUsage Icon { get; init; }
|
||||
public Color4? TextColour { get; init; }
|
||||
|
||||
public OptionButton()
|
||||
{
|
||||
Size = new Vector2(265, 50);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SpriteText.Colour = TextColour ?? Color4.White;
|
||||
Content.CornerRadius = 10;
|
||||
|
||||
Add(new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = new Vector2(17),
|
||||
X = 15,
|
||||
Icon = Icon,
|
||||
Colour = TextColour ?? Color4.White,
|
||||
});
|
||||
}
|
||||
|
||||
protected override SpriteText CreateText() => new OsuSpriteText
|
||||
{
|
||||
Depth = -1,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
X = 40
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
/// <summary>
|
||||
/// Actions exposed by song select which are used by subcomponents to perform top-level operations.
|
||||
/// </summary>
|
||||
public interface ISongSelectBeatmapActions
|
||||
{
|
||||
/// <summary>
|
||||
/// Requests the user for confirmation to delete the given beatmap set.
|
||||
/// </summary>
|
||||
void Delete(BeatmapSetInfo beatmapBeatmapSetInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Requests the user for confirmation to clear all local scores in the given beatmap.
|
||||
/// </summary>
|
||||
void ClearScores(BeatmapInfo beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Opens beatmap editor with the given beatmap.
|
||||
/// </summary>
|
||||
void Edit(BeatmapInfo beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Whether calls to <see cref="Edit"/> will succeed or not.
|
||||
/// </summary>
|
||||
bool EditingAllowed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Opens the manage collections dialog.
|
||||
/// </summary>
|
||||
void ManageCollections();
|
||||
|
||||
/// <summary>
|
||||
/// Marks a beatmap manually as being played.
|
||||
/// </summary>
|
||||
void MarkPlayed(BeatmapInfo beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Hides a beatmap from user's vision.
|
||||
/// </summary>
|
||||
void Hide(BeatmapInfo beatmap);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ namespace osu.Game.Screens.SelectV2
|
||||
[Resolved]
|
||||
private INotificationOverlay? notifications { get; set; }
|
||||
|
||||
public override bool EditingAllowed => true;
|
||||
|
||||
protected override bool OnStart()
|
||||
{
|
||||
if (playerLoader != null) return false;
|
||||
@@ -98,7 +100,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
playerLoader = null;
|
||||
}
|
||||
|
||||
private partial class PlayerLoader : Screens.Play.PlayerLoader
|
||||
private partial class PlayerLoader : Play.PlayerLoader
|
||||
{
|
||||
public override bool ShowFooter => true;
|
||||
|
||||
|
||||
@@ -15,11 +15,13 @@ using osu.Framework.Input.Events;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Collections;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Overlays.Volume;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Footer;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Select;
|
||||
@@ -35,8 +37,8 @@ namespace osu.Game.Screens.SelectV2
|
||||
/// This screen is intended to house all components introduced in the new song select design to add transitions and examine the overall look.
|
||||
/// This will be gradually built upon and ultimately replace <see cref="Select.SongSelect"/> once everything is in place.
|
||||
/// </summary>
|
||||
[Cached(typeof(SongSelect))]
|
||||
public abstract partial class SongSelect : OsuScreen, IKeyBindingHandler<GlobalAction>
|
||||
[Cached(typeof(ISongSelectBeatmapActions))]
|
||||
public abstract partial class SongSelect : OsuScreen, IKeyBindingHandler<GlobalAction>, ISongSelectBeatmapActions
|
||||
{
|
||||
private const float logo_scale = 0.4f;
|
||||
private const double fade_duration = 300;
|
||||
@@ -77,6 +79,12 @@ namespace osu.Game.Screens.SelectV2
|
||||
[Resolved]
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private ManageCollectionsDialog? collectionsDialog { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@@ -343,26 +351,6 @@ namespace osu.Game.Screens.SelectV2
|
||||
|
||||
#endregion
|
||||
|
||||
#region Beatmap management
|
||||
|
||||
/// <summary>
|
||||
/// Requests the user for confirmation to delete the given beatmap set.
|
||||
/// </summary>
|
||||
public void DeleteBeatmap(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
dialogOverlay?.Push(new BeatmapDeleteDialog(beatmapSet));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests the user for confirmation to clear all local scores in the given beatmap.
|
||||
/// </summary>
|
||||
public void ClearScores(BeatmapInfo beatmap)
|
||||
{
|
||||
dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hotkeys
|
||||
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
@@ -395,7 +383,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
if (e.ShiftPressed)
|
||||
{
|
||||
if (!Beatmap.IsDefault)
|
||||
DeleteBeatmap(Beatmap.Value.BeatmapSetInfo);
|
||||
Delete(Beatmap.Value.BeatmapSetInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -406,5 +394,30 @@ namespace osu.Game.Screens.SelectV2
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Beatmap management
|
||||
|
||||
public virtual bool EditingAllowed => false;
|
||||
|
||||
public void ManageCollections() => collectionsDialog?.Show();
|
||||
|
||||
public void MarkPlayed(BeatmapInfo beatmap) => beatmaps.MarkPlayed(beatmap);
|
||||
|
||||
public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap);
|
||||
|
||||
public void Edit(BeatmapInfo beatmap)
|
||||
{
|
||||
if (!EditingAllowed) return;
|
||||
|
||||
// Forced refetch is important here to guarantee correct invalidation across all difficulties.
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, true);
|
||||
this.Push(new EditorLoader());
|
||||
}
|
||||
|
||||
public void Delete(BeatmapSetInfo beatmapSet) => dialogOverlay?.Push(new BeatmapDeleteDialog(beatmapSet));
|
||||
|
||||
public void ClearScores(BeatmapInfo beatmap) => dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap));
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user