osu/osu.Game/Overlays/SettingsToolboxGroup.cs

207 lines
7.3 KiB
C#
Raw Normal View History

// 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.Bindables;
using osu.Framework.Caching;
using osu.Framework.Extensions.EnumExtensions;
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.Framework.Layout;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
public class SettingsToolboxGroup : Container, IExpandable
{
private const float transition_duration = 250;
private const int container_width = 270;
private const int border_thickness = 2;
private const int header_height = 30;
private const int corner_radius = 5;
private const float fade_duration = 800;
private const float inactive_alpha = 0.5f;
private readonly Cached headerTextVisibilityCache = new Cached();
private readonly FillFlowContainer content;
private readonly IconButton button;
public BindableBool Expanded { get; } = new BindableBool(true);
private Color4 expandedColour;
private readonly OsuSpriteText headerText;
/// <summary>
/// Create a new instance.
/// </summary>
/// <param name="title">The title to be displayed in the header of this group.</param>
public SettingsToolboxGroup(string title)
{
AutoSizeAxes = Axes.Y;
Width = container_width;
Masking = true;
CornerRadius = corner_radius;
BorderColour = Color4.Black;
BorderThickness = border_thickness;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new FillFlowContainer
{
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Container
{
Name = @"Header",
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Height = header_height,
Children = new Drawable[]
{
headerText = new OsuSpriteText
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = title.ToUpperInvariant(),
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17),
Padding = new MarginPadding { Left = 10, Right = 30 },
},
button = new IconButton
{
Origin = Anchor.Centre,
Anchor = Anchor.CentreRight,
Position = new Vector2(-15, 0),
Icon = FontAwesome.Solid.Bars,
Scale = new Vector2(0.75f),
Action = () => Expanded.Toggle(),
},
}
},
content = new FillFlowContainer
{
Name = @"Content",
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeDuration = transition_duration,
AutoSizeEasing = Easing.OutQuint,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(15),
Spacing = new Vector2(0, 15),
}
}
},
};
}
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
{
if (invalidation.HasFlagFast(Invalidation.DrawSize))
headerTextVisibilityCache.Invalidate();
return base.OnInvalidate(invalidation, source);
}
protected override void Update()
{
base.Update();
if (!headerTextVisibilityCache.IsValid)
// These toolbox grouped may be contracted to only show icons.
// For now, let's hide the header to avoid text truncation weirdness in such cases.
headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint);
}
[Resolved(canBeNull: true)]
private IExpandingContainer expandingContainer { get; set; }
private bool expandedByContainer;
protected override void LoadComplete()
{
base.LoadComplete();
Fix settings toolbox toggle button starting in incorrect state While displaying replays, the colour of the toolbox toggle button would not match the actual state of the rest of the toolbox, i.e. both buttons would be white, even though the "playback settings" section was expanded and as such should have a yellow toggle button. In the case of the replay player, the failure scenario was as follows: 1. `SettingsToolboxGroup` calls `updateExpanded()` in its BDL to update the initial state of the toolbox, including the toggle button colour, by adding a colour fade transform. 2. An ancestor of both the toolbox groups - `PlayerSettingsOverlay`, which is a `VisibilityContainer` - calls `FinishTransforms(true)` in its `LoadCompleteAsync()`, therefore instantly applying the colour from point (1) to the toggle button instantly. 3. However, `IconButton` inherits from `OsuAnimatedButton`. And `OsuAnimatedButton` changes its colour in `LoadComplete()`, therefore undoing the instant application from point (2). This conjunction of circumstances is instrumental to reproducing the bug, because if the `FinishTransforms(true)` call wasn't there, point (3) wouldn't matter - the transform would get applied at some indeterminate point in the future, ignoring the write from `OsuAnimatedButton`. As for the fix, move the `updateExpanded()` call in `SettingsToolboxGroup` to `LoadComplete()` to avoid the above unfortunate order. Applying initial visual state in `LoadComplete()` is the idiomatic style of doing things these days anyhow.
2022-01-06 19:25:03 +00:00
expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
{
if (containerExpanded.NewValue && !Expanded.Value)
{
Expanded.Value = true;
expandedByContainer = true;
}
else if (!containerExpanded.NewValue && expandedByContainer)
{
Expanded.Value = false;
expandedByContainer = false;
}
updateActiveState();
}, true);
Expanded.BindValueChanged(v =>
{
content.ClearTransforms();
if (v.NewValue)
content.AutoSizeAxes = Axes.Y;
else
{
content.AutoSizeAxes = Axes.None;
content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
}
button.FadeColour(Expanded.Value ? expandedColour : Color4.White, 200, Easing.InOutQuint);
}, true);
this.Delay(600).Schedule(updateActiveState);
}
protected override bool OnHover(HoverEvent e)
{
updateActiveState();
return false;
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateActiveState();
base.OnHoverLost(e);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
expandedColour = colours.Yellow;
}
private void updateActiveState()
{
this.FadeTo(IsHovered || expandingContainer?.Expanded.Value == true ? 1 : inactive_alpha, fade_duration, Easing.OutQuint);
}
protected override Container<Drawable> Content => content;
protected override bool OnMouseDown(MouseDownEvent e) => true;
}
}