Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix buttons receiving input outside the content #21619

Merged
merged 9 commits into from
Dec 13, 2022
Merged
129 changes: 129 additions & 0 deletions osu.Game.Tests/Visual/UserInterface/TestSceneButtonsInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// 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.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Overlays.Settings;
using NUnit.Framework;
using osuTK;
using osu.Game.Overlays;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
using osu.Game.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
using osu.Game.Graphics.Sprites;

namespace osu.Game.Tests.Visual.UserInterface
{
public partial class TestSceneButtonsInput : OsuManualInputManagerTestScene
{
private const int width = 500;

[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);

private readonly SettingsButton settingsButton;
private readonly OsuClickableContainer clickableContainer;
private readonly RoundedButton roundedButton;
private readonly ShearedButton shearedButton;

public TestSceneButtonsInput()
{
Add(new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
Width = 500,
Spacing = new Vector2(0, 5),
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
clickableContainer = new OsuClickableContainer
{
RelativeSizeAxes = Axes.X,
Height = 40,
Enabled = { Value = true },
Masking = true,
CornerRadius = 20,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Red
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "Rounded clickable container"
}
}
},
settingsButton = new SettingsButton
{
Enabled = { Value = true },
Text = "Settings button"
},
roundedButton = new RoundedButton
{
RelativeSizeAxes = Axes.X,
Enabled = { Value = true },
Text = "Rounded button"
},
shearedButton = new ShearedButton(width)
{
Text = "Sheared button",
LighterColour = Colour4.FromHex("#FFFFFF"),
DarkerColour = Colour4.FromHex("#FFCC22"),
TextColour = Colour4.Black,
Height = 40,
Enabled = { Value = true },
Padding = new MarginPadding(0)
}
}
});
}

[Test]
public void TestSettingsButtonInput()
{
AddStep("Move cursor to button", () => InputManager.MoveMouseTo(settingsButton));
AddAssert("Button is hovered", () => settingsButton.IsHovered);
AddStep("Move cursor to padded area", () => InputManager.MoveMouseTo(settingsButton.ScreenSpaceDrawQuad.TopLeft + new Vector2(SettingsPanel.CONTENT_MARGINS / 2f, 10)));
AddAssert("Cursor within a button", () => settingsButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
AddAssert("Button is not hovered", () => !settingsButton.IsHovered);
}

[Test]
public void TestRoundedButtonInput()
{
AddStep("Move cursor to button", () => InputManager.MoveMouseTo(roundedButton));
AddAssert("Button is hovered", () => roundedButton.IsHovered);
AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(roundedButton.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
AddAssert("Cursor within a button", () => roundedButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
AddAssert("Button is not hovered", () => !roundedButton.IsHovered);
}

[Test]
public void TestShearedButtonInput()
{
AddStep("Move cursor to button", () => InputManager.MoveMouseTo(shearedButton));
AddAssert("Button is hovered", () => shearedButton.IsHovered);
AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(shearedButton.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
AddAssert("Cursor within a button", () => shearedButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
AddAssert("Button is not hovered", () => !shearedButton.IsHovered);
}

[Test]
public void TestRoundedClickableContainerInput()
{
AddStep("Move cursor to button", () => InputManager.MoveMouseTo(clickableContainer));
AddAssert("Button is hovered", () => clickableContainer.IsHovered);
AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(clickableContainer.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
AddAssert("Cursor within a button", () => clickableContainer.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
AddAssert("Button is not hovered", () => !clickableContainer.IsHovered);
}
}
}
47 changes: 0 additions & 47 deletions osu.Game.Tests/Visual/UserInterface/TestSceneOsuButton.cs

This file was deleted.

16 changes: 9 additions & 7 deletions osu.Game/Graphics/Containers/OsuClickableContainer.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// 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.

#nullable disable

using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterface;
using osuTK;

namespace osu.Game.Graphics.Containers
{
Expand All @@ -18,6 +17,12 @@ public partial class OsuClickableContainer : ClickableContainer, IHasTooltip

private readonly Container content = new Container { RelativeSizeAxes = Axes.Both };

public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
// base call is checked for cases when `OsuClickableContainer` has masking applied to it directly (ie. externally in object initialisation).
base.ReceivePositionalInputAt(screenSpacePos)
// Implementations often apply masking / edge rounding at a content level, so it's imperative to check that as well.
&& Content.ReceivePositionalInputAt(screenSpacePos);

protected override Container<Drawable> Content => content;

protected virtual HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet) { Enabled = { BindTarget = Enabled } };
Expand All @@ -38,11 +43,8 @@ private void load()
content.AutoSizeAxes = AutoSizeAxes;
}

InternalChildren = new Drawable[]
{
content,
CreateHoverSounds(sampleSet)
};
AddInternal(content);
Add(CreateHoverSounds(sampleSet));
}
}
}
23 changes: 12 additions & 11 deletions osu.Game/Graphics/UserInterface/OsuButton.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// 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.

#nullable disable

using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
Expand All @@ -13,23 +11,20 @@
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Graphics.UserInterface
{
/// <summary>
/// A button with added default sound effects.
/// </summary>
public partial class OsuButton : Button
public abstract partial class OsuButton : Button
{
public LocalisableString Text
{
get => SpriteText?.Text ?? default;
set
{
if (SpriteText != null)
SpriteText.Text = value;
}
get => SpriteText.Text;
set => SpriteText.Text = value;
}

private Color4? backgroundColour;
Expand Down Expand Up @@ -66,13 +61,19 @@ protected Color4 DefaultBackgroundColour

protected override Container<Drawable> Content { get; }

public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
// base call is checked for cases when `OsuClickableContainer` has masking applied to it directly (ie. externally in object initialisation).
base.ReceivePositionalInputAt(screenSpacePos)
// Implementations often apply masking / edge rounding at a content level, so it's imperative to check that as well.
&& Content.ReceivePositionalInputAt(screenSpacePos);

protected Box Hover;
protected Box Background;
protected SpriteText SpriteText;

private readonly Box flashLayer;

public OsuButton(HoverSampleSet? hoverSounds = HoverSampleSet.Button)
protected OsuButton(HoverSampleSet? hoverSounds = HoverSampleSet.Button)
{
Height = 40;

Expand Down Expand Up @@ -115,7 +116,7 @@ public OsuButton(HoverSampleSet? hoverSounds = HoverSampleSet.Button)
});

if (hoverSounds.HasValue)
AddInternal(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
Add(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
}

[BackgroundDependencyLoader]
Expand Down
8 changes: 4 additions & 4 deletions osu.Game/Overlays/Chat/DrawableUsername.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public partial class DrawableUsername : OsuClickableContainer, IHasContextMenu
public Color4 AccentColour { get; }

public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
Child.ReceivePositionalInputAt(screenSpacePos);
colouredDrawable.ReceivePositionalInputAt(screenSpacePos);

public float FontSize
{
Expand Down Expand Up @@ -87,13 +87,13 @@ public DrawableUsername(APIUser user)
{
AccentColour = default_colours[user.Id % default_colours.Length];

Child = colouredDrawable = drawableText;
Add(colouredDrawable = drawableText);
}
else
{
AccentColour = Color4Extensions.FromHex(user.Colour);

Child = new Container
Add(new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Expand Down Expand Up @@ -127,7 +127,7 @@ public DrawableUsername(APIUser user)
}
}
}
};
});
}
}

Expand Down