1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 //
21 // gui_cursor.c
22 //
23
24 #include "gui_local.h"
25
26 /*
27 =============================================================================
28
29 BOUNDS CALCULATION
30
31 =============================================================================
32 */
33
34 /*
35 ==================
36 GUI_GenerateBounds
37 ==================
38 */
GUI_GenerateBounds(gui_t * gui)39 void GUI_GenerateBounds (gui_t *gui)
40 {
41 gui_t *child;
42 int i;
43
44 // Generate bounds
45 gui->mins[0] = gui->rect[0];
46 gui->mins[1] = gui->rect[1];
47 gui->maxs[0] = gui->mins[0] + gui->rect[2];
48 gui->maxs[1] = gui->mins[1] + gui->rect[3];
49
50 // Generate bounds for children
51 for (i=0, child=gui->childList ; i<gui->numChildren ; child++, i++) {
52 GUI_GenerateBounds (child);
53
54 // Add to the parent
55 AddBoundsTo2DBounds (child->mins, child->maxs, gui->mins, gui->maxs);
56 }
57 }
58
59 /*
60 =============================================================================
61
62 CURSOR COLLISION
63
64 =============================================================================
65 */
66
67 /*
68 ==================
69 GUI_CursorUpdate
70 ==================
71 */
72 static gui_t *gui_bestWindow;
GUI_r_CursorUpdate(gui_t * gui,evType_t forceEvent)73 void GUI_r_CursorUpdate (gui_t *gui, evType_t forceEvent)
74 {
75 gui_t *child;
76 int i;
77
78 // Check for collision
79 if (forceEvent == WEV_SHUTDOWN) {
80 GUI_QueueTrigger (gui, WEV_SHUTDOWN);
81 }
82 else if (forceEvent == WEV_MOUSE_EXIT) {
83 GUI_QueueTrigger (gui, WEV_MOUSE_EXIT);
84 }
85 else if (!FRVALUE (gui, FR_VISIBLE)) {
86 GUI_QueueTrigger (gui, WEV_SHUTDOWN);
87 forceEvent = WEV_SHUTDOWN;
88 }
89 else if (FRVALUE (gui, FR_NO_EVENTS)
90 || gui->shared->cursor.d.pos[0] < gui->mins[0]
91 || gui->shared->cursor.d.pos[1] < gui->mins[1]
92 || gui->shared->cursor.d.pos[0] > gui->maxs[0]
93 || gui->shared->cursor.d.pos[1] > gui->maxs[1]) {
94 GUI_QueueTrigger (gui, WEV_MOUSE_EXIT);
95 forceEvent = WEV_MOUSE_EXIT;
96 }
97 else {
98 // Collided with this child
99 if (!FRVALUE (gui_bestWindow, FR_MODAL) || FRVALUE (gui, FR_MODAL)) {
100 GUI_QueueTrigger (gui, WEV_MOUSE_ENTER);
101 forceEvent = WEV_NONE;
102 gui_bestWindow = gui;
103 }
104 }
105
106 // Recurse down the children
107 for (i=0, child=gui->childList ; i<gui->numChildren ; child++, i++)
108 GUI_r_CursorUpdate (child, forceEvent);
109 }
GUI_CursorUpdate(gui_t * gui)110 void GUI_CursorUpdate (gui_t *gui)
111 {
112 evType_t forceEvent;
113 gui_t *child;
114 int i;
115
116 if (!gui->shared->cursor.mouseMoved)
117 return;
118 gui->shared->cursor.mouseMoved = qFalse;
119
120 cl_guiState.inputWindow = NULL;
121 gui_bestWindow = NULL;
122
123 // Check for collision
124 if (!FRVALUE (gui, FR_VISIBLE)) {
125 GUI_QueueTrigger (gui, WEV_SHUTDOWN);
126 forceEvent = WEV_SHUTDOWN;
127 }
128 else if (FRVALUE (gui, FR_NO_EVENTS)
129 || gui->shared->cursor.d.pos[0] < gui->mins[0]
130 || gui->shared->cursor.d.pos[1] < gui->mins[1]
131 || gui->shared->cursor.d.pos[0] > gui->maxs[0]
132 || gui->shared->cursor.d.pos[1] > gui->maxs[1]) {
133 GUI_QueueTrigger (gui, WEV_MOUSE_EXIT);
134 forceEvent = WEV_MOUSE_EXIT;
135 }
136 else {
137 GUI_QueueTrigger (gui, WEV_MOUSE_ENTER);
138 forceEvent = WEV_NONE;
139
140 // Collided with this window
141 cl_guiState.inputWindow = gui;
142 gui_bestWindow = gui;
143 }
144
145 // Recurse down the children
146 for (i=0, child=gui->childList ; i<gui->numChildren ; child++, i++)
147 GUI_r_CursorUpdate (child, forceEvent);
148
149 // Trigger events
150 if (gui->shared->cursor.curWindow != gui_bestWindow) {
151 if (gui->shared->cursor.curWindow)
152 GUI_QueueTrigger (gui->shared->cursor.curWindow, WEV_SHUTDOWN);
153 }
154
155 // Store
156 gui->shared->cursor.curWindow = gui_bestWindow;
157 }
158
159 /*
160 =============================================================================
161
162 CURSOR MOVEMENT
163
164 =============================================================================
165 */
166
167 /*
168 ==================
169 GUI_MoveMouse
170 ==================
171 */
GUI_MoveMouse(int xMove,int yMove)172 void GUI_MoveMouse (int xMove, int yMove)
173 {
174 guiCursor_t *cursor;
175 gui_t *gui;
176 int i;
177
178 // Let open GUIs know the cursor moved
179 if (xMove || yMove) {
180 for (i=0 ; i<cl_guiState.numLayers ; i++)
181 cl_guiState.openLayers[i]->shared->cursor.mouseMoved = qTrue;
182 }
183
184 // Move for the input window
185 gui = cl_guiState.inputWindow;
186 if (!gui)
187 return;
188
189 cursor = &gui->shared->cursor;
190 if (cursor->d.locked)
191 return;
192
193 // Scale
194 xMove *= gui->shared->xScale;
195 yMove *= gui->shared->yScale;
196
197 // Move
198 if (gui_mouseFilter->intVal) {
199 cursor->d.pos[0] = (cursor->d.pos[0] * 2 + (xMove * gui_mouseSensitivity->floatVal)) * 0.5f;
200 cursor->d.pos[1] = (cursor->d.pos[1] * 2 + (yMove * gui_mouseSensitivity->floatVal)) * 0.5f;
201 }
202 else {
203 cursor->d.pos[0] += xMove * gui_mouseSensitivity->floatVal;
204 cursor->d.pos[1] += yMove * gui_mouseSensitivity->floatVal;
205 }
206
207 // Clamp
208 cursor->d.pos[0] = clamp (cursor->d.pos[0], gui->owner->mins[0], gui->owner->maxs[0]);
209 cursor->d.pos[1] = clamp (cursor->d.pos[1], gui->owner->mins[1], gui->owner->maxs[1]);
210 }
211
212
213 /*
214 ==================
215 GUI_AdjustCursor
216 ==================
217 */
218 static qBool gui_bestFurthest;
219 static float gui_bestKeyDist;
220 static gui_t *gui_bestKeyWindow;
GUI_r_AdjustCursor(gui_t * gui,guiCursor_t * cursor,vec4_t bounds)221 void GUI_r_AdjustCursor (gui_t *gui, guiCursor_t *cursor, vec4_t bounds)
222 {
223 gui_t *child;
224 float dist;
225 vec2_t center;
226 int i;
227
228 // Must be visible and accept events
229 if (!FRVALUE (gui, FR_VISIBLE)
230 || FRVALUE (gui, FR_NO_EVENTS)) {
231 return;
232 }
233
234 // Check for collision with movement bounds
235 if (bounds[0] > gui->maxs[0]
236 || bounds[1] > gui->maxs[1]
237 || bounds[2] < gui->mins[0]
238 || bounds[3] < gui->mins[1])
239 return;
240
241 // Can't be the same window and must have a selectable item flag
242 if (gui != cursor->curWindow && gui->flags & WFL_ITEM) {
243 // Find the center point of this window
244 center[0] = gui->mins[0] + ((gui->maxs[0] - gui->mins[0]) * 0.5f);
245 center[1] = gui->mins[1] + ((gui->maxs[1] - gui->mins[1]) * 0.5f);
246
247 // Calculate distance
248 dist = Vec2Dist (cursor->d.pos, center);
249
250 // Check if it's the closest
251 if (gui_bestFurthest) {
252 if (dist > gui_bestKeyDist) {
253 gui_bestKeyDist = dist;
254 gui_bestKeyWindow = gui;
255 }
256 }
257 else if (dist < gui_bestKeyDist) {
258 gui_bestKeyDist = dist;
259 gui_bestKeyWindow = gui;
260 }
261 }
262
263 // Recurse down the children
264 for (i=0, child=gui->childList ; i<gui->numChildren ; child++, i++)
265 GUI_r_AdjustCursor (child, cursor, bounds);
266 }
GUI_AdjustCursor(keyNum_t keyNum)267 void GUI_AdjustCursor (keyNum_t keyNum)
268 {
269 gui_t *gui, *curItem;
270 guiCursor_t *cursor;
271 vec2_t end;
272 vec4_t bounds;
273 float xMove, yMove;
274
275 // Move for the input window
276 gui = cl_guiState.inputWindow;
277 if (!gui)
278 return;
279
280 cursor = &gui->shared->cursor;
281 if (!cursor || cursor->d.locked)
282 return;
283
284 curItem = cursor->curWindow;
285 if (!(curItem->flags & WFL_ITEM))
286 curItem = NULL;
287
288 // Generate bounds
289 switch (keyNum) {
290 case K_UPARROW:
291 case K_KP_UPARROW:
292 bounds[0] = gui->owner->mins[0];
293 bounds[1] = gui->owner->mins[1];
294 bounds[2] = gui->owner->maxs[0];
295 bounds[3] = (curItem) ? curItem->mins[1] + 1 : cursor->d.pos[1] + 1;
296 break;
297
298 case K_DOWNARROW:
299 case K_KP_DOWNARROW:
300 bounds[0] = gui->owner->mins[0];
301 bounds[1] = (curItem) ? curItem->maxs[1] - 1 : cursor->d.pos[1] - 1;
302 bounds[2] = gui->owner->maxs[0];
303 bounds[3] = gui->owner->maxs[1];
304 break;
305
306 case K_LEFTARROW:
307 case K_KP_LEFTARROW:
308 bounds[0] = gui->owner->mins[0];
309 bounds[1] = gui->owner->mins[1];
310 bounds[2] = (curItem) ? curItem->mins[0] - 1 : cursor->d.pos[0] - 1;
311 bounds[3] = gui->owner->maxs[1];
312 break;
313
314 case K_RIGHTARROW:
315 case K_KP_RIGHTARROW:
316 bounds[0] = (curItem) ? curItem->maxs[0] + 1 : cursor->d.pos[0] + 1;
317 bounds[1] = gui->owner->mins[1];
318 bounds[2] = gui->owner->maxs[0];
319 bounds[3] = gui->owner->maxs[1];
320 break;
321
322 default:
323 assert (0);
324 break;
325 }
326
327 // Search for the nearest item in that direction
328 if (!gui_bestFurthest)
329 gui_bestKeyDist = 999999;
330 gui_bestKeyWindow = NULL;
331 GUI_r_AdjustCursor (gui->owner, cursor, bounds);
332
333 // If nothing is found, try the complete opposite direction
334 if (!gui_bestKeyWindow) {
335 if (!gui_bestFurthest) {
336 gui_bestFurthest = qTrue;
337 gui_bestKeyDist = -999999;
338 switch (keyNum) {
339 case K_UPARROW:
340 case K_KP_UPARROW:
341 GUI_AdjustCursor (K_DOWNARROW);
342 break;
343
344 case K_DOWNARROW:
345 case K_KP_DOWNARROW:
346 GUI_AdjustCursor (K_UPARROW);
347 break;
348
349 case K_LEFTARROW:
350 case K_KP_LEFTARROW:
351 GUI_AdjustCursor (K_RIGHTARROW);
352 break;
353
354 case K_RIGHTARROW:
355 case K_KP_RIGHTARROW:
356 GUI_AdjustCursor (K_LEFTARROW);
357 break;
358 }
359 gui_bestFurthest = qFalse;
360 }
361 return;
362 }
363
364 // Calculate the final end point (center of the best item)
365 end[0] = gui_bestKeyWindow->mins[0] + ((gui_bestKeyWindow->maxs[0] - gui_bestKeyWindow->mins[0]) * 0.5f);
366 end[1] = gui_bestKeyWindow->mins[1] + ((gui_bestKeyWindow->maxs[1] - gui_bestKeyWindow->mins[1]) * 0.5f);
367
368 // Find the distance moved
369 if (cursor->d.pos[0] >= end[0])
370 xMove = -(cursor->d.pos[0] - end[0]);
371 else
372 xMove = end[0] - cursor->d.pos[0];
373 if (cursor->d.pos[1] >= end[1])
374 yMove = -(cursor->d.pos[1] - end[1]);
375 else
376 yMove = end[1] - cursor->d.pos[1];
377
378 // Remove scale, GUI_MouseMove scales
379 if (xMove)
380 xMove /= gui->shared->xScale;
381 if (yMove)
382 yMove /= gui->shared->yScale;
383
384 // Move the cursor to that item
385 GUI_MoveMouse (xMove, yMove);
386 }
387