using System; using System.Drawing; using System.Linq; using System.Text; using Gwen.Control; using Gwen.DragDrop; namespace Gwen.Input { /// /// Input handling. /// public static class InputHandler { private static readonly KeyData m_KeyData = new KeyData(); private static readonly float[] m_LastClickTime = new float[MaxMouseButtons]; private static Point m_LastClickPos; /// /// Control currently hovered by mouse. /// public static Base HoveredControl; /// /// Control that corrently has keyboard focus. /// public static Base KeyboardFocus; /// /// Control that currently has mouse focus. /// public static Base MouseFocus; /// /// Maximum number of mouse buttons supported. /// public static int MaxMouseButtons { get { return 5; } } /// /// Maximum time in seconds between mouse clicks to be recognized as double click. /// public static float DoubleClickSpeed { get { return 0.5f; } } /// /// Time in seconds between autorepeating of keys. /// public static float KeyRepeatRate { get { return 0.03f; } } /// /// Time in seconds before key starts to autorepeat. /// public static float KeyRepeatDelay { get { return 0.5f; } } /// /// Indicates whether the left mouse button is down. /// public static bool IsLeftMouseDown { get { return m_KeyData.LeftMouseDown; } } /// /// Indicates whether the right mouse button is down. /// public static bool IsRightMouseDown { get { return m_KeyData.RightMouseDown; } } /// /// Current mouse position. /// public static Point MousePosition; // not property to allow modification of Point fields /// /// Indicates whether the shift key is down. /// public static bool IsShiftDown { get { return IsKeyDown(Key.Shift); } } /// /// Indicates whether the control key is down. /// public static bool IsControlDown { get { return IsKeyDown(Key.Control); } } /// /// Checks if the given key is pressed. /// /// Key to check. /// True if the key is down. public static bool IsKeyDown(Key key) { return m_KeyData.KeyState[(int)key]; } /// /// Handles copy, paste etc. /// /// Canvas. /// Input character. /// True if the key was handled. public static bool DoSpecialKeys(Base canvas, char chr) { if (null == KeyboardFocus) return false; if (KeyboardFocus.GetCanvas() != canvas) return false; if (!KeyboardFocus.IsVisible) return false; if (!IsControlDown) return false; if (chr == 'C' || chr == 'c') { KeyboardFocus.InputCopy(null); return true; } if (chr == 'V' || chr == 'v') { KeyboardFocus.InputPaste(null); return true; } if (chr == 'X' || chr == 'x') { KeyboardFocus.InputCut(null); return true; } if (chr == 'A' || chr == 'a') { KeyboardFocus.InputSelectAll(null); return true; } return false; } /// /// Handles accelerator input. /// /// Canvas. /// Input character. /// True if the key was handled. public static bool HandleAccelerator(Base canvas, char chr) { //Build the accelerator search string StringBuilder accelString = new StringBuilder(); if (IsControlDown) accelString.Append("CTRL+"); if (IsShiftDown) accelString.Append("SHIFT+"); // [omeg] todo: alt? accelString.Append(chr); String acc = accelString.ToString(); //Debug::Msg("Accelerator string :%S\n", accelString.c_str());) if (KeyboardFocus != null && KeyboardFocus.HandleAccelerator(acc)) return true; if (MouseFocus != null && MouseFocus.HandleAccelerator(acc)) return true; if (canvas.HandleAccelerator(acc)) return true; return false; } /// /// Mouse moved handler. /// /// Canvas. /// /// /// /// public static void OnMouseMoved(Base canvas, int x, int y, int dx, int dy) { // Send input to canvas for study MousePosition.X = x; MousePosition.Y = y; UpdateHoveredControl(canvas); } /// /// Handles focus updating and key autorepeats. /// /// Unused. public static void OnCanvasThink(Base control) { if (MouseFocus != null && !MouseFocus.IsVisible) MouseFocus = null; if (KeyboardFocus != null && (!KeyboardFocus.IsVisible || !KeyboardFocus.KeyboardInputEnabled)) KeyboardFocus = null; if (null == KeyboardFocus) return; if (KeyboardFocus.GetCanvas() != control) return; float time = Platform.Neutral.GetTimeInSeconds(); // // Simulate Key-Repeats // for (int i = 0; i < (int)Key.Count; i++) { if (m_KeyData.KeyState[i] && m_KeyData.Target != KeyboardFocus) { m_KeyData.KeyState[i] = false; continue; } if (m_KeyData.KeyState[i] && time > m_KeyData.NextRepeat[i]) { m_KeyData.NextRepeat[i] = Platform.Neutral.GetTimeInSeconds() + KeyRepeatRate; if (KeyboardFocus != null) { KeyboardFocus.InputKeyPressed((Key)i); } } } } /// /// Mouse click handler. /// /// Canvas. /// Mouse button number. /// Specifies if the button is down. /// True if handled. public static bool OnMouseClicked(Base canvas, int mouseButton, bool down) { // If we click on a control that isn't a menu we want to close // all the open menus. Menus are children of the canvas. if (down && (null == HoveredControl || !HoveredControl.IsMenuComponent)) { canvas.CloseMenus(); } if (null == HoveredControl) return false; if (HoveredControl.GetCanvas() != canvas) return false; if (!HoveredControl.IsVisible) return false; if (HoveredControl == canvas) return false; if (mouseButton > MaxMouseButtons) return false; if (mouseButton == 0) m_KeyData.LeftMouseDown = down; else if (mouseButton == 1) m_KeyData.RightMouseDown = down; // Double click. // Todo: Shouldn't double click if mouse has moved significantly bool isDoubleClick = false; if (down && m_LastClickPos.X == MousePosition.X && m_LastClickPos.Y == MousePosition.Y && (Platform.Neutral.GetTimeInSeconds() - m_LastClickTime[mouseButton]) < DoubleClickSpeed) { isDoubleClick = true; } if (down && !isDoubleClick) { m_LastClickTime[mouseButton] = Platform.Neutral.GetTimeInSeconds(); m_LastClickPos = MousePosition; } if (down) { FindKeyboardFocus(HoveredControl); } HoveredControl.UpdateCursor(); // This tells the child it has been touched, which // in turn tells its parents, who tell their parents. // This is basically so that Windows can pop themselves // to the top when one of their children have been clicked. if (down) HoveredControl.Touch(); #if GWEN_HOOKSYSTEM if (bDown) { if (Hook::CallHook(&Hook::BaseHook::OnControlClicked, HoveredControl, MousePosition.x, MousePosition.y)) return true; } #endif switch (mouseButton) { case 0: { if (DragAndDrop.OnMouseButton(HoveredControl, MousePosition.X, MousePosition.Y, down)) return true; if (isDoubleClick) HoveredControl.InputMouseDoubleClickedLeft(MousePosition.X, MousePosition.Y); else HoveredControl.InputMouseClickedLeft(MousePosition.X, MousePosition.Y, down); return true; } case 1: { if (isDoubleClick) HoveredControl.InputMouseDoubleClickedRight(MousePosition.X, MousePosition.Y); else HoveredControl.InputMouseClickedRight(MousePosition.X, MousePosition.Y, down); return true; } } return false; } /// /// Key handler. /// /// Canvas. /// Key. /// True if the key is down. /// True if handled. public static bool OnKeyEvent(Base canvas, Key key, bool down) { if (null == KeyboardFocus) return false; if (KeyboardFocus.GetCanvas() != canvas) return false; if (!KeyboardFocus.IsVisible) return false; int iKey = (int)key; if (down) { if (!m_KeyData.KeyState[iKey]) { m_KeyData.KeyState[iKey] = true; m_KeyData.NextRepeat[iKey] = Platform.Neutral.GetTimeInSeconds() + KeyRepeatDelay; m_KeyData.Target = KeyboardFocus; return KeyboardFocus.InputKeyPressed(key); } } else { if (m_KeyData.KeyState[iKey]) { m_KeyData.KeyState[iKey] = false; // BUG BUG. This causes shift left arrow in textboxes // to not work. What is disabling it here breaking? //m_KeyData.Target = NULL; return KeyboardFocus.InputKeyPressed(key, false); } } return false; } private static void UpdateHoveredControl(Base inCanvas) { Base hovered = inCanvas.GetControlAt(MousePosition.X, MousePosition.Y); if (hovered != HoveredControl) { if (HoveredControl != null) { var oldHover = HoveredControl; HoveredControl = null; oldHover.InputMouseLeft(); } HoveredControl = hovered; if (HoveredControl != null) { HoveredControl.InputMouseEntered(); } } if (MouseFocus != null && MouseFocus.GetCanvas() == inCanvas) { if (HoveredControl != null) { var oldHover = HoveredControl; HoveredControl = null; oldHover.Redraw(); } HoveredControl = MouseFocus; } } private static void FindKeyboardFocus(Base control) { if (null == control) return; if (control.KeyboardInputEnabled) { //Make sure none of our children have keyboard focus first - todo recursive if (control.Children.Any(child => child == KeyboardFocus)) { return; } control.Focus(); return; } FindKeyboardFocus(control.Parent); return; } } }