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 /** Widget Management (Widget) */
18 /** */
19 /**************************************************************************/
20
21 #define GX_SOURCE_CODE
22
23
24 /* Include necessary system files. */
25
26 #include "gx_api.h"
27 #include "gx_system.h"
28 #include "gx_widget.h"
29
30 /**************************************************************************/
31 /* */
32 /* FUNCTION RELEASE */
33 /* */
34 /* _gx_widget_children_show_event_process PORTABLE C */
35 /* 6.1 */
36 /* AUTHOR */
37 /* */
38 /* Kenneth Maxwell, Microsoft Corporation */
39 /* */
40 /* DESCRIPTION */
41 /* */
42 /* Internal helper function to propagate widget show events to the */
43 /* client children of the specified widget first, and then pass the */
44 /* event to the non-client children. */
45 /* */
46 /* INPUT */
47 /* */
48 /* widget Widget control block */
49 /* event_ptr Incoming event to process */
50 /* */
51 /* OUTPUT */
52 /* */
53 /* None */
54 /* */
55 /* CALLS */
56 /* */
57 /* [gx_widget_event_process_function] Child widget event processing */
58 /* */
59 /* CALLED BY */
60 /* */
61 /* _gx_widget_event_process */
62 /* */
63 /* RELEASE HISTORY */
64 /* */
65 /* DATE NAME DESCRIPTION */
66 /* */
67 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
68 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
69 /* resulting in version 6.1 */
70 /* */
71 /**************************************************************************/
_gx_widget_children_show_event_process(GX_WIDGET * widget,GX_EVENT * event_ptr)72 static VOID _gx_widget_children_show_event_process(GX_WIDGET *widget, GX_EVENT *event_ptr)
73 {
74
75 GX_WIDGET *child;
76 GX_WIDGET *start = GX_NULL;
77 GX_WIDGET *end = GX_NULL;
78
79 /* Pickup the first child of the widget. */
80 child = widget -> gx_widget_first_child;
81
82 /* Loop to send event to all client children of widget. */
83 while (child)
84 {
85 if (child -> gx_widget_status & GX_STATUS_NONCLIENT)
86 {
87 if (start == GX_NULL)
88 {
89 /* Record first non-client widget. */
90 start = child;
91 }
92
93 /* Record last non-client widget. */
94 end = child;
95 }
96 else
97 {
98 /* Call widget's event processing. */
99 child -> gx_widget_event_process_function(child, event_ptr);
100 }
101
102 /* Move to next child widget. */
103 child = child -> gx_widget_next;
104 }
105
106 if (start)
107 {
108 /* Loop to send event to all non-client children of widget. */
109 while (start != end)
110 {
111 if (start -> gx_widget_status & GX_STATUS_NONCLIENT)
112 {
113 /* Call widget's event processing. */
114 start -> gx_widget_event_process_function(start, event_ptr);
115 }
116
117 start = start -> gx_widget_next;
118 }
119
120 start -> gx_widget_event_process_function(start, event_ptr);
121 }
122 }
123
124 /**************************************************************************/
125 /* */
126 /* FUNCTION RELEASE */
127 /* */
128 /* _gx_widget_event_process PORTABLE C */
129 /* 6.4.0 */
130 /* AUTHOR */
131 /* */
132 /* Kenneth Maxwell, Microsoft Corporation */
133 /* */
134 /* DESCRIPTION */
135 /* */
136 /* This function processes events for the specified widget. */
137 /* */
138 /* INPUT */
139 /* */
140 /* widget Widget control block */
141 /* event_ptr Incoming event to process */
142 /* */
143 /* OUTPUT */
144 /* */
145 /* status Completion status */
146 /* */
147 /* CALLS */
148 /* */
149 /* _gx_widget_children_event_process Forward event to children */
150 /* [gx_widget_event_process_function] Widget-specific event process */
151 /* routine */
152 /* _gx_system_dirty_mark Mark the widget as dirty */
153 /* _gx_widget_event_to_parent Signal the parent */
154 /* _gx_widget_event_generate Generate an event */
155 /* */
156 /* CALLED BY */
157 /* */
158 /* Application Code */
159 /* GUIX Internal Code */
160 /* */
161 /* RELEASE HISTORY */
162 /* */
163 /* DATE NAME DESCRIPTION */
164 /* */
165 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
166 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
167 /* added new event entries, */
168 /* resulting in version 6.1 */
169 /* 12-31-2020 Kenneth Maxwell Modified comment(s), */
170 /* avoid pass widget delete */
171 /* event to parent, */
172 /* resulting in version 6.1.3 */
173 /* 04-25-2022 Ting Zhu Modified comment(s), modified */
174 /* system input release logic */
175 /* on widget hide event, */
176 /* resulting in version 6.1.11 */
177 /* 12-31-2023 Ting Zhu Modified comment(s), */
178 /* improved focus lose logic, */
179 /* resulting in version 6.4.0 */
180 /* */
181 /**************************************************************************/
_gx_widget_event_process(GX_WIDGET * widget,GX_EVENT * event_ptr)182 UINT _gx_widget_event_process(GX_WIDGET *widget, GX_EVENT *event_ptr)
183 {
184 GX_WIDGET *child;
185 GX_EVENT new_event;
186 UINT status = GX_SUCCESS;
187 GX_EVENT input_release_event;
188
189 /* Process relative to the type of event. */
190 switch (event_ptr -> gx_event_type)
191 {
192 case GX_EVENT_SHOW:
193 if (!(widget -> gx_widget_status & (GX_STATUS_VISIBLE | GX_STATUS_HIDDEN)))
194 {
195 widget -> gx_widget_status |= GX_STATUS_VISIBLE;
196 _gx_widget_children_show_event_process(widget, event_ptr);
197 }
198 break;
199
200 case GX_EVENT_HIDE:
201 if (widget -> gx_widget_status & GX_STATUS_VISIBLE)
202 {
203 widget -> gx_widget_status &= ~GX_STATUS_VISIBLE;
204
205 /* Check if the widget still owns system input. */
206 if (widget -> gx_widget_status & GX_STATUS_OWNS_INPUT)
207 {
208 memset(&input_release_event, 0, sizeof(GX_EVENT));
209 input_release_event.gx_event_target = widget;
210 input_release_event.gx_event_type = GX_EVENT_INPUT_RELEASE;
211 widget -> gx_widget_event_process_function(widget, &input_release_event);
212 }
213 _gx_widget_children_event_process(widget, event_ptr);
214 }
215 break;
216
217 case GX_EVENT_FOCUS_GAINED:
218 /* if I don't already have focus */
219 if (!(widget -> gx_widget_status & GX_STATUS_HAS_FOCUS) &&
220 widget -> gx_widget_status & GX_STATUS_ACCEPTS_FOCUS)
221 {
222 /* then status flag indicating I have focus */
223
224 widget -> gx_widget_status |= GX_STATUS_HAS_FOCUS;
225
226 /* and make sure my parent has focus as well */
227 if (widget -> gx_widget_parent)
228 {
229 if (!(widget -> gx_widget_parent -> gx_widget_status & GX_STATUS_HAS_FOCUS))
230 {
231 widget -> gx_widget_parent -> gx_widget_event_process_function(widget -> gx_widget_parent, event_ptr);
232 }
233
234 /* test to see if this widget should notify it's parent when it gains focus */
235 if (widget -> gx_widget_id && widget -> gx_widget_status & GX_STATUS_NOTIFY_ON_GAIN_FOCUS)
236 {
237 _gx_widget_event_generate(widget, GX_EVENT_FOCUS_GAIN_NOTIFY, 0);
238 }
239 }
240 if (widget -> gx_widget_style & GX_STYLE_ENABLED)
241 {
242 widget -> gx_widget_style |= GX_STYLE_DRAW_SELECTED;
243 _gx_system_dirty_mark(widget);
244 }
245 }
246 break;
247
248 case GX_EVENT_FOCUS_LOST:
249 /* if I previously had focus */
250 if (widget -> gx_widget_status & GX_STATUS_HAS_FOCUS)
251 {
252 /* clear focus status flag */
253 widget -> gx_widget_status &= ~GX_STATUS_HAS_FOCUS;
254
255 if (widget -> gx_widget_style & GX_STYLE_ENABLED)
256 {
257 widget -> gx_widget_style &= ~GX_STYLE_DRAW_SELECTED;
258 _gx_system_dirty_mark(widget);
259 }
260 }
261
262 /* and make sure my children don't think they have focus */
263
264 child = widget -> gx_widget_first_child;
265
266 while (child)
267 {
268 if (child -> gx_widget_status & GX_STATUS_HAS_FOCUS)
269 {
270 child -> gx_widget_event_process_function(child, event_ptr);
271 break;
272 }
273 child = child -> gx_widget_next;
274 }
275 break;
276
277 case GX_EVENT_PEN_DOWN:
278 if (widget -> gx_widget_status & GX_STATUS_SELECTABLE)
279 {
280 if (widget -> gx_widget_id > 0)
281 {
282 _gx_widget_event_generate(widget, GX_EVENT_CLICKED, 0);
283 }
284 }
285 status = _gx_widget_event_to_parent(widget, event_ptr);
286 break;
287
288
289 case GX_EVENT_LANGUAGE_CHANGE:
290 case GX_EVENT_RESOURCE_CHANGE:
291 #if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
292 case GX_EVENT_DYNAMIC_BIDI_TEXT_DISABLE:
293 case GX_EVENT_DYNAMIC_BIDI_TEXT_ENABLE:
294 #endif
295 /* pass this event down to all my children */
296 child = widget -> gx_widget_first_child;
297 while (child)
298 {
299 child -> gx_widget_event_process_function(child, event_ptr);
300 child = child -> gx_widget_next;
301 }
302 break;
303
304 case GX_EVENT_KEY_DOWN:
305 memset(&new_event, 0, sizeof(GX_EVENT));
306
307 switch (event_ptr -> gx_event_payload.gx_event_ushortdata[0])
308 {
309 case GX_KEY_SELECT:
310 if (widget -> gx_widget_style & GX_STYLE_ENABLED)
311 {
312 /* generate pen-down event */
313 new_event.gx_event_type = GX_EVENT_SELECT;
314 widget -> gx_widget_event_process_function(widget, &new_event);
315 }
316 break;
317
318 case GX_KEY_NEXT:
319 new_event.gx_event_type = GX_EVENT_FOCUS_NEXT;
320 new_event.gx_event_sender = widget -> gx_widget_id;
321 widget -> gx_widget_event_process_function(widget, &new_event);
322 break;
323
324 case GX_KEY_PREVIOUS:
325 new_event.gx_event_type = GX_EVENT_FOCUS_PREVIOUS;
326 new_event.gx_event_sender = widget -> gx_widget_id;
327 widget -> gx_widget_event_process_function(widget, &new_event);
328 break;
329
330 default:
331 _gx_widget_event_to_parent(widget, event_ptr);
332 break;
333 }
334 break;
335
336 case GX_EVENT_KEY_UP:
337 if (widget -> gx_widget_style & GX_STYLE_ENABLED)
338 {
339 if (event_ptr -> gx_event_payload.gx_event_ushortdata[0] == GX_KEY_SELECT)
340 {
341 /* generate de-select event */
342 memset(&new_event, 0, sizeof(GX_EVENT));
343 new_event.gx_event_type = GX_EVENT_DESELECT;
344 widget -> gx_widget_event_process_function(widget, &new_event);
345 }
346 else
347 {
348 _gx_widget_event_to_parent(widget, event_ptr);
349 }
350 }
351 break;
352
353 case GX_EVENT_FOCUS_NEXT:
354 _gx_widget_focus_next(widget);
355 break;
356
357 case GX_EVENT_FOCUS_PREVIOUS:
358 _gx_widget_focus_previous(widget);
359 break;
360
361 case GX_EVENT_INPUT_RELEASE:
362 if (widget -> gx_widget_status & GX_STATUS_OWNS_INPUT)
363 {
364 _gx_system_input_release(widget);
365 }
366 break;
367
368 case GX_EVENT_STYLE_CHANGED:
369 case GX_EVENT_CLIENT_UPDATED:
370 case GX_EVENT_PARENT_SIZED:
371 case GX_EVENT_RESIZED:
372 case GX_EVENT_DELETE:
373 break;
374
375 case GX_EVENT_PEN_UP:
376 case GX_EVENT_PEN_DRAG:
377 default:
378 status = _gx_widget_event_to_parent(widget, event_ptr);
379 break;
380 }
381
382 return(status);
383 }
384
385