using Microsoft.Xna.Framework.Input; using MonoGame.Extended.Input; using MonoGame.Extended.Input.InputListeners; namespace MonoGame.Extended.NuclexGui.Controls { /// /// Determines where the text or image describing control will be located. /// Used primarily for radio button and checkbox. /// public enum GuiPressableDescriptionPosition { North, South, West, East } /// User interface element the user can push down public abstract class GuiPressableControl : GuiControl, IFocusable { /// Whether the mouse is hovering over the command private bool _mouseHovering; /// Whether the command is pressed down using the game pad shortcut private bool _pressedDownByGamepadShortcut; /// Whether the command is pressed down using the space key private bool _pressedDownByKeyboard; /// Whether the command is pressed down using the keyboard shortcut private bool _pressedDownByKeyboardShortcut; /// Whether the command is pressed down using the mouse private bool _pressedDownByMouse; /// Whether the user can interact with the choice public bool Enabled; /// Button that can be pressed to activate this command public Buttons? ShortcutButton; /// Initializes a new command control public GuiPressableControl() { Enabled = true; } /// Whether the mouse pointer is hovering over the control public bool MouseHovering => _mouseHovering; /// Whether the pressable control is in the depressed state public virtual bool Depressed { get { var mousePressed = _mouseHovering && _pressedDownByMouse; return mousePressed || _pressedDownByKeyboard || _pressedDownByKeyboardShortcut || _pressedDownByGamepadShortcut; } } /// Whether the control currently has the input focus public bool HasFocus => (Screen != null) && ReferenceEquals(Screen.FocusedControl, this); /// Whether the control can currently obtain the input focus bool IFocusable.CanGetFocus => Enabled; /// /// Called when the mouse has entered the control and is now hovering over it /// protected override void OnMouseEntered() { _mouseHovering = true; } /// /// Called when the mouse has left the control and is no longer hovering over it /// protected override void OnMouseLeft(float x, float y) { // Intentionally not calling OnActivated() here because the user has moved // the mouse away from the command while holding the mouse button down - // a common trick under windows to last-second-abort the clicking of a button _mouseHovering = false; } /// Called when a mouse button has been pressed down /// Index of the button that has been pressed protected override void OnMousePressed(MouseButton button) { if (Enabled) { if (button == MouseButton.Left) _pressedDownByMouse = true; } } /// Called when a mouse button has been released again /// Index of the button that has been released protected override void OnMouseReleased(MouseButton button) { if (button == MouseButton.Left) { _pressedDownByMouse = false; // Only trigger the pressed event if the mouse was released over the control. // The user can move the mouse cursor away from the control while still holding // the mouse button down to do the well-known last-second-abort. if (_mouseHovering && Enabled) { if (!Depressed) OnPressed(); } } } /// Called when a button on the gamepad has been pressed /// Button that has been pressed /// /// True if the button press was handled by the control, otherwise false. /// protected override bool OnButtonPressed(Buttons button) { if (ShortcutButton.HasValue) { if (button == ShortcutButton.Value) { _pressedDownByGamepadShortcut = true; return true; } } return false; } /// Called when a button on the gamepad has been released /// Button that has been released protected override void OnButtonReleased(Buttons button) { if (ShortcutButton.HasValue) { if (_pressedDownByGamepadShortcut) { if (button == ShortcutButton.Value) { _pressedDownByGamepadShortcut = false; if (!Depressed) OnPressed(); } } } } /// Called when a key on the keyboard has been pressed down /// Code of the key that was pressed /// /// True if the key press was handled by the control, otherwise false. /// protected override bool OnKeyPressed(Keys keyCode) { if (ShortcutButton.HasValue) { if (keyCode == KeyFromButton(ShortcutButton.Value)) { _pressedDownByKeyboardShortcut = true; return true; } } if (HasFocus) { if (keyCode == Keys.Space) { _pressedDownByKeyboard = true; return true; } } return false; } /// Called when a key on the keyboard has been released again /// Code of the key that was released protected override void OnKeyReleased(Keys keyCode) { if (_pressedDownByKeyboardShortcut) { if (ShortcutButton.HasValue) { if (keyCode == KeyFromButton(ShortcutButton.Value)) { _pressedDownByKeyboardShortcut = false; if (!Depressed) OnPressed(); } } } if (_pressedDownByKeyboard) { if (keyCode == Keys.Space) { _pressedDownByKeyboard = false; if (!Depressed) OnPressed(); } } } /// Called when the control is pressed /// /// If you were to implement a button, for example, you could trigger a 'Pressed' /// event here are call a user-provided delegate, depending on your design. /// protected virtual void OnPressed() { } /// Looks up the equivalent key to the gamepad button /// /// Gamepad button for which the equivalent key on the keyboard will be found /// /// The key that is equivalent to the specified gamepad button private static Keys KeyFromButton(Buttons button) { switch (button) { case Buttons.A: { return Keys.A; } case Buttons.B: { return Keys.B; } case Buttons.Back: { return Keys.Back; } case Buttons.LeftShoulder: { return Keys.L; } case Buttons.LeftStick: { return Keys.LeftControl; } case Buttons.RightShoulder: { return Keys.R; } case Buttons.RightStick: { return Keys.RightControl; } case Buttons.Start: { return Keys.Enter; } case Buttons.X: { return Keys.X; } case Buttons.Y: { return Keys.Y; } default: { return Keys.None; } } } } }