Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync initial input state of PassThroughInputManager from parent #6398

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions osu.Framework.Tests/Visual/Input/TestSceneKeyBindingContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ public void TestPressKeyBeforeKeyBindingContainerAdded()
AddAssert("ActionA released", () => releasedActions[0], () => Is.EqualTo(TestAction.ActionA));
}

/// <summary>
/// Unlike <see cref="TestPressKeyBeforeKeyBindingContainerAdded"/>, when an intermediate input manager is involved,
/// we expect the key binding container to be fed with any previously pressed key
/// (consider an osu!catch player pressing the left/right/dash keys before entering gameplay).
/// </summary>
[Test]
public void TestPressKeyBeforeKeyBindingContainerAdded_WithPassThroughInputManager()
{
Expand All @@ -142,15 +147,22 @@ public void TestPressKeyBeforeKeyBindingContainerAdded_WithPassThroughInputManag
};
});

AddAssert("only one action pressed", () => pressedActions, () => Has.Count.EqualTo(1));
AddAssert("ActionEnter pressed", () => pressedActions[0], () => Is.EqualTo(TestAction.ActionEnter));
AddAssert("no actions released", () => releasedActions, () => Is.Empty);

AddStep("press key A", () => InputManager.PressKey(Key.A));
AddAssert("only one action triggered", () => pressedActions, () => Has.Count.EqualTo(1));
AddAssert("ActionA triggered", () => pressedActions[0], () => Is.EqualTo(TestAction.ActionA));
AddAssert("two actions pressed", () => pressedActions, () => Has.Count.EqualTo(2));
AddAssert("ActionA pressed", () => pressedActions[1], () => Is.EqualTo(TestAction.ActionA));
AddAssert("no actions released", () => releasedActions, () => Is.Empty);

AddStep("release key A", () => InputManager.ReleaseKey(Key.A));
AddAssert("only one action triggered", () => pressedActions, () => Has.Count.EqualTo(1));
AddAssert("only one action released", () => releasedActions, () => Has.Count.EqualTo(1));
AddAssert("one action released", () => releasedActions, () => Has.Count.EqualTo(1));
AddAssert("ActionA released", () => releasedActions[0], () => Is.EqualTo(TestAction.ActionA));

AddStep("release enter", () => InputManager.ReleaseKey(Key.Enter));
AddAssert("two actions released", () => releasedActions, () => Has.Count.EqualTo(2));
AddAssert("ActionEnter released", () => releasedActions[1], () => Is.EqualTo(TestAction.ActionEnter));
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,26 @@ private void addTestInputManagerStep()
ChildrenEnumerable = Enumerable.Empty<Drawable>();
});

[Test]
public void TestInitialState()
{
AddStep("Press buttons", () =>
{
InputManager.PressButton(MouseButton.Left);
InputManager.PressKey(Key.A);
InputManager.PressJoystickButton(JoystickButton.Button1);
});
addTestInputManagerStep();
AddAssert("pressed", () => mouse.IsPressed(MouseButton.Left) && keyboard.IsPressed(Key.A) && joystick.IsPressed(JoystickButton.Button1));
AddStep("Release on parent", () =>
{
InputManager.ReleaseButton(MouseButton.Left);
InputManager.ReleaseKey(Key.A);
InputManager.ReleaseJoystickButton(JoystickButton.Button1);
});
AddAssert("released", () => !mouse.IsPressed(MouseButton.Left) && !keyboard.IsPressed(Key.A) && !joystick.IsPressed(JoystickButton.Button1));
}

[Test]
public void UseParentInputChange()
{
Expand Down
38 changes: 38 additions & 0 deletions osu.Framework/Input/PassThroughInputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ public virtual bool UseParentInput
protected override void LoadComplete()
{
base.LoadComplete();

parentInputManager = GetContainingInputManager();

// allow a frame for children to be prepared before passing input from parent.
// this is especially necessary if our child is a KeyBindingContainer since the key bindings are not prepared until LoadComplete is called on it.
ScheduleAfterChildren(syncInitialState);
}

public override bool HandleHoverEvents => parentInputManager != null && UseParentInput ? parentInputManager.HandleHoverEvents : base.HandleHoverEvents;
Expand Down Expand Up @@ -185,6 +190,39 @@ protected override void Update()
}
}

/// <summary>
/// Syncs initial state of this input manager to the current state of the parent input manager.
/// </summary>
private void syncInitialState()
{
if (parentInputManager == null)
return;

var parentState = parentInputManager.CurrentState;
var mouseDiff = (parentState?.Mouse?.Buttons ?? new ButtonStates<MouseButton>()).EnumerateDifference(CurrentState.Mouse.Buttons);
var keyDiff = (parentState?.Keyboard.Keys ?? new ButtonStates<Key>()).EnumerateDifference(CurrentState.Keyboard.Keys);
var touchDiff = (parentState?.Touch ?? new TouchState()).EnumerateDifference(CurrentState.Touch);
var joyButtonDiff = (parentState?.Joystick?.Buttons ?? new ButtonStates<JoystickButton>()).EnumerateDifference(CurrentState.Joystick.Buttons);
var midiDiff = (parentState?.Midi?.Keys ?? new ButtonStates<MidiKey>()).EnumerateDifference(CurrentState.Midi.Keys);
var tabletPenDiff = (parentState?.Tablet?.PenButtons ?? new ButtonStates<TabletPenButton>()).EnumerateDifference(CurrentState.Tablet.PenButtons);
var tabletAuxiliaryDiff = (parentState?.Tablet?.AuxiliaryButtons ?? new ButtonStates<TabletAuxiliaryButton>()).EnumerateDifference(CurrentState.Tablet.AuxiliaryButtons);

if (mouseDiff.Pressed.Length > 0)
new MouseButtonInput(mouseDiff.Pressed.Select(button => new ButtonInputEntry<MouseButton>(button, true))).Apply(CurrentState, this);
foreach (var key in keyDiff.Pressed)
new KeyboardKeyInput(key, true).Apply(CurrentState, this);
if (touchDiff.activated.Length > 0)
new TouchInput(touchDiff.activated, true).Apply(CurrentState, this);
foreach (var button in joyButtonDiff.Pressed)
new JoystickButtonInput(button, true).Apply(CurrentState, this);
foreach (var key in midiDiff.Pressed)
new MidiKeyInput(key, parentState?.Midi?.Velocities.GetValueOrDefault(key) ?? 0, true).Apply(CurrentState, this);
foreach (var button in tabletPenDiff.Pressed)
new TabletPenButtonInput(button, true).Apply(CurrentState, this);
foreach (var button in tabletAuxiliaryDiff.Pressed)
new TabletAuxiliaryButtonInput(button, true).Apply(CurrentState, this);
}

/// <summary>
/// Updates state of any buttons that have been released by parent while <see cref="UseParentInput"/> was disabled.
/// </summary>
Expand Down
Loading