mirror of
https://github.com/osukey/osukey.git
synced 2025-05-17 03:27:21 +09:00
Merge pull request #17973 from bdach/mod-overlay/dim-columns-offscreen
Dim offscreen columns on new mod select overlay
This commit is contained in:
commit
652e022fd6
@ -111,6 +111,33 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
&& SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModMirror)));
|
&& SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModMirror)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDimmedState()
|
||||||
|
{
|
||||||
|
createScreen();
|
||||||
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddUntilStep("any column dimmed", () => this.ChildrenOfType<ModColumn>().Any(column => !column.Active.Value));
|
||||||
|
|
||||||
|
ModColumn lastColumn = null;
|
||||||
|
|
||||||
|
AddAssert("last column dimmed", () => !this.ChildrenOfType<ModColumn>().Last().Active.Value);
|
||||||
|
AddStep("request scroll to last column", () =>
|
||||||
|
{
|
||||||
|
var lastDimContainer = this.ChildrenOfType<ModSelectScreen.ColumnDimContainer>().Last();
|
||||||
|
lastColumn = lastDimContainer.Column;
|
||||||
|
lastDimContainer.RequestScroll?.Invoke(lastDimContainer);
|
||||||
|
});
|
||||||
|
AddUntilStep("column undimmed", () => lastColumn.Active.Value);
|
||||||
|
|
||||||
|
AddStep("click panel", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(lastColumn.ChildrenOfType<ModPanel>().First());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("panel selected", () => lastColumn.ChildrenOfType<ModPanel>().First().Active.Value);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCustomisationToggleState()
|
public void TestCustomisationToggleState()
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -57,6 +58,26 @@ namespace osu.Game.Graphics.Containers
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolls a <see cref="Drawable"/> into view.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="d">The <see cref="Drawable"/> to scroll into view.</param>
|
||||||
|
/// <param name="animated">Whether to animate the movement.</param>
|
||||||
|
/// <param name="extraScroll">An added amount to scroll beyond the requirement to bring the target into view.</param>
|
||||||
|
public void ScrollIntoView(Drawable d, bool animated = true, float extraScroll = 0)
|
||||||
|
{
|
||||||
|
float childPos0 = GetChildPosInContent(d);
|
||||||
|
float childPos1 = GetChildPosInContent(d, d.DrawSize);
|
||||||
|
|
||||||
|
float minPos = Math.Min(childPos0, childPos1);
|
||||||
|
float maxPos = Math.Max(childPos0, childPos1);
|
||||||
|
|
||||||
|
if (minPos < Current || (minPos > Current && d.DrawSize[ScrollDim] > DisplayableContent))
|
||||||
|
ScrollTo(minPos - extraScroll, animated);
|
||||||
|
else if (maxPos > Current + DisplayableContent)
|
||||||
|
ScrollTo(maxPos - DisplayableContent + extraScroll, animated);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
if (shouldPerformRightMouseScroll(e))
|
if (shouldPerformRightMouseScroll(e))
|
||||||
|
@ -53,6 +53,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
public Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||||
|
public Bindable<bool> Active = new BindableBool(true);
|
||||||
|
|
||||||
|
protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && Active.Value;
|
||||||
|
|
||||||
protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod);
|
protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod);
|
||||||
|
|
||||||
|
@ -13,7 +13,9 @@ 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.Layout;
|
using osu.Framework.Layout;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -59,7 +61,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private DifficultyMultiplierDisplay? multiplierDisplay;
|
private DifficultyMultiplierDisplay? multiplierDisplay;
|
||||||
private ModSettingsArea modSettingsArea = null!;
|
private ModSettingsArea modSettingsArea = null!;
|
||||||
private FillFlowContainer<ModColumn> columnFlow = null!;
|
private ColumnScrollContainer columnScroll = null!;
|
||||||
|
private ColumnFlowContainer columnFlow = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -95,27 +98,27 @@ namespace osu.Game.Overlays.Mods
|
|||||||
RelativePositionAxes = Axes.Both,
|
RelativePositionAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuScrollContainer(Direction.Horizontal)
|
columnScroll = new ColumnScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = false,
|
Masking = false,
|
||||||
ClampExtension = 100,
|
ClampExtension = 100,
|
||||||
ScrollbarOverlapsContent = false,
|
ScrollbarOverlapsContent = false,
|
||||||
Child = columnFlow = new ModColumnContainer
|
Child = columnFlow = new ColumnFlowContainer
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Shear = new Vector2(SHEAR, 0),
|
Shear = new Vector2(SHEAR, 0),
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Spacing = new Vector2(10, 0),
|
Spacing = new Vector2(10, 0),
|
||||||
Margin = new MarginPadding { Right = 70 },
|
Margin = new MarginPadding { Horizontal = 70 },
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
CreateModColumn(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }),
|
createModColumnContent(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }),
|
||||||
CreateModColumn(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }),
|
createModColumnContent(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }),
|
||||||
CreateModColumn(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }),
|
createModColumnContent(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }),
|
||||||
CreateModColumn(ModType.Conversion),
|
createModColumnContent(ModType.Conversion),
|
||||||
CreateModColumn(ModType.Fun)
|
createModColumnContent(ModType.Fun)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,6 +156,14 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
|
||||||
|
=> new ColumnDimContainer(CreateModColumn(modType, toggleKeys))
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140)
|
||||||
|
};
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@ -166,7 +177,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
updateSelectionFromBindable();
|
updateSelectionFromBindable();
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
foreach (var column in columnFlow)
|
foreach (var column in columnFlow.Columns)
|
||||||
{
|
{
|
||||||
column.SelectedMods.BindValueChanged(updateBindableFromSelection);
|
column.SelectedMods.BindValueChanged(updateBindableFromSelection);
|
||||||
}
|
}
|
||||||
@ -191,7 +202,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void updateAvailableMods()
|
private void updateAvailableMods()
|
||||||
{
|
{
|
||||||
foreach (var column in columnFlow)
|
foreach (var column in columnFlow.Columns)
|
||||||
column.Filter = isValidMod;
|
column.Filter = isValidMod;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +255,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
// to synchronise state correctly, updateBindableFromSelection() computes the final mods (including incompatibility rules) and updates SelectedMods,
|
// to synchronise state correctly, updateBindableFromSelection() computes the final mods (including incompatibility rules) and updates SelectedMods,
|
||||||
// and this method then runs unconditionally again to make sure the new visual selection accurately reflects the final set of selected mods.
|
// and this method then runs unconditionally again to make sure the new visual selection accurately reflects the final set of selected mods.
|
||||||
// selectionBindableSyncInProgress ensures that mutual infinite recursion does not happen after that unconditional call.
|
// selectionBindableSyncInProgress ensures that mutual infinite recursion does not happen after that unconditional call.
|
||||||
foreach (var column in columnFlow)
|
foreach (var column in columnFlow.Columns)
|
||||||
column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray();
|
column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +276,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IReadOnlyList<Mod> ComputeNewModsFromSelection(IEnumerable<Mod> addedMods, IEnumerable<Mod> removedMods)
|
protected virtual IReadOnlyList<Mod> ComputeNewModsFromSelection(IEnumerable<Mod> addedMods, IEnumerable<Mod> removedMods)
|
||||||
=> columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray();
|
=> columnFlow.Columns.SelectMany(column => column.SelectedMods.Value).ToArray();
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
@ -280,7 +291,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
for (int i = 0; i < columnFlow.Count; i++)
|
for (int i = 0; i < columnFlow.Count; i++)
|
||||||
{
|
{
|
||||||
columnFlow[i].TopLevelContent
|
columnFlow[i].Column
|
||||||
|
.TopLevelContent
|
||||||
.Delay(i * 30)
|
.Delay(i * 30)
|
||||||
.MoveToY(0, fade_in_duration, Easing.OutQuint)
|
.MoveToY(0, fade_in_duration, Easing.OutQuint)
|
||||||
.FadeIn(fade_in_duration, Easing.OutQuint);
|
.FadeIn(fade_in_duration, Easing.OutQuint);
|
||||||
@ -301,27 +313,68 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
const float distance = 700;
|
const float distance = 700;
|
||||||
|
|
||||||
columnFlow[i].TopLevelContent
|
columnFlow[i].Column
|
||||||
|
.TopLevelContent
|
||||||
.MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint)
|
.MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint)
|
||||||
.FadeOut(fade_out_duration, Easing.OutQuint);
|
.FadeOut(fade_out_duration, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ModColumnContainer : FillFlowContainer<ModColumn>
|
internal class ColumnScrollContainer : OsuScrollContainer<ColumnFlowContainer>
|
||||||
{
|
{
|
||||||
|
public ColumnScrollContainer()
|
||||||
|
: base(Direction.Horizontal)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space.
|
||||||
|
// note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns.
|
||||||
|
float leftVisibleBound = Math.Clamp(Current, 0, ScrollableExtent);
|
||||||
|
float rightVisibleBound = leftVisibleBound + DrawWidth;
|
||||||
|
|
||||||
|
// if a movement is occurring at this time, the bounds below represent the full range of columns that the scroll movement will encompass.
|
||||||
|
// this will be used to ensure that columns do not change state from active to inactive back and forth until they are fully scrolled past.
|
||||||
|
float leftMovementBound = Math.Min(Current, Target);
|
||||||
|
float rightMovementBound = Math.Max(Current, Target) + DrawWidth;
|
||||||
|
|
||||||
|
foreach (var column in Child)
|
||||||
|
{
|
||||||
|
// DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear,
|
||||||
|
// so we have to manually compensate.
|
||||||
|
var topLeft = column.ToSpaceOfOtherDrawable(Vector2.Zero, ScrollContent);
|
||||||
|
var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * SHEAR, 0), ScrollContent);
|
||||||
|
|
||||||
|
bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound)
|
||||||
|
&& Precision.DefinitelyBigger(rightVisibleBound, bottomRight.X);
|
||||||
|
bool isBeingScrolledToward = Precision.AlmostBigger(topLeft.X, leftMovementBound)
|
||||||
|
&& Precision.DefinitelyBigger(rightMovementBound, bottomRight.X);
|
||||||
|
|
||||||
|
column.Active.Value = isCurrentlyVisible || isBeingScrolledToward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ColumnFlowContainer : FillFlowContainer<ColumnDimContainer>
|
||||||
|
{
|
||||||
|
public IEnumerable<ModColumn> Columns => Children.Select(dimWrapper => dimWrapper.Column);
|
||||||
|
|
||||||
private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize);
|
private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize);
|
||||||
|
|
||||||
public ModColumnContainer()
|
public ColumnFlowContainer()
|
||||||
{
|
{
|
||||||
AddLayout(drawSizeLayout);
|
AddLayout(drawSizeLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Add(ModColumn column)
|
public override void Add(ColumnDimContainer dimContainer)
|
||||||
{
|
{
|
||||||
base.Add(column);
|
base.Add(dimContainer);
|
||||||
|
|
||||||
Debug.Assert(column != null);
|
Debug.Assert(dimContainer != null);
|
||||||
column.Shear = Vector2.Zero;
|
dimContainer.Column.Shear = Vector2.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -341,6 +394,63 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class ColumnDimContainer : Container
|
||||||
|
{
|
||||||
|
public ModColumn Column { get; }
|
||||||
|
|
||||||
|
public readonly Bindable<bool> Active = new BindableBool();
|
||||||
|
public Action<ColumnDimContainer>? RequestScroll { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
|
public ColumnDimContainer(ModColumn column)
|
||||||
|
{
|
||||||
|
Child = Column = column;
|
||||||
|
column.Active.BindTo(Active);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
Active.BindValueChanged(_ => updateDim(), true);
|
||||||
|
FinishTransforms();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDim()
|
||||||
|
{
|
||||||
|
Colour4 targetColour;
|
||||||
|
|
||||||
|
if (Active.Value)
|
||||||
|
targetColour = Colour4.White;
|
||||||
|
else
|
||||||
|
targetColour = IsHovered ? colours.GrayC : colours.Gray8;
|
||||||
|
|
||||||
|
this.FadeColour(targetColour, 800, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
{
|
||||||
|
if (!Active.Value)
|
||||||
|
RequestScroll?.Invoke(this);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
base.OnHover(e);
|
||||||
|
updateDim();
|
||||||
|
return Active.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
updateDim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class ClickToReturnContainer : Container
|
private class ClickToReturnContainer : Container
|
||||||
{
|
{
|
||||||
public BindableBool HandleMouse { get; } = new BindableBool();
|
public BindableBool HandleMouse { get; } = new BindableBool();
|
||||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NestedVerticalScrollContainer : OsuScrollContainer
|
public class NestedVerticalScrollContainer : OsuScrollContainer
|
||||||
{
|
{
|
||||||
private OsuScrollContainer? parentScrollContainer;
|
private ModSelectScreen.ColumnScrollContainer? parentScrollContainer;
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
parentScrollContainer = this.FindClosestParent<OsuScrollContainer>();
|
parentScrollContainer = this.FindClosestParent<ModSelectScreen.ColumnScrollContainer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnScroll(ScrollEvent e)
|
protected override bool OnScroll(ScrollEvent e)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user