From d796b7d53c0bb135f667cd68dbd6633a442816c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Jul 2022 21:56:23 +0200 Subject: [PATCH] Extract base mod select overlay panel presentation logic --- osu.Game/Overlays/Mods/ModPanel.cs | 237 ++--------------- .../Overlays/Mods/ModSelectOverlayPanel.cs | 250 ++++++++++++++++++ 2 files changed, 267 insertions(+), 220 deletions(-) create mode 100644 osu.Game/Overlays/Mods/ModSelectOverlayPanel.cs diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 02eb395bd9..5de6290248 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -1,144 +1,42 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Framework.Utils; -using osu.Game.Audio; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osuTK; -using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class ModPanel : OsuClickableContainer + public class ModPanel : ModSelectOverlayPanel { public Mod Mod => modState.Mod; - public BindableBool Active => modState.Active; + public override BindableBool Active => modState.Active; public BindableBool Filtered => modState.Filtered; + protected override float IdleSwitchWidth => 54; + protected override float ExpandedSwitchWidth => 70; + private readonly ModState modState; - protected readonly Box Background; - protected readonly Container SwitchContainer; - protected readonly Container MainContentContainer; - protected readonly Box TextBackground; - protected readonly FillFlowContainer TextFlow; - - [Resolved] - protected OverlayColourProvider ColourProvider { get; private set; } = null!; - - protected const double TRANSITION_DURATION = 150; - - public const float CORNER_RADIUS = 7; - - protected const float HEIGHT = 42; - protected const float IDLE_SWITCH_WIDTH = 54; - protected const float EXPANDED_SWITCH_WIDTH = 70; - - private Colour4 activeColour; - - private readonly Bindable samplePlaybackDisabled = new BindableBool(); - private Sample? sampleOff; - private Sample? sampleOn; - public ModPanel(ModState modState) { this.modState = modState; - RelativeSizeAxes = Axes.X; - Height = 42; + Title = Mod.Name; + Description = Mod.Description; - // all below properties are applied to `Content` rather than the `ModPanel` in its entirety - // to allow external components to set these properties on the panel without affecting - // its "internal" appearance. - Content.Masking = true; - Content.CornerRadius = CORNER_RADIUS; - Content.BorderThickness = 2; - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); - - Children = new Drawable[] + SwitchContainer.Child = new ModSwitchSmall(Mod) { - Background = new Box - { - RelativeSizeAxes = Axes.Both - }, - SwitchContainer = new Container - { - RelativeSizeAxes = Axes.Y, - Child = new ModSwitchSmall(Mod) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Active = { BindTarget = Active }, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Scale = new Vector2(HEIGHT / ModSwitchSmall.DEFAULT_SIZE) - } - }, - MainContentContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Child = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = CORNER_RADIUS, - Children = new Drawable[] - { - TextBackground = new Box - { - RelativeSizeAxes = Axes.Both - }, - TextFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Horizontal = 17.5f, - Vertical = 4 - }, - Direction = FillDirection.Vertical, - Children = new[] - { - new OsuSpriteText - { - Text = Mod.Name, - Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Margin = new MarginPadding - { - Left = -18 * ShearedOverlayContainer.SHEAR - } - }, - new OsuSpriteText - { - Text = Mod.Description, - Font = OsuFont.Default.With(size: 12), - RelativeSizeAxes = Axes.X, - Truncate = true, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) - } - } - } - } - } - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Active = { BindTarget = Active }, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Scale = new Vector2(HEIGHT / ModSwitchSmall.DEFAULT_SIZE) }; - - Action = Active.Toggle; } public ModPanel(Mod mod) @@ -146,122 +44,21 @@ namespace osu.Game.Overlays.Mods { } - [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, OsuColour colours, ISamplePlaybackDisabler? samplePlaybackDisabler) + [BackgroundDependencyLoader] + private void load(OsuColour colours) { - sampleOn = audio.Samples.Get(@"UI/check-on"); - sampleOff = audio.Samples.Get(@"UI/check-off"); - - activeColour = colours.ForModType(Mod.Type); - - if (samplePlaybackDisabler != null) - ((IBindable)samplePlaybackDisabled).BindTo(samplePlaybackDisabler.SamplePlaybackDisabled); + AccentColour = colours.ForModType(Mod.Type); } - protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); - protected override void LoadComplete() { base.LoadComplete(); - Active.BindValueChanged(_ => - { - playStateChangeSamples(); - UpdateState(); - }); + Filtered.BindValueChanged(_ => updateFilterState(), true); - - UpdateState(); - FinishTransforms(true); - } - - private void playStateChangeSamples() - { - if (samplePlaybackDisabled.Value) - return; - - if (Active.Value) - sampleOn?.Play(); - else - sampleOff?.Play(); - } - - protected override bool OnHover(HoverEvent e) - { - UpdateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - UpdateState(); - base.OnHoverLost(e); - } - - private bool mouseDown; - - protected override bool OnMouseDown(MouseDownEvent e) - { - if (e.Button == MouseButton.Left) - mouseDown = true; - - UpdateState(); - return false; - } - - protected override void OnMouseUp(MouseUpEvent e) - { - mouseDown = false; - - UpdateState(); - base.OnMouseUp(e); - } - - protected virtual Colour4 BackgroundColour => Active.Value ? activeColour.Darken(0.3f) : ColourProvider.Background3; - protected virtual Colour4 ForegroundColour => Active.Value ? activeColour : ColourProvider.Background2; - protected virtual Colour4 TextColour => Active.Value ? ColourProvider.Background6 : Colour4.White; - - protected virtual void UpdateState() - { - float targetWidth = Active.Value ? EXPANDED_SWITCH_WIDTH : IDLE_SWITCH_WIDTH; - double transitionDuration = TRANSITION_DURATION; - - Colour4 backgroundColour = BackgroundColour; - Colour4 foregroundColour = ForegroundColour; - Colour4 textColour = TextColour; - - // Hover affects colour of button background - if (IsHovered) - { - backgroundColour = backgroundColour.Lighten(0.1f); - foregroundColour = foregroundColour.Lighten(0.1f); - } - - // Mouse down adds a halfway tween of the movement - if (mouseDown) - { - targetWidth = (float)Interpolation.Lerp(IDLE_SWITCH_WIDTH, EXPANDED_SWITCH_WIDTH, 0.5f); - transitionDuration *= 4; - } - - Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, foregroundColour), transitionDuration, Easing.OutQuint); - Background.FadeColour(backgroundColour, transitionDuration, Easing.OutQuint); - SwitchContainer.ResizeWidthTo(targetWidth, transitionDuration, Easing.OutQuint); - MainContentContainer.TransformTo(nameof(Padding), new MarginPadding - { - Left = targetWidth, - Right = CORNER_RADIUS - }, transitionDuration, Easing.OutQuint); - TextBackground.FadeColour(foregroundColour, transitionDuration, Easing.OutQuint); - TextFlow.FadeColour(textColour, transitionDuration, Easing.OutQuint); } #region Filtering support - public void ApplyFilter(Func? filter) - { - Filtered.Value = filter != null && !filter.Invoke(Mod); - } - private void updateFilterState() { this.FadeTo(Filtered.Value ? 0 : 1); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlayPanel.cs b/osu.Game/Overlays/Mods/ModSelectOverlayPanel.cs new file mode 100644 index 0000000000..d4969e0a81 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModSelectOverlayPanel.cs @@ -0,0 +1,250 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Framework.Utils; +using osu.Game.Audio; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Overlays.Mods +{ + public abstract class ModSelectOverlayPanel : OsuClickableContainer, IHasAccentColour + { + public abstract BindableBool Active { get; } + + public Color4 AccentColour { get; set; } + + public LocalisableString Title + { + get => titleText.Text; + set => titleText.Text = value; + } + + public LocalisableString Description + { + get => descriptionText.Text; + set => descriptionText.Text = value; + } + + public const float CORNER_RADIUS = 7; + + protected const float HEIGHT = 42; + + protected virtual float IdleSwitchWidth => 14; + protected virtual float ExpandedSwitchWidth => 30; + protected virtual Colour4 BackgroundColour => Active.Value ? AccentColour.Darken(0.3f) : ColourProvider.Background3; + protected virtual Colour4 ForegroundColour => Active.Value ? AccentColour : ColourProvider.Background2; + protected virtual Colour4 TextColour => Active.Value ? ColourProvider.Background6 : Colour4.White; + + protected const double TRANSITION_DURATION = 150; + + protected readonly Box Background; + protected readonly Container SwitchContainer; + protected readonly Container MainContentContainer; + protected readonly Box TextBackground; + protected readonly FillFlowContainer TextFlow; + + [Resolved] + protected OverlayColourProvider ColourProvider { get; private set; } = null!; + + private readonly OsuSpriteText titleText; + private readonly OsuSpriteText descriptionText; + + private readonly Bindable samplePlaybackDisabled = new BindableBool(); + private Sample? sampleOff; + private Sample? sampleOn; + + protected ModSelectOverlayPanel() + { + RelativeSizeAxes = Axes.X; + Height = HEIGHT; + + // all below properties are applied to `Content` rather than the `ModPanel` in its entirety + // to allow external components to set these properties on the panel without affecting + // its "internal" appearance. + Content.Masking = true; + Content.CornerRadius = CORNER_RADIUS; + Content.BorderThickness = 2; + + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); + + Children = new Drawable[] + { + Background = new Box + { + RelativeSizeAxes = Axes.Both + }, + SwitchContainer = new Container + { + RelativeSizeAxes = Axes.Y, + }, + MainContentContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = CORNER_RADIUS, + Children = new Drawable[] + { + TextBackground = new Box + { + RelativeSizeAxes = Axes.Both + }, + TextFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = 17.5f, + Vertical = 4 + }, + Direction = FillDirection.Vertical, + Children = new[] + { + titleText = new OsuSpriteText + { + Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Margin = new MarginPadding + { + Left = -18 * ShearedOverlayContainer.SHEAR + } + }, + descriptionText = new OsuSpriteText + { + Font = OsuFont.Default.With(size: 12), + RelativeSizeAxes = Axes.X, + Truncate = true, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) + } + } + } + } + } + } + }; + + Action = () => Active.Toggle(); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio, ISamplePlaybackDisabler? samplePlaybackDisabler) + { + sampleOn = audio.Samples.Get(@"UI/check-on"); + sampleOff = audio.Samples.Get(@"UI/check-off"); + + if (samplePlaybackDisabler != null) + ((IBindable)samplePlaybackDisabled).BindTo(samplePlaybackDisabler.SamplePlaybackDisabled); + } + + protected sealed override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); + + protected override void LoadComplete() + { + base.LoadComplete(); + Active.BindValueChanged(_ => + { + playStateChangeSamples(); + UpdateState(); + }); + + UpdateState(); + FinishTransforms(true); + } + + private void playStateChangeSamples() + { + if (samplePlaybackDisabled.Value) + return; + + if (Active.Value) + sampleOn?.Play(); + else + sampleOff?.Play(); + } + + protected override bool OnHover(HoverEvent e) + { + UpdateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + UpdateState(); + base.OnHoverLost(e); + } + + private bool mouseDown; + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (e.Button == MouseButton.Left) + mouseDown = true; + + UpdateState(); + return false; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + mouseDown = false; + + UpdateState(); + base.OnMouseUp(e); + } + + protected virtual void UpdateState() + { + float targetWidth = Active.Value ? ExpandedSwitchWidth : IdleSwitchWidth; + double transitionDuration = TRANSITION_DURATION; + + Colour4 backgroundColour = BackgroundColour; + Colour4 foregroundColour = ForegroundColour; + Colour4 textColour = TextColour; + + // Hover affects colour of button background + if (IsHovered) + { + backgroundColour = backgroundColour.Lighten(0.1f); + foregroundColour = foregroundColour.Lighten(0.1f); + } + + // Mouse down adds a halfway tween of the movement + if (mouseDown) + { + targetWidth = (float)Interpolation.Lerp(IdleSwitchWidth, ExpandedSwitchWidth, 0.5f); + transitionDuration *= 4; + } + + Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, foregroundColour), transitionDuration, Easing.OutQuint); + Background.FadeColour(backgroundColour, transitionDuration, Easing.OutQuint); + SwitchContainer.ResizeWidthTo(targetWidth, transitionDuration, Easing.OutQuint); + MainContentContainer.TransformTo(nameof(Padding), new MarginPadding + { + Left = targetWidth, + Right = CORNER_RADIUS + }, transitionDuration, Easing.OutQuint); + TextBackground.FadeColour(foregroundColour, transitionDuration, Easing.OutQuint); + TextFlow.FadeColour(textColour, transitionDuration, Easing.OutQuint); + } + } +}