1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 17:43:05 +08:00

Merge branch 'master' into slider-combo-matching-2

This commit is contained in:
Dean Herbert 2023-11-09 18:45:37 +09:00
commit 92043f45a4
No known key found for this signature in database
42 changed files with 694 additions and 200 deletions

View File

@ -70,7 +70,7 @@ namespace osu.Android
}, },
new SettingsCheckbox new SettingsCheckbox
{ {
LabelText = MouseSettingsStrings.DisableMouseButtons, LabelText = MouseSettingsStrings.DisableClicksDuringGameplay,
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons), Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
}, },
}); });

View File

@ -94,16 +94,16 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("load content", loadContent); AddStep("load content", loadContent);
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize) * userScale); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.CursorScale.Value == OsuCursor.GetScaleForCircleSize(circleSize) * userScale);
AddStep("set user scale to 1", () => config.SetValue(OsuSetting.GameplayCursorSize, 1f)); AddStep("set user scale to 1", () => config.SetValue(OsuSetting.GameplayCursorSize, 1f));
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize)); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.CursorScale.Value == OsuCursor.GetScaleForCircleSize(circleSize));
AddStep("turn off autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, false)); AddStep("turn off autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, false));
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == 1); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.CursorScale.Value == 1);
AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale)); AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale));
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == userScale); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.CursorScale.Value == userScale);
} }
[Test] [Test]

View File

@ -133,8 +133,11 @@ namespace osu.Game.Rulesets.Osu.Tests
} }
[Test] [Test]
public void TestSimpleInput() public void TestSimpleInput([Values] bool disableMouseButtons)
{ {
// OsuSetting.MouseDisableButtons should not affect touch taps
AddStep($"{(disableMouseButtons ? "disable" : "enable")} mouse buttons", () => config.SetValue(OsuSetting.MouseDisableButtons, disableMouseButtons));
beginTouch(TouchSource.Touch1); beginTouch(TouchSource.Touch1);
assertKeyCounter(1, 0); assertKeyCounter(1, 0);
@ -468,7 +471,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[Test] [Test]
public void TestInputWhileMouseButtonsDisabled() public void TestInputWhileMouseButtonsDisabled()
{ {
AddStep("Disable mouse buttons", () => config.SetValue(OsuSetting.MouseDisableButtons, true)); AddStep("Disable gameplay taps", () => config.SetValue(OsuSetting.TouchDisableGameplayTaps, true));
beginTouch(TouchSource.Touch1); beginTouch(TouchSource.Touch1);
@ -620,6 +623,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("Release all touches", () => AddStep("Release all touches", () =>
{ {
config.SetValue(OsuSetting.MouseDisableButtons, false); config.SetValue(OsuSetting.MouseDisableButtons, false);
config.SetValue(OsuSetting.TouchDisableGameplayTaps, false);
foreach (TouchSource source in InputManager.CurrentState.Touch.ActiveSources) foreach (TouchSource source in InputManager.CurrentState.Touch.ActiveSources)
InputManager.EndTouch(new Touch(source, osuInputManager.ScreenSpaceDrawQuad.Centre)); InputManager.EndTouch(new Touch(source, osuInputManager.ScreenSpaceDrawQuad.Centre));
}); });

View File

@ -1,38 +1,69 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Tests.Gameplay;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public partial class TestSceneResumeOverlay : OsuManualInputManagerTestScene public partial class TestSceneResumeOverlay : OsuManualInputManagerTestScene
{ {
private ManualOsuInputManager osuInputManager = null!;
private CursorContainer cursor = null!;
private ResumeOverlay resume = null!;
private bool resumeFired;
private OsuConfigManager localConfig = null!;
[Cached]
private GameplayState gameplayState;
public TestSceneResumeOverlay() public TestSceneResumeOverlay()
{ {
ManualOsuInputManager osuInputManager; gameplayState = TestGameplayState.Create(new OsuRuleset());
CursorContainer cursor; }
ResumeOverlay resume;
bool resumeFired = false; [BackgroundDependencyLoader]
private void load()
{
Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage));
}
Child = osuInputManager = new ManualOsuInputManager(new OsuRuleset().RulesetInfo) protected override void LoadComplete()
{
base.LoadComplete();
AddSliderStep("cursor size", 0.1f, 2f, 1f, v => localConfig.SetValue(OsuSetting.GameplayCursorSize, v));
AddSliderStep("circle size", 0f, 10f, 0f, val =>
{ {
Children = new Drawable[] gameplayState.Beatmap.Difficulty.CircleSize = val;
{ SetUp();
cursor = new CursorContainer(), });
resume = new OsuResumeOverlay
{
GameplayCursor = cursor
},
}
};
resume.ResumeAction = () => resumeFired = true; AddToggleStep("auto size", v => localConfig.SetValue(OsuSetting.AutoCursorSize, v));
}
[SetUp]
public void SetUp() => Schedule(loadContent);
[TestCase(1)]
[TestCase(0.5f)]
[TestCase(2)]
public void TestResume(float cursorSize)
{
AddStep($"set cursor size to {cursorSize}", () => localConfig.SetValue(OsuSetting.GameplayCursorSize, cursorSize));
AddStep("move mouse to center", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre)); AddStep("move mouse to center", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
AddStep("show", () => resume.Show()); AddStep("show", () => resume.Show());
@ -41,11 +72,39 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("click", () => osuInputManager.GameClick()); AddStep("click", () => osuInputManager.GameClick());
AddAssert("not dismissed", () => !resumeFired && resume.State.Value == Visibility.Visible); AddAssert("not dismissed", () => !resumeFired && resume.State.Value == Visibility.Visible);
AddStep("move mouse back", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre)); AddStep("move mouse just out of range", () =>
{
var resumeOverlay = this.ChildrenOfType<OsuResumeOverlay>().Single();
var resumeOverlayCursor = resumeOverlay.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single();
Vector2 offset = resumeOverlay.ToScreenSpace(new Vector2(OsuCursor.SIZE / 2)) - resumeOverlay.ToScreenSpace(Vector2.Zero);
InputManager.MoveMouseTo(resumeOverlayCursor.ScreenSpaceDrawQuad.Centre - offset - new Vector2(1));
});
AddStep("click", () => osuInputManager.GameClick());
AddAssert("not dismissed", () => !resumeFired && resume.State.Value == Visibility.Visible);
AddStep("move mouse just within range", () =>
{
var resumeOverlay = this.ChildrenOfType<OsuResumeOverlay>().Single();
var resumeOverlayCursor = resumeOverlay.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single();
Vector2 offset = resumeOverlay.ToScreenSpace(new Vector2(OsuCursor.SIZE / 2)) - resumeOverlay.ToScreenSpace(Vector2.Zero);
InputManager.MoveMouseTo(resumeOverlayCursor.ScreenSpaceDrawQuad.Centre - offset + new Vector2(1));
});
AddStep("click", () => osuInputManager.GameClick()); AddStep("click", () => osuInputManager.GameClick());
AddAssert("dismissed", () => resumeFired && resume.State.Value == Visibility.Hidden); AddAssert("dismissed", () => resumeFired && resume.State.Value == Visibility.Hidden);
} }
private void loadContent()
{
Child = osuInputManager = new ManualOsuInputManager(new OsuRuleset().RulesetInfo) { Children = new Drawable[] { cursor = new CursorContainer(), resume = new OsuResumeOverlay { GameplayCursor = cursor }, } };
resumeFired = false;
resume.ResumeAction = () => resumeFired = true;
}
private partial class ManualOsuInputManager : OsuInputManager private partial class ManualOsuInputManager : OsuInputManager
{ {
public ManualOsuInputManager(RulesetInfo ruleset) public ManualOsuInputManager(RulesetInfo ruleset)

View File

@ -4,12 +4,16 @@
#nullable disable #nullable disable
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;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Screens.Play;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -18,30 +22,42 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{ {
public partial class OsuCursor : SkinReloadableDrawable public partial class OsuCursor : SkinReloadableDrawable
{ {
private const float size = 28; public const float SIZE = 28;
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
private bool cursorExpand; private bool cursorExpand;
private SkinnableDrawable cursorSprite; private SkinnableDrawable cursorSprite;
private Container cursorScaleContainer = null!;
private Drawable expandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite; private Drawable expandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite;
public IBindable<float> CursorScale => cursorScale;
private readonly Bindable<float> cursorScale = new BindableFloat(1);
private Bindable<float> userCursorScale = null!;
private Bindable<bool> autoCursorScale = null!;
[Resolved(canBeNull: true)]
private GameplayState state { get; set; }
[Resolved]
private OsuConfigManager config { get; set; }
public OsuCursor() public OsuCursor()
{ {
Origin = Anchor.Centre; Origin = Anchor.Centre;
Size = new Vector2(size); Size = new Vector2(SIZE);
}
protected override void SkinChanged(ISkinSource skin)
{
cursorExpand = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorExpand)?.Value ?? true;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
InternalChild = new Container InternalChild = cursorScaleContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -52,10 +68,39 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
} }
}; };
userCursorScale = config.GetBindable<float>(OsuSetting.GameplayCursorSize);
userCursorScale.ValueChanged += _ => calculateCursorScale();
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
autoCursorScale.ValueChanged += _ => calculateCursorScale();
cursorScale.BindValueChanged(e => cursorScaleContainer.Scale = new Vector2(e.NewValue), true);
} }
private const float pressed_scale = 1.2f; protected override void LoadComplete()
private const float released_scale = 1f; {
base.LoadComplete();
calculateCursorScale();
}
private void calculateCursorScale()
{
float scale = userCursorScale.Value;
if (autoCursorScale.Value && state != null)
{
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
scale *= GetScaleForCircleSize(state.Beatmap.Difficulty.CircleSize);
}
cursorScale.Value = scale;
}
protected override void SkinChanged(ISkinSource skin)
{
cursorExpand = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorExpand)?.Value ?? true;
}
public void Expand() public void Expand()
{ {
@ -66,6 +111,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
public void Contract() => expandTarget.ScaleTo(released_scale, 400, Easing.OutQuad); public void Contract() => expandTarget.ScaleTo(released_scale, 400, Easing.OutQuad);
/// <summary>
/// Get the scale applicable to the ActiveCursor based on a beatmap's circle size.
/// </summary>
public static float GetScaleForCircleSize(float circleSize) =>
1f - 0.7f * (1f + circleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY;
private partial class DefaultCursor : OsuCursorSprite private partial class DefaultCursor : OsuCursorSprite
{ {
public DefaultCursor() public DefaultCursor()
@ -83,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true, Masking = true,
BorderThickness = size / 6, BorderThickness = SIZE / 6,
BorderColour = Color4.White, BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
@ -105,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true, Masking = true,
BorderThickness = size / 3, BorderThickness = SIZE / 3,
BorderColour = Color4.White.Opacity(0.5f), BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[] Children = new Drawable[]
{ {

View File

@ -11,11 +11,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
@ -23,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{ {
public partial class OsuCursorContainer : GameplayCursorContainer, IKeyBindingHandler<OsuAction> public partial class OsuCursorContainer : GameplayCursorContainer, IKeyBindingHandler<OsuAction>
{ {
public new OsuCursor ActiveCursor => (OsuCursor)base.ActiveCursor;
protected override Drawable CreateCursor() => new OsuCursor(); protected override Drawable CreateCursor() => new OsuCursor();
protected override Container<Drawable> Content => fadeContainer; protected override Container<Drawable> Content => fadeContainer;
@ -33,13 +32,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly Drawable cursorTrail; private readonly Drawable cursorTrail;
public IBindable<float> CursorScale => cursorScale;
private readonly Bindable<float> cursorScale = new BindableFloat(1);
private Bindable<float> userCursorScale;
private Bindable<bool> autoCursorScale;
private readonly CursorRippleVisualiser rippleVisualiser; private readonly CursorRippleVisualiser rippleVisualiser;
public OsuCursorContainer() public OsuCursorContainer()
@ -56,12 +48,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}; };
} }
[Resolved(canBeNull: true)]
private GameplayState state { get; set; }
[Resolved]
private OsuConfigManager config { get; set; }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(OsuRulesetConfigManager rulesetConfig) private void load(OsuRulesetConfigManager rulesetConfig)
{ {
@ -74,46 +60,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
showTrail.BindValueChanged(v => cursorTrail.FadeTo(v.NewValue ? 1 : 0, 200), true); showTrail.BindValueChanged(v => cursorTrail.FadeTo(v.NewValue ? 1 : 0, 200), true);
userCursorScale = config.GetBindable<float>(OsuSetting.GameplayCursorSize); ActiveCursor.CursorScale.BindValueChanged(e =>
userCursorScale.ValueChanged += _ => calculateScale();
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
autoCursorScale.ValueChanged += _ => calculateScale();
CursorScale.BindValueChanged(e =>
{ {
var newScale = new Vector2(e.NewValue); var newScale = new Vector2(e.NewValue);
ActiveCursor.Scale = newScale;
rippleVisualiser.CursorScale = newScale; rippleVisualiser.CursorScale = newScale;
cursorTrail.Scale = newScale; cursorTrail.Scale = newScale;
}, true); }, true);
calculateScale();
}
/// <summary>
/// Get the scale applicable to the ActiveCursor based on a beatmap's circle size.
/// </summary>
public static float GetScaleForCircleSize(float circleSize) =>
1f - 0.7f * (1f + circleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY;
private void calculateScale()
{
float scale = userCursorScale.Value;
if (autoCursorScale.Value && state != null)
{
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
scale *= GetScaleForCircleSize(state.Beatmap.Difficulty.CircleSize);
}
cursorScale.Value = scale;
var newScale = new Vector2(scale);
ActiveCursor.ScaleTo(newScale, 400, Easing.OutQuint);
cursorTrail.Scale = newScale;
} }
private int downCount; private int downCount;
@ -121,9 +74,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private void updateExpandedState() private void updateExpandedState()
{ {
if (downCount > 0) if (downCount > 0)
(ActiveCursor as OsuCursor)?.Expand(); ActiveCursor.Expand();
else else
(ActiveCursor as OsuCursor)?.Contract(); ActiveCursor.Contract();
} }
public bool OnPressed(KeyBindingPressEvent<OsuAction> e) public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
@ -160,13 +113,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void PopIn() protected override void PopIn()
{ {
fadeContainer.FadeTo(1, 300, Easing.OutQuint); fadeContainer.FadeTo(1, 300, Easing.OutQuint);
ActiveCursor.ScaleTo(CursorScale.Value, 400, Easing.OutQuint); ActiveCursor.ScaleTo(1f, 400, Easing.OutQuint);
} }
protected override void PopOut() protected override void PopOut()
{ {
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(CursorScale.Value * 0.8f, 450, Easing.OutQuint); ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
} }
private partial class DefaultCursorTrail : CursorTrail private partial class DefaultCursorTrail : CursorTrail

View File

@ -5,7 +5,6 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
@ -14,7 +13,6 @@ using osu.Framework.Input.Events;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.UI namespace osu.Game.Rulesets.Osu.UI
@ -25,7 +23,6 @@ namespace osu.Game.Rulesets.Osu.UI
private OsuClickToResumeCursor clickToResumeCursor; private OsuClickToResumeCursor clickToResumeCursor;
private OsuCursorContainer localCursorContainer; private OsuCursorContainer localCursorContainer;
private IBindable<float> localCursorScale;
public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null; public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null;
@ -49,13 +46,7 @@ namespace osu.Game.Rulesets.Osu.UI
clickToResumeCursor.Appear(); clickToResumeCursor.Appear();
if (localCursorContainer == null) if (localCursorContainer == null)
{
Add(localCursorContainer = new OsuCursorContainer()); Add(localCursorContainer = new OsuCursorContainer());
localCursorScale = new BindableFloat();
localCursorScale.BindTo(localCursorContainer.CursorScale);
localCursorScale.BindValueChanged(scale => cursorScaleContainer.Scale = new Vector2(scale.NewValue), true);
}
} }
protected override void PopOut() protected override void PopOut()
@ -98,7 +89,8 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:
if (!IsHovered) return false; if (!IsHovered)
return false;
this.ScaleTo(2, TRANSITION_TIME, Easing.OutQuint); this.ScaleTo(2, TRANSITION_TIME, Easing.OutQuint);

View File

@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI
private readonly OsuInputManager osuInputManager; private readonly OsuInputManager osuInputManager;
private Bindable<bool> mouseDisabled = null!; private Bindable<bool> tapsDisabled = null!;
public OsuTouchInputMapper(OsuInputManager inputManager) public OsuTouchInputMapper(OsuInputManager inputManager)
{ {
@ -43,9 +43,7 @@ namespace osu.Game.Rulesets.Osu.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
// The mouse button disable setting affects touch. It's a bit weird. tapsDisabled = config.GetBindable<bool>(OsuSetting.TouchDisableGameplayTaps);
// This is mostly just doing the same as what is done in RulesetInputManager to match behaviour.
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
} }
// Required to handle touches outside of the playfield when screen scaling is enabled. // Required to handle touches outside of the playfield when screen scaling is enabled.
@ -64,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.UI
: OsuAction.LeftButton; : OsuAction.LeftButton;
// Ignore any taps which trigger an action which is already handled. But track them for potential positional input in the future. // Ignore any taps which trigger an action which is already handled. But track them for potential positional input in the future.
bool shouldResultInAction = osuInputManager.AllowGameplayInputs && !mouseDisabled.Value && trackedTouches.All(t => t.Action != action); bool shouldResultInAction = osuInputManager.AllowGameplayInputs && !tapsDisabled.Value && trackedTouches.All(t => t.Action != action);
// If we can actually accept as an action, check whether this tap was on a circle's receptor. // If we can actually accept as an action, check whether this tap was on a circle's receptor.
// This case gets special handling to allow for empty-space stream tapping. // This case gets special handling to allow for empty-space stream tapping.

View File

@ -54,7 +54,9 @@ namespace osu.Game.Tests.Skins
// Covers key counters // Covers key counters
"Archives/modified-argon-pro-20230618.osk", "Archives/modified-argon-pro-20230618.osk",
// Covers "Argon" health display // Covers "Argon" health display
"Archives/modified-argon-pro-20231001.osk" "Archives/modified-argon-pro-20231001.osk",
// Covers player name text component.
"Archives/modified-argon-20231106.osk",
}; };
/// <summary> /// <summary>

View File

@ -31,19 +31,66 @@ namespace osu.Game.Tests.Visual.Gameplay
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Spacing = new Vector2(72.7f), Spacing = new Vector2(20),
Children = new KeyCounterDisplay[] Children = new Drawable[]
{ {
new DefaultKeyCounterDisplay new DefaultKeyCounterDisplay
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
}, },
new DefaultKeyCounterDisplay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Scale = new Vector2(1, -1)
},
new ArgonKeyCounterDisplay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
},
new ArgonKeyCounterDisplay new ArgonKeyCounterDisplay
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
} Scale = new Vector2(1, -1)
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Spacing = new Vector2(20),
Children = new Drawable[]
{
new DefaultKeyCounterDisplay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Rotation = -90,
},
new DefaultKeyCounterDisplay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Rotation = 90,
},
new ArgonKeyCounterDisplay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Rotation = -90,
},
new ArgonKeyCounterDisplay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Rotation = 90,
},
}
},
} }
} }
}; };
@ -77,8 +124,15 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("Disable counting", () => controller.IsCounting.Value = false); AddStep("Disable counting", () => controller.IsCounting.Value = false);
addPressKeyStep(); addPressKeyStep();
AddAssert($"Check {testKey} count has not changed", () => testTrigger.ActivationCount.Value == 2); AddAssert($"Check {testKey} count has not changed", () => testTrigger.ActivationCount.Value == 2);
AddStep("Enable counting", () => controller.IsCounting.Value = true);
addPressKeyStep(100);
addPressKeyStep(1000);
void addPressKeyStep() => AddStep($"Press {testKey} key", () => InputManager.Key(testKey)); void addPressKeyStep(int repeat = 1) => AddStep($"Press {testKey} key {repeat} times", () =>
{
for (int i = 0; i < repeat; i++)
InputManager.Key(testKey);
});
} }
} }
} }

View File

@ -6,11 +6,13 @@ using NUnit.Framework;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Overlays;
using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
@ -232,6 +234,35 @@ namespace osu.Game.Tests.Visual.Navigation
() => Is.EqualTo(beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0).ID)); () => Is.EqualTo(beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0).ID));
} }
[Test]
public void TestCreateNewDifficultyOnNonExistentBeatmap()
{
AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType<DialogOverlay>().SingleOrDefault() != null);
AddStep("open editor", () => Game.ChildrenOfType<ButtonSystem>().Single().OnEdit.Invoke());
AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.IsLoaded);
AddStep("click on file", () =>
{
var item = getEditor().ChildrenOfType<Menu.DrawableMenuItem>().Single(i => i.Item.Text.Value.ToString() == "File");
item.TriggerClick();
});
AddStep("click on create new difficulty", () =>
{
var item = getEditor().ChildrenOfType<Menu.DrawableMenuItem>().Single(i => i.Item.Text.Value.ToString() == "Create new difficulty");
item.TriggerClick();
});
AddStep("click on catch", () =>
{
var item = getEditor().ChildrenOfType<Menu.DrawableMenuItem>().Single(i => i.Item.Text.Value.ToString() == "osu!catch");
item.TriggerClick();
});
AddAssert("save dialog displayed", () => Game.ChildrenOfType<DialogOverlay>().Single().CurrentDialog is SaveRequiredPopupDialog);
AddStep("press save", () => Game.ChildrenOfType<DialogOverlay>().Single().CurrentDialog!.PerformOkAction());
AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.IsLoaded);
AddAssert("editor beatmap uses catch ruleset", () => getEditorBeatmap().BeatmapInfo.Ruleset.ShortName == "fruits");
}
private EditorBeatmap getEditorBeatmap() => getEditor().ChildrenOfType<EditorBeatmap>().Single(); private EditorBeatmap getEditorBeatmap() => getEditor().ChildrenOfType<EditorBeatmap>().Single();
private Editor getEditor() => (Editor)Game.ScreenStack.CurrentScreen; private Editor getEditor() => (Editor)Game.ScreenStack.CurrentScreen;

View File

@ -17,18 +17,16 @@ namespace osu.Game.Tests.Visual.Online
{ {
BarGraph graph; BarGraph graph;
Children = new[] Child = graph = new BarGraph
{ {
graph = new BarGraph RelativeSizeAxes = Axes.Both,
{ Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Size = new Vector2(0.5f),
Origin = Anchor.Centre,
Size = new Vector2(0.5f),
},
}; };
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i)); AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i));
AddStep("small values", () => graph.Values = Enumerable.Range(1, 10).Select(i => i * 0.01f).Concat(new[] { 100f }));
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i)); AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i)); AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
AddStep("empty values", () => graph.Values = Array.Empty<float>()); AddStep("empty values", () => graph.Values = Array.Empty<float>());
@ -36,6 +34,14 @@ namespace osu.Game.Tests.Visual.Online
AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom); AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom);
AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight); AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight);
AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft); AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft);
AddToggleStep("Toggle movement", enabled =>
{
if (enabled)
graph.MoveToY(-10, 1000).Then().MoveToY(10, 1000).Loop();
else
graph.ClearTransforms();
});
} }
} }
} }

View File

@ -108,6 +108,8 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.MouseDisableWheel, false); SetDefault(OsuSetting.MouseDisableWheel, false);
SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay);
SetDefault(OsuSetting.TouchDisableGameplayTaps, false);
// Graphics // Graphics
SetDefault(OsuSetting.ShowFpsDisplay, false); SetDefault(OsuSetting.ShowFpsDisplay, false);
@ -330,6 +332,10 @@ namespace osu.Game.Configuration
ShowHealthDisplayWhenCantFail, ShowHealthDisplayWhenCantFail,
FadePlayfieldWhenHealthLow, FadePlayfieldWhenHealthLow,
/// <summary>
/// Disables mouse buttons clicks during gameplay.
/// </summary>
MouseDisableButtons, MouseDisableButtons,
MouseDisableWheel, MouseDisableWheel,
ConfineMouseMode, ConfineMouseMode,
@ -408,6 +414,7 @@ namespace osu.Game.Configuration
EditorLimitedDistanceSnap, EditorLimitedDistanceSnap,
ReplaySettingsOverlay, ReplaySettingsOverlay,
AutomaticallyDownloadMissingBeatmaps, AutomaticallyDownloadMissingBeatmaps,
EditorShowSpeedChanges EditorShowSpeedChanges,
TouchDisableGameplayTaps,
} }
} }

View File

@ -162,7 +162,7 @@ namespace osu.Game.Graphics
return Pink1; return Pink1;
case ModType.System: case ModType.System:
return Gray7; return Yellow;
default: default:
throw new ArgumentOutOfRangeException(nameof(modType), modType, "Unknown mod type"); throw new ArgumentOutOfRangeException(nameof(modType), modType, "Unknown mod type");

View File

@ -145,6 +145,13 @@ namespace osu.Game.Graphics.UserInterface
float barHeight = drawSize.Y * ((direction == BarDirection.TopToBottom || direction == BarDirection.BottomToTop) ? lengths[i] : barBreadth); float barHeight = drawSize.Y * ((direction == BarDirection.TopToBottom || direction == BarDirection.BottomToTop) ? lengths[i] : barBreadth);
float barWidth = drawSize.X * ((direction == BarDirection.LeftToRight || direction == BarDirection.RightToLeft) ? lengths[i] : barBreadth); float barWidth = drawSize.X * ((direction == BarDirection.LeftToRight || direction == BarDirection.RightToLeft) ? lengths[i] : barBreadth);
if (barHeight == 0 || barWidth == 0)
continue;
// Apply minimum sizing to hide the fact that we don't have fractional anti-aliasing.
barHeight = Math.Max(barHeight, 1.5f);
barWidth = Math.Max(barWidth, 1.5f);
Vector2 topLeft; Vector2 topLeft;
switch (direction) switch (direction)

View File

@ -40,14 +40,14 @@ namespace osu.Game.Localisation
public static LocalisableString DisableMouseWheelVolumeAdjust => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust"), @"Disable mouse wheel adjusting volume during gameplay"); public static LocalisableString DisableMouseWheelVolumeAdjust => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust"), @"Disable mouse wheel adjusting volume during gameplay");
/// <summary> /// <summary>
/// "Volume can still be adjusted using the mouse wheel by holding "Alt"" /// "Volume can still be adjusted using the mouse wheel by holding &quot;Alt&quot;"
/// </summary> /// </summary>
public static LocalisableString DisableMouseWheelVolumeAdjustTooltip => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust_tooltip"), @"Volume can still be adjusted using the mouse wheel by holding ""Alt"""); public static LocalisableString DisableMouseWheelVolumeAdjustTooltip => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust_tooltip"), @"Volume can still be adjusted using the mouse wheel by holding ""Alt""");
/// <summary> /// <summary>
/// "Disable mouse buttons during gameplay" /// "Disable clicks during gameplay"
/// </summary> /// </summary>
public static LocalisableString DisableMouseButtons => new TranslatableString(getKey(@"disable_mouse_buttons"), @"Disable mouse buttons during gameplay"); public static LocalisableString DisableClicksDuringGameplay => new TranslatableString(getKey(@"disable_clicks"), @"Disable clicks during gameplay");
/// <summary> /// <summary>
/// "Enable high precision mouse to adjust sensitivity" /// "Enable high precision mouse to adjust sensitivity"

View File

@ -0,0 +1,24 @@
// 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.Localisation;
namespace osu.Game.Localisation
{
public static class TouchSettingsStrings
{
private const string prefix = @"osu.Game.Resources.Localisation.TouchSettings";
/// <summary>
/// "Touch"
/// </summary>
public static LocalisableString Touch => new TranslatableString(getKey(@"touch"), @"Touch");
/// <summary>
/// "Disable taps during gameplay"
/// </summary>
public static LocalisableString DisableTapsDuringGameplay => new TranslatableString(getKey(@"disable_taps_during_gameplay"), @"Disable taps during gameplay");
private static string getKey(string key) => $@"{prefix}:{key}";
}
}

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.Framework.IO.Network;
namespace osu.Game.Online.API.Requests
{
public class GetKudosuRankingsRequest : APIRequest<GetKudosuRankingsResponse>
{
private readonly int page;
public GetKudosuRankingsRequest(int page = 1)
{
this.page = page;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.AddParameter(@"page", page.ToString());
return req;
}
protected override string Target => @"rankings/kudosu";
}
}

View File

@ -0,0 +1,15 @@
// 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.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class GetKudosuRankingsResponse
{
[JsonProperty("ranking")]
public List<APIUser> Users = null!;
}
}

View File

@ -34,20 +34,15 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"previous_usernames")] [JsonProperty(@"previous_usernames")]
public string[] PreviousUsernames; public string[] PreviousUsernames;
private CountryCode? countryCode; [JsonProperty(@"country_code")]
private string countryCodeString;
public CountryCode CountryCode public CountryCode CountryCode
{ {
get => countryCode ??= (Enum.TryParse(country?.Code, out CountryCode result) ? result : default); get => Enum.TryParse(countryCodeString, out CountryCode result) ? result : CountryCode.Unknown;
set => countryCode = value; set => countryCodeString = value.ToString();
} }
#pragma warning disable 649
[CanBeNull]
[JsonProperty(@"country")]
private Country country;
#pragma warning restore 649
public readonly Bindable<UserStatus> Status = new Bindable<UserStatus>(); public readonly Bindable<UserStatus> Status = new Bindable<UserStatus>();
public readonly Bindable<UserActivity> Activity = new Bindable<UserActivity>(); public readonly Bindable<UserActivity> Activity = new Bindable<UserActivity>();

View File

@ -0,0 +1,95 @@
// 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.Collections.Generic;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Rankings.Tables;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Users;
namespace osu.Game.Overlays
{
public partial class KudosuTable : RankingsTable<APIUser>
{
public KudosuTable(int page, List<APIUser> users)
: base(page, users)
{
}
protected override Drawable CreateRowBackground(APIUser item)
{
var background = base.CreateRowBackground(item);
// see: https://github.com/ppy/osu-web/blob/9de00a0b874c56893d98261d558d78d76259d81b/resources/views/multiplayer/rooms/_rankings_table.blade.php#L23
if (!item.Active)
background.Alpha = 0.5f;
return background;
}
protected override Drawable[] CreateRowContent(int index, APIUser item)
{
var content = base.CreateRowContent(index, item);
// see: https://github.com/ppy/osu-web/blob/9de00a0b874c56893d98261d558d78d76259d81b/resources/views/multiplayer/rooms/_rankings_table.blade.php#L23
if (!item.Active)
{
foreach (var d in content)
d.Alpha = 0.5f;
}
return content;
}
protected override RankingsTableColumn[] CreateAdditionalHeaders()
{
const int min_width = 120;
return new[]
{
new RankingsTableColumn(RankingsStrings.KudosuTotal, Anchor.Centre, new Dimension(GridSizeMode.AutoSize, minSize: min_width), true),
new RankingsTableColumn(RankingsStrings.KudosuAvailable, Anchor.Centre, new Dimension(GridSizeMode.AutoSize, minSize: min_width)),
new RankingsTableColumn(RankingsStrings.KudosuUsed, Anchor.Centre, new Dimension(GridSizeMode.AutoSize, minSize: min_width)),
};
}
protected override Drawable[] CreateAdditionalContent(APIUser item)
{
int kudosuTotal = item.Kudosu.Total;
int kudosuAvailable = item.Kudosu.Available;
return new Drawable[]
{
new RowText
{
Text = kudosuTotal.ToLocalisableString(@"N0")
},
new ColouredRowText
{
Text = kudosuAvailable.ToLocalisableString(@"N0")
},
new ColouredRowText
{
Text = (kudosuTotal - kudosuAvailable).ToLocalisableString(@"N0")
},
};
}
protected override CountryCode GetCountryCode(APIUser item) => item.CountryCode;
protected override Drawable CreateFlagContent(APIUser item)
{
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true))
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
TextAnchor = Anchor.CentreLeft
};
username.AddUserLink(item);
return username;
}
}
}

View File

@ -18,6 +18,9 @@ namespace osu.Game.Overlays.Rankings
Score, Score,
[LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeCountry))] [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeCountry))]
Country Country,
[LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeKudosu))]
Kudosu,
} }
} }

View File

@ -135,6 +135,9 @@ namespace osu.Game.Overlays
case RankingsScope.Score: case RankingsScope.Score:
return new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score); return new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score);
case RankingsScope.Kudosu:
return new GetKudosuRankingsRequest();
} }
return null; return null;
@ -166,6 +169,12 @@ namespace osu.Game.Overlays
return new CountriesTable(1, countryRequest.Response.Countries); return new CountriesTable(1, countryRequest.Response.Countries);
} }
case GetKudosuRankingsRequest kudosuRequest:
if (kudosuRequest.Response == null)
return null;
return new KudosuTable(1, kudosuRequest.Response.Users);
} }
return null; return null;

View File

@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
}, },
new SettingsCheckbox new SettingsCheckbox
{ {
LabelText = MouseSettingsStrings.DisableMouseButtons, LabelText = MouseSettingsStrings.DisableClicksDuringGameplay,
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons) Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons)
}, },
}; };

View File

@ -4,37 +4,43 @@
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.Graphics; using osu.Framework.Input.Handlers;
using osu.Framework.Input.Handlers.Touch;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Localisation; using osu.Game.Localisation;
namespace osu.Game.Overlays.Settings.Sections.Input namespace osu.Game.Overlays.Settings.Sections.Input
{ {
/// <summary>
/// Touch input settings subsection common to all touch handlers (even on different platforms).
/// </summary>
public partial class TouchSettings : SettingsSubsection public partial class TouchSettings : SettingsSubsection
{ {
private readonly TouchHandler handler; private readonly InputHandler handler;
public TouchSettings(TouchHandler handler) protected override LocalisableString Header => TouchSettingsStrings.Touch;
public TouchSettings(InputHandler handler)
{ {
this.handler = handler; this.handler = handler;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(OsuConfigManager osuConfig)
{ {
Children = new Drawable[] Add(new SettingsCheckbox
{ {
new SettingsCheckbox LabelText = CommonStrings.Enabled,
{ Current = handler.Enabled
LabelText = CommonStrings.Enabled, });
Current = handler.Enabled
}, Add(new SettingsCheckbox
}; {
LabelText = TouchSettingsStrings.DisableTapsDuringGameplay,
Current = osuConfig.GetBindable<bool>(OsuSetting.TouchDisableGameplayTaps)
});
} }
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"touchscreen" }); public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"touchscreen" });
protected override LocalisableString Header => handler.Description;
} }
} }

View File

@ -198,12 +198,26 @@ namespace osu.Game.Overlays.SkinEditor
Items = createAnchorItems((d, o) => ((Drawable)d).Origin == o, applyOrigins).ToArray() Items = createAnchorItems((d, o) => ((Drawable)d).Origin == o, applyOrigins).ToArray()
}; };
yield return new EditorMenuItemSpacer();
yield return new OsuMenuItem("Reset position", MenuItemType.Standard, () => yield return new OsuMenuItem("Reset position", MenuItemType.Standard, () =>
{ {
foreach (var blueprint in SelectedBlueprints) foreach (var blueprint in SelectedBlueprints)
((Drawable)blueprint.Item).Position = Vector2.Zero; ((Drawable)blueprint.Item).Position = Vector2.Zero;
}); });
yield return new OsuMenuItem("Reset rotation", MenuItemType.Standard, () =>
{
foreach (var blueprint in SelectedBlueprints)
((Drawable)blueprint.Item).Rotation = 0;
});
yield return new OsuMenuItem("Reset scale", MenuItemType.Standard, () =>
{
foreach (var blueprint in SelectedBlueprints)
((Drawable)blueprint.Item).Scale = Vector2.One;
});
yield return new EditorMenuItemSpacer(); yield return new EditorMenuItemSpacer();
yield return new OsuMenuItem("Bring to front", MenuItemType.Standard, () => skinEditor.BringSelectionToFront()); yield return new OsuMenuItem("Bring to front", MenuItemType.Standard, () => skinEditor.BringSelectionToFront());

View File

@ -138,7 +138,6 @@ namespace osu.Game.Rulesets.UI
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Colour = OsuColour.Gray(84),
Alpha = 0, Alpha = 0,
Font = OsuFont.Numeric.With(null, 22f), Font = OsuFont.Numeric.With(null, 22f),
UseFullGlyphHeight = false, UseFullGlyphHeight = false,
@ -148,7 +147,6 @@ namespace osu.Game.Rulesets.UI
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Colour = OsuColour.Gray(84),
Size = new Vector2(45), Size = new Vector2(45),
Icon = FontAwesome.Solid.Question Icon = FontAwesome.Solid.Question
}, },
@ -206,6 +204,8 @@ namespace osu.Game.Rulesets.UI
private void updateColour() private void updateColour()
{ {
modAcronym.Colour = modIcon.Colour = OsuColour.Gray(84);
extendedText.Colour = background.Colour = Selected.Value ? backgroundColour.Lighten(0.2f) : backgroundColour; extendedText.Colour = background.Colour = Selected.Value ? backgroundColour.Lighten(0.2f) : backgroundColour;
extendedBackground.Colour = Selected.Value ? backgroundColour.Darken(2.4f) : backgroundColour.Darken(2.8f); extendedBackground.Colour = Selected.Value ? backgroundColour.Darken(2.4f) : backgroundColour.Darken(2.8f);
} }

View File

@ -85,11 +85,13 @@ namespace osu.Game.Rulesets.UI
tinySwitch.Scale = new Vector2(0.3f); tinySwitch.Scale = new Vector2(0.3f);
} }
var modTypeColour = colours.ForModType(mod.Type);
inactiveForegroundColour = colourProvider?.Background5 ?? colours.Gray3; inactiveForegroundColour = colourProvider?.Background5 ?? colours.Gray3;
activeForegroundColour = colours.ForModType(mod.Type); activeForegroundColour = modTypeColour;
inactiveBackgroundColour = colourProvider?.Background2 ?? colours.Gray5; inactiveBackgroundColour = colourProvider?.Background2 ?? colours.Gray5;
activeBackgroundColour = Interpolation.ValueAt<Colour4>(0.1f, Colour4.Black, activeForegroundColour, 0, 1); activeBackgroundColour = Interpolation.ValueAt<Colour4>(0.1f, Colour4.Black, modTypeColour, 0, 1);
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -106,11 +106,13 @@ namespace osu.Game.Rulesets.UI
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(OsuColour colours, OverlayColourProvider? colourProvider) private void load(OsuColour colours, OverlayColourProvider? colourProvider)
{ {
var modTypeColour = colours.ForModType(Mod.Type);
inactiveBackgroundColour = colourProvider?.Background5 ?? colours.Gray3; inactiveBackgroundColour = colourProvider?.Background5 ?? colours.Gray3;
activeBackgroundColour = colours.ForModType(Mod.Type); activeBackgroundColour = modTypeColour;
inactiveForegroundColour = colourProvider?.Background2 ?? colours.Gray5; inactiveForegroundColour = colourProvider?.Background2 ?? colours.Gray5;
activeForegroundColour = Interpolation.ValueAt<Colour4>(0.1f, Colour4.Black, activeForegroundColour, 0, 1); activeForegroundColour = Interpolation.ValueAt<Colour4>(0.1f, Colour4.Black, modTypeColour, 0, 1);
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -72,6 +72,7 @@ namespace osu.Game.Rulesets.UI
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons); mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
tapsDisabled = config.GetBindable<bool>(OsuSetting.TouchDisableGameplayTaps);
} }
#region Action mapping (for replays) #region Action mapping (for replays)
@ -124,6 +125,7 @@ namespace osu.Game.Rulesets.UI
#region Setting application (disables etc.) #region Setting application (disables etc.)
private Bindable<bool> mouseDisabled; private Bindable<bool> mouseDisabled;
private Bindable<bool> tapsDisabled;
protected override bool Handle(UIEvent e) protected override bool Handle(UIEvent e)
{ {
@ -147,9 +149,9 @@ namespace osu.Game.Rulesets.UI
protected override bool HandleMouseTouchStateChange(TouchStateChangeEvent e) protected override bool HandleMouseTouchStateChange(TouchStateChangeEvent e)
{ {
if (mouseDisabled.Value) if (tapsDisabled.Value)
{ {
// Only propagate positional data when mouse buttons are disabled. // Only propagate positional data when taps are disabled.
e = new TouchStateChangeEvent(e.State, e.Input, e.Touch, false, e.LastPosition); e = new TouchStateChangeEvent(e.State, e.Input, e.Touch, false, e.LastPosition);
} }

View File

@ -1095,6 +1095,19 @@ namespace osu.Game.Screens.Edit
protected void CreateNewDifficulty(RulesetInfo rulesetInfo) protected void CreateNewDifficulty(RulesetInfo rulesetInfo)
{ {
if (isNewBeatmap)
{
dialogOverlay.Push(new SaveRequiredPopupDialog("This beatmap will be saved in order to create another difficulty.", () =>
{
if (!Save())
return;
CreateNewDifficulty(rulesetInfo);
}));
return;
}
if (!rulesetInfo.Equals(editorBeatmap.BeatmapInfo.Ruleset)) if (!rulesetInfo.Equals(editorBeatmap.BeatmapInfo.Ruleset))
{ {
switchToNewDifficulty(rulesetInfo, false); switchToNewDifficulty(rulesetInfo, false);

View File

@ -72,7 +72,7 @@ namespace osu.Game.Screens.Edit.Verify
new RoundedButton new RoundedButton
{ {
Text = "Refresh", Text = "Refresh",
Action = refresh, Action = Refresh,
Size = new Vector2(120, 40), Size = new Vector2(120, 40),
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
@ -86,13 +86,13 @@ namespace osu.Game.Screens.Edit.Verify
{ {
base.LoadComplete(); base.LoadComplete();
verify.InterpretedDifficulty.BindValueChanged(_ => refresh()); verify.InterpretedDifficulty.BindValueChanged(_ => Refresh());
verify.HiddenIssueTypes.BindCollectionChanged((_, _) => refresh()); verify.HiddenIssueTypes.BindCollectionChanged((_, _) => Refresh());
refresh(); Refresh();
} }
private void refresh() public void Refresh()
{ {
var issues = generalVerifier.Run(context); var issues = generalVerifier.Run(context);

View File

@ -56,5 +56,11 @@ namespace osu.Game.Screens.Edit.Verify
} }
}; };
} }
protected override void PopIn()
{
base.PopIn();
IssueList.Refresh();
}
} }
} }

View File

@ -85,7 +85,8 @@ namespace osu.Game.Screens.Menu
private readonly List<MainMenuButton> buttonsTopLevel = new List<MainMenuButton>(); private readonly List<MainMenuButton> buttonsTopLevel = new List<MainMenuButton>();
private readonly List<MainMenuButton> buttonsPlay = new List<MainMenuButton>(); private readonly List<MainMenuButton> buttonsPlay = new List<MainMenuButton>();
private Sample sampleBack; private Sample sampleBackToLogo;
private Sample sampleLogoSwoosh;
private readonly LogoTrackingContainer logoTrackingContainer; private readonly LogoTrackingContainer logoTrackingContainer;
@ -104,7 +105,7 @@ namespace osu.Game.Screens.Menu
buttonArea.AddRange(new Drawable[] buttonArea.AddRange(new Drawable[]
{ {
new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O),
backButton = new MainMenuButton(ButtonSystemStrings.Back, @"button-back-select", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel,
-WEDGE_WIDTH) -WEDGE_WIDTH)
{ {
VisibleState = ButtonSystemState.Play, VisibleState = ButtonSystemState.Play,
@ -127,14 +128,14 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(AudioManager audio, IdleTracker idleTracker, GameHost host) private void load(AudioManager audio, IdleTracker idleTracker, GameHost host)
{ {
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M));
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L));
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P));
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-edit-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-default-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D)); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D));
if (host.CanExit) if (host.CanExit)
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q));
@ -155,7 +156,8 @@ namespace osu.Game.Screens.Menu
if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle);
sampleBack = audio.Samples.Get(@"Menu/button-back-select"); sampleBackToLogo = audio.Samples.Get(@"Menu/back-to-logo");
sampleLogoSwoosh = audio.Samples.Get(@"Menu/osu-logo-swoosh");
} }
private void onMultiplayer() private void onMultiplayer()
@ -197,6 +199,7 @@ namespace osu.Game.Screens.Menu
{ {
if (State == ButtonSystemState.Initial) if (State == ButtonSystemState.Initial)
{ {
StopSamplePlayback();
logo?.TriggerClick(); logo?.TriggerClick();
return true; return true;
} }
@ -260,10 +263,15 @@ namespace osu.Game.Screens.Menu
{ {
case ButtonSystemState.TopLevel: case ButtonSystemState.TopLevel:
State = ButtonSystemState.Initial; State = ButtonSystemState.Initial;
sampleBack?.Play();
// Samples are explicitly played here in response to user interaction and not when transitioning due to idle.
StopSamplePlayback();
sampleBackToLogo?.Play();
return true; return true;
case ButtonSystemState.Play: case ButtonSystemState.Play:
StopSamplePlayback();
backButton.TriggerClick(); backButton.TriggerClick();
return true; return true;
@ -272,6 +280,13 @@ namespace osu.Game.Screens.Menu
} }
} }
public void StopSamplePlayback()
{
buttonsPlay.ForEach(button => button.StopSamplePlayback());
buttonsTopLevel.ForEach(button => button.StopSamplePlayback());
logo?.StopSamplePlayback();
}
private bool onOsuLogo() private bool onOsuLogo()
{ {
switch (state) switch (state)
@ -346,6 +361,9 @@ namespace osu.Game.Screens.Menu
logo?.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo); logo?.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo);
logo?.ScaleTo(1, 800, Easing.OutExpo); logo?.ScaleTo(1, 800, Easing.OutExpo);
}, buttonArea.Alpha * 150); }, buttonArea.Alpha * 150);
if (lastState == ButtonSystemState.TopLevel)
sampleLogoSwoosh?.Play();
break; break;
case ButtonSystemState.TopLevel: case ButtonSystemState.TopLevel:

View File

@ -7,6 +7,8 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -89,8 +91,10 @@ namespace osu.Game.Screens.Menu
private SongTicker songTicker; private SongTicker songTicker;
private Container logoTarget; private Container logoTarget;
private Sample reappearSampleSwoosh;
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics, AudioManager audio)
{ {
holdDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay); holdDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
loginDisplayed = statics.GetBindable<bool>(Static.LoginOverlayDisplayed); loginDisplayed = statics.GetBindable<bool>(Static.LoginOverlayDisplayed);
@ -162,6 +166,8 @@ namespace osu.Game.Screens.Menu
Buttons.OnSettings = () => settings?.ToggleVisibility(); Buttons.OnSettings = () => settings?.ToggleVisibility();
Buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); Buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility();
reappearSampleSwoosh = audio.Samples.Get(@"Menu/reappear-swoosh");
preloadSongSelect(); preloadSongSelect();
} }
@ -291,6 +297,10 @@ namespace osu.Game.Screens.Menu
{ {
base.OnResuming(e); base.OnResuming(e);
// Ensures any playing `ButtonSystem` samples are stopped when returning to MainMenu (as to not overlap with the 'back' sample)
Buttons.StopSamplePlayback();
reappearSampleSwoosh?.Play();
ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next()); ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next());
// we may have consumed our preloaded instance, so let's make another. // we may have consumed our preloaded instance, so let's make another.

View File

@ -51,6 +51,7 @@ namespace osu.Game.Screens.Menu
private readonly Action clickAction; private readonly Action clickAction;
private Sample sampleClick; private Sample sampleClick;
private Sample sampleHover; private Sample sampleHover;
private SampleChannel sampleChannel;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos);
@ -225,7 +226,8 @@ namespace osu.Game.Screens.Menu
private void trigger() private void trigger()
{ {
sampleClick?.Play(); sampleChannel = sampleClick?.GetChannel();
sampleChannel?.Play();
clickAction?.Invoke(); clickAction?.Invoke();
@ -237,6 +239,8 @@ namespace osu.Game.Screens.Menu
public override bool HandleNonPositionalInput => state == ButtonState.Expanded; public override bool HandleNonPositionalInput => state == ButtonState.Expanded;
public override bool HandlePositionalInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; public override bool HandlePositionalInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f;
public void StopSamplePlayback() => sampleChannel?.Stop();
protected override void Update() protected override void Update()
{ {
iconText.Alpha = Math.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1); iconText.Alpha = Math.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1);

View File

@ -52,6 +52,8 @@ namespace osu.Game.Screens.Menu
private readonly IntroSequence intro; private readonly IntroSequence intro;
private Sample sampleClick; private Sample sampleClick;
private SampleChannel sampleClickChannel;
private Sample sampleBeat; private Sample sampleBeat;
private Sample sampleDownbeat; private Sample sampleDownbeat;
@ -391,7 +393,11 @@ namespace osu.Game.Screens.Menu
flashLayer.FadeOut(1500, Easing.OutExpo); flashLayer.FadeOut(1500, Easing.OutExpo);
if (Action?.Invoke() == true) if (Action?.Invoke() == true)
sampleClick.Play(); {
StopSamplePlayback();
sampleClickChannel = sampleClick.GetChannel();
sampleClickChannel.Play();
}
return true; return true;
} }
@ -440,6 +446,8 @@ namespace osu.Game.Screens.Menu
private Container currentProxyTarget; private Container currentProxyTarget;
private Drawable proxy; private Drawable proxy;
public void StopSamplePlayback() => sampleClickChannel?.Stop();
public Drawable ProxyToContainer(Container c) public Drawable ProxyToContainer(Container c)
{ {
if (currentProxyTarget != null) if (currentProxyTarget != null)

View File

@ -1,13 +1,15 @@
// 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 osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osuTK;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
@ -17,6 +19,8 @@ namespace osu.Game.Screens.Play
private OsuSpriteText keyNameText = null!; private OsuSpriteText keyNameText = null!;
private OsuSpriteText countText = null!; private OsuSpriteText countText = null!;
private UprightAspectMaintainingContainer uprightContainer = null!;
// These values were taken from Figma // These values were taken from Figma
private const float line_height = 3; private const float line_height = 3;
private const float name_font_size = 10; private const float name_font_size = 10;
@ -25,6 +29,8 @@ namespace osu.Game.Screens.Play
// Make things look bigger without using Scale // Make things look bigger without using Scale
private const float scale_factor = 1.5f; private const float scale_factor = 1.5f;
private const float indicator_press_offset = 4;
[Resolved] [Resolved]
private OsuColour colours { get; set; } = null!; private OsuColour colours { get; set; } = null!;
@ -40,26 +46,40 @@ namespace osu.Game.Screens.Play
{ {
inputIndicator = new Circle inputIndicator = new Circle
{ {
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = line_height * scale_factor, Height = line_height * scale_factor,
Alpha = 0.5f Alpha = 0.5f
}, },
keyNameText = new OsuSpriteText new Container
{ {
Anchor = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both,
Origin = Anchor.BottomLeft, Padding = new MarginPadding { Top = line_height * scale_factor + indicator_press_offset },
Position = new Vector2(0, -13) * scale_factor, Children = new Drawable[]
Font = OsuFont.Torus.With(size: name_font_size * scale_factor, weight: FontWeight.Bold), {
Colour = colours.Blue0, uprightContainer = new UprightAspectMaintainingContainer
Text = Trigger.Name {
}, RelativeSizeAxes = Axes.Both,
countText = new OsuSpriteText Anchor = Anchor.Centre,
{ Origin = Anchor.Centre,
Anchor = Anchor.BottomLeft, Children = new Drawable[]
Origin = Anchor.BottomLeft, {
Font = OsuFont.Torus.With(size: count_font_size * scale_factor, weight: FontWeight.Bold), keyNameText = new OsuSpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Font = OsuFont.Torus.With(size: name_font_size * scale_factor, weight: FontWeight.Bold),
Colour = colours.Blue0,
Text = Trigger.Name
},
countText = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.Torus.With(size: count_font_size * scale_factor, weight: FontWeight.Bold),
},
}
}
}
}, },
}; };
@ -76,6 +96,21 @@ namespace osu.Game.Screens.Play
CountPresses.BindValueChanged(e => countText.Text = e.NewValue.ToString(@"#,0"), true); CountPresses.BindValueChanged(e => countText.Text = e.NewValue.ToString(@"#,0"), true);
} }
protected override void Update()
{
base.Update();
const float allowance = 6;
float absRotation = Math.Abs(uprightContainer.Rotation) % 180;
bool isRotated = absRotation > allowance && absRotation < (180 - allowance);
keyNameText.Anchor =
keyNameText.Origin = isRotated ? Anchor.TopCentre : Anchor.TopLeft;
countText.Anchor =
countText.Origin = isRotated ? Anchor.BottomCentre : Anchor.BottomLeft;
}
protected override void Activate(bool forwardPlayback = true) protected override void Activate(bool forwardPlayback = true)
{ {
base.Activate(forwardPlayback); base.Activate(forwardPlayback);
@ -87,7 +122,7 @@ namespace osu.Game.Screens.Play
.FadeIn(10, Easing.OutQuint) .FadeIn(10, Easing.OutQuint)
.MoveToY(0) .MoveToY(0)
.Then() .Then()
.MoveToY(4, 60, Easing.OutQuint); .MoveToY(indicator_press_offset, 60, Easing.OutQuint);
} }
protected override void Deactivate(bool forwardPlayback = true) protected override void Deactivate(bool forwardPlayback = true)

View File

@ -10,21 +10,22 @@ namespace osu.Game.Screens.Play.PlayerSettings
{ {
public partial class InputSettings : PlayerSettingsGroup public partial class InputSettings : PlayerSettingsGroup
{ {
private readonly PlayerCheckbox mouseButtonsCheckbox;
public InputSettings() public InputSettings()
: base("Input Settings") : base("Input Settings")
{ {
Children = new Drawable[]
{
mouseButtonsCheckbox = new PlayerCheckbox
{
LabelText = MouseSettingsStrings.DisableMouseButtons
}
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) => mouseButtonsCheckbox.Current = config.GetBindable<bool>(OsuSetting.MouseDisableButtons); private void load(OsuConfigManager config)
{
Children = new Drawable[]
{
new PlayerCheckbox
{
LabelText = MouseSettingsStrings.DisableClicksDuringGameplay,
Current = config.GetBindable<bool>(OsuSetting.MouseDisableButtons)
}
};
}
} }
} }

View File

@ -0,0 +1,40 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Play;
namespace osu.Game.Skinning.Components
{
[UsedImplicitly]
public partial class PlayerName : FontAdjustableSkinComponent
{
private readonly OsuSpriteText text;
public PlayerName()
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
text = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
}
};
}
[BackgroundDependencyLoader]
private void load(GameplayState gameplayState)
{
text.Text = gameplayState.Score.ScoreInfo.User.Username;
}
protected override void SetFont(FontUsage font) => text.Font = font.With(size: 40);
}
}

View File

@ -37,7 +37,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="Realm" Version="11.5.0" /> <PackageReference Include="Realm" Version="11.5.0" />
<PackageReference Include="ppy.osu.Framework" Version="2023.1030.0" /> <PackageReference Include="ppy.osu.Framework" Version="2023.1030.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.1023.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2023.1109.0" />
<PackageReference Include="Sentry" Version="3.40.0" /> <PackageReference Include="Sentry" Version="3.40.0" />
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. --> <!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
<PackageReference Include="SharpCompress" Version="0.33.0" /> <PackageReference Include="SharpCompress" Version="0.33.0" />