1 /*
2 GWEN
3 Copyright (c) 2010 Facepunch Studios
4 See license in Gwen.h
5 */
6
7 #include "Gwen/InputHandler.h"
8 #include "Gwen/Controls/Base.h"
9 #include "Gwen/DragAndDrop.h"
10 #include "Gwen/Hook.h"
11 #include "Gwen/Platform.h"
12
13 #define DOUBLE_CLICK_SPEED 0.5f
14 #define MAX_MOUSE_BUTTONS 5
15
16 using namespace Gwen;
17
18 struct Action
19 {
20 unsigned char type;
21
22 int x, y;
23 Gwen::UnicodeChar chr;
24 };
25
26 static const float KeyRepeatRate = 0.03f;
27 static const float KeyRepeatDelay = 0.3f;
28
29 struct t_KeyData
30 {
t_KeyDatat_KeyData31 t_KeyData()
32 {
33 for (int i = 0; i < Gwen::Key::Count; i++)
34 {
35 KeyState[i] = false;
36 NextRepeat[i] = 0;
37 }
38
39 Target = NULL;
40 LeftMouseDown = false;
41 RightMouseDown = false;
42 }
43
44 bool KeyState[Gwen::Key::Count];
45 float NextRepeat[Gwen::Key::Count];
46 Controls::Base* Target;
47 bool LeftMouseDown;
48 bool RightMouseDown;
49
50 } KeyData;
51
52 Gwen::Point MousePosition;
53
54 static float g_fLastClickTime[MAX_MOUSE_BUTTONS];
55 static Gwen::Point g_pntLastClickPos;
56
57 enum
58 {
59 ACT_MOUSEMOVE,
60 ACT_MOUSEBUTTON,
61 ACT_CHAR,
62 ACT_MOUSEWHEEL,
63 ACT_KEYPRESS,
64 ACT_KEYRELEASE,
65 ACT_MESSAGE
66 };
67
UpdateHoveredControl(Controls::Base * pInCanvas)68 void UpdateHoveredControl(Controls::Base* pInCanvas)
69 {
70 Controls::Base* pHovered = pInCanvas->GetControlAt(MousePosition.x, MousePosition.y);
71
72 if (Gwen::HoveredControl && pHovered != Gwen::HoveredControl)
73 {
74 Gwen::HoveredControl->OnMouseLeave();
75
76 pInCanvas->Redraw();
77 }
78
79 if (pHovered != Gwen::HoveredControl)
80 {
81 Gwen::HoveredControl = pHovered;
82
83 if (Gwen::HoveredControl)
84 Gwen::HoveredControl->OnMouseEnter();
85
86 pInCanvas->Redraw();
87 }
88
89 if (Gwen::MouseFocus && Gwen::MouseFocus->GetCanvas() == pInCanvas)
90 {
91 Gwen::HoveredControl = Gwen::MouseFocus;
92 }
93 }
94
FindKeyboardFocus(Controls::Base * pControl)95 void FindKeyboardFocus(Controls::Base* pControl)
96 {
97 if (!pControl) return;
98 if (pControl->GetKeyboardInputEnabled())
99 {
100 //Make sure none of our children have keyboard focus first - todo recursive
101 for (Controls::Base::List::iterator iter = pControl->Children.begin(); iter != pControl->Children.end(); ++iter)
102 {
103 Controls::Base* pChild = *iter;
104 if (pChild == Gwen::KeyboardFocus)
105 return;
106 }
107
108 pControl->Focus();
109 return;
110 }
111
112 return FindKeyboardFocus(pControl->GetParent());
113 }
114
GetMousePosition()115 Gwen::Point Gwen::Input::GetMousePosition()
116 {
117 return MousePosition;
118 }
119
OnCanvasThink(Controls::Base * pControl)120 void Gwen::Input::OnCanvasThink(Controls::Base* pControl)
121 {
122 if (Gwen::MouseFocus && !Gwen::MouseFocus->Visible())
123 Gwen::MouseFocus = NULL;
124
125 if (Gwen::KeyboardFocus)
126 {
127 bool isVisible = Gwen::KeyboardFocus->Visible();
128 bool isEnabled = KeyboardFocus->GetKeyboardInputEnabled();
129
130 if (!isVisible || !isEnabled)
131 Gwen::KeyboardFocus = NULL;
132 }
133
134 if (!KeyboardFocus) return;
135 if (KeyboardFocus->GetCanvas() != pControl) return;
136
137 float fTime = Gwen::Platform::GetTimeInSeconds();
138
139 //
140 // Simulate Key-Repeats
141 //
142 for (int i = 0; i < Gwen::Key::Count; i++)
143 {
144 if (KeyData.KeyState[i] && KeyData.Target != KeyboardFocus)
145 {
146 KeyData.KeyState[i] = false;
147 continue;
148 }
149
150 if (KeyData.KeyState[i] && fTime > KeyData.NextRepeat[i])
151 {
152 KeyData.NextRepeat[i] = Gwen::Platform::GetTimeInSeconds() + KeyRepeatRate;
153
154 if (KeyboardFocus)
155 {
156 KeyboardFocus->OnKeyPress(i);
157 }
158 }
159 }
160 }
161
IsKeyDown(int iKey)162 bool Gwen::Input::IsKeyDown(int iKey)
163 {
164 return KeyData.KeyState[iKey];
165 }
166
IsLeftMouseDown()167 bool Gwen::Input::IsLeftMouseDown()
168 {
169 return KeyData.LeftMouseDown;
170 }
171
IsRightMouseDown()172 bool Gwen::Input::IsRightMouseDown()
173 {
174 return KeyData.RightMouseDown;
175 }
176
OnMouseMoved(Controls::Base * pCanvas,int x,int y,int,int)177 void Gwen::Input::OnMouseMoved(Controls::Base* pCanvas, int x, int y, int /*deltaX*/, int /*deltaY*/)
178 {
179 MousePosition.x = x;
180 MousePosition.y = y;
181
182 UpdateHoveredControl(pCanvas);
183 }
184
OnMouseClicked(Controls::Base * pCanvas,int iMouseButton,bool bDown)185 bool Gwen::Input::OnMouseClicked(Controls::Base* pCanvas, int iMouseButton, bool bDown)
186 {
187 // If we click on a control that isn't a menu we want to close
188 // all the open menus. Menus are children of the canvas.
189 if (bDown && (!Gwen::HoveredControl || !Gwen::HoveredControl->IsMenuComponent()))
190 {
191 pCanvas->CloseMenus();
192 }
193
194 if (!Gwen::HoveredControl) return false;
195 if (Gwen::HoveredControl->GetCanvas() != pCanvas) return false;
196 if (!Gwen::HoveredControl->Visible()) return false;
197 if (Gwen::HoveredControl == pCanvas) return false;
198
199 if (iMouseButton > MAX_MOUSE_BUTTONS)
200 return false;
201
202 if (iMouseButton == 0)
203 KeyData.LeftMouseDown = bDown;
204 else if (iMouseButton == 1)
205 KeyData.RightMouseDown = bDown;
206
207 // Double click.
208 // Todo: Shouldn't double click if mouse has moved significantly
209 bool bIsDoubleClick = false;
210
211 if (bDown &&
212 g_pntLastClickPos.x == MousePosition.x &&
213 g_pntLastClickPos.y == MousePosition.y &&
214 (Gwen::Platform::GetTimeInSeconds() - g_fLastClickTime[iMouseButton]) < DOUBLE_CLICK_SPEED)
215 {
216 bIsDoubleClick = true;
217 }
218
219 if (bDown && !bIsDoubleClick)
220 {
221 g_fLastClickTime[iMouseButton] = Gwen::Platform::GetTimeInSeconds();
222 g_pntLastClickPos = MousePosition;
223 }
224
225 if (bDown)
226 {
227 FindKeyboardFocus(Gwen::HoveredControl);
228 }
229
230 Gwen::HoveredControl->UpdateCursor();
231
232 // This tells the child it has been touched, which
233 // in turn tells its parents, who tell their parents.
234 // This is basically so that Windows can pop themselves
235 // to the top when one of their children have been clicked.
236 if (bDown)
237 Gwen::HoveredControl->Touch();
238
239 #ifdef GWEN_HOOKSYSTEM
240 if (bDown)
241 {
242 if (Hook::CallHook(&Hook::BaseHook::OnControlClicked, Gwen::HoveredControl, MousePosition.x, MousePosition.y))
243 return true;
244 }
245 #endif
246
247 switch (iMouseButton)
248 {
249 case 0:
250 {
251 if (DragAndDrop::OnMouseButton(Gwen::HoveredControl, MousePosition.x, MousePosition.y, bDown))
252 return true;
253
254 if (bIsDoubleClick)
255 Gwen::HoveredControl->OnMouseDoubleClickLeft(MousePosition.x, MousePosition.y);
256 else
257 Gwen::HoveredControl->OnMouseClickLeft(MousePosition.x, MousePosition.y, bDown);
258 return true;
259 }
260
261 case 1:
262 {
263 if (bIsDoubleClick)
264 Gwen::HoveredControl->OnMouseDoubleClickRight(MousePosition.x, MousePosition.y);
265 else
266 Gwen::HoveredControl->OnMouseClickRight(MousePosition.x, MousePosition.y, bDown);
267 return true;
268 }
269 }
270
271 return false;
272 }
273
HandleAccelerator(Controls::Base * pCanvas,Gwen::UnicodeChar chr)274 bool Gwen::Input::HandleAccelerator(Controls::Base* pCanvas, Gwen::UnicodeChar chr)
275 {
276 //Build the accelerator search string
277 Gwen::UnicodeString accelString;
278 if (Gwen::Input::IsControlDown())
279 accelString += L"Ctrl + ";
280 if (Gwen::Input::IsShiftDown())
281 accelString += L"Shift + ";
282
283 accelString += chr;
284
285 //Debug::Msg("Accelerator string :%S\n", accelString.c_str());
286
287 if (Gwen::KeyboardFocus && Gwen::KeyboardFocus->HandleAccelerator(accelString))
288 return true;
289
290 if (Gwen::MouseFocus && Gwen::MouseFocus->HandleAccelerator(accelString))
291 return true;
292
293 if (pCanvas->HandleAccelerator(accelString))
294 return true;
295
296 return false;
297 }
298
DoSpecialKeys(Controls::Base * pCanvas,Gwen::UnicodeChar chr)299 bool Gwen::Input::DoSpecialKeys(Controls::Base* pCanvas, Gwen::UnicodeChar chr)
300 {
301 if (!Gwen::KeyboardFocus) return false;
302 if (Gwen::KeyboardFocus->GetCanvas() != pCanvas) return false;
303 if (!Gwen::KeyboardFocus->Visible()) return false;
304 if (!Gwen::Input::IsControlDown()) return false;
305
306 if (chr == L'C' || chr == L'c')
307 {
308 Gwen::KeyboardFocus->OnCopy(NULL);
309 return true;
310 }
311
312 if (chr == L'V' || chr == L'v')
313 {
314 Gwen::KeyboardFocus->OnPaste(NULL);
315 return true;
316 }
317
318 if (chr == L'X' || chr == L'x')
319 {
320 Gwen::KeyboardFocus->OnCut(NULL);
321 return true;
322 }
323
324 if (chr == L'A' || chr == L'a')
325 {
326 Gwen::KeyboardFocus->OnSelectAll(NULL);
327 return true;
328 }
329
330 return false;
331 }
332
OnKeyEvent(Controls::Base * pCanvas,int iKey,bool bDown)333 bool Gwen::Input::OnKeyEvent(Controls::Base* pCanvas, int iKey, bool bDown)
334 {
335 if (!Gwen::KeyboardFocus) return false;
336 if (Gwen::KeyboardFocus->GetCanvas() != pCanvas) return false;
337 if (!Gwen::KeyboardFocus->Visible()) return false;
338
339 if (bDown)
340 {
341 if (!KeyData.KeyState[iKey])
342 {
343 KeyData.KeyState[iKey] = true;
344 KeyData.NextRepeat[iKey] = Gwen::Platform::GetTimeInSeconds() + KeyRepeatDelay;
345 KeyData.Target = KeyboardFocus;
346
347 return KeyboardFocus->OnKeyPress(iKey);
348 }
349 }
350 else
351 {
352 if (KeyData.KeyState[iKey])
353 {
354 KeyData.KeyState[iKey] = false;
355
356 // BUG BUG. This causes shift left arrow in textboxes
357 // to not work. What is disabling it here breaking?
358 //KeyData.Target = NULL;
359
360 return KeyboardFocus->OnKeyRelease(iKey);
361 }
362 }
363
364 return false;
365 }