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_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 up or down the vertical list by pages. */
45 /* */
46 /* INPUT */
47 /* */
48 /* list Vertical 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_vertical_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_vertical_list_invisible_page_scroll(GX_VERTICAL_LIST * list,INT num_pages)72 static VOID _gx_vertical_list_invisible_page_scroll(GX_VERTICAL_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_vertical_list_total_rows - list -> gx_vertical_list_top_index - list -> gx_vertical_list_child_count);
85 maximum_scroll_pages /= list -> gx_vertical_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_vertical_list_top_index / list -> gx_vertical_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_vertical_list_top_index -= num_pages * list -> gx_vertical_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_vertical_list_top_index >= list -> gx_vertical_list_total_rows)
130 {
131 list -> gx_vertical_list_top_index -= list -> gx_vertical_list_total_rows;
132 }
133 }
134 else
135 {
136 while (list -> gx_vertical_list_top_index < 0)
137 {
138 list -> gx_vertical_list_top_index += list -> gx_vertical_list_total_rows;
139 }
140 }
141 }
142 }
143 }
144
145 /**************************************************************************/
146 /* */
147 /* FUNCTION RELEASE */
148 /* */
149 /* _gx_vertical_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 /* vertical_list Vertical list widget control */
162 /* block */
163 /* amount Shifting value */
164 /* */
165 /* OUTPUT */
166 /* */
167 /* None */
168 /* */
169 /* CALLS */
170 /* */
171 /* _gx_widget_scroll_shift Shift a widget */
172 /* _gx_vertical_list_up_wrap Scroll up the vertical list */
173 /* _gx_vertical_list_down_wrap Scroll down the vertical list */
174 /* _gx_widget_block_move Widget block move */
175 /* */
176 /* CALLED BY */
177 /* */
178 /* _gx_vertical_list_event_process Vertical list event process */
179 /* */
180 /* RELEASE HISTORY */
181 /* */
182 /* DATE NAME DESCRIPTION */
183 /* */
184 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
185 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
186 /* resulting in version 6.1 */
187 /* 08-02-2021 Ting Zhu Modified comment(s), */
188 /* fixed scroll overflow issue,*/
189 /* resulting in version 6.1.8 */
190 /* */
191 /**************************************************************************/
_gx_vertical_list_scroll(GX_VERTICAL_LIST * list,INT amount)192 VOID _gx_vertical_list_scroll(GX_VERTICAL_LIST *list, INT amount)
193 {
194 GX_WIDGET *child;
195 GX_WIDGET *last_child;
196 GX_RECTANGLE block;
197 GX_SCROLLBAR *scroll;
198 GX_BOOL do_block_move = GX_TRUE;
199 GX_BOOL reuse_child_widgets = GX_FALSE;
200 INT page_size;
201 INT num_pages;
202
203 if (!amount)
204 {
205 return;
206 }
207
208 if ((list -> gx_vertical_list_callback != GX_NULL) &&
209 (list -> gx_vertical_list_visible_rows < list -> gx_vertical_list_total_rows) &&
210 ((list -> gx_vertical_list_child_count < list -> gx_vertical_list_total_rows) || (list -> gx_widget_style & GX_STYLE_WRAP)))
211 {
212 reuse_child_widgets = GX_TRUE;
213
214 /* Calculate the page size. */
215 page_size = (list -> gx_vertical_list_child_height * list -> gx_vertical_list_child_count);
216
217 if (!page_size)
218 {
219 return;
220 }
221
222 /* Calculate the number of pages to be scrolled. */
223 if (amount < 0)
224 {
225 num_pages = (amount + page_size) / page_size;
226 }
227 else
228 {
229 num_pages = (amount - page_size) / page_size;
230 }
231
232 if (num_pages)
233 {
234
235 /* Calculate the remainning scroll amount. */
236 amount -= (num_pages * page_size);
237
238 /* Scroll pages. */
239 _gx_vertical_list_invisible_page_scroll(list, num_pages);
240 }
241 }
242
243 /* first shift my child widgets */
244 child = list -> gx_widget_first_child;
245
246 while (child)
247 {
248 if (!(child -> gx_widget_status & GX_STATUS_NONCLIENT))
249 {
250 _gx_widget_scroll_shift(child, 0, amount, GX_TRUE);
251 }
252 child = child -> gx_widget_next;
253 }
254
255 /* next check to see if we need to wrap any child widgets */
256
257 if (reuse_child_widgets)
258 {
259 /* this means we have fewer children than list rows, so we
260 need to move and re-use the child widgets
261 */
262 if (amount < 0)
263 {
264 _gx_vertical_list_up_wrap(list);
265 }
266 else
267 {
268 _gx_vertical_list_down_wrap(list);
269 }
270 }
271
272 _gx_window_scrollbar_find((GX_WINDOW *)list, GX_TYPE_VERTICAL_SCROLL, &scroll);
273
274 if (scroll)
275 {
276 _gx_scrollbar_reset(scroll, GX_NULL);
277 }
278
279 if (list -> gx_widget_status & GX_STATUS_VISIBLE)
280 {
281 block = list -> gx_window_client;
282
283 if (!(list -> gx_widget_style & (GX_STYLE_WRAP | GX_STYLE_TRANSPARENT)) &&
284 (list -> gx_widget_style & GX_STYLE_BORDER_THIN))
285 {
286 child = _gx_widget_first_client_child_get((GX_WIDGET *)list);
287 last_child = _gx_widget_last_client_child_get((GX_WIDGET *)list);
288
289 if (child)
290 {
291 if ((child -> gx_widget_size.gx_rectangle_top > block.gx_rectangle_top ||
292 last_child -> gx_widget_size.gx_rectangle_bottom < block.gx_rectangle_bottom))
293 {
294 /* If the widget has thin border, the round corder of the border
295 will cover the client area, block move will cause trouble. */
296 _gx_system_dirty_mark((GX_WIDGET *)list);
297 do_block_move = GX_FALSE;
298 }
299 }
300 }
301
302 if (do_block_move)
303 {
304 _gx_widget_block_move((GX_WIDGET *)list, &block, 0, amount);
305 }
306 }
307 }
308
309