1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 00:47:24 +08:00

Refactor ExpandingControlContainer to no longer rely on controls

This commit is contained in:
Salman Ahmed 2022-02-04 05:45:12 +03:00
parent b2efce2656
commit bbef12e72c
6 changed files with 28 additions and 109 deletions

View File

@ -1,20 +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 System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Settings.Sections;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneExpandingControlContainer : OsuManualInputManagerTestScene
public class TestSceneExpandingContainer : OsuManualInputManagerTestScene
{
private TestExpandingContainer container;
private SettingsToolboxGroup toolboxGroup;
@ -84,44 +80,22 @@ namespace osu.Game.Tests.Visual.UserInterface
}
/// <summary>
/// Tests hovering over controls expands the parenting container appropriately and does not contract until hover is lost from container.
/// Tests hovering expands the container and does not contract until hover is lost.
/// </summary>
[Test]
public void TestHoveringControlExpandsContainer()
public void TestHoveringExpandsContainer()
{
AddAssert("ensure container contracted", () => !container.Expanded.Value);
AddStep("hover slider", () => InputManager.MoveMouseTo(slider1));
AddStep("hover container", () => InputManager.MoveMouseTo(container));
AddAssert("container expanded", () => container.Expanded.Value);
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
AddStep("hover group top", () => InputManager.MoveMouseTo(toolboxGroup.ScreenSpaceDrawQuad.TopLeft + new Vector2(5)));
AddAssert("container still expanded", () => container.Expanded.Value);
AddAssert("controls still 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 dragging a UI control (e.g. <see cref="OsuSliderBar{T}"/>) outside its parenting container does not contract it until dragging is finished.
/// </summary>
[Test]
public void TestDraggingControlOutsideDoesntContractContainer()
{
AddStep("hover slider", () => InputManager.MoveMouseTo(slider1));
AddAssert("container expanded", () => container.Expanded.Value);
AddStep("hover slider nub", () => InputManager.MoveMouseTo(slider1.ChildrenOfType<Nub>().Single()));
AddStep("hold slider nub", () => InputManager.PressButton(MouseButton.Left));
AddStep("drag outside container", () => InputManager.MoveMouseTo(Vector2.Zero));
AddAssert("container still expanded", () => container.Expanded.Value);
AddStep("release slider nub", () => InputManager.ReleaseButton(MouseButton.Left));
AddAssert("container contracted", () => !container.Expanded.Value);
}
/// <summary>
/// Tests expanding a container will expand underlying groups if contracted.
/// </summary>
@ -157,21 +131,21 @@ namespace osu.Game.Tests.Visual.UserInterface
}
/// <summary>
/// Tests expanding a container via <see cref="ExpandingControlContainer{TControl}.Expanded"/> does not get contracted by losing hover.
/// 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 control", () => InputManager.MoveMouseTo(slider1));
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 : ExpandingControlContainer<IExpandableControl>
private class TestExpandingContainer : ExpandingContainer
{
public TestExpandingContainer()
: base(120, 250)

View File

@ -17,7 +17,7 @@ namespace osu.Game.Overlays
/// <summary>
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
/// </summary>
public class ExpandableSlider<T, TSlider> : CompositeDrawable, IExpandableControl, IHasCurrentValue<T>
public class ExpandableSlider<T, TSlider> : CompositeDrawable, IExpandable, IHasCurrentValue<T>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
where TSlider : OsuSliderBar<T>, new()
{
@ -72,8 +72,6 @@ namespace osu.Game.Overlays
public BindableBool Expanded { get; } = new BindableBool();
bool IExpandable.ShouldBeExpanded => IsHovered || slider.IsDragged;
public override bool HandlePositionalInput => true;
public ExpandableSlider()

View File

@ -1,17 +1,15 @@
// 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.Game.Graphics.UserInterface;
namespace osu.Game.Overlays
{
/// <summary>
/// An <see cref="ExpandingControlContainer{TControl}"/> with a long hover expansion delay for buttons.
/// 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 : ExpandingControlContainer<OsuButton>
public class ExpandingButtonContainer : ExpandingContainer
{
protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
: base(contractedWidth, expandedWidth)

View File

@ -1,23 +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 System.Linq;
using osu.Framework.Bindables;
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;
namespace osu.Game.Overlays
{
/// <summary>
/// Represents a <see cref="Container"/> with the ability to expand/contract when hovering the controls within it.
/// Represents a <see cref="Container"/> with the ability to expand/contract on hover.
/// </summary>
/// <typeparam name="TControl">The type of UI control to lookup for hover expansion.</typeparam>
public class ExpandingControlContainer<TControl> : Container, IExpandingContainer
where TControl : class, IDrawable
public class ExpandingContainer : Container, IExpandingContainer
{
private readonly float contractedWidth;
private readonly float expandedWidth;
@ -33,7 +29,7 @@ namespace osu.Game.Overlays
protected FillFlowContainer FillFlow { get; }
protected ExpandingControlContainer(float contractedWidth, float expandedWidth)
protected ExpandingContainer(float contractedWidth, float expandedWidth)
{
this.contractedWidth = contractedWidth;
this.expandedWidth = expandedWidth;
@ -57,7 +53,6 @@ namespace osu.Game.Overlays
}
private ScheduledDelegate hoverExpandEvent;
private TControl activeControl;
protected override void LoadComplete()
{
@ -69,61 +64,38 @@ namespace osu.Game.Overlays
}, true);
}
protected override void Update()
{
base.Update();
// if the container was expanded from hovering over a control, we have to check per-frame whether we can contract it back.
// that's because contracting the container depends not only on whether it's no longer hovered,
// but also on whether the hovered control is no longer in a dragged state (if it was).
if (hoverExpandEvent != null && !IsHovered && (activeControl == null || !isControlActive(activeControl)))
{
hoverExpandEvent?.Cancel();
Expanded.Value = false;
hoverExpandEvent = null;
activeControl = null;
}
}
protected override bool OnHover(HoverEvent e)
{
queueExpandIfHovering();
updateHoverExpansion();
return true;
}
protected override bool OnMouseMove(MouseMoveEvent e)
{
queueExpandIfHovering();
updateHoverExpansion();
return base.OnMouseMove(e);
}
private void queueExpandIfHovering()
protected override void OnHoverLost(HoverLostEvent e)
{
// if the same control is hovered or dragged, let the scheduled expand play out..
if (activeControl != null && isControlActive(activeControl))
if (hoverExpandEvent != null)
{
hoverExpandEvent?.Cancel();
hoverExpandEvent = null;
Expanded.Value = false;
return;
}
// ..otherwise check whether a new control is hovered, and if so, queue a new hover operation.
hoverExpandEvent?.Cancel();
// usually we wouldn't use ChildrenOfType in implementations, but this is the simplest way
// to handle cases like the editor where the controls may be nested within a child hierarchy.
activeControl = FillFlow.ChildrenOfType<TControl>().FirstOrDefault(isControlActive);
if (activeControl != null && !Expanded.Value)
hoverExpandEvent = Scheduler.AddDelayed(() => Expanded.Value = true, HoverExpansionDelay);
base.OnHoverLost(e);
}
/// <summary>
/// Whether the given control is currently active, by checking whether it's hovered or dragged.
/// </summary>
private bool isControlActive(TControl control)
private void updateHoverExpansion()
{
if (control is IExpandable expandable)
return expandable.ShouldBeExpanded;
hoverExpandEvent?.Cancel();
return control.IsHovered || control.IsDragged;
if (IsHovered && !Expanded.Value)
hoverExpandEvent = Scheduler.AddDelayed(() => Expanded.Value = true, HoverExpansionDelay);
}
}
}

View File

@ -3,7 +3,6 @@
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays
{
@ -16,15 +15,5 @@ namespace osu.Game.Overlays
/// Whether this drawable is in an expanded state.
/// </summary>
BindableBool Expanded { get; }
/// <summary>
/// Whether this drawable should be/stay expanded by a parenting <see cref="IExpandingContainer"/>.
/// By default, this is <see langword="true"/> when this drawable is in a hovered or dragged state.
/// </summary>
/// <remarks>
/// This is defined for certain controls which may have a child handling dragging instead.
/// (e.g. <see cref="ExpandableSlider{T,TSlider}"/> in which dragging is handled by their underlying <see cref="OsuSliderBar{T}"/> control).
/// </remarks>
bool ShouldBeExpanded => IsHovered || IsDragged;
}
}

View File

@ -1,12 +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.
namespace osu.Game.Overlays
{
/// <summary>
/// An interface for UI controls with the ability to expand/contract.
/// </summary>
public interface IExpandableControl : IExpandable
{
}
}