mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 03:22:55 +08:00
Merge pull request #16574 from frenzibyte/expandable-controls
Abstractify `ExpandingButtonContainer` and support contraction of settings controls
This commit is contained in:
commit
4c8018a675
@ -19,6 +19,10 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
|
|
||||||
AddAssert("no screen offset applied", () => Game.ScreenOffsetContainer.X == 0f);
|
AddAssert("no screen offset applied", () => Game.ScreenOffsetContainer.X == 0f);
|
||||||
|
|
||||||
|
// avoids mouse interacting with settings overlay.
|
||||||
|
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre));
|
||||||
|
|
||||||
AddUntilStep("wait for overlays", () => Game.Settings.IsLoaded && Game.Notifications.IsLoaded);
|
AddUntilStep("wait for overlays", () => Game.Settings.IsLoaded && Game.Notifications.IsLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,158 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Settings.Sections;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public class TestSceneExpandingContainer : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private TestExpandingContainer container;
|
||||||
|
private SettingsToolboxGroup toolboxGroup;
|
||||||
|
|
||||||
|
private ExpandableSlider<float, SizeSlider> slider1;
|
||||||
|
private ExpandableSlider<double> slider2;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
Child = container = new TestExpandingContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Height = 0.33f,
|
||||||
|
Child = toolboxGroup = new SettingsToolboxGroup("sliders")
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 1,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
slider1 = new ExpandableSlider<float, SizeSlider>
|
||||||
|
{
|
||||||
|
Current = new BindableFloat
|
||||||
|
{
|
||||||
|
Default = 1.0f,
|
||||||
|
MinValue = 1.0f,
|
||||||
|
MaxValue = 10.0f,
|
||||||
|
Precision = 0.01f,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slider2 = new ExpandableSlider<double>
|
||||||
|
{
|
||||||
|
Current = new BindableDouble
|
||||||
|
{
|
||||||
|
Default = 1.0,
|
||||||
|
MinValue = 1.0,
|
||||||
|
MaxValue = 10.0,
|
||||||
|
Precision = 0.01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
slider1.Current.BindValueChanged(v =>
|
||||||
|
{
|
||||||
|
slider1.ExpandedLabelText = $"Slider One ({v.NewValue:0.##x})";
|
||||||
|
slider1.ContractedLabelText = $"S. 1. ({v.NewValue:0.##x})";
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
slider2.Current.BindValueChanged(v =>
|
||||||
|
{
|
||||||
|
slider2.ExpandedLabelText = $"Slider Two ({v.NewValue:N2})";
|
||||||
|
slider2.ContractedLabelText = $"S. 2. ({v.NewValue:N2})";
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDisplay()
|
||||||
|
{
|
||||||
|
AddStep("switch to contracted", () => container.Expanded.Value = false);
|
||||||
|
AddStep("switch to expanded", () => container.Expanded.Value = true);
|
||||||
|
AddStep("set left origin", () => container.Origin = Anchor.CentreLeft);
|
||||||
|
AddStep("set centre origin", () => container.Origin = Anchor.Centre);
|
||||||
|
AddStep("set right origin", () => container.Origin = Anchor.CentreRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests hovering expands the container and does not contract until hover is lost.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestHoveringExpandsContainer()
|
||||||
|
{
|
||||||
|
AddAssert("ensure container contracted", () => !container.Expanded.Value);
|
||||||
|
|
||||||
|
AddStep("hover container", () => InputManager.MoveMouseTo(container));
|
||||||
|
AddAssert("container expanded", () => container.Expanded.Value);
|
||||||
|
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
|
||||||
|
|
||||||
|
AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
|
||||||
|
AddAssert("container contracted", () => !container.Expanded.Value);
|
||||||
|
AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests expanding a container will expand underlying groups if contracted.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestExpandingContainerExpandsContractedGroup()
|
||||||
|
{
|
||||||
|
AddStep("contract group", () => toolboxGroup.Expanded.Value = false);
|
||||||
|
|
||||||
|
AddStep("expand container", () => container.Expanded.Value = true);
|
||||||
|
AddAssert("group expanded", () => toolboxGroup.Expanded.Value);
|
||||||
|
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
|
||||||
|
|
||||||
|
AddStep("contract container", () => container.Expanded.Value = false);
|
||||||
|
AddAssert("group contracted", () => !toolboxGroup.Expanded.Value);
|
||||||
|
AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests contracting a container does not contract underlying groups if expanded by user (i.e. by setting <see cref="SettingsToolboxGroup.Expanded"/> directly).
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestContractingContainerDoesntContractUserExpandedGroup()
|
||||||
|
{
|
||||||
|
AddAssert("ensure group expanded", () => toolboxGroup.Expanded.Value);
|
||||||
|
|
||||||
|
AddStep("expand container", () => container.Expanded.Value = true);
|
||||||
|
AddAssert("group still expanded", () => toolboxGroup.Expanded.Value);
|
||||||
|
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
|
||||||
|
|
||||||
|
AddStep("contract container", () => container.Expanded.Value = false);
|
||||||
|
AddAssert("group still expanded", () => toolboxGroup.Expanded.Value);
|
||||||
|
AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests expanding a container via <see cref="ExpandingContainer.Expanded"/> does not get contracted by losing hover.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestExpandingContainerDoesntGetContractedByHover()
|
||||||
|
{
|
||||||
|
AddStep("expand container", () => container.Expanded.Value = true);
|
||||||
|
|
||||||
|
AddStep("hover container", () => InputManager.MoveMouseTo(container));
|
||||||
|
AddAssert("container still expanded", () => container.Expanded.Value);
|
||||||
|
|
||||||
|
AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
|
||||||
|
AddAssert("container still expanded", () => container.Expanded.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestExpandingContainer : ExpandingContainer
|
||||||
|
{
|
||||||
|
public TestExpandingContainer()
|
||||||
|
: base(120, 250)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
osu.Game/Graphics/Containers/ExpandingButtonContainer.cs
Normal file
21
osu.Game/Graphics/Containers/ExpandingButtonContainer.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Containers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="ExpandingContainer"/> with a long hover expansion delay.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Mostly used for buttons with explanatory labels, in which the label would display after a "long hover".
|
||||||
|
/// </remarks>
|
||||||
|
public class ExpandingButtonContainer : ExpandingContainer
|
||||||
|
{
|
||||||
|
protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
|
||||||
|
: base(contractedWidth, expandedWidth)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double HoverExpansionDelay => 400;
|
||||||
|
}
|
||||||
|
}
|
100
osu.Game/Graphics/Containers/ExpandingContainer.cs
Normal file
100
osu.Game/Graphics/Containers/ExpandingContainer.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Containers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a <see cref="Container"/> with the ability to expand/contract on hover.
|
||||||
|
/// </summary>
|
||||||
|
public class ExpandingContainer : Container, IExpandingContainer
|
||||||
|
{
|
||||||
|
private readonly float contractedWidth;
|
||||||
|
private readonly float expandedWidth;
|
||||||
|
|
||||||
|
public BindableBool Expanded { get; } = new BindableBool();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delay before the container switches to expanded state from hover.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual double HoverExpansionDelay => 0;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => FillFlow;
|
||||||
|
|
||||||
|
protected FillFlowContainer FillFlow { get; }
|
||||||
|
|
||||||
|
protected ExpandingContainer(float contractedWidth, float expandedWidth)
|
||||||
|
{
|
||||||
|
this.contractedWidth = contractedWidth;
|
||||||
|
this.expandedWidth = expandedWidth;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
Width = contractedWidth;
|
||||||
|
|
||||||
|
InternalChild = new OsuScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ScrollbarVisible = false,
|
||||||
|
Child = FillFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScheduledDelegate hoverExpandEvent;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Expanded.BindValueChanged(v =>
|
||||||
|
{
|
||||||
|
this.ResizeWidthTo(v.NewValue ? expandedWidth : contractedWidth, 500, Easing.OutQuint);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
updateHoverExpansion();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
|
{
|
||||||
|
updateHoverExpansion();
|
||||||
|
return base.OnMouseMove(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
if (hoverExpandEvent != null)
|
||||||
|
{
|
||||||
|
hoverExpandEvent?.Cancel();
|
||||||
|
hoverExpandEvent = null;
|
||||||
|
|
||||||
|
Expanded.Value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateHoverExpansion()
|
||||||
|
{
|
||||||
|
hoverExpandEvent?.Cancel();
|
||||||
|
|
||||||
|
if (IsHovered && !Expanded.Value)
|
||||||
|
hoverExpandEvent = Scheduler.AddDelayed(() => Expanded.Value = true, HoverExpansionDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
osu.Game/Graphics/Containers/IExpandable.cs
Normal file
19
osu.Game/Graphics/Containers/IExpandable.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Containers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An interface for drawables with ability to expand/contract.
|
||||||
|
/// </summary>
|
||||||
|
public interface IExpandable : IDrawable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this drawable is in an expanded state.
|
||||||
|
/// </summary>
|
||||||
|
BindableBool Expanded { get; }
|
||||||
|
}
|
||||||
|
}
|
16
osu.Game/Graphics/Containers/IExpandingContainer.cs
Normal file
16
osu.Game/Graphics/Containers/IExpandingContainer.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 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.Graphics.Containers;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Containers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A target expanding container that should be resolved by children <see cref="IExpandable"/>s to propagate state changes.
|
||||||
|
/// </summary>
|
||||||
|
[Cached(typeof(IExpandingContainer))]
|
||||||
|
public interface IExpandingContainer : IContainer, IExpandable
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
126
osu.Game/Graphics/UserInterface/ExpandableSlider.cs
Normal file
126
osu.Game/Graphics/UserInterface/ExpandableSlider.cs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
// 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;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
|
||||||
|
/// </summary>
|
||||||
|
public class ExpandableSlider<T, TSlider> : CompositeDrawable, IExpandable, IHasCurrentValue<T>
|
||||||
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
|
where TSlider : OsuSliderBar<T>, new()
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText label;
|
||||||
|
private readonly TSlider slider;
|
||||||
|
|
||||||
|
private LocalisableString contractedLabelText;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The label text to display when this slider is in a contracted state.
|
||||||
|
/// </summary>
|
||||||
|
public LocalisableString ContractedLabelText
|
||||||
|
{
|
||||||
|
get => contractedLabelText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == contractedLabelText)
|
||||||
|
return;
|
||||||
|
|
||||||
|
contractedLabelText = value;
|
||||||
|
|
||||||
|
if (!Expanded.Value)
|
||||||
|
label.Text = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalisableString expandedLabelText;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The label text to display when this slider is in an expanded state.
|
||||||
|
/// </summary>
|
||||||
|
public LocalisableString ExpandedLabelText
|
||||||
|
{
|
||||||
|
get => expandedLabelText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == expandedLabelText)
|
||||||
|
return;
|
||||||
|
|
||||||
|
expandedLabelText = value;
|
||||||
|
|
||||||
|
if (Expanded.Value)
|
||||||
|
label.Text = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bindable<T> Current
|
||||||
|
{
|
||||||
|
get => slider.Current;
|
||||||
|
set => slider.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindableBool Expanded { get; } = new BindableBool();
|
||||||
|
|
||||||
|
public override bool HandlePositionalInput => true;
|
||||||
|
|
||||||
|
public ExpandableSlider()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(0f, 10f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
label = new OsuSpriteText(),
|
||||||
|
slider = new TSlider
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private IExpandingContainer expandingContainer { get; set; }
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
|
||||||
|
{
|
||||||
|
Expanded.Value = containerExpanded.NewValue;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
Expanded.BindValueChanged(v =>
|
||||||
|
{
|
||||||
|
label.Text = v.NewValue ? expandedLabelText : contractedLabelText;
|
||||||
|
slider.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint);
|
||||||
|
slider.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
|
||||||
|
/// </summary>
|
||||||
|
public class ExpandableSlider<T> : ExpandableSlider<T, OsuSliderBar<T>>
|
||||||
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,141 +0,0 @@
|
|||||||
// 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;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
|
||||||
{
|
|
||||||
public abstract class ExpandingButtonContainer : Container, IStateful<ExpandedState>
|
|
||||||
{
|
|
||||||
private readonly float contractedWidth;
|
|
||||||
private readonly float expandedWidth;
|
|
||||||
|
|
||||||
public event Action<ExpandedState> StateChanged;
|
|
||||||
|
|
||||||
protected override Container<Drawable> Content => FillFlow;
|
|
||||||
|
|
||||||
protected FillFlowContainer FillFlow { get; }
|
|
||||||
|
|
||||||
protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
|
|
||||||
{
|
|
||||||
this.contractedWidth = contractedWidth;
|
|
||||||
this.expandedWidth = expandedWidth;
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Y;
|
|
||||||
Width = contractedWidth;
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
new SidebarScrollContainer
|
|
||||||
{
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
FillFlow = new FillFlowContainer
|
|
||||||
{
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScheduledDelegate expandEvent;
|
|
||||||
private ExpandedState state;
|
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
|
||||||
{
|
|
||||||
queueExpandIfHovering();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
|
||||||
{
|
|
||||||
expandEvent?.Cancel();
|
|
||||||
hoveredButton = null;
|
|
||||||
State = ExpandedState.Contracted;
|
|
||||||
|
|
||||||
base.OnHoverLost(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
|
||||||
{
|
|
||||||
queueExpandIfHovering();
|
|
||||||
return base.OnMouseMove(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SidebarScrollContainer : OsuScrollContainer
|
|
||||||
{
|
|
||||||
public SidebarScrollContainer()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
ScrollbarVisible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExpandedState State
|
|
||||||
{
|
|
||||||
get => state;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
expandEvent?.Cancel();
|
|
||||||
|
|
||||||
if (state == value) return;
|
|
||||||
|
|
||||||
state = value;
|
|
||||||
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
this.ResizeTo(new Vector2(contractedWidth, Height), 500, Easing.OutQuint);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ExpandedState.Expanded:
|
|
||||||
this.ResizeTo(new Vector2(expandedWidth, Height), 500, Easing.OutQuint);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChanged?.Invoke(State);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Drawable hoveredButton;
|
|
||||||
|
|
||||||
private void queueExpandIfHovering()
|
|
||||||
{
|
|
||||||
// if the same button is hovered, let the scheduled expand play out..
|
|
||||||
if (hoveredButton?.IsHovered == true)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// ..otherwise check whether a new button is hovered, and if so, queue a new hover operation.
|
|
||||||
|
|
||||||
// usually we wouldn't use ChildrenOfType in implementations, but this is the simplest way
|
|
||||||
// to handle cases like the editor where the buttons may be nested within a child hierarchy.
|
|
||||||
hoveredButton = FillFlow.ChildrenOfType<OsuButton>().FirstOrDefault(c => c.IsHovered);
|
|
||||||
|
|
||||||
expandEvent?.Cancel();
|
|
||||||
|
|
||||||
if (hoveredButton?.IsHovered == true && State != ExpandedState.Expanded)
|
|
||||||
expandEvent = Scheduler.AddDelayed(() => State = ExpandedState.Expanded, 750);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ExpandedState
|
|
||||||
{
|
|
||||||
Contracted,
|
|
||||||
Expanded,
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
|
@ -265,7 +265,7 @@ namespace osu.Game.Overlays
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
SectionsContainer.ScrollTo(section);
|
SectionsContainer.ScrollTo(section);
|
||||||
Sidebar.State = ExpandedState.Contracted;
|
Sidebar.Expanded.Value = false;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -11,6 +12,7 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Layout;
|
using osu.Framework.Layout;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -18,7 +20,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public abstract class SettingsToolboxGroup : Container
|
public class SettingsToolboxGroup : Container, IExpandable
|
||||||
{
|
{
|
||||||
private const float transition_duration = 250;
|
private const float transition_duration = 250;
|
||||||
private const int container_width = 270;
|
private const int container_width = 270;
|
||||||
@ -34,30 +36,7 @@ namespace osu.Game.Overlays
|
|||||||
private readonly FillFlowContainer content;
|
private readonly FillFlowContainer content;
|
||||||
private readonly IconButton button;
|
private readonly IconButton button;
|
||||||
|
|
||||||
private bool expanded = true;
|
public BindableBool Expanded { get; } = new BindableBool(true);
|
||||||
|
|
||||||
public bool Expanded
|
|
||||||
{
|
|
||||||
get => expanded;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (expanded == value) return;
|
|
||||||
|
|
||||||
expanded = value;
|
|
||||||
|
|
||||||
content.ClearTransforms();
|
|
||||||
|
|
||||||
if (expanded)
|
|
||||||
content.AutoSizeAxes = Axes.Y;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
content.AutoSizeAxes = Axes.None;
|
|
||||||
content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateExpanded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Color4 expandedColour;
|
private Color4 expandedColour;
|
||||||
|
|
||||||
@ -67,7 +46,7 @@ namespace osu.Game.Overlays
|
|||||||
/// Create a new instance.
|
/// Create a new instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="title">The title to be displayed in the header of this group.</param>
|
/// <param name="title">The title to be displayed in the header of this group.</param>
|
||||||
protected SettingsToolboxGroup(string title)
|
public SettingsToolboxGroup(string title)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
Width = container_width;
|
Width = container_width;
|
||||||
@ -115,7 +94,7 @@ namespace osu.Game.Overlays
|
|||||||
Position = new Vector2(-15, 0),
|
Position = new Vector2(-15, 0),
|
||||||
Icon = FontAwesome.Solid.Bars,
|
Icon = FontAwesome.Solid.Bars,
|
||||||
Scale = new Vector2(0.75f),
|
Scale = new Vector2(0.75f),
|
||||||
Action = () => Expanded = !Expanded,
|
Action = () => Expanded.Toggle(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -155,23 +134,58 @@ namespace osu.Game.Overlays
|
|||||||
headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint);
|
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()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
this.Delay(600).FadeTo(inactive_alpha, fade_duration, Easing.OutQuint);
|
expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
|
||||||
updateExpanded();
|
{
|
||||||
|
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)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
this.FadeIn(fade_duration, Easing.OutQuint);
|
updateActiveState();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
{
|
{
|
||||||
this.FadeTo(inactive_alpha, fade_duration, Easing.OutQuint);
|
updateActiveState();
|
||||||
base.OnHoverLost(e);
|
base.OnHoverLost(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +195,10 @@ namespace osu.Game.Overlays
|
|||||||
expandedColour = colours.Yellow;
|
expandedColour = colours.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateExpanded() => button.FadeColour(expanded ? expandedColour : Color4.White, 200, Easing.InOutQuint);
|
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 Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ using osu.Framework.Input;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Configuration;
|
using osu.Game.Rulesets.Configuration;
|
||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
//CollectionSettings = new CollectionSettings(),
|
//CollectionSettings = new CollectionSettings(),
|
||||||
//DiscussionSettings = new DiscussionSettings(),
|
//DiscussionSettings = new DiscussionSettings(),
|
||||||
PlaybackSettings = new PlaybackSettings(),
|
PlaybackSettings = new PlaybackSettings(),
|
||||||
VisualSettings = new VisualSettings { Expanded = false }
|
VisualSettings = new VisualSettings { Expanded = { Value = false } }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user