mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 18:23:04 +08:00
Refactor ExpandingControlContainer
to no longer rely on controls
This commit is contained in:
parent
b2efce2656
commit
bbef12e72c
@ -1,20 +1,16 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Settings.Sections;
|
using osu.Game.Overlays.Settings.Sections;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
{
|
{
|
||||||
public class TestSceneExpandingControlContainer : OsuManualInputManagerTestScene
|
public class TestSceneExpandingContainer : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private TestExpandingContainer container;
|
private TestExpandingContainer container;
|
||||||
private SettingsToolboxGroup toolboxGroup;
|
private SettingsToolboxGroup toolboxGroup;
|
||||||
@ -84,44 +80,22 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHoveringControlExpandsContainer()
|
public void TestHoveringExpandsContainer()
|
||||||
{
|
{
|
||||||
AddAssert("ensure container contracted", () => !container.Expanded.Value);
|
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("container expanded", () => container.Expanded.Value);
|
||||||
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.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));
|
AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
|
||||||
AddAssert("container contracted", () => !container.Expanded.Value);
|
AddAssert("container contracted", () => !container.Expanded.Value);
|
||||||
AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.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>
|
/// <summary>
|
||||||
/// Tests expanding a container will expand underlying groups if contracted.
|
/// Tests expanding a container will expand underlying groups if contracted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -157,21 +131,21 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestExpandingContainerDoesntGetContractedByHover()
|
public void TestExpandingContainerDoesntGetContractedByHover()
|
||||||
{
|
{
|
||||||
AddStep("expand container", () => container.Expanded.Value = true);
|
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);
|
AddAssert("container still expanded", () => container.Expanded.Value);
|
||||||
|
|
||||||
AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
|
AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
|
||||||
AddAssert("container still expanded", () => container.Expanded.Value);
|
AddAssert("container still expanded", () => container.Expanded.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestExpandingContainer : ExpandingControlContainer<IExpandableControl>
|
private class TestExpandingContainer : ExpandingContainer
|
||||||
{
|
{
|
||||||
public TestExpandingContainer()
|
public TestExpandingContainer()
|
||||||
: base(120, 250)
|
: base(120, 250)
|
@ -17,7 +17,7 @@ namespace osu.Game.Overlays
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
|
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
|
||||||
/// </summary>
|
/// </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 T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
where TSlider : OsuSliderBar<T>, new()
|
where TSlider : OsuSliderBar<T>, new()
|
||||||
{
|
{
|
||||||
@ -72,8 +72,6 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public BindableBool Expanded { get; } = new BindableBool();
|
public BindableBool Expanded { get; } = new BindableBool();
|
||||||
|
|
||||||
bool IExpandable.ShouldBeExpanded => IsHovered || slider.IsDragged;
|
|
||||||
|
|
||||||
public override bool HandlePositionalInput => true;
|
public override bool HandlePositionalInput => true;
|
||||||
|
|
||||||
public ExpandableSlider()
|
public ExpandableSlider()
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Mostly used for buttons with explanatory labels, in which the label would display after a "long hover".
|
/// Mostly used for buttons with explanatory labels, in which the label would display after a "long hover".
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class ExpandingButtonContainer : ExpandingControlContainer<OsuButton>
|
public class ExpandingButtonContainer : ExpandingContainer
|
||||||
{
|
{
|
||||||
protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
|
protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
|
||||||
: base(contractedWidth, expandedWidth)
|
: base(contractedWidth, expandedWidth)
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <typeparam name="TControl">The type of UI control to lookup for hover expansion.</typeparam>
|
public class ExpandingContainer : Container, IExpandingContainer
|
||||||
public class ExpandingControlContainer<TControl> : Container, IExpandingContainer
|
|
||||||
where TControl : class, IDrawable
|
|
||||||
{
|
{
|
||||||
private readonly float contractedWidth;
|
private readonly float contractedWidth;
|
||||||
private readonly float expandedWidth;
|
private readonly float expandedWidth;
|
||||||
@ -33,7 +29,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
protected FillFlowContainer FillFlow { get; }
|
protected FillFlowContainer FillFlow { get; }
|
||||||
|
|
||||||
protected ExpandingControlContainer(float contractedWidth, float expandedWidth)
|
protected ExpandingContainer(float contractedWidth, float expandedWidth)
|
||||||
{
|
{
|
||||||
this.contractedWidth = contractedWidth;
|
this.contractedWidth = contractedWidth;
|
||||||
this.expandedWidth = expandedWidth;
|
this.expandedWidth = expandedWidth;
|
||||||
@ -57,7 +53,6 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate hoverExpandEvent;
|
private ScheduledDelegate hoverExpandEvent;
|
||||||
private TControl activeControl;
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
@ -69,61 +64,38 @@ namespace osu.Game.Overlays
|
|||||||
}, true);
|
}, 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)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
queueExpandIfHovering();
|
updateHoverExpansion();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
queueExpandIfHovering();
|
updateHoverExpansion();
|
||||||
return base.OnMouseMove(e);
|
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 (hoverExpandEvent != null)
|
||||||
if (activeControl != null && isControlActive(activeControl))
|
{
|
||||||
|
hoverExpandEvent?.Cancel();
|
||||||
|
hoverExpandEvent = null;
|
||||||
|
|
||||||
|
Expanded.Value = false;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// ..otherwise check whether a new control is hovered, and if so, queue a new hover operation.
|
base.OnHoverLost(e);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void updateHoverExpansion()
|
||||||
/// Whether the given control is currently active, by checking whether it's hovered or dragged.
|
|
||||||
/// </summary>
|
|
||||||
private bool isControlActive(TControl control)
|
|
||||||
{
|
{
|
||||||
if (control is IExpandable expandable)
|
hoverExpandEvent?.Cancel();
|
||||||
return expandable.ShouldBeExpanded;
|
|
||||||
|
|
||||||
return control.IsHovered || control.IsDragged;
|
if (IsHovered && !Expanded.Value)
|
||||||
|
hoverExpandEvent = Scheduler.AddDelayed(() => Expanded.Value = true, HoverExpansionDelay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
@ -16,15 +15,5 @@ namespace osu.Game.Overlays
|
|||||||
/// Whether this drawable is in an expanded state.
|
/// Whether this drawable is in an expanded state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BindableBool Expanded { get; }
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user