Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hover to expand mod customise panel #29136

Merged
merged 17 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void TestCustomisationNotAvailable()

AddStep("select difficulty adjust", () => freeModSelectOverlay.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() });
AddWaitStep("wait some", 3);
AddAssert("customisation area not expanded", () => !this.ChildrenOfType<ModCustomisationPanel>().Single().Expanded.Value);
AddAssert("customisation area not expanded", () => !this.ChildrenOfType<ModCustomisationPanel>().Single().Expanded);
}

[Test]
Expand Down
103 changes: 99 additions & 4 deletions osu.Game.Tests/Visual/UserInterface/TestSceneModCustomisationPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
Expand All @@ -10,6 +11,8 @@
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osuTK;
using osuTK.Input;

namespace osu.Game.Tests.Visual.UserInterface
{
Expand All @@ -19,10 +22,15 @@ public partial class TestSceneModCustomisationPanel : OsuManualInputManagerTestS
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);

private ModCustomisationPanel panel = null!;
private ModCustomisationHeader header = null!;
private Container content = null!;

[SetUp]
public void SetUp() => Schedule(() =>
{
SelectedMods.Value = Array.Empty<Mod>();
InputManager.MoveMouseTo(Vector2.One);

Child = new Container
{
RelativeSizeAxes = Axes.Both,
Expand All @@ -36,6 +44,9 @@ public void SetUp() => Schedule(() =>
SelectedMods = { BindTarget = SelectedMods },
}
};

header = panel.Children.OfType<ModCustomisationHeader>().First();
content = panel.Children.OfType<Container>().First();
});

[Test]
Expand All @@ -44,23 +55,107 @@ public void TestDisplay()
AddStep("set DT", () =>
{
SelectedMods.Value = new[] { new OsuModDoubleTime() };
panel.Enabled.Value = panel.Expanded.Value = true;
panel.Enabled.Value = panel.Expanded = true;
});
AddStep("set DA", () =>
{
SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() };
panel.Enabled.Value = panel.Expanded.Value = true;
panel.Enabled.Value = panel.Expanded = true;
});
AddStep("set FL+WU+DA+AD", () =>
{
SelectedMods.Value = new Mod[] { new OsuModFlashlight(), new ModWindUp(), new OsuModDifficultyAdjust(), new OsuModApproachDifferent() };
panel.Enabled.Value = panel.Expanded.Value = true;
panel.Enabled.Value = panel.Expanded = true;
});
AddStep("set empty", () =>
{
SelectedMods.Value = Array.Empty<Mod>();
panel.Enabled.Value = panel.Expanded.Value = false;
panel.Enabled.Value = panel.Expanded = false;
});
}

[Test]
public void TestHoverDoesNotExpandWhenNoCustomisableMods()
{
AddStep("hover header", () => InputManager.MoveMouseTo(header));

checkExpanded(false);

AddStep("hover content", () => InputManager.MoveMouseTo(content));

checkExpanded(false);

AddStep("left from content", () => InputManager.MoveMouseTo(Vector2.One));
}

[Test]
public void TestHoverExpandsWithCustomisableMods()
{
AddStep("add customisable mod", () =>
{
SelectedMods.Value = new[] { new OsuModDoubleTime() };
panel.Enabled.Value = true;
});

AddStep("hover header", () => InputManager.MoveMouseTo(header));
checkExpanded(true);

AddStep("move to content", () => InputManager.MoveMouseTo(content));
checkExpanded(true);

AddStep("move away", () => InputManager.MoveMouseTo(Vector2.One));
checkExpanded(false);

AddStep("hover header", () => InputManager.MoveMouseTo(header));
checkExpanded(true);

AddStep("move away", () => InputManager.MoveMouseTo(Vector2.One));
checkExpanded(false);
}

[Test]
public void TestExpandedStatePersistsWhenClicked()
{
AddStep("add customisable mod", () =>
{
SelectedMods.Value = new[] { new OsuModDoubleTime() };
panel.Enabled.Value = true;
});

AddStep("hover header", () => InputManager.MoveMouseTo(header));
checkExpanded(true);

AddStep("click", () => InputManager.Click(MouseButton.Left));
checkExpanded(false);
AddStep("click", () => InputManager.Click(MouseButton.Left));
checkExpanded(true);

AddStep("move away", () => InputManager.MoveMouseTo(Vector2.One));
checkExpanded(true);

AddStep("click", () => InputManager.Click(MouseButton.Left));
checkExpanded(false);
}

[Test]
public void TestHoverExpandsAndCollapsesWhenHeaderClicked()
{
AddStep("add customisable mod", () =>
{
SelectedMods.Value = new[] { new OsuModDoubleTime() };
panel.Enabled.Value = true;
});

AddStep("hover header", () => InputManager.MoveMouseTo(header));
checkExpanded(true);

AddStep("click", () => InputManager.Click(MouseButton.Left));
checkExpanded(false);
}

private void checkExpanded(bool expanded)
{
AddUntilStep(expanded ? "is expanded" : "not expanded", () => panel.Expanded, () => Is.EqualTo(expanded));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public void SetUpSteps()
AddStep("reset ruleset", () => Ruleset.Value = rulesetStore.GetRuleset(0));
AddStep("reset mods", () => SelectedMods.SetDefault());
AddStep("reset config", () => configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, true));
AddStep("reset mouse", () => InputManager.MoveMouseTo(Vector2.One));
AddStep("set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo));
AddStep("set up presets", () =>
{
Expand Down Expand Up @@ -998,7 +999,7 @@ public void TestCustomisationPanelAbsorbsInput([Values] bool textSearchStartsAct
AddStep("press mouse", () => InputManager.PressButton(MouseButton.Left));
AddAssert("search still not focused", () => !this.ChildrenOfType<ShearedSearchTextBox>().Single().HasFocus);
AddStep("release mouse", () => InputManager.ReleaseButton(MouseButton.Left));
AddAssert("customisation panel closed by click", () => !this.ChildrenOfType<ModCustomisationPanel>().Single().Expanded.Value);
AddAssert("customisation panel closed by click", () => !this.ChildrenOfType<ModCustomisationPanel>().Single().Expanded);

if (textSearchStartsActive)
AddAssert("search focused", () => this.ChildrenOfType<ShearedSearchTextBox>().Single().HasFocus);
Expand All @@ -1021,7 +1022,7 @@ private void changeRuleset(int id)
private void assertCustomisationToggleState(bool disabled, bool active)
{
AddUntilStep($"customisation panel is {(disabled ? "" : "not ")}disabled", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single().Enabled.Value == !disabled);
AddAssert($"customisation panel is {(active ? "" : "not ")}active", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single().Expanded.Value == active);
AddAssert($"customisation panel is {(active ? "" : "not ")}active", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single().Expanded == active);
}

private T getSelectedMod<T>() where T : Mod => SelectedMods.Value.OfType<T>().Single();
Expand Down
52 changes: 47 additions & 5 deletions osu.Game/Overlays/Mods/ModCustomisationHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Localisation;
using osuTK;
using osuTK.Graphics;
using static osu.Game.Overlays.Mods.ModCustomisationPanel;

namespace osu.Game.Overlays.Mods
{
Expand All @@ -29,11 +31,13 @@ public partial class ModCustomisationHeader : OsuHoverContainer

protected override IEnumerable<Drawable> EffectTargets => new[] { background };

public readonly BindableBool Expanded = new BindableBool();
public readonly Bindable<ModCustomisationPanelState> ExpandedState = new Bindable<ModCustomisationPanelState>(ModCustomisationPanelState.Collapsed);

public ModCustomisationHeader()
private readonly ModCustomisationPanel panel;

public ModCustomisationHeader(ModCustomisationPanel panel)
{
Action = Expanded.Toggle;
this.panel = panel;
Enabled.Value = false;
}

Expand Down Expand Up @@ -102,10 +106,48 @@ protected override void LoadComplete()
}
}, true);

Expanded.BindValueChanged(v =>
ExpandedState.BindValueChanged(v =>
{
icon.ScaleTo(v.NewValue ? new Vector2(1, -1) : Vector2.One, 300, Easing.OutQuint);
icon.ScaleTo(v.NewValue > ModCustomisationPanelState.Collapsed ? new Vector2(1, -1) : Vector2.One, 300, Easing.OutQuint);
}, true);
}

protected override bool OnClick(ClickEvent e)
{
if (Enabled.Value)
{
ExpandedState.Value = ExpandedState.Value switch
{
ModCustomisationPanelState.Collapsed => ModCustomisationPanelState.Expanded,
_ => ModCustomisationPanelState.Collapsed
};
}

return base.OnClick(e);
}

private bool touchedThisFrame;

protected override bool OnTouchDown(TouchDownEvent e)
{
if (Enabled.Value)
{
touchedThisFrame = true;
Schedule(() => touchedThisFrame = false);
}

return base.OnTouchDown(e);
}

protected override bool OnHover(HoverEvent e)
{
if (Enabled.Value)
{
if (!touchedThisFrame)
panel.UpdateHoverExpansion(ModCustomisationPanelState.ExpandedByHover);
}

return base.OnHover(e);
}
}
}
Loading
Loading