// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Input.StateChanges.Events; using osu.Framework.Input.States; using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Input.Handlers; using osu.Game.Screens.Play; using osuTK.Input; using static osu.Game.Input.Handlers.ReplayInputHandler; using JoystickState = osu.Framework.Input.States.JoystickState; using KeyboardState = osu.Framework.Input.States.KeyboardState; using MouseState = osu.Framework.Input.States.MouseState; namespace osu.Game.Rulesets.UI { public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler where T : struct { protected override InputState CreateInitialState() { var state = base.CreateInitialState(); return new RulesetInputManagerInputState(state.Mouse, state.Keyboard, state.Joystick); } protected readonly KeyBindingContainer KeyBindingContainer; protected override Container Content => content; private readonly Container content; protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) { InternalChild = KeyBindingContainer = CreateKeyBindingContainer(ruleset, variant, unique) .WithChild(content = new Container { RelativeSizeAxes = Axes.Both }); } [BackgroundDependencyLoader(true)] private void load(OsuConfigManager config) { mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); } #region Action mapping (for replays) public override void HandleInputStateChange(InputStateChangeEvent inputStateChange) { if (inputStateChange is ReplayStateChangeEvent replayStateChanged) { foreach (var action in replayStateChanged.ReleasedActions) KeyBindingContainer.TriggerReleased(action); foreach (var action in replayStateChanged.PressedActions) KeyBindingContainer.TriggerPressed(action); } else { base.HandleInputStateChange(inputStateChange); } } #endregion #region IHasReplayHandler private ReplayInputHandler replayInputHandler; public ReplayInputHandler ReplayInputHandler { get => replayInputHandler; set { if (replayInputHandler != null) RemoveHandler(replayInputHandler); replayInputHandler = value; UseParentInput = replayInputHandler == null; if (replayInputHandler != null) AddHandler(replayInputHandler); } } #endregion #region Setting application (disables etc.) private Bindable mouseDisabled; protected override bool Handle(UIEvent e) { switch (e) { case MouseDownEvent mouseDown when mouseDown.Button == MouseButton.Left || mouseDown.Button == MouseButton.Right: if (mouseDisabled.Value) return false; break; case MouseUpEvent mouseUp: if (!CurrentState.Mouse.IsPressed(mouseUp.Button)) return false; break; } return base.Handle(e); } #endregion #region Key Counter Attachment public void Attach(KeyCounterCollection keyCounter) { var receptor = new ActionReceptor(keyCounter); Add(receptor); keyCounter.SetReceptor(receptor); keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b))); } public class ActionReceptor : KeyCounterCollection.Receptor, IKeyBindingHandler { public ActionReceptor(KeyCounterCollection target) : base(target) { } public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action)); public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action)); } #endregion protected virtual RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) => new RulesetKeyBindingContainer(ruleset, variant, unique); public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer { public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique) { } } } /// /// Expose the in a capable . /// public interface IHasReplayHandler { ReplayInputHandler ReplayInputHandler { get; set; } } /// /// Supports attaching a . /// Keys will be populated automatically and a receptor will be injected inside. /// public interface ICanAttachKeyCounter { void Attach(KeyCounterCollection keyCounter); } public class RulesetInputManagerInputState : InputState where T : struct { public ReplayState LastReplayState; public RulesetInputManagerInputState(MouseState mouse = null, KeyboardState keyboard = null, JoystickState joystick = null) : base(mouse, keyboard, joystick) { } } }