Skip to content

Commit

Permalink
Merge pull request #1233 from peppy/input-revamp
Browse files Browse the repository at this point in the history
Make KeyBindingInputManagers into Containers
  • Loading branch information
smoogipoo authored Dec 7, 2017
2 parents ba2a9c2 + 2070f27 commit 797a351
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 83 deletions.
4 changes: 1 addition & 3 deletions osu.Framework.Tests/Visual/TestCaseKeyBindings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private enum TestAction
RightMouse
}

private class TestInputManager : KeyBindingInputManager<TestAction>
private class TestInputManager : KeyBindingContainer<TestAction>
{
public TestInputManager(SimultaneousBindingMode concurrencyMode = SimultaneousBindingMode.None) : base(concurrencyMode)
{
Expand Down Expand Up @@ -121,7 +121,6 @@ public bool OnPressed(TestAction action)
{
alphaTarget += 0.2f;
Background.FadeTo(alphaTarget, 100, Easing.OutQuint);
return true;
}

return false;
Expand All @@ -133,7 +132,6 @@ public bool OnReleased(TestAction action)
{
alphaTarget -= 0.2f;
Background.FadeTo(alphaTarget, 100, Easing.OutQuint);
return true;
}

return false;
Expand Down
53 changes: 23 additions & 30 deletions osu.Framework/Graphics/UserInterface/TextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,40 +73,33 @@ public TextBox()
{
Masking = true;
CornerRadius = 3;
// TextBoxes currently require their own top-level PlatformInputManager, as InputManagers
// will not propagate events through other InputManagers.
// Once this restriction has been addressed, we can utilise a single instance of PlatformInputManager
// at the Game level, and TextBoxes need only implement IKeyBindingHandler<PlatformAction>.
Child = new PlatformInputManager

Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
Background = new Box
{
Background = new Box
{
Colour = BackgroundUnfocused,
RelativeSizeAxes = Axes.Both,
},
TextContainer = new KeyBindingContainer(this)
Colour = BackgroundUnfocused,
RelativeSizeAxes = Axes.Both,
},
TextContainer = new TextBoxPlatformBindingHandler(this)
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Position = new Vector2(LeftRightPadding, 0),
Children = new[]
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Position = new Vector2(LeftRightPadding, 0),
Children = new[]
Placeholder = CreatePlaceholder(),
Caret = new DrawableCaret(),
TextFlow = new FillFlowContainer
{
Placeholder = CreatePlaceholder(),
Caret = new DrawableCaret(),
TextFlow = new FillFlowContainer
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
},
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
},
},
}
},
};

Current.ValueChanged += newValue => { Text = newValue; };
Expand Down Expand Up @@ -878,11 +871,11 @@ public DrawableCaret()
}
}

private class KeyBindingContainer : Container, IKeyBindingHandler<PlatformAction>
private class TextBoxPlatformBindingHandler : Container, IKeyBindingHandler<PlatformAction>
{
private readonly TextBox textBox;

public KeyBindingContainer(TextBox textBox)
public TextBoxPlatformBindingHandler(TextBox textBox)
{
this.textBox = textBox;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;

namespace osu.Framework.Input.Bindings
Expand All @@ -14,7 +15,7 @@ namespace osu.Framework.Input.Bindings
/// Maps input actions to custom action data of type <see cref="T"/>. Use in conjunction with <see cref="Drawable"/>s implementing <see cref="IKeyBindingHandler{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the custom action.</typeparam>
public abstract class KeyBindingInputManager<T> : KeyBindingInputManager
public abstract class KeyBindingContainer<T> : KeyBindingContainer
where T : struct
{
private readonly SimultaneousBindingMode simultaneousMode;
Expand All @@ -23,8 +24,10 @@ public abstract class KeyBindingInputManager<T> : KeyBindingInputManager
/// Create a new instance.
/// </summary>
/// <param name="simultaneousMode">Specify how to deal with multiple matches of <see cref="KeyCombination"/>s and <see cref="T"/>s.</param>
protected KeyBindingInputManager(SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
protected KeyBindingContainer(SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
{
RelativeSizeAxes = Axes.Both;

this.simultaneousMode = simultaneousMode;
}

Expand All @@ -43,52 +46,61 @@ protected KeyBindingInputManager(SimultaneousBindingMode simultaneousMode = Simu
/// The input queue to be used for processing key bindings. Based on the non-positional <see cref="InputManager.InputQueue"/>.
/// Can be overridden to change priorities.
/// </summary>
protected virtual IEnumerable<Drawable> KeyBindingInputQueue => InputQueue;
protected virtual IEnumerable<Drawable> KeyBindingInputQueue => localQueue;

private readonly List<Drawable> localQueue = new List<Drawable>();

/// <summary>
/// Override to enable or disable sending of repeated actions (disabled by default).
/// Each repeated action will have its own pressed/released event pair.
/// </summary>
protected virtual bool SendRepeats => false;

protected override bool PropagateWheel(IEnumerable<Drawable> drawables, InputState state)
protected override bool OnWheel(InputState state)
{
if (base.PropagateWheel(drawables, state)) return true;
InputKey key = state.Mouse.WheelDelta > 0 ? InputKey.MouseWheelUp : InputKey.MouseWheelDown;

// we need to create a local cloned state to ensure the underlying code in handleNewReleased thinks we are in a sane state,
// even though we are pressing and releasing an InputKey in a single frame.
// the important part of this cloned state is the value of Wheel reset to zero.
var clonedState = state.Clone();
var clonedMouseState = (MouseState)clonedState.Mouse;
clonedState.Mouse = new MouseState { Buttons = clonedState.Mouse.Buttons };

clonedMouseState.Wheel = 0;
clonedMouseState.LastState = null;
return handleNewPressed(state, key, false) | handleNewReleased(clonedState, key);
}

InputKey key = state.Mouse.WheelDelta > 0 ? InputKey.MouseWheelUp : InputKey.MouseWheelDown;
internal override bool BuildKeyboardInputQueue(List<Drawable> queue)
{
localQueue.Clear();

return handleNewPressed(state, key, false) | handleNewReleased(clonedState, key);
var addedSelf = base.BuildKeyboardInputQueue(localQueue);

queue.AddRange(localQueue);

if (addedSelf)
localQueue.Remove(this);

return true;
}

protected override bool PropagateMouseDown(IEnumerable<Drawable> drawables, InputState state, MouseDownEventArgs args) =>
base.PropagateMouseDown(drawables, state, args) || handleNewPressed(state, KeyCombination.FromMouseButton(args.Button), false);
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => handleNewPressed(state, KeyCombination.FromMouseButton(args.Button), false);

protected override bool PropagateMouseUp(IEnumerable<Drawable> drawables, InputState state, MouseUpEventArgs args) =>
base.PropagateMouseUp(drawables, state, args) || handleNewReleased(state, KeyCombination.FromMouseButton(args.Button));
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => handleNewReleased(state, KeyCombination.FromMouseButton(args.Button));

protected override bool PropagateKeyDown(IEnumerable<Drawable> drawables, InputState state, KeyDownEventArgs args)
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat && !SendRepeats)
{
if (pressedBindings.Count > 0)
return true;

return base.PropagateKeyDown(drawables, state, args);
return false;
}

return base.PropagateKeyDown(drawables, state, args) || handleNewPressed(state, KeyCombination.FromKey(args.Key), args.Repeat);
return handleNewPressed(state, KeyCombination.FromKey(args.Key), args.Repeat);
}

protected override bool PropagateKeyUp(IEnumerable<Drawable> drawables, InputState state, KeyUpEventArgs args) =>
base.PropagateKeyUp(drawables, state, args) || handleNewReleased(state, KeyCombination.FromKey(args.Key));
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => handleNewReleased(state, KeyCombination.FromKey(args.Key));

private bool handleNewPressed(InputState state, InputKey newKey, bool repeat)
{
Expand Down Expand Up @@ -188,12 +200,16 @@ protected virtual bool PropagateReleased(IEnumerable<Drawable> drawables, T rele

return handled != null;
}

public void TriggerReleased(T released) => PropagateReleased(KeyBindingInputQueue, released);

public void TriggerPressed(T pressed) => PropagatePressed(KeyBindingInputQueue, pressed);
}

/// <summary>
/// Maps input actions to custom action data.
/// </summary>
public abstract class KeyBindingInputManager : PassThroughInputManager
public abstract class KeyBindingContainer : Container
{
protected IEnumerable<KeyBinding> KeyBindings;

Expand All @@ -217,12 +233,14 @@ public enum SimultaneousBindingMode
/// One action can be in a pressed state at once. If a new matching binding is encountered, any existing binding is first released.
/// </summary>
None,

/// <summary>
/// Unique actions are allowed to be pressed at the same time. There may therefore be more than one action in an actuated state at once.
/// If one action has multiple bindings, only the first will trigger an <see cref="IKeyBindingHandler{T}.OnPressed"/>.
/// The last binding to be released will trigger an <see cref="IKeyBindingHandler{T}.OnReleased(T)"/>.
/// </summary>
Unique,

/// <summary>
/// Unique actions are allowed to be pressed at the same time, as well as multiple times from different bindings. There may therefore be
/// more than one action in an pressed state at once, as well as multiple consecutive <see cref="IKeyBindingHandler{T}.OnPressed"/> events
Expand Down
31 changes: 31 additions & 0 deletions osu.Framework/Input/FrameworkActionContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE

using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;

namespace osu.Framework.Input
{
public class FrameworkActionContainer : KeyBindingContainer<FrameworkAction>
{
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
{
new KeyBinding(new[] { InputKey.Control, InputKey.F1 }, FrameworkAction.ToggleDrawVisualiser),
new KeyBinding(new[] { InputKey.Control, InputKey.F11 }, FrameworkAction.CycleFrameStatistics),
new KeyBinding(new[] { InputKey.Control, InputKey.F10 }, FrameworkAction.ToggleLogOverlay),
new KeyBinding(new[] { InputKey.Alt, InputKey.Enter }, FrameworkAction.ToggleFullscreen),
};

protected override IEnumerable<Drawable> KeyBindingInputQueue => new[] { Child }.Concat(base.KeyBindingInputQueue);
}

public enum FrameworkAction
{
CycleFrameStatistics,
ToggleDrawVisualiser,
ToggleLogOverlay,
ToggleFullscreen
}
}
10 changes: 9 additions & 1 deletion osu.Framework/Input/MouseState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ namespace osu.Framework.Input
{
public class MouseState : IMouseState
{
public IReadOnlyList<MouseButton> Buttons => buttons;
public IReadOnlyList<MouseButton> Buttons
{
get { return buttons; }
set
{
buttons.Clear();
buttons.AddRange(value);
}
}

private List<MouseButton> buttons { get; set; } = new List<MouseButton>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE

using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Input.Bindings;
using osu.Framework.Platform;

namespace osu.Framework.Input
{
Expand All @@ -12,9 +14,17 @@ namespace osu.Framework.Input
/// can be created to handle events that should trigger specifically on a focused drawable.
/// Will send repeat events by default.
/// </summary>
public class PlatformInputManager : KeyBindingInputManager<PlatformAction>
public class PlatformActionContainer : KeyBindingContainer<PlatformAction>
{
public override IEnumerable<KeyBinding> DefaultKeyBindings => Host.PlatformKeyBindings;
private GameHost host;

[BackgroundDependencyLoader]
private void load(GameHost host)
{
this.host = host;
}

public override IEnumerable<KeyBinding> DefaultKeyBindings => host.PlatformKeyBindings;

protected override bool SendRepeats => true;
}
Expand Down
23 changes: 1 addition & 22 deletions osu.Framework/Input/UserInputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,19 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE

using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Handlers;

namespace osu.Framework.Input
{
public class UserInputManager : KeyBindingInputManager<FrameworkAction>
public class UserInputManager : PassThroughInputManager
{
protected override IEnumerable<InputHandler> InputHandlers => Host.AvailableInputHandlers;

public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
{
new KeyBinding(new[] { InputKey.Control, InputKey.F1 }, FrameworkAction.ToggleDrawVisualiser),
new KeyBinding(new[] { InputKey.Control, InputKey.F11 }, FrameworkAction.CycleFrameStatistics),
new KeyBinding(new[] { InputKey.Control, InputKey.F10 }, FrameworkAction.ToggleLogOverlay),
new KeyBinding(new[] { InputKey.Alt, InputKey.Enter }, FrameworkAction.ToggleFullscreen),
};

protected override bool HandleHoverEvents => Host.Window?.CursorInWindow ?? true;

public UserInputManager()
{
UseParentState = false;
}

protected override IEnumerable<Drawable> KeyBindingInputQueue => new[] { Child }.Concat(base.KeyBindingInputQueue);
}

public enum FrameworkAction
{
CycleFrameStatistics,
ToggleDrawVisualiser,
ToggleLogOverlay,
ToggleFullscreen
}
}
15 changes: 12 additions & 3 deletions osu.Framework/Platform/GameHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,16 @@ private void resetInputHandlers()

private void bootstrapSceneGraph(Game game)
{
var root = new UserInputManager { Child = game };
var root = new UserInputManager
{
Child = new PlatformActionContainer
{
Child = new FrameworkActionContainer
{
Child = game
}
}
};

Dependencies.Cache(root);
Dependencies.Cache(game);
Expand Down Expand Up @@ -582,7 +591,7 @@ public void Dispose()
#endregion

/// <summary>
/// Defines the platform-specific key bindings that will be used by <see cref="osu.Framework.Input.PlatformInputManager"/>.
/// Defines the platform-specific key bindings that will be used by <see cref="PlatformActionContainer"/>.
/// Should be overridden per-platform to provide native key bindings.
/// </summary>
public virtual IEnumerable<KeyBinding> PlatformKeyBindings => new[]
Expand All @@ -599,7 +608,7 @@ public void Dispose()
new KeyBinding(new KeyCombination(new[] { InputKey.Shift, InputKey.Right }), new PlatformAction(PlatformActionType.CharNext, PlatformActionMethod.Select)),
new KeyBinding(new KeyCombination(new[] { InputKey.Control, InputKey.Left }), new PlatformAction(PlatformActionType.WordPrevious, PlatformActionMethod.Move)),
new KeyBinding(new KeyCombination(new[] { InputKey.Control, InputKey.Right }), new PlatformAction(PlatformActionType.WordNext, PlatformActionMethod.Move)),
new KeyBinding(new KeyCombination(new[] { InputKey.Control, InputKey.BackSpace}), new PlatformAction(PlatformActionType.WordPrevious, PlatformActionMethod.Delete)),
new KeyBinding(new KeyCombination(new[] { InputKey.Control, InputKey.BackSpace }), new PlatformAction(PlatformActionType.WordPrevious, PlatformActionMethod.Delete)),
new KeyBinding(new KeyCombination(new[] { InputKey.Control, InputKey.Delete }), new PlatformAction(PlatformActionType.WordNext, PlatformActionMethod.Delete)),
new KeyBinding(new KeyCombination(new[] { InputKey.Control, InputKey.Shift, InputKey.Left }), new PlatformAction(PlatformActionType.WordPrevious, PlatformActionMethod.Select)),
new KeyBinding(new KeyCombination(new[] { InputKey.Control, InputKey.Shift, InputKey.Right }), new PlatformAction(PlatformActionType.WordNext, PlatformActionMethod.Select)),
Expand Down
Loading

0 comments on commit 797a351

Please sign in to comment.