1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-18 07:22:57 +08:00
osu-lazer/osu.Game/Graphics/UserInterface/OsuDropdown.cs
Dean Herbert 76c5e743d7
Remove opacity from old style dropdown menus
These aren't used in many places, but we've since moved away from
opacity in UI elements like this, so let's just nuke it here for
legibility.

Addresses https://github.com/ppy/osu/discussions/29906.
2024-09-18 13:49:27 +09:00

459 lines
16 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 System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
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.Localisation;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public partial class OsuDropdown<T> : Dropdown<T>, IKeyBindingHandler<GlobalAction>
{
private const float corner_radius = 5;
protected override DropdownHeader CreateHeader() => new OsuDropdownHeader();
protected override DropdownMenu CreateMenu() => new OsuDropdownMenu();
public OsuDropdown()
{
if (Header is OsuDropdownHeader osuHeader)
osuHeader.Dropdown = this;
}
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
if (e.Repeat) return false;
if (e.Action == GlobalAction.Back)
return Back();
return false;
}
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{
}
#region OsuDropdownMenu
protected partial class OsuDropdownMenu : DropdownMenu
{
public override bool HandleNonPositionalInput => State == MenuState.Open;
private Sample? sampleOpen;
private Sample? sampleClose;
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
public OsuDropdownMenu()
{
CornerRadius = corner_radius;
MaskingContainer.CornerRadius = corner_radius;
Alpha = 0;
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
ItemsContainer.Padding = new MarginPadding(5);
}
[BackgroundDependencyLoader(true)]
private void load(OverlayColourProvider? colourProvider, OsuColour colours, AudioManager audio)
{
BackgroundColour = colourProvider?.Background5 ?? Color4.Black;
HoverColour = colourProvider?.Light4 ?? colours.PinkDarker;
SelectionColour = colourProvider?.Background3 ?? colours.PinkDarker.Opacity(0.5f);
sampleOpen = audio.Samples.Get(@"UI/dropdown-open");
sampleClose = audio.Samples.Get(@"UI/dropdown-close");
}
// todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed.
private bool wasOpened;
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
protected override void AnimateOpen()
{
wasOpened = true;
this.FadeIn(300, Easing.OutQuint);
sampleOpen?.Play();
}
protected override void AnimateClose()
{
if (wasOpened)
{
this.FadeOut(300, Easing.OutQuint);
sampleClose?.Play();
}
}
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
protected override void UpdateSize(Vector2 newSize)
{
if (Direction == Direction.Vertical)
{
Width = newSize.X;
this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint);
}
else
{
Height = newSize.Y;
this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint);
}
}
private Color4 hoverColour;
public Color4 HoverColour
{
get => hoverColour;
set
{
hoverColour = value;
foreach (var c in Children.OfType<DrawableOsuDropdownMenuItem>())
c.BackgroundColourHover = value;
}
}
private Color4 selectionColour;
public Color4 SelectionColour
{
get => selectionColour;
set
{
selectionColour = value;
foreach (var c in Children.OfType<DrawableOsuDropdownMenuItem>())
c.BackgroundColourSelected = value;
}
}
protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical);
protected override DrawableDropdownMenuItem CreateDrawableDropdownMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item)
{
BackgroundColourHover = HoverColour,
BackgroundColourSelected = SelectionColour
};
protected override ScrollContainer<Drawable> CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction);
#region DrawableOsuDropdownMenuItem
public partial class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem
{
// IsHovered is used
public override bool HandlePositionalInput => true;
public new Color4 BackgroundColourHover
{
get => base.BackgroundColourHover;
set
{
base.BackgroundColourHover = value;
updateColours();
}
}
public new Color4 BackgroundColourSelected
{
get => base.BackgroundColourSelected;
set
{
base.BackgroundColourSelected = value;
updateColours();
}
}
private void updateColours()
{
BackgroundColour = BackgroundColourHover.Opacity(0);
UpdateBackgroundColour();
UpdateForegroundColour();
}
public DrawableOsuDropdownMenuItem(MenuItem item)
: base(item)
{
Foreground.Padding = new MarginPadding(2);
Foreground.AutoSizeAxes = Axes.Y;
Foreground.RelativeSizeAxes = Axes.X;
Masking = true;
CornerRadius = corner_radius;
}
[BackgroundDependencyLoader]
private void load()
{
AddInternal(new HoverSounds());
}
protected override void UpdateBackgroundColour()
{
Background.FadeColour(IsPreSelected ? BackgroundColourHover : BackgroundColourSelected, 100, Easing.OutQuint);
if (IsPreSelected || IsSelected)
Background.FadeIn(100, Easing.OutQuint);
else
Background.FadeOut(600, Easing.OutQuint);
}
protected override void UpdateForegroundColour()
{
base.UpdateForegroundColour();
if (Foreground.Children.FirstOrDefault() is Content content)
content.Hovering = IsHovered;
}
protected override Drawable CreateContent() => new Content();
protected new partial class Content : CompositeDrawable, IHasText
{
public LocalisableString Text
{
get => Label.Text;
set => Label.Text = value;
}
public readonly OsuSpriteText Label;
public readonly SpriteIcon Chevron;
private const float chevron_offset = -3;
public Content()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
{
Chevron = new SpriteIcon
{
Icon = FontAwesome.Solid.ChevronRight,
Size = new Vector2(8),
Alpha = 0,
X = chevron_offset,
Margin = new MarginPadding { Left = 3, Right = 3 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
},
Label = new TruncatingSpriteText
{
Padding = new MarginPadding { Left = 15 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
},
};
}
[BackgroundDependencyLoader(true)]
private void load(OverlayColourProvider? colourProvider)
{
Chevron.Colour = colourProvider?.Background5 ?? Color4.Black;
}
private bool hovering;
public bool Hovering
{
get => hovering;
set
{
if (value == hovering)
return;
hovering = value;
if (hovering)
{
Chevron.FadeIn(400, Easing.OutQuint);
Chevron.MoveToX(0, 400, Easing.OutQuint);
}
else
{
Chevron.FadeOut(200);
Chevron.MoveToX(chevron_offset, 200, Easing.In);
}
}
}
}
}
#endregion
}
#endregion
public partial class OsuDropdownHeader : DropdownHeader
{
protected readonly SpriteText Text;
protected override LocalisableString Label
{
get => Text.Text;
set => Text.Text = value;
}
protected readonly SpriteIcon Chevron;
public OsuDropdown<T>? Dropdown { get; set; }
public OsuDropdownHeader()
{
Foreground.Padding = new MarginPadding(10);
AutoSizeAxes = Axes.None;
Margin = new MarginPadding { Bottom = 4 };
CornerRadius = corner_radius;
Height = 40;
Foreground.Child = new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
},
ColumnDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
{
new Drawable[]
{
Text = new TruncatingSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
},
Chevron = new SpriteIcon
{
Icon = FontAwesome.Solid.ChevronDown,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Size = new Vector2(16),
},
}
}
};
AddInternal(new HoverClickSounds());
}
[Resolved]
private OverlayColourProvider? colourProvider { get; set; }
[Resolved]
private OsuColour colours { get; set; } = null!;
protected override void LoadComplete()
{
base.LoadComplete();
if (Dropdown != null)
Dropdown.Menu.StateChanged += _ => updateChevron();
SearchBar.State.ValueChanged += _ => updateColour();
Enabled.BindValueChanged(_ => updateColour());
updateColour();
}
protected override bool OnHover(HoverEvent e)
{
updateColour();
return false;
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateColour();
}
private void updateColour()
{
bool hovered = Enabled.Value && IsHovered;
var hoveredColour = colourProvider?.Light4 ?? colours.PinkDarker;
var unhoveredColour = colourProvider?.Background5 ?? Color4.Black;
Colour = Color4.White;
Alpha = Enabled.Value ? 1 : 0.3f;
if (SearchBar.State.Value == Visibility.Visible)
{
Chevron.Colour = hovered ? hoveredColour.Lighten(0.5f) : Colour4.White;
Background.Colour = unhoveredColour;
}
else
{
Chevron.Colour = Color4.White;
Background.Colour = hovered ? hoveredColour : unhoveredColour;
}
}
private void updateChevron()
{
Debug.Assert(Dropdown != null);
bool open = Dropdown.Menu.State == MenuState.Open;
Chevron.ScaleTo(open ? new Vector2(1f, -1f) : Vector2.One, 300, Easing.OutQuint);
}
protected override DropdownSearchBar CreateSearchBar() => new OsuDropdownSearchBar
{
Padding = new MarginPadding { Right = 26 },
};
private partial class OsuDropdownSearchBar : DropdownSearchBar
{
protected override void PopIn() => this.FadeIn();
protected override void PopOut() => this.FadeOut();
protected override TextBox CreateTextBox() => new DropdownSearchTextBox
{
FontSize = OsuFont.Default.Size,
};
private partial class DropdownSearchTextBox : OsuTextBox
{
[BackgroundDependencyLoader]
private void load(OverlayColourProvider? colourProvider)
{
BackgroundUnfocused = colourProvider?.Background5 ?? new Color4(10, 10, 10, 255);
BackgroundFocused = colourProvider?.Background5 ?? new Color4(10, 10, 10, 255);
}
protected override void OnFocus(FocusEvent e)
{
base.OnFocus(e);
BorderThickness = 0;
}
}
}
}
}
}