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 /** Vertical List (List) */
18 /** */
19 /**************************************************************************/
20
21 #define GX_SOURCE_CODE
22
23
24 /* Include necessary system files. */
25
26 #include "gx_api.h"
27 #include "gx_widget.h"
28 #include "gx_window.h"
29 #include "gx_system.h"
30 #include "gx_scrollbar.h"
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _gx_vertical_list_event_process PORTABLE C */
37 /* 6.1.12 */
38 /* AUTHOR */
39 /* */
40 /* Kenneth Maxwell, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This service processes an event for the vertical list. */
45 /* */
46 /* INPUT */
47 /* */
48 /* list Vertical list control block */
49 /* event_ptr Pointer to event to process */
50 /* */
51 /* OUTPUT */
52 /* */
53 /* status Completion status */
54 /* */
55 /* CALLS */
56 /* */
57 /* _gx_vertical_list_selected_set Set the list entry at the */
58 /* current list index */
59 /* _gx_widget_find Retrieve the height of the */
60 /* widget */
61 /* _gx_vertical_list_children_position Position the children for */
62 /* the vertical list */
63 /* _gx_first_client_child_get Get the first client child */
64 /* _gx_vertical_list_scroll Move up or down the scrollbar */
65 /* _gx_vertical_list_slide_back_check Check the sliding back of */
66 /* the scrollbar */
67 /* _gx_window_scrollbar_find Assign a background wallpaper */
68 /* to a GX_WINDOW object */
69 /* _gx_scrollbar_reset Calculate new scrollbar value */
70 /* _gx_system_timer_start Allocate a free timer and */
71 /* activates it */
72 /* _gx_system_top_widget_find Find top widget under pen */
73 /* _gx_system_timer_stop Stop an active GUIX timer */
74 /* _gx_window_event_process Process events for the */
75 /* specified window */
76 /* */
77 /* CALLED BY */
78 /* */
79 /* Application Code */
80 /* */
81 /* RELEASE HISTORY */
82 /* */
83 /* DATE NAME DESCRIPTION */
84 /* */
85 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
86 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
87 /* resulting in version 6.1 */
88 /* 07-29-2022 Kenneth Maxwell Modified comment(s), */
89 /* fixed bug in EVENT_PEN_DRAG */
90 /* handler, */
91 /* resulting in version 6.1.12 */
92 /* */
93 /**************************************************************************/
_gx_vertical_list_event_process(GX_VERTICAL_LIST * list,GX_EVENT * event_ptr)94 UINT _gx_vertical_list_event_process(GX_VERTICAL_LIST *list, GX_EVENT *event_ptr)
95 {
96 GX_WIDGET *widget = (GX_WIDGET *)list;
97 GX_SCROLLBAR *pScroll;
98 INT snap_dist;
99 UINT timer_id;
100 UINT status = GX_SUCCESS;
101 GX_WIDGET *child;
102 INT list_height;
103 INT widget_height;
104 INT new_pen_index;
105 GX_WIDGET **stackptr;
106 GX_WIDGET **stacktop;
107 GX_EVENT input_release_event;
108
109 switch (event_ptr -> gx_event_type)
110 {
111 case GX_EVENT_SHOW:
112
113 /* show the children before attempting to position them, because child
114 widgets often do not know their size until shown
115 */
116
117 status = _gx_window_event_process((GX_WINDOW *)list, event_ptr);
118
119 /* now position the child widgets */
120 if (!list -> gx_vertical_list_child_count)
121 {
122 _gx_vertical_list_children_position(list);
123 }
124
125 /* and now re-calculate scrolling parameters */
126 _gx_window_scrollbar_find((GX_WINDOW *)list, GX_TYPE_VERTICAL_SCROLL, &pScroll);
127
128 if (pScroll)
129 {
130 _gx_scrollbar_reset(pScroll, GX_NULL);
131 }
132 break;
133
134 case GX_EVENT_FOCUS_NEXT:
135 if (list -> gx_vertical_list_selected < list -> gx_vertical_list_total_rows - 1)
136 {
137 _gx_vertical_list_selected_set(list, list -> gx_vertical_list_selected + 1);
138 }
139 break;
140
141 case GX_EVENT_FOCUS_PREVIOUS:
142 if (list -> gx_vertical_list_selected > 0)
143 {
144 _gx_vertical_list_selected_set(list, list -> gx_vertical_list_selected - 1);
145 }
146 break;
147
148 case GX_EVENT_VERTICAL_SCROLL:
149 _gx_vertical_list_scroll(list, event_ptr -> gx_event_payload.gx_event_intdata[1] - event_ptr -> gx_event_payload.gx_event_intdata[0]);
150 return 0;
151
152 case GX_EVENT_PEN_DOWN:
153 _gx_system_input_capture(widget);
154 list -> gx_window_move_start = event_ptr -> gx_event_payload.gx_event_pointdata;
155
156 /* determine which child widget has been selected for future list select event */
157 child = _gx_system_top_widget_find((GX_WIDGET *)list, event_ptr -> gx_event_payload.gx_event_pointdata, GX_STATUS_SELECTABLE);
158
159 while (child && child -> gx_widget_parent != (GX_WIDGET *)list)
160 {
161 child = child -> gx_widget_parent;
162 }
163
164 if (child)
165 {
166 list -> gx_vertical_list_pen_index = list -> gx_vertical_list_top_index + _gx_widget_client_index_get(widget, child);
167 if (list -> gx_vertical_list_pen_index >= list -> gx_vertical_list_total_rows)
168 {
169 list -> gx_vertical_list_pen_index -= list -> gx_vertical_list_total_rows;
170 }
171 }
172 break;
173
174 case GX_EVENT_PEN_UP:
175 if (list -> gx_widget_status & GX_STATUS_OWNS_INPUT)
176 {
177 _gx_system_input_release(widget);
178
179 list_height = list -> gx_vertical_list_child_count * list -> gx_vertical_list_child_height;
180 widget_height = list -> gx_window_client.gx_rectangle_bottom - list -> gx_window_client.gx_rectangle_top + 1;
181
182 if (list_height > widget_height)
183 {
184 _gx_vertical_list_slide_back_check(list);
185 }
186 if (list -> gx_vertical_list_pen_index >= 0 && list -> gx_vertical_list_snap_back_distance == 0)
187 {
188 /* test to see if pen-up is over same child widget as pen-down */
189 child = _gx_system_top_widget_find((GX_WIDGET *)list, event_ptr -> gx_event_payload.gx_event_pointdata, GX_STATUS_SELECTABLE);
190 while (child && child -> gx_widget_parent != (GX_WIDGET *)list)
191 {
192 child = child -> gx_widget_parent;
193 }
194
195 if (child)
196 {
197 new_pen_index = list -> gx_vertical_list_top_index + _gx_widget_client_index_get(widget, child);
198 if (new_pen_index >= list -> gx_vertical_list_total_rows)
199 {
200 new_pen_index -= list -> gx_vertical_list_total_rows;
201 }
202 if (new_pen_index == list -> gx_vertical_list_pen_index)
203 {
204 _gx_vertical_list_selected_set(list, list -> gx_vertical_list_pen_index);
205 }
206 }
207 }
208 }
209 else
210 {
211 _gx_widget_event_to_parent((GX_WIDGET *)list, event_ptr);
212 }
213 break;
214
215 case GX_EVENT_PEN_DRAG:
216 if ((widget -> gx_widget_status & GX_STATUS_OWNS_INPUT) &&
217 (event_ptr -> gx_event_payload.gx_event_pointdata.gx_point_y - list -> gx_window_move_start.gx_point_y) != 0)
218 {
219 list_height = list -> gx_vertical_list_child_count * list -> gx_vertical_list_child_height;
220 widget_height = list -> gx_window_client.gx_rectangle_bottom - list -> gx_window_client.gx_rectangle_top + 1;
221
222 if (list_height > widget_height)
223 {
224 /* Start sliding, remove other widgets from input capture stack. */
225 stackptr = _gx_system_input_capture_stack;
226 stacktop = _gx_system_input_capture_stack + _gx_system_capture_count;
227
228 memset(&input_release_event, 0, sizeof(GX_EVENT));
229 input_release_event.gx_event_type = GX_EVENT_INPUT_RELEASE;
230
231 while (stackptr < stacktop)
232 {
233 if (*stackptr != GX_NULL && *stackptr != widget)
234 {
235 input_release_event.gx_event_target = *stackptr;
236 _gx_system_event_send(&input_release_event);
237 }
238 stackptr++;
239 }
240
241 _gx_vertical_list_scroll(list,
242 event_ptr -> gx_event_payload.gx_event_pointdata.gx_point_y -
243 list -> gx_window_move_start.gx_point_y);
244 _gx_window_scrollbar_find((GX_WINDOW *)list, GX_TYPE_VERTICAL_SCROLL, &pScroll);
245
246 if (pScroll)
247 {
248 _gx_scrollbar_reset(pScroll, GX_NULL);
249 }
250 list -> gx_window_move_start = event_ptr -> gx_event_payload.gx_event_pointdata;
251 list -> gx_vertical_list_pen_index = -1;
252 }
253 }
254 else
255 {
256 _gx_widget_event_to_parent((GX_WIDGET *)list, event_ptr);
257 }
258 break;
259
260 case GX_EVENT_VERTICAL_FLICK:
261 list_height = list -> gx_vertical_list_child_count * list -> gx_vertical_list_child_height;
262 widget_height = list -> gx_window_client.gx_rectangle_bottom - list -> gx_window_client.gx_rectangle_top + 1;
263
264 if (list_height > widget_height)
265 {
266 list -> gx_vertical_list_snap_back_distance = (GX_VALUE)(GX_FIXED_VAL_TO_INT(event_ptr -> gx_event_payload.gx_event_intdata[0]) * 8);
267 _gx_system_timer_start((GX_WIDGET *)list, GX_FLICK_TIMER, 1, 1);
268 }
269 list -> gx_vertical_list_pen_index = -1;
270 break;
271
272 case GX_EVENT_TIMER:
273 timer_id = event_ptr -> gx_event_payload.gx_event_timer_id;
274
275 if (timer_id == GX_FLICK_TIMER || timer_id == GX_SNAP_TIMER)
276 {
277 if (GX_ABS(list -> gx_vertical_list_snap_back_distance) < GX_ABS(list -> gx_vertical_list_child_height) / 3)
278 {
279 _gx_system_timer_stop(widget, event_ptr -> gx_event_payload.gx_event_timer_id);
280 _gx_vertical_list_scroll(list, list -> gx_vertical_list_snap_back_distance);
281
282 if (event_ptr -> gx_event_payload.gx_event_timer_id == GX_FLICK_TIMER)
283 {
284 _gx_vertical_list_slide_back_check(list);
285 }
286 }
287 else
288 {
289 snap_dist = (list -> gx_vertical_list_snap_back_distance) / 5;
290 list -> gx_vertical_list_snap_back_distance = (GX_VALUE)(list -> gx_vertical_list_snap_back_distance - snap_dist);
291 _gx_vertical_list_scroll(list, snap_dist);
292 }
293 _gx_window_scrollbar_find((GX_WINDOW *)list, GX_TYPE_VERTICAL_SCROLL, &pScroll);
294
295 if (pScroll)
296 {
297 _gx_scrollbar_reset(pScroll, GX_NULL);
298 }
299 }
300 else
301 {
302 status = _gx_window_event_process((GX_WINDOW *)list, event_ptr);
303 }
304 break;
305
306 default:
307 status = _gx_window_event_process((GX_WINDOW *)list, event_ptr);
308 break;
309 }
310
311 return status;
312 }
313
314