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