Merge pull request #12728 from SleepySwords/single-bind-reset-button

Add a reset button to individual keybinds
This commit is contained in:
Dan Balasescu
2021-05-28 00:31:26 +09:00
committed by GitHub
6 changed files with 253 additions and 153 deletions

View File

@ -70,6 +70,7 @@ namespace osu.Game.Tests.Visual.Settings
AddStep("click first row", () => AddStep("click first row", () =>
{ {
firstRow = panel.ChildrenOfType<KeyBindingRow>().First(); firstRow = panel.ChildrenOfType<KeyBindingRow>().First();
InputManager.MoveMouseTo(firstRow); InputManager.MoveMouseTo(firstRow);
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });
@ -138,6 +139,64 @@ namespace osu.Game.Tests.Visual.Settings
} }
} }
[Test]
public void TestSingleBindingResetButton()
{
KeyBindingRow settingsKeyBindingRow = null;
AddStep("click first row", () =>
{
settingsKeyBindingRow = panel.ChildrenOfType<KeyBindingRow>().First();
InputManager.MoveMouseTo(settingsKeyBindingRow);
InputManager.Click(MouseButton.Left);
InputManager.PressKey(Key.P);
InputManager.ReleaseKey(Key.P);
});
AddUntilStep("restore button shown", () => settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First().Alpha > 0);
AddStep("click reset button for bindings", () =>
{
var resetButton = settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First();
resetButton.Click();
});
AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First().Alpha == 0);
AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType<KeyBindingRow.KeyButton>().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.Defaults.ElementAt(0)));
}
[Test]
public void TestResetAllBindingsButton()
{
KeyBindingRow settingsKeyBindingRow = null;
AddStep("click first row", () =>
{
settingsKeyBindingRow = panel.ChildrenOfType<KeyBindingRow>().First();
InputManager.MoveMouseTo(settingsKeyBindingRow);
InputManager.Click(MouseButton.Left);
InputManager.PressKey(Key.P);
InputManager.ReleaseKey(Key.P);
});
AddUntilStep("restore button shown", () => settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First().Alpha > 0);
AddStep("click reset button for bindings", () =>
{
var resetButton = panel.ChildrenOfType<ResetButton>().First();
resetButton.Click();
});
AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First().Alpha == 0);
AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType<KeyBindingRow.KeyButton>().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.Defaults.ElementAt(0)));
}
[Test] [Test]
public void TestClickRowSelectsFirstBinding() public void TestClickRowSelectsFirstBinding()
{ {

View File

@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Settings namespace osu.Game.Tests.Visual.Settings
{ {
@ -37,7 +38,7 @@ namespace osu.Game.Tests.Visual.Settings
private class TestSettingsTextBox : SettingsTextBox private class TestSettingsTextBox : SettingsTextBox
{ {
public new Drawable RestoreDefaultValueButton => this.ChildrenOfType<RestoreDefaultValueButton>().Single(); public Drawable RestoreDefaultValueButton => this.ChildrenOfType<RestoreDefaultValueButton<string>>().Single();
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -46,12 +47,19 @@ namespace osu.Game.Overlays.KeyBinding
} }
} }
private Container content;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
content.ReceivePositionalInputAt(screenSpacePos);
public bool FilteringActive { get; set; } public bool FilteringActive { get; set; }
private OsuSpriteText text; private OsuSpriteText text;
private FillFlowContainer cancelAndClearButtons; private FillFlowContainer cancelAndClearButtons;
private FillFlowContainer<KeyButton> buttons; private FillFlowContainer<KeyButton> buttons;
private Bindable<bool> isDefault { get; } = new BindableBool(true);
public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text.ToString()); public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text.ToString());
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings) public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
@ -61,9 +69,6 @@ namespace osu.Game.Overlays.KeyBinding
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = padding;
} }
[Resolved] [Resolved]
@ -72,51 +77,72 @@ namespace osu.Game.Overlays.KeyBinding
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
EdgeEffect = new EdgeEffectParameters RelativeSizeAxes = Axes.X;
{ AutoSizeAxes = Axes.Y;
Radius = 2, Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS };
Colour = colours.YellowDark.Opacity(0),
Type = EdgeEffectType.Shadow,
Hollow = true,
};
Children = new Drawable[] InternalChildren = new Drawable[]
{ {
new Box new RestoreDefaultValueButton<bool>
{ {
RelativeSizeAxes = Axes.Both, Current = isDefault,
Colour = Color4.Black, Action = RestoreDefaults,
Alpha = 0.6f,
},
text = new OsuSpriteText
{
Text = action.GetDescription(),
Margin = new MarginPadding(padding),
},
buttons = new FillFlowContainer<KeyButton>
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight
},
cancelAndClearButtons = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding(padding) { Top = height + padding * 2 },
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
Alpha = 0, },
Spacing = new Vector2(5), content = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Masking = true,
CornerRadius = padding,
EdgeEffect = new EdgeEffectParameters
{
Radius = 2,
Colour = colours.YellowDark.Opacity(0),
Type = EdgeEffectType.Shadow,
Hollow = true,
},
Children = new Drawable[] Children = new Drawable[]
{ {
new CancelButton { Action = finalise }, new Box
new ClearButton { Action = clear }, {
}, RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f,
},
text = new OsuSpriteText
{
Text = action.GetDescription(),
Margin = new MarginPadding(padding),
},
buttons = new FillFlowContainer<KeyButton>
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight
},
cancelAndClearButtons = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding(padding) { Top = height + padding * 2 },
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Alpha = 0,
Spacing = new Vector2(5),
Children = new Drawable[]
{
new CancelButton { Action = finalise },
new ClearButton { Action = clear },
},
}
}
} }
}; };
foreach (var b in bindings) foreach (var b in bindings)
buttons.Add(new KeyButton(b)); buttons.Add(new KeyButton(b));
updateIsDefaultValue();
} }
public void RestoreDefaults() public void RestoreDefaults()
@ -129,18 +155,20 @@ namespace osu.Game.Overlays.KeyBinding
button.UpdateKeyCombination(d); button.UpdateKeyCombination(d);
store.Update(button.KeyBinding); store.Update(button.KeyBinding);
} }
isDefault.Value = true;
} }
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); content.FadeEdgeEffectTo(1, transition_time, Easing.OutQuint);
return base.OnHover(e); return base.OnHover(e);
} }
protected override void OnHoverLost(HoverLostEvent e) protected override void OnHoverLost(HoverLostEvent e)
{ {
FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); content.FadeEdgeEffectTo(0, transition_time, Easing.OutQuint);
base.OnHoverLost(e); base.OnHoverLost(e);
} }
@ -288,6 +316,8 @@ namespace osu.Game.Overlays.KeyBinding
{ {
store.Update(bindTarget.KeyBinding); store.Update(bindTarget.KeyBinding);
updateIsDefaultValue();
bindTarget.IsBinding = false; bindTarget.IsBinding = false;
Schedule(() => Schedule(() =>
{ {
@ -305,8 +335,8 @@ namespace osu.Game.Overlays.KeyBinding
protected override void OnFocus(FocusEvent e) protected override void OnFocus(FocusEvent e)
{ {
AutoSizeDuration = 500; content.AutoSizeDuration = 500;
AutoSizeEasing = Easing.OutQuint; content.AutoSizeEasing = Easing.OutQuint;
cancelAndClearButtons.FadeIn(300, Easing.OutQuint); cancelAndClearButtons.FadeIn(300, Easing.OutQuint);
cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y; cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y;
@ -331,6 +361,11 @@ namespace osu.Game.Overlays.KeyBinding
if (bindTarget != null) bindTarget.IsBinding = true; if (bindTarget != null) bindTarget.IsBinding = true;
} }
private void updateIsDefaultValue()
{
isDefault.Value = bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults);
}
private class CancelButton : TriangleButton private class CancelButton : TriangleButton
{ {
public CancelButton() public CancelButton()
@ -379,9 +414,6 @@ namespace osu.Game.Overlays.KeyBinding
Margin = new MarginPadding(padding); Margin = new MarginPadding(padding);
// todo: use this in a meaningful way
// var isDefault = keyBinding.Action is Enum;
Masking = true; Masking = true;
CornerRadius = padding; CornerRadius = padding;

View File

@ -61,8 +61,11 @@ namespace osu.Game.Overlays.KeyBinding
{ {
Text = "Reset all bindings in section"; Text = "Reset all bindings in section";
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Margin = new MarginPadding { Top = 5 }; Width = 0.5f;
Height = 20; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
Margin = new MarginPadding { Top = 15 };
Height = 30;
Content.CornerRadius = 5; Content.CornerRadius = 5;
} }

View File

@ -0,0 +1,106 @@
// 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.Allocation;
using osu.Framework.Bindables;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays
{
public class RestoreDefaultValueButton<T> : OsuButton, IHasTooltip, IHasCurrentValue<T>
{
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
// this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button.
public override bool AcceptsFocus => true;
public Bindable<T> Current
{
get => current.Current;
set => current.Current = value;
}
private Color4 buttonColour;
private bool hovering;
public RestoreDefaultValueButton()
{
Height = 1;
RelativeSizeAxes = Axes.Y;
Width = SettingsPanel.CONTENT_MARGINS;
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
BackgroundColour = colour.Yellow;
buttonColour = colour.Yellow;
Content.Width = 0.33f;
Content.CornerRadius = 3;
Content.EdgeEffect = new EdgeEffectParameters
{
Colour = buttonColour.Opacity(0.1f),
Type = EdgeEffectType.Glow,
Radius = 2,
};
Padding = new MarginPadding { Vertical = 1.5f };
Alpha = 0f;
Action += () =>
{
if (!current.Disabled) current.SetDefault();
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.ValueChanged += _ => UpdateState();
Current.DisabledChanged += _ => UpdateState();
Current.DefaultChanged += _ => UpdateState();
UpdateState();
}
public string TooltipText => "revert to default";
protected override bool OnHover(HoverEvent e)
{
hovering = true;
UpdateState();
return false;
}
protected override void OnHoverLost(HoverLostEvent e)
{
hovering = false;
UpdateState();
}
public void UpdateState() => Scheduler.AddOnce(updateState);
private void updateState()
{
if (current == null)
return;
this.FadeTo(current.IsDefault ? 0f :
hovering && !current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint);
this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint);
}
}
}

View File

@ -5,16 +5,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osuTK.Graphics;
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.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -108,7 +103,7 @@ namespace osu.Game.Overlays.Settings
protected SettingsItem() protected SettingsItem()
{ {
RestoreDefaultValueButton restoreDefaultButton; RestoreDefaultValueButton<T> restoreDefaultButton;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
@ -116,7 +111,7 @@ namespace osu.Game.Overlays.Settings
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
restoreDefaultButton = new RestoreDefaultValueButton(), restoreDefaultButton = new RestoreDefaultValueButton<T>(),
FlowContent = new FillFlowContainer FlowContent = new FillFlowContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -137,7 +132,7 @@ namespace osu.Game.Overlays.Settings
controlWithCurrent.Current.DisabledChanged += _ => updateDisabled(); controlWithCurrent.Current.DisabledChanged += _ => updateDisabled();
if (ShowsDefaultIndicator) if (ShowsDefaultIndicator)
restoreDefaultButton.Bindable = controlWithCurrent.Current; restoreDefaultButton.Current = controlWithCurrent.Current;
} }
} }
@ -146,101 +141,5 @@ namespace osu.Game.Overlays.Settings
if (labelText != null) if (labelText != null)
labelText.Alpha = controlWithCurrent.Current.Disabled ? 0.3f : 1; labelText.Alpha = controlWithCurrent.Current.Disabled ? 0.3f : 1;
} }
protected internal class RestoreDefaultValueButton : Container, IHasTooltip
{
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
private Bindable<T> bindable;
public Bindable<T> Bindable
{
get => bindable;
set
{
bindable = value;
bindable.ValueChanged += _ => UpdateState();
bindable.DisabledChanged += _ => UpdateState();
bindable.DefaultChanged += _ => UpdateState();
UpdateState();
}
}
private Color4 buttonColour;
private bool hovering;
public RestoreDefaultValueButton()
{
RelativeSizeAxes = Axes.Y;
Width = SettingsPanel.CONTENT_MARGINS;
Padding = new MarginPadding { Vertical = 1.5f };
Alpha = 0f;
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
buttonColour = colour.Yellow;
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
CornerRadius = 3,
Masking = true,
Colour = buttonColour,
EdgeEffect = new EdgeEffectParameters
{
Colour = buttonColour.Opacity(0.1f),
Type = EdgeEffectType.Glow,
Radius = 2,
},
Width = 0.33f,
Child = new Box { RelativeSizeAxes = Axes.Both },
};
}
protected override void LoadComplete()
{
base.LoadComplete();
UpdateState();
}
public string TooltipText => "revert to default";
protected override bool OnClick(ClickEvent e)
{
if (bindable != null && !bindable.Disabled)
bindable.SetDefault();
return true;
}
protected override bool OnHover(HoverEvent e)
{
hovering = true;
UpdateState();
return false;
}
protected override void OnHoverLost(HoverLostEvent e)
{
hovering = false;
UpdateState();
}
public void UpdateState() => Scheduler.AddOnce(updateState);
private void updateState()
{
if (bindable == null)
return;
this.FadeTo(bindable.IsDefault ? 0f :
hovering && !bindable.Disabled ? 1f : 0.65f, 200, Easing.OutQuint);
this.FadeColour(bindable.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint);
}
}
} }
} }