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 }