#region CPL License /* Nuclex Framework Copyright (C) 2002-2010 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using Microsoft.Xna.Framework.Input; using Nuclex.Input; using Nuclex.UserInterface.Input; namespace Nuclex.UserInterface.Controls { /// User interface element the user can push down public abstract class PressableControl : Control, IFocusable { /// Initializes a new command control public PressableControl() { this.Enabled = true; } /// Whether the mouse pointer is hovering over the control public bool MouseHovering { get { return this.mouseHovering; } } /// Whether the pressable control is in the depressed state public virtual bool Depressed { get { bool mousePressed = (this.mouseHovering && this.pressedDownByMouse); return mousePressed || this.pressedDownByKeyboard || this.pressedDownByKeyboardShortcut || this.pressedDownByGamepadShortcut; } } /// Whether the control currently has the input focus public bool HasFocus { get { return (Screen != null) && ReferenceEquals(Screen.FocusedControl, this); } } /// /// Called when the mouse has entered the control and is now hovering over it /// protected override void OnMouseEntered() { this.mouseHovering = true; } /// /// Called when the mouse has left the control and is no longer hovering over it /// protected override void OnMouseLeft() { // 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 this.mouseHovering = false; } /// Called when a mouse button has been pressed down /// Index of the button that has been pressed protected override void OnMousePressed(MouseButtons button) { if(this.Enabled) { if(button == MouseButtons.Left) { this.pressedDownByMouse = true; } } } /// Called when a mouse button has been released again /// Index of the button that has been released protected override void OnMouseReleased(MouseButtons button) { if(button == MouseButtons.Left) { this.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(this.mouseHovering && this.Enabled) { // If this was the final input device holding down the control, meaning it's // not depressed any longer, this counts as a click and we trigger // the notification! 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(this.ShortcutButton.HasValue) { if(button == this.ShortcutButton.Value) { this.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(this.ShortcutButton.HasValue) { if(this.pressedDownByGamepadShortcut) { if(button == this.ShortcutButton.Value) { this.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(this.ShortcutButton.HasValue) { if(keyCode == keyFromButton(this.ShortcutButton.Value)) { this.pressedDownByKeyboardShortcut = true; return true; } } if(HasFocus) { if(keyCode == Keys.Space) { this.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(this.pressedDownByKeyboardShortcut) { if(this.ShortcutButton.HasValue) { if(keyCode == keyFromButton(this.ShortcutButton.Value)) { this.pressedDownByKeyboardShortcut = false; if(!Depressed) { OnPressed(); } } } } if(this.pressedDownByKeyboard) { if(keyCode == Keys.Space) { this.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() { } /// Whether the control can currently obtain the input focus bool IFocusable.CanGetFocus { get { return this.Enabled; } } /// 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; } } } /// Whether the user can interact with the choice public bool Enabled; /// Button that can be pressed to activate this command public Buttons? ShortcutButton; /// 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 game pad shortcut private bool pressedDownByGamepadShortcut; /// Whether the command is pressed down using the mouse private bool pressedDownByMouse; /// Whether the mouse is hovering over the command private bool mouseHovering; } } // namespace Nuclex.UserInterface.Controls