mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 23:53:51 +09:00
Merge pull request #13771 from LumpBloom7/volume-meter-switch
Add ability to navigate between volume meters via Alt+Left/Right arrows
This commit is contained in:
@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
showOverlay();
|
showOverlay();
|
||||||
|
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected.Value);
|
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
showOverlay();
|
showOverlay();
|
||||||
|
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("First button selected", () => getButton(0).Selected.Value);
|
AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -111,11 +111,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Show overlay", () => failOverlay.Show());
|
AddStep("Show overlay", () => failOverlay.Show());
|
||||||
|
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
|
AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected);
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
|
AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected);
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
|
AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -127,11 +127,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Show overlay", () => failOverlay.Show());
|
AddStep("Show overlay", () => failOverlay.Show());
|
||||||
|
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
|
AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected);
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
|
AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected);
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
|
AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First()));
|
AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First()));
|
||||||
AddStep("Hide overlay", () => failOverlay.Hide());
|
AddStep("Hide overlay", () => failOverlay.Hide());
|
||||||
|
|
||||||
AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected.Value));
|
AddAssert("Overlay state is reset", () => failOverlay.Buttons.All(b => b.State == SelectionState.NotSelected));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -162,11 +162,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||||
showOverlay();
|
showOverlay();
|
||||||
|
|
||||||
AddAssert("First button not selected", () => !getButton(0).Selected.Value);
|
AddAssert("First button not selected", () => getButton(0).State == SelectionState.NotSelected);
|
||||||
|
|
||||||
AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1)));
|
AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1)));
|
||||||
|
|
||||||
AddAssert("First button selected", () => getButton(0).Selected.Value);
|
AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -179,8 +179,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
||||||
AddAssert("First button not selected", () => !getButton(0).Selected.Value);
|
AddAssert("First button not selected", () => getButton(0).State == SelectionState.NotSelected);
|
||||||
AddAssert("Second button selected", () => getButton(1).Selected.Value);
|
AddAssert("Second button selected", () => getButton(1).State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -196,8 +196,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("Second button not selected", () => !getButton(1).Selected.Value);
|
AddAssert("Second button not selected", () => getButton(1).State == SelectionState.NotSelected);
|
||||||
AddAssert("First button selected", () => getButton(0).Selected.Value);
|
AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -211,7 +211,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
||||||
AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero));
|
AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero));
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("First button selected", () => getButton(0).Selected.Value); // Initial state condition
|
AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected); // Initial state condition
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -282,7 +282,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
showOverlay();
|
showOverlay();
|
||||||
|
|
||||||
AddAssert("No button selected",
|
AddAssert("No button selected",
|
||||||
() => pauseOverlay.Buttons.All(button => !button.Selected.Value));
|
() => pauseOverlay.Buttons.All(button => button.State == SelectionState.NotSelected));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showOverlay() => AddStep("Show overlay", () => pauseOverlay.Show());
|
private void showOverlay() => AddStep("Show overlay", () => pauseOverlay.Show());
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Volume;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public class TestSceneVolumeOverlay : OsuTestScene
|
||||||
|
{
|
||||||
|
private VolumeOverlay volume;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
volume = new VolumeOverlay(),
|
||||||
|
new VolumeControlReceptor
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ActionRequested = action => volume.Adjust(action),
|
||||||
|
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("show controls", () => volume.Show());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using osu.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Containers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A FillFlowContainer that provides functionality to cycle selection between children
|
||||||
|
/// The selection wraps around when overflowing past the first or last child.
|
||||||
|
/// </summary>
|
||||||
|
public class SelectionCycleFillFlowContainer<T> : FillFlowContainer<T> where T : Drawable, IStateful<SelectionState>
|
||||||
|
{
|
||||||
|
public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex.Value] : null;
|
||||||
|
|
||||||
|
private int? selectedIndex;
|
||||||
|
|
||||||
|
public void SelectNext()
|
||||||
|
{
|
||||||
|
if (!selectedIndex.HasValue || selectedIndex == Count - 1)
|
||||||
|
setSelected(0);
|
||||||
|
else
|
||||||
|
setSelected(selectedIndex.Value + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectPrevious()
|
||||||
|
{
|
||||||
|
if (!selectedIndex.HasValue || selectedIndex == 0)
|
||||||
|
setSelected(Count - 1);
|
||||||
|
else
|
||||||
|
setSelected(selectedIndex.Value - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deselect() => setSelected(null);
|
||||||
|
|
||||||
|
public void Select(T item)
|
||||||
|
{
|
||||||
|
var newIndex = IndexOf(item);
|
||||||
|
|
||||||
|
if (newIndex < 0)
|
||||||
|
setSelected(null);
|
||||||
|
else
|
||||||
|
setSelected(IndexOf(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Add(T drawable)
|
||||||
|
{
|
||||||
|
base.Add(drawable);
|
||||||
|
|
||||||
|
Debug.Assert(drawable != null);
|
||||||
|
|
||||||
|
drawable.StateChanged += state => selectionChanged(drawable, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Remove(T drawable)
|
||||||
|
=> throw new NotSupportedException($"Cannot remove drawables from {nameof(SelectionCycleFillFlowContainer<T>)}");
|
||||||
|
|
||||||
|
private void setSelected(int? value)
|
||||||
|
{
|
||||||
|
if (selectedIndex == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Deselect the previously-selected button
|
||||||
|
if (selectedIndex.HasValue)
|
||||||
|
this[selectedIndex.Value].State = SelectionState.NotSelected;
|
||||||
|
|
||||||
|
selectedIndex = value;
|
||||||
|
|
||||||
|
// Select the newly-selected button
|
||||||
|
if (selectedIndex.HasValue)
|
||||||
|
this[selectedIndex.Value].State = SelectionState.Selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectionChanged(T drawable, SelectionState state)
|
||||||
|
{
|
||||||
|
if (state == SelectionState.NotSelected)
|
||||||
|
Deselect();
|
||||||
|
else
|
||||||
|
Select(drawable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,26 @@
|
|||||||
// 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.Framework.Bindables;
|
using System;
|
||||||
using osuTK;
|
using osu.Framework;
|
||||||
using osuTK.Graphics;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics.Backgrounds;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics.Effects;
|
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics.Backgrounds;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class DialogButton : OsuClickableContainer
|
public class DialogButton : OsuClickableContainer, IStateful<SelectionState>
|
||||||
{
|
{
|
||||||
private const float idle_width = 0.8f;
|
private const float idle_width = 0.8f;
|
||||||
private const float hover_width = 0.9f;
|
private const float hover_width = 0.9f;
|
||||||
@ -27,7 +28,22 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private const float hover_duration = 500;
|
private const float hover_duration = 500;
|
||||||
private const float click_duration = 200;
|
private const float click_duration = 200;
|
||||||
|
|
||||||
public readonly BindableBool Selected = new BindableBool();
|
public event Action<SelectionState> StateChanged;
|
||||||
|
|
||||||
|
private SelectionState state;
|
||||||
|
|
||||||
|
public SelectionState State
|
||||||
|
{
|
||||||
|
get => state;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (state == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = value;
|
||||||
|
StateChanged?.Invoke(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Container backgroundContainer;
|
private readonly Container backgroundContainer;
|
||||||
private readonly Container colourContainer;
|
private readonly Container colourContainer;
|
||||||
@ -153,7 +169,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
updateGlow();
|
updateGlow();
|
||||||
|
|
||||||
Selected.ValueChanged += selectionChanged;
|
StateChanged += selectionChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 buttonColour;
|
private Color4 buttonColour;
|
||||||
@ -221,7 +237,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
.OnComplete(_ =>
|
.OnComplete(_ =>
|
||||||
{
|
{
|
||||||
clickAnimating = false;
|
clickAnimating = false;
|
||||||
Selected.TriggerChange();
|
StateChanged?.Invoke(State);
|
||||||
});
|
});
|
||||||
|
|
||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
@ -235,7 +251,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
protected override void OnMouseUp(MouseUpEvent e)
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
{
|
{
|
||||||
if (Selected.Value)
|
if (State == SelectionState.Selected)
|
||||||
colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In);
|
colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In);
|
||||||
base.OnMouseUp(e);
|
base.OnMouseUp(e);
|
||||||
}
|
}
|
||||||
@ -243,7 +259,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
base.OnHover(e);
|
base.OnHover(e);
|
||||||
Selected.Value = true;
|
State = SelectionState.Selected;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -251,15 +267,15 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected override void OnHoverLost(HoverLostEvent e)
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
{
|
{
|
||||||
base.OnHoverLost(e);
|
base.OnHoverLost(e);
|
||||||
Selected.Value = false;
|
State = SelectionState.NotSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectionChanged(ValueChangedEvent<bool> args)
|
private void selectionChanged(SelectionState newState)
|
||||||
{
|
{
|
||||||
if (clickAnimating)
|
if (clickAnimating)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (args.NewValue)
|
if (newState == SelectionState.Selected)
|
||||||
{
|
{
|
||||||
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
|
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
|
||||||
colourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic);
|
colourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic);
|
||||||
|
@ -103,6 +103,9 @@ namespace osu.Game.Input.Bindings
|
|||||||
new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume),
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume),
|
||||||
new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume),
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume),
|
||||||
|
|
||||||
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Left }, GlobalAction.PreviousVolumeMeter),
|
||||||
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Right }, GlobalAction.NextVolumeMeter),
|
||||||
|
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.F4 }, GlobalAction.ToggleMute),
|
new KeyBinding(new[] { InputKey.Control, InputKey.F4 }, GlobalAction.ToggleMute),
|
||||||
|
|
||||||
new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev),
|
new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev),
|
||||||
@ -263,5 +266,11 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[Description("Toggle skin editor")]
|
[Description("Toggle skin editor")]
|
||||||
ToggleSkinEditor,
|
ToggleSkinEditor,
|
||||||
|
|
||||||
|
[Description("Previous volume meter")]
|
||||||
|
PreviousVolumeMeter,
|
||||||
|
|
||||||
|
[Description("Next volume meter")]
|
||||||
|
NextVolumeMeter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Overlays.Volume
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.ToggleMute:
|
case GlobalAction.ToggleMute:
|
||||||
|
case GlobalAction.NextVolumeMeter:
|
||||||
|
case GlobalAction.PreviousVolumeMeter:
|
||||||
ActionRequested?.Invoke(action);
|
ActionRequested?.Invoke(action);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@ -19,13 +20,14 @@ using osu.Framework.Threading;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Volume
|
namespace osu.Game.Overlays.Volume
|
||||||
{
|
{
|
||||||
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
|
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>, IStateful<SelectionState>
|
||||||
{
|
{
|
||||||
private CircularProgress volumeCircle;
|
private CircularProgress volumeCircle;
|
||||||
private CircularProgress volumeCircleGlow;
|
private CircularProgress volumeCircleGlow;
|
||||||
@ -38,9 +40,32 @@ namespace osu.Game.Overlays.Volume
|
|||||||
private OsuSpriteText text;
|
private OsuSpriteText text;
|
||||||
private BufferedContainer maxGlow;
|
private BufferedContainer maxGlow;
|
||||||
|
|
||||||
|
private Container selectedGlowContainer;
|
||||||
|
|
||||||
private Sample sample;
|
private Sample sample;
|
||||||
private double sampleLastPlaybackTime;
|
private double sampleLastPlaybackTime;
|
||||||
|
|
||||||
|
public event Action<SelectionState> StateChanged;
|
||||||
|
|
||||||
|
private SelectionState state;
|
||||||
|
|
||||||
|
public SelectionState State
|
||||||
|
{
|
||||||
|
get => state;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (state == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = value;
|
||||||
|
StateChanged?.Invoke(value);
|
||||||
|
|
||||||
|
updateSelectedState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const float transition_length = 500;
|
||||||
|
|
||||||
public VolumeMeter(string name, float circleSize, Color4 meterColour)
|
public VolumeMeter(string name, float circleSize, Color4 meterColour)
|
||||||
{
|
{
|
||||||
this.circleSize = circleSize;
|
this.circleSize = circleSize;
|
||||||
@ -75,7 +100,6 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
new BufferedContainer
|
new BufferedContainer
|
||||||
{
|
{
|
||||||
Alpha = 0.9f,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -147,6 +171,24 @@ namespace osu.Game.Overlays.Volume
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
selectedGlowContainer = new CircularContainer
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
},
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = meterColour.Opacity(0.1f),
|
||||||
|
Radius = 10,
|
||||||
|
}
|
||||||
|
},
|
||||||
maxGlow = (text = new OsuSpriteText
|
maxGlow = (text = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -171,7 +213,6 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Alpha = 0.9f,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = backgroundColour,
|
Colour = backgroundColour,
|
||||||
},
|
},
|
||||||
@ -305,17 +346,14 @@ namespace osu.Game.Overlays.Volume
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const float transition_length = 500;
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
|
||||||
{
|
{
|
||||||
this.ScaleTo(1.04f, transition_length, Easing.OutExpo);
|
State = SelectionState.Selected;
|
||||||
return false;
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
{
|
{
|
||||||
this.ScaleTo(1f, transition_length, Easing.OutExpo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action)
|
public bool OnPressed(GlobalAction action)
|
||||||
@ -326,10 +364,12 @@ namespace osu.Game.Overlays.Volume
|
|||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case GlobalAction.SelectPrevious:
|
case GlobalAction.SelectPrevious:
|
||||||
|
State = SelectionState.Selected;
|
||||||
adjust(1, false);
|
adjust(1, false);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.SelectNext:
|
case GlobalAction.SelectNext:
|
||||||
|
State = SelectionState.Selected;
|
||||||
adjust(-1, false);
|
adjust(-1, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -340,5 +380,21 @@ namespace osu.Game.Overlays.Volume
|
|||||||
public void OnReleased(GlobalAction action)
|
public void OnReleased(GlobalAction action)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateSelectedState()
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case SelectionState.Selected:
|
||||||
|
this.ScaleTo(1.04f, transition_length, Easing.OutExpo);
|
||||||
|
selectedGlowContainer.FadeIn(transition_length, Easing.OutExpo);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SelectionState.NotSelected:
|
||||||
|
this.ScaleTo(1f, transition_length, Easing.OutExpo);
|
||||||
|
selectedGlowContainer.FadeOut(transition_length, Easing.OutExpo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Overlays.Volume;
|
using osu.Game.Overlays.Volume;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -32,6 +33,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public Bindable<bool> IsMuted { get; } = new Bindable<bool>();
|
public Bindable<bool> IsMuted { get; } = new Bindable<bool>();
|
||||||
|
|
||||||
|
private SelectionCycleFillFlowContainer<VolumeMeter> volumeMeters;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, OsuColour colours)
|
private void load(AudioManager audio, OsuColour colours)
|
||||||
{
|
{
|
||||||
@ -53,7 +56,7 @@ namespace osu.Game.Overlays
|
|||||||
Margin = new MarginPadding(10),
|
Margin = new MarginPadding(10),
|
||||||
Current = { BindTarget = IsMuted }
|
Current = { BindTarget = IsMuted }
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
volumeMeters = new SelectionCycleFillFlowContainer<VolumeMeter>
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
@ -61,7 +64,7 @@ namespace osu.Game.Overlays
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Spacing = new Vector2(0, offset),
|
Spacing = new Vector2(0, offset),
|
||||||
Margin = new MarginPadding { Left = offset },
|
Margin = new MarginPadding { Left = offset },
|
||||||
Children = new Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker),
|
volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker),
|
||||||
volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker),
|
volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker),
|
||||||
@ -87,9 +90,9 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
volumeMeterMaster.Bindable.ValueChanged += _ => Show();
|
foreach (var volumeMeter in volumeMeters)
|
||||||
volumeMeterEffect.Bindable.ValueChanged += _ => Show();
|
volumeMeter.Bindable.ValueChanged += _ => Show();
|
||||||
volumeMeterMusic.Bindable.ValueChanged += _ => Show();
|
|
||||||
muteButton.Current.ValueChanged += _ => Show();
|
muteButton.Current.ValueChanged += _ => Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,23 +105,27 @@ namespace osu.Game.Overlays
|
|||||||
case GlobalAction.DecreaseVolume:
|
case GlobalAction.DecreaseVolume:
|
||||||
if (State.Value == Visibility.Hidden)
|
if (State.Value == Visibility.Hidden)
|
||||||
Show();
|
Show();
|
||||||
else if (volumeMeterMusic.IsHovered)
|
|
||||||
volumeMeterMusic.Decrease(amount, isPrecise);
|
|
||||||
else if (volumeMeterEffect.IsHovered)
|
|
||||||
volumeMeterEffect.Decrease(amount, isPrecise);
|
|
||||||
else
|
else
|
||||||
volumeMeterMaster.Decrease(amount, isPrecise);
|
volumeMeters.Selected?.Decrease(amount, isPrecise);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.IncreaseVolume:
|
case GlobalAction.IncreaseVolume:
|
||||||
if (State.Value == Visibility.Hidden)
|
if (State.Value == Visibility.Hidden)
|
||||||
Show();
|
Show();
|
||||||
else if (volumeMeterMusic.IsHovered)
|
|
||||||
volumeMeterMusic.Increase(amount, isPrecise);
|
|
||||||
else if (volumeMeterEffect.IsHovered)
|
|
||||||
volumeMeterEffect.Increase(amount, isPrecise);
|
|
||||||
else
|
else
|
||||||
volumeMeterMaster.Increase(amount, isPrecise);
|
volumeMeters.Selected?.Increase(amount, isPrecise);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case GlobalAction.NextVolumeMeter:
|
||||||
|
if (State.Value == Visibility.Visible)
|
||||||
|
volumeMeters.SelectNext();
|
||||||
|
Show();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case GlobalAction.PreviousVolumeMeter:
|
||||||
|
if (State.Value == Visibility.Visible)
|
||||||
|
volumeMeters.SelectPrevious();
|
||||||
|
Show();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.ToggleMute:
|
case GlobalAction.ToggleMute:
|
||||||
@ -134,6 +141,10 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public override void Show()
|
public override void Show()
|
||||||
{
|
{
|
||||||
|
// Focus on the master meter as a default if previously hidden
|
||||||
|
if (State.Value == Visibility.Hidden)
|
||||||
|
volumeMeters.Select(volumeMeterMaster);
|
||||||
|
|
||||||
if (State.Value == Visibility.Visible)
|
if (State.Value == Visibility.Visible)
|
||||||
schedulePopOut();
|
schedulePopOut();
|
||||||
|
|
||||||
|
@ -2,23 +2,24 @@
|
|||||||
// 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;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Humanizer;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using Humanizer;
|
using osuTK;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
@ -46,13 +47,13 @@ namespace osu.Game.Screens.Play
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Action that is invoked when <see cref="GlobalAction.Select"/> is triggered.
|
/// Action that is invoked when <see cref="GlobalAction.Select"/> is triggered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual Action SelectAction => () => InternalButtons.Children.FirstOrDefault(f => f.Selected.Value)?.Click();
|
protected virtual Action SelectAction => () => InternalButtons.Selected?.Click();
|
||||||
|
|
||||||
public abstract string Header { get; }
|
public abstract string Header { get; }
|
||||||
|
|
||||||
public abstract string Description { get; }
|
public abstract string Description { get; }
|
||||||
|
|
||||||
protected ButtonContainer InternalButtons;
|
protected SelectionCycleFillFlowContainer<DialogButton> InternalButtons;
|
||||||
public IReadOnlyList<DialogButton> Buttons => InternalButtons;
|
public IReadOnlyList<DialogButton> Buttons => InternalButtons;
|
||||||
|
|
||||||
private FillFlowContainer retryCounterContainer;
|
private FillFlowContainer retryCounterContainer;
|
||||||
@ -116,7 +117,7 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InternalButtons = new ButtonContainer
|
InternalButtons = new SelectionCycleFillFlowContainer<DialogButton>
|
||||||
{
|
{
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
@ -183,8 +184,6 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
button.Selected.ValueChanged += selected => buttonSelectionChanged(button, selected.NewValue);
|
|
||||||
|
|
||||||
InternalButtons.Add(button);
|
InternalButtons.Add(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,14 +215,6 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buttonSelectionChanged(DialogButton button, bool isSelected)
|
|
||||||
{
|
|
||||||
if (!isSelected)
|
|
||||||
InternalButtons.Deselect();
|
|
||||||
else
|
|
||||||
InternalButtons.Select(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateRetryCount()
|
private void updateRetryCount()
|
||||||
{
|
{
|
||||||
// "You've retried 1,065 times in this session"
|
// "You've retried 1,065 times in this session"
|
||||||
@ -255,46 +246,6 @@ namespace osu.Game.Screens.Play
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class ButtonContainer : FillFlowContainer<DialogButton>
|
|
||||||
{
|
|
||||||
private int selectedIndex = -1;
|
|
||||||
|
|
||||||
private void setSelected(int value)
|
|
||||||
{
|
|
||||||
if (selectedIndex == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Deselect the previously-selected button
|
|
||||||
if (selectedIndex != -1)
|
|
||||||
this[selectedIndex].Selected.Value = false;
|
|
||||||
|
|
||||||
selectedIndex = value;
|
|
||||||
|
|
||||||
// Select the newly-selected button
|
|
||||||
if (selectedIndex != -1)
|
|
||||||
this[selectedIndex].Selected.Value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SelectNext()
|
|
||||||
{
|
|
||||||
if (selectedIndex == -1 || selectedIndex == Count - 1)
|
|
||||||
setSelected(0);
|
|
||||||
else
|
|
||||||
setSelected(selectedIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SelectPrevious()
|
|
||||||
{
|
|
||||||
if (selectedIndex == -1 || selectedIndex == 0)
|
|
||||||
setSelected(Count - 1);
|
|
||||||
else
|
|
||||||
setSelected(selectedIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Deselect() => setSelected(-1);
|
|
||||||
public void Select(DialogButton button) => setSelected(IndexOf(button));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Button : DialogButton
|
private class Button : DialogButton
|
||||||
{
|
{
|
||||||
// required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved)
|
// required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved)
|
||||||
@ -302,7 +253,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
Selected.Value = true;
|
State = SelectionState.Selected;
|
||||||
return base.OnMouseMove(e);
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user