diff --git a/osu.Desktop.Tests/Visual/TestCaseKeyConfiguration.cs b/osu.Desktop.Tests/Visual/TestCaseKeyConfiguration.cs index 10a15d0a96..077417577a 100644 --- a/osu.Desktop.Tests/Visual/TestCaseKeyConfiguration.cs +++ b/osu.Desktop.Tests/Visual/TestCaseKeyConfiguration.cs @@ -1,10 +1,27 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; +using System.Linq; using osu.Game.Overlays; using osu.Game.Overlays.Settings; -using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Input; +using osu.Game.Rulesets; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Input; +using OpenTK.Input; namespace osu.Desktop.Tests.Visual { @@ -29,16 +46,340 @@ namespace osu.Desktop.Tests.Visual public class KeyConfiguration : SettingsOverlay { - protected override IEnumerable CreateSections() => new[] + protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!"); + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(RulesetStore rulesets, GlobalBindingInputManager global) { - new BindingsSection(), - new BindingsSection() - }; + AddSection(new GlobalBindingsSection(global, "Global")); + + foreach (var ruleset in rulesets.Query()) + AddSection(new RulesetBindingsSection(ruleset)); + } + + public KeyConfiguration() + : base(false) + { + } } - public class BindingsSection : SettingsSection + public class GlobalBindingsSection : KeyBindingsSection + { + private readonly string name; + + public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail; + public override string Header => name; + + public GlobalBindingsSection(KeyBindingInputManager manager, string name) + { + this.name = name; + + Defaults = manager.DefaultMappings; + } + } + + public class RulesetBindingsSection : KeyBindingsSection { public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail; - public override string Header => "Header"; + public override string Header => Ruleset.Name; + + public RulesetBindingsSection(RulesetInfo ruleset) + { + Ruleset = ruleset; + + Defaults = ruleset.CreateInstance().GetDefaultKeyBindings(); + } + } + + public abstract class KeyBindingsSection : SettingsSection + { + protected IEnumerable Defaults; + + protected RulesetInfo Ruleset; + + protected KeyBindingsSection() + { + FlowContent.Spacing = new Vector2(0, 1); + } + + [BackgroundDependencyLoader] + private void load(KeyBindingStore store) + { + var firstDefault = Defaults?.FirstOrDefault(); + + if (firstDefault == null) return; + + var actionType = firstDefault.Action.GetType(); + + var bindings = store.GetProcessedList(Defaults, Ruleset?.ID); + + foreach (Enum v in Enum.GetValues(actionType)) + { + Add(new KeyBindingRow(v, bindings.Where(b => (int)b.Action == (int)(object)v))); + } + } + } + + internal class KeyBindingRow : Container, IFilterable + { + private readonly Enum action; + private readonly IEnumerable bindings; + + private const float transition_time = 150; + + private const float height = 20; + + private const float padding = 5; + + private bool matchingFilter; + + public bool MatchingFilter + { + get { return matchingFilter; } + set + { + matchingFilter = value; + this.FadeTo(!matchingFilter ? 0 : 1); + } + } + + private OsuSpriteText text; + private OsuSpriteText pressAKey; + + private FillFlowContainer buttons; + + public string[] FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray(); + + public KeyBindingRow(Enum action, IEnumerable bindings) + { + this.action = action; + this.bindings = bindings; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Masking = true; + CornerRadius = padding; + } + + private KeyBindingStore store; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, KeyBindingStore store) + { + this.store = store; + + EdgeEffect = new EdgeEffectParameters + { + Radius = 2, + Colour = colours.YellowDark.Opacity(0), + Type = EdgeEffectType.Shadow, + Hollow = true, + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f, + }, + text = new OsuSpriteText + { + Text = action.GetDescription(), + Margin = new MarginPadding(padding), + }, + buttons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + pressAKey = new OsuSpriteText + { + Text = "Press a key to change the binding, or ESC to cancel.", + Y = height, + Margin = new MarginPadding(padding), + Alpha = 0, + Colour = colours.YellowDark + } + }; + + reloadBindings(); + } + + private void reloadBindings() + { + buttons.Clear(); + foreach (var b in bindings) + buttons.Add(new KeyButton(b)); + } + + protected override bool OnHover(InputState state) + { + this.FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + this.FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); + + base.OnHoverLost(state); + } + + public override bool AcceptsFocus => true; + + private KeyButton bindTarget; + + protected override void OnFocus(InputState state) + { + AutoSizeDuration = 500; + AutoSizeEasing = Easing.OutQuint; + + pressAKey.FadeIn(300, Easing.OutQuint); + pressAKey.Padding = new MarginPadding(); + + base.OnFocus(state); + } + + private bool isModifier(Key k) => k < Key.F1; + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (HasFocus) + { + if (!isModifier(args.Key)) + { + bindTarget.KeyBinding.KeyCombination = new KeyCombination(state.Keyboard.Keys); + store.Update(bindTarget.KeyBinding); + GetContainingInputManager().ChangeFocus(null); + } + return true; + } + + return base.OnKeyDown(state, args); + } + + protected override void OnFocusLost(InputState state) + { + bindTarget.IsBinding = false; + bindTarget = null; + reloadBindings(); + + pressAKey.FadeOut(300, Easing.OutQuint); + pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight }; + base.OnFocusLost(state); + } + + protected override bool OnClick(InputState state) + { + bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); + if (bindTarget != null) bindTarget.IsBinding = true; + + return bindTarget != null; + } + + private class KeyButton : Container + { + public readonly KeyBinding KeyBinding; + + private readonly Box box; + public readonly OsuSpriteText Text; + + private Color4 hoverColour; + + private bool isBinding; + + public bool IsBinding + { + get { return isBinding; } + set + { + isBinding = value; + + if (value) + { + box.FadeColour(Color4.White, transition_time, Easing.OutQuint); + Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); + + } + else + { + box.FadeColour(Color4.Black, transition_time, Easing.OutQuint); + Text.FadeColour(Color4.White, transition_time, Easing.OutQuint); + } + } + } + + public KeyButton(KeyBinding keyBinding) + { + KeyBinding = keyBinding; + + Margin = new MarginPadding(padding); + + var isDefault = keyBinding.Action is Enum; + + Masking = true; + CornerRadius = padding; + + Height = height; + AutoSizeAxes = Axes.X; + + Children = new Drawable[] + { + new Container + { + AlwaysPresent = true, + Width = 80, + Height = height, + }, + box = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + Text = new OsuSpriteText + { + Font = "Venera", + TextSize = 10, + Margin = new MarginPadding(5), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = keyBinding.KeyCombination.ReadableString(), + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverColour = colours.YellowDark; + } + + protected override bool OnHover(InputState state) + { + if (isBinding) + return false; + + box.FadeColour(hoverColour, transition_time, Easing.OutQuint); + Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (isBinding) + return; + + box.FadeColour(Color4.Black, transition_time, Easing.OutQuint); + Text.FadeColour(Color4.White, transition_time, Easing.OutQuint); + + base.OnHoverLost(state); + } + } } }