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