Merge branch 'master' into BPM_counter_fix

This commit is contained in:
Bartłomiej Dach 2023-02-04 23:44:58 +01:00 committed by GitHub
commit ef900bccdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 339 additions and 105 deletions

View File

@ -187,28 +187,19 @@ namespace osu.Game.Rulesets.Osu.Edit
if (b.IsSelected) if (b.IsSelected)
continue; continue;
var hitObject = (OsuHitObject)b.Item; var snapPositions = b.ScreenSpaceSnapPoints;
Vector2? snap = checkSnap(hitObject.Position); if (!snapPositions.Any())
if (snap == null && hitObject.Position != hitObject.EndPosition) continue;
snap = checkSnap(hitObject.EndPosition);
if (snap != null) var closestSnapPosition = snapPositions.MinBy(p => Vector2.Distance(p, screenSpacePosition));
if (Vector2.Distance(closestSnapPosition, screenSpacePosition) < snapRadius)
{ {
// only return distance portion, since time is not really valid // only return distance portion, since time is not really valid
snapResult = new SnapResult(snap.Value, null, playfield); snapResult = new SnapResult(closestSnapPosition, null, playfield);
return true; return true;
} }
Vector2? checkSnap(Vector2 checkPos)
{
Vector2 checkScreenPos = playfield.GamefieldToScreenSpace(checkPos);
if (Vector2.Distance(checkScreenPos, screenSpacePosition) < snapRadius)
return checkScreenPos;
return null;
}
} }
snapResult = null; snapResult = null;

View File

@ -2,8 +2,11 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Testing; using osu.Game.Rulesets.Taiko.Configuration;
using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -14,36 +17,48 @@ namespace osu.Game.Rulesets.Taiko.Tests
{ {
private DrumTouchInputArea drumTouchInputArea = null!; private DrumTouchInputArea drumTouchInputArea = null!;
[SetUpSteps] private readonly Bindable<TaikoTouchControlScheme> controlScheme = new Bindable<TaikoTouchControlScheme>();
public void SetUpSteps()
[BackgroundDependencyLoader]
private void load()
{ {
AddStep("create drum", () => var config = (TaikoRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
config.BindWith(TaikoRulesetSetting.TouchControlScheme, controlScheme);
}
private void createDrum()
{
Child = new TaikoInputManager(new TaikoRuleset().RulesetInfo)
{ {
Child = new TaikoInputManager(new TaikoRuleset().RulesetInfo) RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, new InputDrum
Children = new Drawable[]
{ {
new InputDrum Anchor = Anchor.TopCentre,
{ Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre, Height = 0.2f,
Origin = Anchor.TopCentre,
Height = 0.2f,
},
drumTouchInputArea = new DrumTouchInputArea
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
},
}, },
}; drumTouchInputArea = new DrumTouchInputArea
}); {
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
}
}
};
} }
[Test] [Test]
public void TestDrum() public void TestDrum()
{ {
AddStep("create drum", createDrum);
AddStep("show drum", () => drumTouchInputArea.Show()); AddStep("show drum", () => drumTouchInputArea.Show());
AddStep("change scheme (kddk)", () => controlScheme.Value = TaikoTouchControlScheme.KDDK);
AddStep("change scheme (kkdd)", () => controlScheme.Value = TaikoTouchControlScheme.KKDD);
AddStep("change scheme (ddkk)", () => controlScheme.Value = TaikoTouchControlScheme.DDKK);
} }
protected override Ruleset CreateRuleset() => new TaikoRuleset();
} }
} }

View File

@ -0,0 +1,28 @@
// 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.Configuration;
using osu.Game.Rulesets.Configuration;
namespace osu.Game.Rulesets.Taiko.Configuration
{
public class TaikoRulesetConfigManager : RulesetConfigManager<TaikoRulesetSetting>
{
public TaikoRulesetConfigManager(SettingsStore? settings, RulesetInfo ruleset, int? variant = null)
: base(settings, ruleset, variant)
{
}
protected override void InitialiseDefaults()
{
base.InitialiseDefaults();
SetDefault(TaikoRulesetSetting.TouchControlScheme, TaikoTouchControlScheme.KDDK);
}
}
public enum TaikoRulesetSetting
{
TouchControlScheme
}
}

View File

@ -0,0 +1,12 @@
// 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.
namespace osu.Game.Rulesets.Taiko.Configuration
{
public enum TaikoTouchControlScheme
{
KDDK,
DDKK,
KKDD
}
}

View File

@ -28,9 +28,13 @@ using osu.Game.Rulesets.Taiko.Skinning.Argon;
using osu.Game.Rulesets.Taiko.Skinning.Legacy; using osu.Game.Rulesets.Taiko.Skinning.Legacy;
using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Overlays.Settings;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Statistics; using osu.Game.Screens.Ranking.Statistics;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Rulesets.Configuration;
using osu.Game.Configuration;
using osu.Game.Rulesets.Taiko.Configuration;
namespace osu.Game.Rulesets.Taiko namespace osu.Game.Rulesets.Taiko
{ {
@ -194,6 +198,10 @@ namespace osu.Game.Rulesets.Taiko
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new TaikoRulesetConfigManager(settings, RulesetInfo);
public override RulesetSettingsSubsection CreateSettings() => new TaikoSettingsSubsection(this);
protected override IEnumerable<HitResult> GetValidHitResults() protected override IEnumerable<HitResult> GetValidHitResults()
{ {
return new[] return new[]

View File

@ -0,0 +1,36 @@
// 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.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Taiko.Configuration;
namespace osu.Game.Rulesets.Taiko
{
public partial class TaikoSettingsSubsection : RulesetSettingsSubsection
{
protected override LocalisableString Header => "osu!taiko";
public TaikoSettingsSubsection(TaikoRuleset ruleset)
: base(ruleset)
{
}
[BackgroundDependencyLoader]
private void load()
{
var config = (TaikoRulesetConfigManager)Config;
Children = new Drawable[]
{
new SettingsEnumDropdown<TaikoTouchControlScheme>
{
LabelText = "Touch control scheme",
Current = config.GetBindable<TaikoTouchControlScheme>(TaikoRulesetSetting.TouchControlScheme)
}
};
}
}
}

View File

@ -1,9 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -11,6 +13,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Configuration;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -31,15 +34,18 @@ namespace osu.Game.Rulesets.Taiko.UI
private Container mainContent = null!; private Container mainContent = null!;
private QuarterCircle leftCentre = null!; private DrumSegment leftCentre = null!;
private QuarterCircle rightCentre = null!; private DrumSegment rightCentre = null!;
private QuarterCircle leftRim = null!; private DrumSegment leftRim = null!;
private QuarterCircle rightRim = null!; private DrumSegment rightRim = null!;
private readonly Bindable<TaikoTouchControlScheme> configTouchControlScheme = new Bindable<TaikoTouchControlScheme>();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(TaikoInputManager taikoInputManager, OsuColour colours) private void load(TaikoInputManager taikoInputManager, TaikoRulesetConfigManager config)
{ {
Debug.Assert(taikoInputManager.KeyBindingContainer != null); Debug.Assert(taikoInputManager.KeyBindingContainer != null);
keyBindingContainer = taikoInputManager.KeyBindingContainer; keyBindingContainer = taikoInputManager.KeyBindingContainer;
// Container should handle input everywhere. // Container should handle input everywhere.
@ -65,27 +71,27 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
leftRim = new QuarterCircle(TaikoAction.LeftRim, colours.Blue) leftRim = new DrumSegment
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
X = -2, X = -2,
}, },
rightRim = new QuarterCircle(TaikoAction.RightRim, colours.Blue) rightRim = new DrumSegment
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
X = 2, X = 2,
Rotation = 90, Rotation = 90,
}, },
leftCentre = new QuarterCircle(TaikoAction.LeftCentre, colours.Pink) leftCentre = new DrumSegment
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
X = -2, X = -2,
Scale = new Vector2(centre_region), Scale = new Vector2(centre_region),
}, },
rightCentre = new QuarterCircle(TaikoAction.RightCentre, colours.Pink) rightCentre = new DrumSegment
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
@ -98,6 +104,17 @@ namespace osu.Game.Rulesets.Taiko.UI
} }
}, },
}; };
config.BindWith(TaikoRulesetSetting.TouchControlScheme, configTouchControlScheme);
configTouchControlScheme.BindValueChanged(scheme =>
{
var actions = getOrderedActionsForScheme(scheme.NewValue);
leftRim.Action = actions[0];
leftCentre.Action = actions[1];
rightCentre.Action = actions[2];
rightRim.Action = actions[3];
}, true);
} }
protected override bool OnKeyDown(KeyDownEvent e) protected override bool OnKeyDown(KeyDownEvent e)
@ -119,11 +136,47 @@ namespace osu.Game.Rulesets.Taiko.UI
base.OnTouchUp(e); base.OnTouchUp(e);
} }
private static TaikoAction[] getOrderedActionsForScheme(TaikoTouchControlScheme scheme)
{
switch (scheme)
{
case TaikoTouchControlScheme.KDDK:
return new[]
{
TaikoAction.LeftRim,
TaikoAction.LeftCentre,
TaikoAction.RightCentre,
TaikoAction.RightRim
};
case TaikoTouchControlScheme.DDKK:
return new[]
{
TaikoAction.LeftCentre,
TaikoAction.RightCentre,
TaikoAction.LeftRim,
TaikoAction.RightRim
};
case TaikoTouchControlScheme.KKDD:
return new[]
{
TaikoAction.LeftRim,
TaikoAction.RightRim,
TaikoAction.LeftCentre,
TaikoAction.RightCentre
};
default:
throw new ArgumentOutOfRangeException(nameof(scheme), scheme, null);
}
}
private void handleDown(object source, Vector2 position) private void handleDown(object source, Vector2 position)
{ {
Show(); Show();
TaikoAction taikoAction = getTaikoActionFromInput(position); TaikoAction taikoAction = getTaikoActionFromPosition(position);
// Not too sure how this can happen, but let's avoid throwing. // Not too sure how this can happen, but let's avoid throwing.
if (trackedActions.ContainsKey(source)) if (trackedActions.ContainsKey(source))
@ -139,18 +192,15 @@ namespace osu.Game.Rulesets.Taiko.UI
trackedActions.Remove(source); trackedActions.Remove(source);
} }
private bool validMouse(MouseButtonEvent e) => private TaikoAction getTaikoActionFromPosition(Vector2 inputPosition)
leftRim.Contains(e.ScreenSpaceMouseDownPosition) || rightRim.Contains(e.ScreenSpaceMouseDownPosition);
private TaikoAction getTaikoActionFromInput(Vector2 inputPosition)
{ {
bool centreHit = leftCentre.Contains(inputPosition) || rightCentre.Contains(inputPosition); bool centreHit = leftCentre.Contains(inputPosition) || rightCentre.Contains(inputPosition);
bool leftSide = ToLocalSpace(inputPosition).X < DrawWidth / 2; bool leftSide = ToLocalSpace(inputPosition).X < DrawWidth / 2;
if (leftSide) if (leftSide)
return centreHit ? TaikoAction.LeftCentre : TaikoAction.LeftRim; return centreHit ? leftCentre.Action : leftRim.Action;
return centreHit ? TaikoAction.RightCentre : TaikoAction.RightRim; return centreHit ? rightCentre.Action : rightRim.Action;
} }
protected override void PopIn() protected override void PopIn()
@ -163,23 +213,42 @@ namespace osu.Game.Rulesets.Taiko.UI
mainContent.FadeOut(300); mainContent.FadeOut(300);
} }
private partial class QuarterCircle : CompositeDrawable, IKeyBindingHandler<TaikoAction> private partial class DrumSegment : CompositeDrawable, IKeyBindingHandler<TaikoAction>
{ {
private readonly Circle overlay; private TaikoAction action;
private readonly TaikoAction handledAction; public TaikoAction Action
{
get => action;
set
{
if (action == value)
return;
private readonly Circle circle; action = value;
updateColoursFromAction();
}
}
private Circle overlay = null!;
private Circle circle = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
public override bool Contains(Vector2 screenSpacePos) => circle.Contains(screenSpacePos); public override bool Contains(Vector2 screenSpacePos) => circle.Contains(screenSpacePos);
public QuarterCircle(TaikoAction handledAction, Color4 colour) public DrumSegment()
{ {
this.handledAction = handledAction;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit; FillMode = FillMode.Fit;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new Container new Container
@ -191,7 +260,6 @@ namespace osu.Game.Rulesets.Taiko.UI
circle = new Circle circle = new Circle
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colour.Multiply(1.4f).Darken(2.8f),
Alpha = 0.8f, Alpha = 0.8f,
Scale = new Vector2(2), Scale = new Vector2(2),
}, },
@ -200,7 +268,6 @@ namespace osu.Game.Rulesets.Taiko.UI
Alpha = 0, Alpha = 0,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Blending = BlendingParameters.Additive, Blending = BlendingParameters.Additive,
Colour = colour,
Scale = new Vector2(2), Scale = new Vector2(2),
} }
} }
@ -208,18 +275,52 @@ namespace osu.Game.Rulesets.Taiko.UI
}; };
} }
protected override void LoadComplete()
{
base.LoadComplete();
updateColoursFromAction();
}
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e) public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{ {
if (e.Action == handledAction) if (e.Action == Action)
overlay.FadeTo(1f, 80, Easing.OutQuint); overlay.FadeTo(1f, 80, Easing.OutQuint);
return false; return false;
} }
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e) public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{ {
if (e.Action == handledAction) if (e.Action == Action)
overlay.FadeOut(1000, Easing.OutQuint); overlay.FadeOut(1000, Easing.OutQuint);
} }
private void updateColoursFromAction()
{
if (!IsLoaded)
return;
var colour = getColourFromTaikoAction(Action);
circle.Colour = colour.Multiply(1.4f).Darken(2.8f);
overlay.Colour = colour;
}
private Color4 getColourFromTaikoAction(TaikoAction handledAction)
{
switch (handledAction)
{
case TaikoAction.LeftRim:
case TaikoAction.RightRim:
return colours.Blue;
case TaikoAction.LeftCentre:
case TaikoAction.RightCentre:
return colours.Pink;
}
throw new ArgumentOutOfRangeException();
}
} }
} }
} }

View File

@ -36,21 +36,24 @@ namespace osu.Game.Overlays.Profile
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Masking = true;
CornerRadius = 10;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0, 1),
Radius = 3,
Colour = Colour4.Black.Opacity(0.25f)
};
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
background = new Box new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 10,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0, 1),
Radius = 3,
Colour = Colour4.Black.Opacity(0.25f)
},
Child = background = new Box
{
RelativeSizeAxes = Axes.Both,
},
}, },
new FillFlowContainer new FillFlowContainer
{ {

View File

@ -40,7 +40,7 @@ namespace osu.Game.Overlays.SkinEditor
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Spacing = new Vector2(2) Spacing = new Vector2(EditorSidebar.PADDING)
}; };
reloadComponents(); reloadComponents();

View File

@ -18,6 +18,8 @@ namespace osu.Game.Screens.Edit.Components
{ {
public const float WIDTH = 250; public const float WIDTH = 250;
public const float PADDING = 3;
private readonly Box background; private readonly Box background;
protected override Container<EditorSidebarSection> Content { get; } protected override Container<EditorSidebarSection> Content { get; }
@ -35,13 +37,13 @@ namespace osu.Game.Screens.Edit.Components
}, },
new OsuScrollContainer new OsuScrollContainer
{ {
Padding = new MarginPadding { Left = 20 },
ScrollbarOverlapsContent = false, ScrollbarOverlapsContent = false,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = Content = new FillFlowContainer<EditorSidebarSection> Child = Content = new FillFlowContainer<EditorSidebarSection>
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(PADDING),
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
}, },
} }

View File

@ -91,6 +91,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
blueprint.DrawableObject = drawableObject; blueprint.DrawableObject = drawableObject;
} }
private bool nudgeMovementActive;
protected override bool OnKeyDown(KeyDownEvent e) protected override bool OnKeyDown(KeyDownEvent e)
{ {
if (e.ControlPressed) if (e.ControlPressed)
@ -98,19 +100,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
switch (e.Key) switch (e.Key)
{ {
case Key.Left: case Key.Left:
moveSelection(new Vector2(-1, 0)); nudgeSelection(new Vector2(-1, 0));
return true; return true;
case Key.Right: case Key.Right:
moveSelection(new Vector2(1, 0)); nudgeSelection(new Vector2(1, 0));
return true; return true;
case Key.Up: case Key.Up:
moveSelection(new Vector2(0, -1)); nudgeSelection(new Vector2(0, -1));
return true; return true;
case Key.Down: case Key.Down:
moveSelection(new Vector2(0, 1)); nudgeSelection(new Vector2(0, 1));
return true; return true;
} }
} }
@ -118,12 +120,29 @@ namespace osu.Game.Screens.Edit.Compose.Components
return false; return false;
} }
protected override void OnKeyUp(KeyUpEvent e)
{
base.OnKeyUp(e);
if (nudgeMovementActive && !e.ControlPressed)
{
Beatmap.EndChange();
nudgeMovementActive = false;
}
}
/// <summary> /// <summary>
/// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints). /// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints).
/// </summary> /// </summary>
/// <param name="delta"></param> /// <param name="delta"></param>
private void moveSelection(Vector2 delta) private void nudgeSelection(Vector2 delta)
{ {
if (!nudgeMovementActive)
{
nudgeMovementActive = true;
Beatmap.BeginChange();
}
var firstBlueprint = SelectionHandler.SelectedBlueprints.FirstOrDefault(); var firstBlueprint = SelectionHandler.SelectedBlueprints.FirstOrDefault();
if (firstBlueprint == null) if (firstBlueprint == null)

View File

@ -47,8 +47,9 @@ namespace osu.Game.Screens.Select.Carousel
private Sprite background = null!; private Sprite background = null!;
private Action<BeatmapInfo>? startRequested; private MenuItem[]? mainMenuItems;
private Action<BeatmapInfo>? editRequested;
private Action<BeatmapInfo>? selectRequested;
private Action<BeatmapInfo>? hideRequested; private Action<BeatmapInfo>? hideRequested;
private Triangles triangles = null!; private Triangles triangles = null!;
@ -84,9 +85,8 @@ namespace osu.Game.Screens.Select.Carousel
if (songSelect != null) if (songSelect != null)
{ {
startRequested = b => songSelect.FinaliseSelection(b); mainMenuItems = songSelect.CreateForwardNavigationMenuItemsForBeatmap(beatmapInfo);
if (songSelect.AllowEditing) selectRequested = b => songSelect.FinaliseSelection(b);
editRequested = songSelect.Edit;
} }
if (manager != null) if (manager != null)
@ -193,7 +193,7 @@ namespace osu.Game.Screens.Select.Carousel
protected override bool OnClick(ClickEvent e) protected override bool OnClick(ClickEvent e)
{ {
if (Item?.State.Value == CarouselItemState.Selected) if (Item?.State.Value == CarouselItemState.Selected)
startRequested?.Invoke(beatmapInfo); selectRequested?.Invoke(beatmapInfo);
return base.OnClick(e); return base.OnClick(e);
} }
@ -227,11 +227,8 @@ namespace osu.Game.Screens.Select.Carousel
{ {
List<MenuItem> items = new List<MenuItem>(); List<MenuItem> items = new List<MenuItem>();
if (startRequested != null) if (mainMenuItems != null)
items.Add(new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested(beatmapInfo))); items.AddRange(mainMenuItems);
if (editRequested != null)
items.Add(new OsuMenuItem(CommonStrings.ButtonsEdit, MenuItemType.Standard, () => editRequested(beatmapInfo)));
if (beatmapInfo.OnlineID > 0 && beatmapOverlay != null) if (beatmapInfo.OnlineID > 0 && beatmapOverlay != null)
items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineID))); items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineID)));

View File

@ -4,10 +4,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
@ -30,6 +34,12 @@ namespace osu.Game.Screens.Select
public override bool AllowExternalScreenChange => true; public override bool AllowExternalScreenChange => true;
public override MenuItem[] CreateForwardNavigationMenuItemsForBeatmap(BeatmapInfo beatmap) => new MenuItem[]
{
new OsuMenuItem(ButtonSystemStrings.Play.ToSentence(), MenuItemType.Highlighted, () => FinaliseSelection(beatmap)),
new OsuMenuItem(ButtonSystemStrings.Edit.ToSentence(), MenuItemType.Standard, () => Edit(beatmap))
};
protected override UserActivity InitialActivity => new UserActivity.ChoosingBeatmap(); protected override UserActivity InitialActivity => new UserActivity.ChoosingBeatmap();
private PlayBeatmapDetailArea playBeatmapDetailArea = null!; private PlayBeatmapDetailArea playBeatmapDetailArea = null!;
@ -37,7 +47,7 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
BeatmapOptions.AddButton(@"Edit", @"beatmap", FontAwesome.Solid.PencilAlt, colours.Yellow, () => Edit()); BeatmapOptions.AddButton(ButtonSystemStrings.Edit.ToSentence(), @"beatmap", FontAwesome.Solid.PencilAlt, colours.Yellow, () => Edit());
} }
protected void PresentScore(ScoreInfo score) => protected void PresentScore(ScoreInfo score) =>

View File

@ -1,19 +1,31 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Collections;
using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods;
@ -21,23 +33,12 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Options; using osu.Game.Screens.Select.Options;
using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osuTK.Input; using osuTK.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Game.Collections;
using osu.Game.Graphics.UserInterface;
using System.Diagnostics;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Game.Configuration;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
@ -83,6 +84,17 @@ namespace osu.Game.Screens.Select
public bool BeatmapSetsLoaded => IsLoaded && Carousel.BeatmapSetsLoaded; public bool BeatmapSetsLoaded => IsLoaded && Carousel.BeatmapSetsLoaded;
/// <summary>
/// Creates any "action" menu items for the provided beatmap (ie. "Select", "Play", "Edit").
/// These will always be placed at the top of the context menu, with common items added below them.
/// </summary>
/// <param name="beatmap">The beatmap to create items for.</param>
/// <returns>The menu items.</returns>
public virtual MenuItem[] CreateForwardNavigationMenuItemsForBeatmap(BeatmapInfo beatmap) => new MenuItem[]
{
new OsuMenuItem(@"Select", MenuItemType.Highlighted, () => FinaliseSelection(beatmap))
};
[Resolved] [Resolved]
private Bindable<IReadOnlyList<Mod>> selectedMods { get; set; } = null!; private Bindable<IReadOnlyList<Mod>> selectedMods { get; set; } = null!;