Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement beatmap options popover #24712

Merged
merged 14 commits into from
Sep 6, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Testing;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
Expand Down Expand Up @@ -37,10 +38,10 @@ public partial class TestSceneSongSelectFooterV2 : OsuManualInputManagerTestScen

Children = new Drawable[]
{
footer = new FooterV2
new PopoverContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
RelativeSizeAxes = Axes.Both,
Child = footer = new FooterV2(),
},
overlay = new DummyOverlay()
};
Expand Down
148 changes: 148 additions & 0 deletions osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// 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 System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Collections;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;

namespace osu.Game.Screens.Select.FooterV2
{
public partial class BeatmapOptionsPopover : OsuPopover
{
private FillFlowContainer<OptionButton> buttonFlow = null!;
private readonly FooterButtonOptionsV2 footerButton;

public BeatmapOptionsPopover(FooterButtonOptionsV2 footerButton)
{
this.footerButton = footerButton;
}

[BackgroundDependencyLoader]
private void load(ManageCollectionsDialog? manageCollectionsDialog, SongSelect? songSelect, OsuColour colours)
{
Content.Padding = new MarginPadding(5);

Child = buttonFlow = new FillFlowContainer<OptionButton>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(3),
};

addButton(@"Manage collections", FontAwesome.Solid.Book, () => manageCollectionsDialog?.Show());
addButton(@"Delete all difficulties", FontAwesome.Solid.Trash, () => songSelect?.DeleteBeatmap(), colours.Red);
addButton(@"Remove from unplayed", FontAwesome.Regular.TimesCircle, null);
addButton(@"Clear local scores", FontAwesome.Solid.Eraser, () => songSelect?.ClearScores());

if (songSelect != null && songSelect.AllowEditing)
addButton(@"Edit beatmap", FontAwesome.Solid.PencilAlt, () => songSelect.Edit());
}

protected override void LoadComplete()
{
base.LoadComplete();

ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(this));
}

private void addButton(LocalisableString text, IconUsage icon, Action? action, Color4? colour = null)
{
var button = new OptionButton
{
Text = text,
Icon = icon,
TextColour = colour,
Action = () =>
{
Hide();
action?.Invoke();
},
};

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
{
Blending = BlendingParameters.Additive,
bdach marked this conversation as resolved.
Show resolved Hide resolved
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
if (e.ControlPressed) return false;
Joehuu marked this conversation as resolved.
Show resolved Hide resolved

if (!e.Repeat && e.Key >= Key.Number1 && e.Key <= Key.Number9)
{
int requested = e.Key - Key.Number1;

OptionButton? found = buttonFlow.Children.ElementAtOrDefault(requested);

if (found != null)
{
found.TriggerClick();
return true;
}
}

return base.OnKeyDown(e);
}

protected override void UpdateState(ValueChangedEvent<Visibility> state)
{
base.UpdateState(state);

if (state.NewValue == Visibility.Hidden)
footerButton.IsActive.Value = false;
}
}
}
37 changes: 36 additions & 1 deletion osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,56 @@
// 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.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Input.Bindings;

namespace osu.Game.Screens.Select.FooterV2
{
public partial class FooterButtonOptionsV2 : FooterButtonV2
public partial class FooterButtonOptionsV2 : FooterButtonV2, IHasPopover
{
public readonly BindableBool IsActive = new BindableBool();

[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
Text = "Options";
Icon = FontAwesome.Solid.Cog;
AccentColour = colour.Purple1;
Hotkey = GlobalAction.ToggleBeatmapOptions;

Action = () => IsActive.Toggle();
}

protected override void LoadComplete()
{
base.LoadComplete();

IsActive.BindValueChanged(active =>
{
OverlayState.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden;
});

OverlayState.BindValueChanged(state =>
{
switch (state.NewValue)
{
case Visibility.Hidden:
this.HidePopover();
break;

case Visibility.Visible:
this.ShowPopover();
break;
}
});
}

public Popover GetPopover() => new BeatmapOptionsPopover(this);
}
}
10 changes: 8 additions & 2 deletions osu.Game/Screens/Select/FooterV2/FooterV2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,17 @@ private void showOverlay(OverlayContainer overlay)

private FillFlowContainer<FooterButtonV2> buttons = null!;

[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
public FooterV2()
{
RelativeSizeAxes = Axes.X;
Height = height;
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
}

[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
InternalChildren = new Drawable[]
{
new Box
Expand Down
24 changes: 15 additions & 9 deletions osu.Game/Screens/Select/SongSelect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,9 @@ private void load(AudioManager audio, OsuColour colours, ManageCollectionsDialog
Footer.AddButton(button, overlay);

BeatmapOptions.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, () => manageCollectionsDialog?.Show());
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo));
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, DeleteBeatmap);
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null);
BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo));
BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, ClearScores);
}

sampleChangeDifficulty = audio.Samples.Get(@"SongSelect/select-difficulty");
Expand Down Expand Up @@ -916,18 +916,24 @@ private bool transferRulesetValue()
return true;
}

private void delete(BeatmapSetInfo? beatmap)
/// <summary>
/// Request to delete the current beatmap.
/// </summary>
public void DeleteBeatmap()
peppy marked this conversation as resolved.
Show resolved Hide resolved
{
if (beatmap == null) return;
if (Beatmap.Value.BeatmapSetInfo == null) return;

dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
dialogOverlay?.Push(new BeatmapDeleteDialog(Beatmap.Value.BeatmapSetInfo));
}

private void clearScores(BeatmapInfo? beatmapInfo)
/// <summary>
/// Request to clear the scores of the current beatmap.
/// </summary>
public void ClearScores()
{
if (beatmapInfo == null) return;
if (Beatmap.Value.BeatmapInfo == null) return;

dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmapInfo, () =>
dialogOverlay?.Push(new BeatmapClearScoresDialog(Beatmap.Value.BeatmapInfo, () =>
// schedule done here rather than inside the dialog as the dialog may fade out and never callback.
Schedule(() => BeatmapDetails.Refresh())));
}
Expand Down Expand Up @@ -963,7 +969,7 @@ protected override bool OnKeyDown(KeyDownEvent e)
if (e.ShiftPressed)
{
if (!Beatmap.IsDefault)
delete(Beatmap.Value.BeatmapSetInfo);
DeleteBeatmap();
return true;
}

Expand Down