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