1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-05 02:23:38 +08:00
Files
osu-lazer/osu.Game/Beatmaps/Drawables/Cards/Buttons/BeatmapCardIconButton.cs
T
Bartłomiej Dach 199bcd7fdb Improve input handling in beatmap card buttons
This is in response to feedback in
https://osu.ppy.sh/community/forums/topics/2056547?n=1.

Upon examining the button further, there was indeed some rather weird...
almost hysteresis in how the button behaved with respect to the area on
the screen that activated it. Because of the following scourge of a
method that continues to haunt us to this day:

https://github.com/ppy/osu/blob/31487545d0d17c4337d4b4cc5d4afb3ba1dae838/osu.Game/Graphics/Containers/OsuClickableContainer.cs#L24-L25

the button would effectively only be activated by 80% of its drawable
area when it was not hovered, because of the scale applied to the
`content` container which `Container.Content` redirected to.

This is resolved here by various rearrangements of paddings and sizes
such that the clickable area of any of the buttons of the card is always
the full top or bottom half of the button area.

Also included are some cosmetic touch-ups which happened to be
convenient like folding the loading spinner into the base
`BeatmapCardIconButton`, adding loading support for the favourite
button, using BDL more, and resolving some "virtual member call in
constructor" inspections.
2025-03-25 12:59:37 +01:00

131 lines
4.0 KiB
C#

// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Beatmaps.Drawables.Cards.Buttons
{
public abstract partial class BeatmapCardIconButton : OsuClickableContainer
{
private Colour4 idleColour;
public Colour4 IdleColour
{
get => idleColour;
set
{
idleColour = value;
if (IsLoaded)
updateState();
}
}
private Colour4 hoverColour;
public Colour4 HoverColour
{
get => hoverColour;
set
{
hoverColour = value;
if (IsLoaded)
updateState();
}
}
protected SpriteIcon Icon { get; private set; } = null!;
private Container content = null!;
private Container hover = null!;
private LoadingSpinner spinner = null!;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
RelativeSizeAxes = Axes.Both;
Add(content = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Scale = new Vector2(0.8f),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new Drawable[]
{
hover = new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = BeatmapCard.CORNER_RADIUS,
Masking = true,
Colour = Color4.White.Opacity(0.1f),
Blending = BlendingParameters.Additive,
Child = new Box { RelativeSizeAxes = Axes.Both, }
},
Icon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(14),
},
spinner = new LoadingSpinner
{
Size = new Vector2(14),
},
}
});
IdleColour = colourProvider.Light1;
HoverColour = colourProvider.Content1;
}
protected override void LoadComplete()
{
base.LoadComplete();
Enabled.BindValueChanged(_ => updateState(), true);
FinishTransforms(true);
}
protected override bool OnHover(HoverEvent e)
{
updateState();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
updateState();
}
private void updateState()
{
bool isHovered = IsHovered && Enabled.Value;
hover.FadeTo(isHovered ? 1f : 0f, 500, Easing.OutQuint);
content.ScaleTo(isHovered ? 0.9f : 0.8f, 500, Easing.OutQuint);
Icon.FadeColour(isHovered ? HoverColour : IdleColour, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
spinner.FadeColour(isHovered ? HoverColour : IdleColour, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
}
protected void SetLoading(bool isLoading)
{
Icon.Alpha = isLoading ? 0 : 1;
spinner.Alpha = isLoading ? 1 : 0;
Enabled.Value = !isLoading;
}
}
}