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;
}
}
}
}
}