1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** GUIX Component */
16 /** */
17 /** Scroll Wheel Management (Scroll Wheel) */
18 /** */
19 /**************************************************************************/
20
21 #define GX_SOURCE_CODE
22
23
24 /* Include necessary system files. */
25
26 #include "gx_api.h"
27 #include "gx_system.h"
28 #include "gx_window.h"
29 #include "gx_widget.h"
30 #include "gx_utility.h"
31 #include "gx_scroll_wheel.h"
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _gx_scroll_wheel_timer_event_handler PORTABLE C */
38 /* 6.1.7 */
39 /* AUTHOR */
40 /* */
41 /* Kenneth Maxwell, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* Internal helper function to handle timer event for scroll wheel */
46 /* widget. */
47 /* */
48 /* INPUT */
49 /* */
50 /* wheel Scroll wheel control block */
51 /* timer_id Id of the handled timer */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* status Completion status */
56 /* */
57 /* CALLS */
58 /* */
59 /* _gx_system_timer_start Start a timer for a widget */
60 /* _gx_system_timer_stop Stop a timer for a widget */
61 /* _gx_system_dirty_mark Mark a widget as dirty */
62 /* _gx_scroll_wheel_scroll Scroll a scroll wheel widget */
63 /* _gx_widget_event_generate Create an event and send it */
64 /* to parent */
65 /* */
66 /* CALLED BY */
67 /* */
68 /* _gx_scroll_wheel_event_process */
69 /* */
70 /* RELEASE HISTORY */
71 /* */
72 /* DATE NAME DESCRIPTION */
73 /* */
74 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
75 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
76 /* resulting in version 6.1 */
77 /* 06-02-2021 Ting Zhu Modified comment(s), */
78 /* updated with scroll wheel */
79 /* control block members, */
80 /* resulting in version 6.1.7 */
81 /* */
82 /**************************************************************************/
_gx_scroll_wheel_timer_event_handler(GX_SCROLL_WHEEL * wheel,UINT timer_id)83 static UINT _gx_scroll_wheel_timer_event_handler(GX_SCROLL_WHEEL *wheel, UINT timer_id)
84 {
85 GX_WIDGET *widget = (GX_WIDGET *)wheel;
86 INT shift;
87 INT increment;
88 GX_BOOL generate_event = GX_FALSE;
89
90 switch(timer_id)
91 {
92 case GX_SNAP_TIMER:
93 /* Handle snap timer. */
94 if (GX_ABS(wheel -> gx_scroll_wheel_selected_yshift) < GX_ABS(wheel -> gx_scroll_wheel_row_height) / 3)
95 {
96 _gx_system_timer_stop(widget, GX_SNAP_TIMER);
97 generate_event = GX_TRUE;
98 shift = wheel -> gx_scroll_wheel_selected_yshift;
99 }
100 else
101 {
102 if (wheel -> gx_scroll_wheel_selected_yshift > 0)
103 {
104 shift = wheel -> gx_scroll_wheel_row_height / 3;
105 }
106 else
107 {
108 shift = (GX_VALUE)(-wheel -> gx_scroll_wheel_row_height / 3);
109 }
110 }
111
112 wheel -> gx_scroll_wheel_scroll(wheel, (GX_VALUE)(-shift));
113 break;
114
115 case GX_FLICK_TIMER:
116 /* Handle flick timer. */
117
118 wheel -> gx_scroll_wheel_animation_steps--;
119 shift = wheel -> gx_scroll_wheel_animation_speed;
120
121 if (wheel -> gx_scroll_wheel_shift_error)
122 {
123 shift += wheel -> gx_scroll_wheel_shift_error;
124 wheel -> gx_scroll_wheel_shift_error = 0;
125 }
126
127 wheel -> gx_scroll_wheel_scroll(wheel, (GX_VALUE)shift);
128
129 if (wheel -> gx_scroll_wheel_animation_steps > 0)
130 {
131 increment = (wheel -> gx_scroll_wheel_animation_end_speed - wheel -> gx_scroll_wheel_animation_speed) /
132 wheel -> gx_scroll_wheel_animation_steps;
133
134 wheel -> gx_scroll_wheel_animation_speed = (GX_VALUE)(wheel -> gx_scroll_wheel_animation_speed + increment);
135 }
136
137 if (!wheel -> gx_scroll_wheel_wrap_style_check(wheel))
138 {
139 if ((wheel -> gx_scroll_wheel_selected_row == 0 && wheel -> gx_scroll_wheel_selected_yshift > 0) ||
140 (wheel -> gx_scroll_wheel_selected_row == wheel -> gx_scroll_wheel_total_rows - 1 &&
141 wheel -> gx_scroll_wheel_selected_yshift < 0))
142 {
143 wheel -> gx_scroll_wheel_animation_steps = 0;
144 }
145 }
146
147 if (wheel -> gx_scroll_wheel_animation_steps == 0)
148 {
149 _gx_system_timer_stop(widget, GX_FLICK_TIMER);
150
151 if (wheel -> gx_scroll_wheel_selected_yshift)
152 {
153 _gx_system_timer_start((GX_WIDGET *)wheel, GX_SNAP_TIMER, 1, 1);
154 }
155 else
156 {
157 generate_event = GX_TRUE;
158 }
159 }
160 break;
161
162 case GX_ANIMATION_TIMER:
163 /* Handle animation timer. */
164 wheel -> gx_scroll_wheel_animation_steps--;
165
166 if (wheel -> gx_scroll_wheel_shift_error)
167 {
168 wheel -> gx_scroll_wheel_scroll(wheel, wheel -> gx_scroll_wheel_shift_error);
169 wheel -> gx_scroll_wheel_shift_error = 0;
170 }
171
172 wheel -> gx_scroll_wheel_scroll(wheel, wheel -> gx_scroll_wheel_animation_speed);
173
174 if (wheel -> gx_scroll_wheel_animation_steps == 0)
175 {
176 _gx_system_timer_stop(widget, GX_ANIMATION_TIMER);
177
178 if (wheel -> gx_scroll_wheel_selected_yshift)
179 {
180 _gx_system_timer_start((GX_WIDGET *)wheel, GX_SNAP_TIMER, 1, 1);
181 }
182 else
183 {
184 generate_event = GX_TRUE;
185 }
186 }
187 break;
188 }
189
190 if (wheel -> gx_widget_id && generate_event)
191 {
192 _gx_widget_event_generate((GX_WIDGET *)wheel, GX_EVENT_LIST_SELECT, wheel -> gx_scroll_wheel_selected_row);
193 }
194
195 return GX_SUCCESS;
196 }
197
198 /**************************************************************************/
199 /* */
200 /* FUNCTION RELEASE */
201 /* */
202 /* _gx_scroll_wheel_flick_event_handler PORTABLE C */
203 /* 6.1 */
204 /* AUTHOR */
205 /* */
206 /* Kenneth Maxwell, Microsoft Corporation */
207 /* */
208 /* DESCRIPTION */
209 /* */
210 /* Internal helper function to handle flick event for scrol wheel */
211 /* widget. */
212 /* */
213 /* INPUT */
214 /* */
215 /* wheel Scroll wheel control block */
216 /* flick_speed Flick speed */
217 /* */
218 /* OUTPUT */
219 /* */
220 /* status Completion status */
221 /* */
222 /* CALLS */
223 /* */
224 /* _gx_system_timer_start Start a timer for a widget */
225 /* _gx_system_timer_stop Stop a timer for a widget */
226 /* */
227 /* CALLED BY */
228 /* */
229 /* _gx_scroll_wheel_event_process */
230 /* */
231 /* RELEASE HISTORY */
232 /* */
233 /* DATE NAME DESCRIPTION */
234 /* */
235 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
236 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
237 /* resulting in version 6.1 */
238 /* */
239 /**************************************************************************/
_gx_scroll_wheel_flick_event_handler(GX_SCROLL_WHEEL * wheel,INT flick_speed)240 static UINT _gx_scroll_wheel_flick_event_handler(GX_SCROLL_WHEEL *wheel, INT flick_speed)
241 {
242 INT shift;
243 INT start_speed;
244 INT end_speed;
245 INT total_steps;
246 INT speed;
247
248 shift = flick_speed / GX_FIXED_VAL_HALF;
249
250 if ((GX_ABS(shift) > 5) &&
251 (wheel -> gx_scroll_wheel_total_rows))
252 {
253 _gx_system_timer_stop((GX_WIDGET *)wheel, GX_SNAP_TIMER);
254
255 if (GX_ABS(shift) < wheel -> gx_scroll_wheel_row_height / 3)
256 {
257 shift /= GX_ABS(shift);
258 shift *= wheel -> gx_scroll_wheel_row_height / 3;
259 }
260 start_speed = (wheel -> gx_scroll_wheel_animation_start_speed_rate * shift) >> GX_FIXED_VAL_SHIFT;
261 end_speed = (wheel -> gx_scroll_wheel_animation_end_speed_rate * shift) >> GX_FIXED_VAL_SHIFT;
262
263 if (GX_ABS(end_speed) < wheel -> gx_scroll_wheel_row_height / 10)
264 {
265 end_speed = wheel -> gx_scroll_wheel_row_height / 10;
266
267 if (shift < 0)
268 {
269 end_speed = -end_speed;
270 }
271 }
272
273 total_steps = GX_ABS(shift) / 2;
274
275 if (total_steps > wheel -> gx_scroll_wheel_animation_max_steps)
276 {
277 total_steps = wheel -> gx_scroll_wheel_animation_max_steps;
278 }
279
280 wheel -> gx_scroll_wheel_animation_speed = (GX_VALUE)start_speed;
281 wheel -> gx_scroll_wheel_animation_end_speed = (GX_VALUE)end_speed;
282 wheel -> gx_scroll_wheel_animation_steps = (GX_VALUE)total_steps;
283
284 speed = start_speed;
285 shift = start_speed;
286 while (total_steps > 1)
287 {
288 total_steps--;
289 speed += (end_speed - speed) / total_steps;
290
291 shift += speed;
292
293 if (GX_ABS(shift) > wheel -> gx_scroll_wheel_row_height)
294 {
295 shift %= wheel -> gx_scroll_wheel_row_height;
296 }
297 }
298
299 if (start_speed > 0)
300 {
301 shift += wheel -> gx_scroll_wheel_selected_yshift;
302 shift %= wheel -> gx_scroll_wheel_row_height;
303 if (shift < 0)
304 {
305 wheel -> gx_scroll_wheel_shift_error = (GX_VALUE)(-shift);
306 }
307 else if (shift > 0)
308 {
309 wheel -> gx_scroll_wheel_shift_error = (GX_VALUE)(wheel -> gx_scroll_wheel_row_height - shift);
310 }
311 }
312 else if (start_speed < 0)
313 {
314 shift += wheel -> gx_scroll_wheel_selected_yshift;
315 shift %= wheel -> gx_scroll_wheel_row_height;
316
317 if (shift < 0)
318 {
319 wheel -> gx_scroll_wheel_shift_error = (GX_VALUE)(-shift - wheel -> gx_scroll_wheel_row_height);
320 }
321 else if (shift > 0)
322 {
323 wheel -> gx_scroll_wheel_shift_error = (GX_VALUE)(-shift);
324 }
325 }
326
327 /* Start flick timer. */
328 _gx_system_timer_start((GX_WIDGET *) wheel, GX_FLICK_TIMER,
329 (UINT)wheel -> gx_scroll_wheel_animation_delay,
330 (UINT)wheel -> gx_scroll_wheel_animation_delay);
331 }
332
333 return GX_SUCCESS;
334 }
335
336 /**************************************************************************/
337 /* */
338 /* FUNCTION RELEASE */
339 /* */
340 /* _gx_scroll_wheel_pen_up_event_handler PORTABLE C */
341 /* 6.1.4 */
342 /* AUTHOR */
343 /* */
344 /* Kenneth Maxwell, Microsoft Corporation */
345 /* */
346 /* DESCRIPTION */
347 /* */
348 /* Internal helper function to handle pen up event for scroll wheel */
349 /* widget. */
350 /* */
351 /* INPUT */
352 /* */
353 /* wheel Scroll wheel control block */
354 /* */
355 /* OUTPUT */
356 /* */
357 /* status Completion status */
358 /* */
359 /* CALLS */
360 /* */
361 /* _gx_system_timer_start Start a timer for a widget */
362 /* _gx_system_dirty_partial_add Mark partial area of a widget */
363 /* as dirty */
364 /* _gx_widget_event_generate Create an event and send it to*/
365 /* parent */
366 /* */
367 /* CALLED BY */
368 /* */
369 /* _gx_scroll_wheel_event_process */
370 /* */
371 /* RELEASE HISTORY */
372 /* */
373 /* DATE NAME DESCRIPTION */
374 /* */
375 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
376 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
377 /* resulting in version 6.1 */
378 /* 02-02-2021 Kenneth Maxwell Modified comment(s), */
379 /* renamed */
380 /* GX_STYLE_SCROLL_WHEEL_DRAG */
381 /* to GX_STATUS_TRACKING_PEN, */
382 /* resulting in version 6.1.4 */
383 /* */
384 /**************************************************************************/
_gx_scroll_wheel_pen_up_event_handler(GX_SCROLL_WHEEL * wheel)385 static UINT _gx_scroll_wheel_pen_up_event_handler(GX_SCROLL_WHEEL *wheel)
386 {
387 GX_RECTANGLE dirty;
388
389 wheel -> gx_widget_status &= (ULONG)(~GX_STATUS_TRACKING_PEN);
390
391 if (wheel -> gx_scroll_wheel_selected_yshift)
392 {
393 /* Start a timer to move selected item to center. */
394 _gx_system_timer_start((GX_WIDGET *)wheel, GX_SNAP_TIMER, 1, 1);
395 }
396 else
397 {
398 /* Mark center area dirty. */
399 dirty = wheel -> gx_window_client;
400 dirty.gx_rectangle_top = (GX_VALUE)(dirty.gx_rectangle_top + (dirty.gx_rectangle_bottom - dirty.gx_rectangle_top + 1) / 2);
401 dirty.gx_rectangle_top = (GX_VALUE)(dirty.gx_rectangle_top - (wheel -> gx_scroll_wheel_row_height >> 1));
402 dirty.gx_rectangle_bottom = (GX_VALUE)(dirty.gx_rectangle_top + wheel -> gx_scroll_wheel_row_height - 1);
403 _gx_system_dirty_partial_add((GX_WIDGET *)wheel, &dirty);
404
405 if (wheel -> gx_widget_id)
406 {
407 /* Generate a list selected event. */
408 _gx_widget_event_generate((GX_WIDGET *)wheel, GX_EVENT_LIST_SELECT, wheel -> gx_scroll_wheel_selected_row);
409 }
410 }
411
412 return GX_SUCCESS;
413 }
414
415 /**************************************************************************/
416 /* */
417 /* FUNCTION RELEASE */
418 /* */
419 /* _gx_scroll_wheel_event_process PORTABLE C */
420 /* 6.1.7 */
421 /* AUTHOR */
422 /* */
423 /* Kenneth Maxwell, Microsoft Corporation */
424 /* */
425 /* DESCRIPTION */
426 /* */
427 /* This function processes the comming events for a scroll wheel */
428 /* widget. */
429 /* */
430 /* INPUT */
431 /* */
432 /* wheel Text scroll wheel control */
433 /* block */
434 /* event_ptr Event to be processed */
435 /* */
436 /* OUTPUT */
437 /* */
438 /* status Completion status */
439 /* */
440 /* CALLS */
441 /* */
442 /* _gx_system_input_capture Temporarily direct all inputs */
443 /* to specified widget */
444 /* _gx_system_input_release Release captured input events */
445 /* _gx_window_event_process Default window event process */
446 /* _gx_widget_event_to_parent Send event to widget's parent */
447 /* _gx_scroll_wheel_scroll Scroll a scroll wheel widget */
448 /* _gx_scroll_wheel_pen_up_event_handler Handle pen up event */
449 /* _gx_scroll_wheel_flick_event_handler Handle flick event */
450 /* _gx_scroll_wheel_timer_event_handler Handle timer event */
451 /* _gx_scroll_wheel_gradient_create Create a gradient pixelmap */
452 /* _gx_utility_gradient_delete Delete a gradient */
453 /* CALLED BY */
454 /* */
455 /* Application Code */
456 /* GUIX Internal Code */
457 /* */
458 /* RELEASE HISTORY */
459 /* */
460 /* DATE NAME DESCRIPTION */
461 /* */
462 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
463 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
464 /* resulting in version 6.1 */
465 /* 02-02-2021 Kenneth Maxwell Modified comment(s), */
466 /* renamed */
467 /* GX_STYLE_SCROLL_WHEEL_DRAG */
468 /* to GX_STATUS_TRACKING_PEN, */
469 /* resulting in version 6.1.4 */
470 /* 06-02-2021 Ting Zhu Modified comment(s), */
471 /* updated with scroll wheel */
472 /* control block change, */
473 /* resulting in version 6.1.7 */
474 /* */
475 /**************************************************************************/
_gx_scroll_wheel_event_process(GX_SCROLL_WHEEL * wheel,GX_EVENT * event_ptr)476 UINT _gx_scroll_wheel_event_process(GX_SCROLL_WHEEL *wheel, GX_EVENT *event_ptr)
477 {
478 GX_VALUE shift;
479
480 switch (event_ptr -> gx_event_type)
481 {
482 case GX_EVENT_PEN_DOWN:
483 if (wheel -> gx_scroll_wheel_total_rows)
484 {
485 _gx_system_input_capture((GX_WIDGET *)wheel);
486 wheel -> gx_window_move_start = event_ptr -> gx_event_payload.gx_event_pointdata;
487 wheel -> gx_scroll_wheel_shift_error = 0;
488 }
489 break;
490
491 case GX_EVENT_PEN_DRAG:
492 if (wheel -> gx_widget_status & GX_STATUS_OWNS_INPUT)
493 {
494 wheel -> gx_widget_status |= GX_STATUS_TRACKING_PEN;
495 shift = (GX_VALUE)(event_ptr -> gx_event_payload.gx_event_pointdata.gx_point_y - wheel -> gx_window_move_start.gx_point_y);
496 if (shift)
497 {
498 wheel -> gx_scroll_wheel_scroll(wheel, shift);
499
500 wheel -> gx_window_move_start = event_ptr -> gx_event_payload.gx_event_pointdata;
501 }
502 }
503 else
504 {
505 _gx_widget_event_to_parent((GX_WIDGET *)wheel, event_ptr);
506 }
507 break;
508
509 case GX_EVENT_PEN_UP:
510 if (wheel -> gx_widget_status & GX_STATUS_OWNS_INPUT)
511 {
512 _gx_system_input_release((GX_WIDGET *)wheel);
513 _gx_scroll_wheel_pen_up_event_handler(wheel);
514 }
515 else
516 {
517 _gx_widget_event_to_parent((GX_WIDGET *)wheel, event_ptr);
518 }
519 break;
520
521 case GX_EVENT_VERTICAL_FLICK:
522 _gx_scroll_wheel_flick_event_handler(wheel, event_ptr -> gx_event_payload.gx_event_intdata[0]);
523 break;
524
525 case GX_EVENT_TIMER:
526 _gx_scroll_wheel_timer_event_handler(wheel, event_ptr -> gx_event_payload.gx_event_timer_id);
527 break;
528
529 case GX_EVENT_SHOW:
530 _gx_window_event_process((GX_WINDOW *)wheel, event_ptr);
531 _gx_scroll_wheel_gradient_create(wheel);
532 break;
533
534 case GX_EVENT_DELETE:
535 _gx_utility_gradient_delete(&wheel -> gx_scroll_wheel_gradient);
536 break;
537
538 default:
539 return _gx_window_event_process((GX_WINDOW *)wheel, event_ptr);
540 }
541
542 return GX_SUCCESS;
543 }
544
545