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 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _gx_horizontal_list_invisible_page_scroll PORTABLE C */
37 /* 6.1.8 */
38 /* AUTHOR */
39 /* */
40 /* Ting Zhu, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function moves left or right the horizontal list by pages. */
45 /* */
46 /* INPUT */
47 /* */
48 /* list Horizontal list widget control*/
49 /* block */
50 /* num_pages The number of pages to scroll */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* None */
55 /* */
56 /* CALLS */
57 /* */
58 /* _gx_widget_first_client_child_get Get first client child */
59 /* _gx_widget_next_client_child_get Get next client child */
60 /* */
61 /* CALLED BY */
62 /* */
63 /* _gx_horizontal_list_scroll */
64 /* */
65 /* RELEASE HISTORY */
66 /* */
67 /* DATE NAME DESCRIPTION */
68 /* */
69 /* 08-02-2021 Ting Zhu Initial Version 6.1.8 */
70 /* */
71 /**************************************************************************/
_gx_horizontal_list_invisible_page_scroll(GX_HORIZONTAL_LIST * list,INT num_pages)72 static VOID _gx_horizontal_list_invisible_page_scroll(GX_HORIZONTAL_LIST *list, INT num_pages)
73 {
74 INT maximum_scroll_pages;
75
76 if (!(list -> gx_widget_style & GX_STYLE_WRAP))
77 {
78
79 /* Wrap style is not applied, need to calculate scroll limit. */
80 if (num_pages < 0)
81 {
82
83 /* Calculate maximum up scroll num_pages. */
84 maximum_scroll_pages = (list -> gx_horizontal_list_total_columns - list -> gx_horizontal_list_top_index - list -> gx_horizontal_list_child_count);
85 maximum_scroll_pages /= list -> gx_horizontal_list_child_count;
86
87 /* Remain one page for normal scroll routine. */
88 if (maximum_scroll_pages >= 1)
89 {
90 maximum_scroll_pages--;
91 }
92
93 if (maximum_scroll_pages < (-num_pages))
94 {
95 num_pages = (-maximum_scroll_pages);
96 }
97 }
98 else
99 {
100
101 /* Calculate maximum down scroll num_pages. */
102 maximum_scroll_pages = list -> gx_horizontal_list_top_index / list -> gx_horizontal_list_child_count;
103
104 /* Remain one page for normal scroll routine. */
105 if (maximum_scroll_pages >= 1)
106 {
107 maximum_scroll_pages--;
108 }
109
110 if (maximum_scroll_pages < num_pages)
111 {
112 num_pages = maximum_scroll_pages;
113 }
114 }
115 }
116
117 if (num_pages)
118 {
119
120 /* Update top index according to scroll num_pages. */
121 list -> gx_horizontal_list_top_index -= num_pages * list -> gx_horizontal_list_child_count;
122
123 if (list -> gx_widget_style & GX_STYLE_WRAP)
124 {
125
126 /* Wrap page index. */
127 if (num_pages < 0)
128 {
129 while (list -> gx_horizontal_list_top_index >= list -> gx_horizontal_list_total_columns)
130 {
131 list -> gx_horizontal_list_top_index -= list -> gx_horizontal_list_total_columns;
132 }
133 }
134 else
135 {
136 while (list -> gx_horizontal_list_top_index < 0)
137 {
138 list -> gx_horizontal_list_top_index += list -> gx_horizontal_list_total_columns;
139 }
140 }
141 }
142 }
143 }
144
145 /**************************************************************************/
146 /* */
147 /* FUNCTION RELEASE */
148 /* */
149 /* _gx_horizontal_list_scroll PORTABLE C */
150 /* 6.1.8 */
151 /* AUTHOR */
152 /* */
153 /* Kenneth Maxwell, Microsoft Corporation */
154 /* */
155 /* DESCRIPTION */
156 /* */
157 /* This function moves up or down the scrollbar. */
158 /* */
159 /* INPUT */
160 /* */
161 /* list Horizontal list widget control*/
162 /* block */
163 /* amount Shifting value */
164 /* */
165 /* OUTPUT */
166 /* */
167 /* None */
168 /* */
169 /* CALLS */
170 /* */
171 /* _gx_window_client_scroll Scroll the window client */
172 /* _gx_horizontal_list_left_wrap Scroll the horizontal list */
173 /* to the left */
174 /* _gx_horizontal_list_right_wrap Scroll the horizontal list */
175 /* to the right */
176 /* */
177 /* CALLED BY */
178 /* */
179 /* _gx_horizontal_list_event_process */
180 /* */
181 /* RELEASE HISTORY */
182 /* */
183 /* DATE NAME DESCRIPTION */
184 /* */
185 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
186 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
187 /* resulting in version 6.1 */
188 /* 08-02-2021 Ting Zhu Modified comment(s), */
189 /* fixed scroll overflow issue,*/
190 /* resulting in version 6.1.8 */
191 /* */
192 /**************************************************************************/
_gx_horizontal_list_scroll(GX_HORIZONTAL_LIST * list,INT amount)193 VOID _gx_horizontal_list_scroll(GX_HORIZONTAL_LIST *list, INT amount)
194 {
195 GX_WIDGET *child;
196 GX_WIDGET *last_child;
197 GX_RECTANGLE block;
198 GX_SCROLLBAR *scroll;
199 GX_BOOL do_block_move = GX_TRUE;
200 GX_BOOL reuse_child_widgets = GX_FALSE;
201 INT page_size;
202 INT num_pages;
203
204 if (!amount)
205 {
206 return;
207 }
208
209 if ((list -> gx_horizontal_list_callback != GX_NULL) &&
210 (list -> gx_horizontal_list_visible_columns < list -> gx_horizontal_list_total_columns) &&
211 ((list -> gx_horizontal_list_child_count < list -> gx_horizontal_list_total_columns) || (list -> gx_widget_style & GX_STYLE_WRAP)))
212 {
213 reuse_child_widgets = GX_TRUE;
214
215 /* Calculate the page size. */
216 page_size = (list -> gx_horizontal_list_child_width * list -> gx_horizontal_list_child_count);
217
218 if (!page_size)
219 {
220 return;
221 }
222
223 /* Calculate the number of pages to be scrolled. */
224 if (amount < 0)
225 {
226 num_pages = (amount + page_size) / page_size;
227 }
228 else
229 {
230 num_pages = (amount - page_size) / page_size;
231 }
232
233 if (num_pages)
234 {
235
236 /* Calculate the remainning scroll amount. */
237 amount -= (num_pages * page_size);
238
239 /* Scroll pages. */
240 _gx_horizontal_list_invisible_page_scroll(list, num_pages);
241 }
242 }
243
244 /* first shift my child widgets */
245 child = list -> gx_widget_first_child;
246
247 while (child)
248 {
249 if (!(child -> gx_widget_status & GX_STATUS_NONCLIENT))
250 {
251 _gx_widget_scroll_shift(child, amount, 0, GX_TRUE);
252 }
253 child = child -> gx_widget_next;
254 }
255
256
257 /* now check to see if we need to wrap any child widgets */
258
259 if (reuse_child_widgets)
260 {
261 /* this means we have fewer children than list rows, so we
262 need to move and re-use the child widgets
263 */
264 if (amount < 0)
265 {
266 _gx_horizontal_list_left_wrap(list);
267 }
268 else
269 {
270 _gx_horizontal_list_right_wrap(list);
271 }
272 }
273
274 _gx_window_scrollbar_find((GX_WINDOW *)list, GX_TYPE_HORIZONTAL_SCROLL, &scroll);
275
276 if (scroll)
277 {
278 _gx_scrollbar_reset(scroll, GX_NULL);
279 }
280
281 if (list -> gx_widget_status & GX_STATUS_VISIBLE)
282 {
283 block = list -> gx_window_client;
284
285 if (!(list -> gx_widget_style & (GX_STYLE_WRAP | GX_STYLE_TRANSPARENT)) &&
286 (list -> gx_widget_style & GX_STYLE_BORDER_THIN))
287 {
288 child = _gx_widget_first_client_child_get((GX_WIDGET *)list);
289 last_child = _gx_widget_last_client_child_get((GX_WIDGET *)list);
290
291 if (child)
292 {
293 if ((child -> gx_widget_size.gx_rectangle_left > block.gx_rectangle_left) ||
294 (last_child -> gx_widget_size.gx_rectangle_right < block.gx_rectangle_right))
295 {
296 /* If the widget has thin border, the round corder of the border
297 will cover the client area, block move will cause trouble. */
298 _gx_system_dirty_mark((GX_WIDGET *)list);
299 do_block_move = GX_FALSE;
300 }
301 }
302 }
303
304 if (do_block_move)
305 {
306 _gx_widget_block_move((GX_WIDGET *)list, &block, amount, 0);
307 }
308 }
309 }
310
311