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 /** Text Input Management (Single Line Text Input) */
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 #include "gx_single_line_text_input.h"
30 #include "gx_text_input_cursor.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _gx_single_line_text_input_pen_down_process PORTABLE C */
38 /* 6.1 */
39 /* AUTHOR */
40 /* */
41 /* Kenneth Maxwell, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function handles pen down event for single line text input */
46 /* widget. */
47 /* */
48 /* INPUT */
49 /* */
50 /* text_input Single-line text input widget */
51 /* control block */
52 /* event_ptr Pointer to GX_EVENT structure */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* status Completion status */
57 /* */
58 /* CALLS */
59 /* */
60 /* _gx_single_line_text_input_text_rectangle_get */
61 /* Retrieve rectangle from cursor*/
62 /* to specified offset position*/
63 /* _gx_single_line_text_input_position_get */
64 /* Update text insert position */
65 /* _gx_system_dirty_partial_add Mark the partial area of a */
66 /* widget as dirty */
67 /* _gx_widget_event_process Default widget event process */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* _gx_single_line_text_input_event_process */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
78 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
79 /* resulting in version 6.1 */
80 /* */
81 /**************************************************************************/
_gx_single_line_text_input_pen_down_process(GX_SINGLE_LINE_TEXT_INPUT * text_input,GX_EVENT * event_ptr)82 static UINT _gx_single_line_text_input_pen_down_process(GX_SINGLE_LINE_TEXT_INPUT *text_input, GX_EVENT *event_ptr)
83 {
84 GX_RECTANGLE dirty_area;
85 UINT start_mark = text_input -> gx_single_line_text_input_start_mark;
86 UINT end_mark = text_input -> gx_single_line_text_input_end_mark;
87
88 _gx_system_input_capture((GX_WIDGET *)text_input);
89
90 if (start_mark != end_mark)
91 {
92 /* Retrieve highlight text bounding rectangle. */
93 _gx_single_line_text_input_text_rectangle_get(text_input, (INT)(start_mark - end_mark), &dirty_area);
94
95 /* Mark highlight area as dirty. */
96 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &dirty_area);
97 }
98
99 /* Calculate insert position according to click position. */
100 _gx_single_line_text_input_position_get(text_input, (UINT)((*event_ptr).gx_event_payload.gx_event_pointdata.gx_point_x));
101
102 /* Set highlight start/end mark to insert position. */
103 text_input -> gx_single_line_text_input_start_mark = text_input -> gx_single_line_text_input_insert_pos;
104 text_input -> gx_single_line_text_input_end_mark = text_input -> gx_single_line_text_input_insert_pos;
105
106 /* Call the widget default processing. */
107 return _gx_widget_event_process((GX_WIDGET *)text_input, event_ptr);
108 }
109
110 /**************************************************************************/
111 /* */
112 /* FUNCTION RELEASE */
113 /* */
114 /* _gx_single_line_text_input_pen_drag_process PORTABLE C */
115 /* 6.1 */
116 /* AUTHOR */
117 /* */
118 /* Kenneth Maxwell, Microsoft Corporation */
119 /* */
120 /* DESCRIPTION */
121 /* */
122 /* This function handles pen drag event for single line text input */
123 /* widget. */
124 /* */
125 /* INPUT */
126 /* */
127 /* text_input Single-line text input widget */
128 /* control block */
129 /* event_ptr Pointer to GX_EVENT structure */
130 /* */
131 /* OUTPUT */
132 /* */
133 /* status Completion status */
134 /* */
135 /* CALLS */
136 /* */
137 /* _gx_text_input_cursor_dirty_rectangle_get */
138 /* Retrieve cursor rectangle */
139 /* _gx_single_line_text_input_text_rectangle_get */
140 /* Retrieve rectangle from cursor*/
141 /* to specified offset position*/
142 /* _gx_single_line_text_input_position_get */
143 /* Update text insert position */
144 /* _gx_system_dirty_partial_add Mark the partial area of a */
145 /* widget as dirty */
146 /* _gx_widget_event_process Default widget event process */
147 /* */
148 /* CALLED BY */
149 /* */
150 /* _gx_single_line_text_input_event_process */
151 /* */
152 /* RELEASE HISTORY */
153 /* */
154 /* DATE NAME DESCRIPTION */
155 /* */
156 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
157 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
158 /* resulting in version 6.1 */
159 /* */
160 /**************************************************************************/
_gx_single_line_text_input_pen_drag_process(GX_SINGLE_LINE_TEXT_INPUT * text_input,GX_EVENT * event_ptr)161 static UINT _gx_single_line_text_input_pen_drag_process(GX_SINGLE_LINE_TEXT_INPUT *text_input, GX_EVENT *event_ptr)
162 {
163 GX_RECTANGLE dirty_area;
164 UINT start_mark = text_input -> gx_single_line_text_input_start_mark;
165 UINT end_mark = text_input -> gx_single_line_text_input_end_mark;
166 GX_VALUE border_width;
167 GX_RECTANGLE client;
168 GX_VALUE click_x;
169
170 if (text_input -> gx_widget_status & GX_STATUS_OWNS_INPUT)
171 {
172 click_x = event_ptr -> gx_event_payload.gx_event_pointdata.gx_point_x;
173
174 /* Calculate client rectangle. */
175 _gx_widget_border_width_get((GX_WIDGET *)text_input, &border_width);
176 _gx_widget_client_get((GX_WIDGET *)text_input, border_width, &client);
177
178 /* Calculate insert position. */
179 _gx_single_line_text_input_position_get(text_input, (UINT)click_x);
180
181 /* Update end mark. */
182 text_input -> gx_single_line_text_input_end_mark = text_input -> gx_single_line_text_input_insert_pos;
183
184 if ((click_x < client.gx_rectangle_left) && (text_input -> gx_single_line_text_input_end_mark > 0))
185 {
186 if (!(text_input -> gx_widget_status & (GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS)))
187 {
188 /* Start a timer to move text right. */
189 _gx_system_timer_start((GX_WIDGET *)text_input, GX_MARK_TIMER,
190 GX_MARK_INTERVAL, GX_MARK_INTERVAL);
191 }
192
193 text_input -> gx_widget_status &= ~GX_STATUS_MARK_NEXT;
194 text_input -> gx_widget_status |= GX_STATUS_MARK_PREVIOUS;
195 }
196 else if ((click_x > client.gx_rectangle_right) &&
197 (text_input -> gx_single_line_text_input_end_mark < text_input -> gx_single_line_text_input_string_size))
198 {
199 if (!(text_input -> gx_widget_status & (GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS)))
200 {
201 /* Start a timer to move text left. */
202 _gx_system_timer_start((GX_WIDGET *)text_input, GX_MARK_TIMER,
203 GX_MARK_INTERVAL, GX_MARK_INTERVAL);
204 }
205
206 text_input -> gx_widget_status &= ~GX_STATUS_MARK_PREVIOUS;
207 text_input -> gx_widget_status |= GX_STATUS_MARK_NEXT;
208 }
209 else
210 {
211 if (text_input -> gx_widget_status & (GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS))
212 {
213 _gx_system_timer_stop((GX_WIDGET *)text_input, GX_MARK_TIMER);
214 text_input -> gx_widget_status &= ~(GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS);
215 }
216 }
217
218 if ((start_mark != text_input -> gx_single_line_text_input_end_mark) ||
219 (start_mark != end_mark))
220 {
221 /* Retrieve text bounding rectangle between old and new end mark. */
222 _gx_single_line_text_input_text_rectangle_get(text_input, (INT)(end_mark - text_input -> gx_single_line_text_input_end_mark), &dirty_area);
223
224 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &dirty_area);
225 }
226 }
227 else
228 {
229 _gx_widget_event_process((GX_WIDGET *)text_input, event_ptr);
230 }
231
232 return GX_SUCCESS;
233 }
234
235 /**************************************************************************/
236 /* */
237 /* FUNCTION RELEASE */
238 /* */
239 /* _gx_single_line_text_input_event_process PORTABLE C */
240 /* 6.4.0 */
241 /* AUTHOR */
242 /* */
243 /* Kenneth Maxwell, Microsoft Corporation */
244 /* */
245 /* DESCRIPTION */
246 /* */
247 /* This service processes a single line text input event. This function*/
248 /* is internally referenced by the gx_single_line_text_input_create */
249 /* function, but is exposed for use by the application in those cases */
250 /* where the application defines a custom single line text input event */
251 /* processing function. */
252 /* */
253 /* INPUT */
254 /* */
255 /* text_input Single-line text input widget */
256 /* control block */
257 /* event_ptr Pointer to GX_EVENT structure */
258 /* */
259 /* OUTPUT */
260 /* */
261 /* status Completion status */
262 /* */
263 /* CALLS */
264 /* */
265 /* _gx_widget_event_process Default widget event process */
266 /* _gx_widget_event_generate Create a event and send it to */
267 /* parent */
268 /* _gx_system_timer_start Start the system timer */
269 /* _gx_system_timer_stop Stop the system timer */
270 /* _gx_system_dirty_partial_add Mark the partial area of a */
271 /* widget as dirty */
272 /* _gx_text_input_cursor_dirty_rectangle_get */
273 /* Get cursor rectangle */
274 /* _gx_single_line_text_input_keydown_process */
275 /* Keydown event process function*/
276 /* _gx_single_line_text_input_position_update */
277 /* Update cursor position by */
278 /* insert position */
279 /* */
280 /* CALLED BY */
281 /* */
282 /* Application Code */
283 /* GUIX Internal Code */
284 /* */
285 /* RELEASE HISTORY */
286 /* */
287 /* DATE NAME DESCRIPTION */
288 /* */
289 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
290 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
291 /* resulting in version 6.1 */
292 /* 12-31-2020 Kenneth Maxwell Modified comment(s), */
293 /* added logic to release */
294 /* dynamic input buffer, */
295 /* resulting in version 6.1.3 */
296 /* 12-31-2023 Ting Zhu Modified comment(s), */
297 /* modified to always call */
298 /* default widget event */
299 /* process on a pen up event, */
300 /* resulting in version 6.4.0 */
301 /* */
302 /**************************************************************************/
_gx_single_line_text_input_event_process(GX_SINGLE_LINE_TEXT_INPUT * text_input,GX_EVENT * event_ptr)303 UINT _gx_single_line_text_input_event_process(GX_SINGLE_LINE_TEXT_INPUT *text_input, GX_EVENT *event_ptr)
304 {
305 UINT status;
306 GX_WIDGET *widget = (GX_WIDGET *)text_input;
307 GX_RECTANGLE dirty_area;
308 GX_VALUE blink_interval;
309 ULONG old_style;
310
311 /* Default status to success. */
312 status = GX_SUCCESS;
313
314 /* Process relative to the type of event. */
315 switch (event_ptr -> gx_event_type)
316 {
317 case GX_EVENT_SHOW:
318 _gx_widget_event_process(widget, event_ptr);
319
320 if ((text_input -> gx_widget_style & GX_STYLE_CURSOR_ALWAYS) &&
321 (text_input -> gx_widget_style & GX_STYLE_CURSOR_BLINK))
322 {
323 _gx_system_timer_start((GX_WIDGET *)text_input, ID_TEXT_INPUT_TIMER, GX_CURSOR_BLINK_INTERVAL, GX_CURSOR_BLINK_INTERVAL);
324 }
325
326 /* Update cursor position. */
327 _gx_single_line_text_input_position_update(text_input);
328 break;
329
330 case GX_EVENT_RESIZED:
331 _gx_single_line_text_input_position_update(text_input);
332 break;
333
334 case GX_EVENT_STYLE_CHANGED:
335 if (widget -> gx_widget_status & GX_STATUS_VISIBLE)
336 {
337 old_style = event_ptr -> gx_event_payload.gx_event_ulongdata;
338 if ((old_style & (GX_STYLE_BORDER_MASK | GX_STYLE_TEXT_ALIGNMENT_MASK)) !=
339 (widget -> gx_widget_style & (GX_STYLE_BORDER_MASK | GX_STYLE_TEXT_ALIGNMENT_MASK)))
340 {
341 _gx_single_line_text_input_position_update(text_input);
342 }
343 }
344 break;
345
346 case GX_EVENT_KEY_DOWN:
347 _gx_single_line_text_input_keydown_process(text_input, event_ptr);
348
349 /*If input was done, send event to parent. */
350 if (widget -> gx_widget_style & GX_STYLE_TEXT_INPUT_NOTIFY_ALL)
351 {
352 _gx_widget_event_generate(widget, GX_EVENT_TEXT_EDITED, 0);
353 }
354 break;
355
356 case GX_EVENT_FOCUS_GAINED:
357 if (!(text_input -> gx_widget_style & GX_STYLE_CURSOR_ALWAYS))
358 {
359 text_input -> gx_widget_status |= (GX_STATUS_CURSOR_SHOW | GX_STATUS_CURSOR_DRAW);
360
361 if (text_input -> gx_widget_style & GX_STYLE_CURSOR_BLINK)
362 {
363 blink_interval = text_input -> gx_single_line_text_input_cursor_instance.gx_text_input_cursor_blink_interval;
364 _gx_system_timer_start(widget, ID_TEXT_INPUT_TIMER, (UINT)blink_interval, (UINT)blink_interval);
365 }
366 }
367
368 /* Select all text. */
369 _gx_single_line_text_input_text_select(text_input, 0, text_input -> gx_single_line_text_input_string_size - 1);
370
371 /* this widget wants to be notified if it is moved or re-sized */
372 text_input -> gx_widget_status |= GX_STATUS_RESIZE_NOTIFY;
373
374 /* Call the widget default processing. */
375 status = _gx_widget_event_process(widget, event_ptr);
376 break;
377
378 case GX_EVENT_PEN_DOWN:
379 _gx_single_line_text_input_pen_down_process(text_input, event_ptr);
380 break;
381
382 case GX_EVENT_PEN_DRAG:
383 _gx_single_line_text_input_pen_drag_process(text_input, event_ptr);
384 break;
385
386 case GX_EVENT_PEN_UP:
387 if (text_input -> gx_widget_status & GX_STATUS_OWNS_INPUT)
388 {
389 _gx_system_input_release((GX_WIDGET *)text_input);
390
391 if (text_input -> gx_widget_status & (GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS))
392 {
393 _gx_system_timer_stop((GX_WIDGET *)text_input, GX_MARK_TIMER);
394 text_input -> gx_widget_status &= ~(GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS);
395 }
396 }
397
398 _gx_widget_event_process(widget, event_ptr);
399 break;
400
401 case GX_EVENT_TIMER:
402 if (event_ptr -> gx_event_payload.gx_event_timer_id == GX_MARK_TIMER)
403 {
404 if (text_input -> gx_widget_status & GX_STATUS_MARK_PREVIOUS)
405 {
406 _gx_single_line_text_input_mark_previous(text_input);
407 }
408 else
409 {
410 _gx_single_line_text_input_mark_next(text_input);
411 }
412 }
413 else if ((event_ptr -> gx_event_payload.gx_event_timer_id == ID_TEXT_INPUT_TIMER) &&
414 (text_input -> gx_widget_status & GX_STATUS_CURSOR_SHOW))
415 {
416 if (text_input -> gx_widget_status & GX_STATUS_CURSOR_DRAW)
417 {
418 text_input -> gx_widget_status &= (ULONG)(~GX_STATUS_CURSOR_DRAW);
419 }
420 else
421 {
422 text_input -> gx_widget_status |= GX_STATUS_CURSOR_DRAW;
423 }
424
425 _gx_text_input_cursor_dirty_rectangle_get(&text_input -> gx_single_line_text_input_cursor_instance, &dirty_area);
426 _gx_system_dirty_partial_add(widget, &dirty_area);
427 }
428 break;
429
430 case GX_EVENT_FOCUS_LOST:
431 if (!(text_input -> gx_widget_style & GX_STYLE_CURSOR_ALWAYS))
432 {
433 if (text_input -> gx_widget_style & GX_STYLE_CURSOR_BLINK)
434 {
435 _gx_system_timer_stop(widget, ID_TEXT_INPUT_TIMER);
436 }
437 text_input -> gx_widget_status &= (ULONG)(~GX_STATUS_CURSOR_SHOW);
438
439 if (text_input -> gx_single_line_text_input_start_mark == text_input -> gx_single_line_text_input_end_mark)
440 {
441 _gx_text_input_cursor_dirty_rectangle_get(&text_input -> gx_single_line_text_input_cursor_instance, &dirty_area);
442 _gx_system_dirty_partial_add(widget, &dirty_area);
443 }
444
445 /* If cursor always is not set, do-not receive resize notify when lose focus */
446 text_input -> gx_widget_status &= ~GX_STATUS_RESIZE_NOTIFY;
447 }
448
449 if (text_input -> gx_single_line_text_input_start_mark != text_input -> gx_single_line_text_input_end_mark)
450 {
451 _gx_single_line_text_input_text_rectangle_get(text_input,
452 (INT)(text_input -> gx_single_line_text_input_start_mark - text_input -> gx_single_line_text_input_end_mark), &dirty_area);
453
454 text_input -> gx_single_line_text_input_start_mark = 0;
455 text_input -> gx_single_line_text_input_end_mark = 0;
456
457 _gx_system_dirty_partial_add(widget, &dirty_area);
458 }
459
460 /*If string was modified, send event to parent. */
461 if (text_input -> gx_single_line_text_input_was_modified)
462 {
463 _gx_widget_event_generate(widget, GX_EVENT_TEXT_EDITED, 0);
464 text_input -> gx_single_line_text_input_was_modified = GX_FALSE;
465 }
466
467 /* Call the widget default processing. */
468 status = _gx_widget_event_process(widget, event_ptr);
469 break;
470
471 case GX_EVENT_COPY:
472 _gx_single_line_text_input_copy(text_input);
473 break;
474
475 case GX_EVENT_CUT:
476 _gx_single_line_text_input_cut(text_input);
477 break;
478
479 case GX_EVENT_PASTE:
480 _gx_single_line_text_input_paste(text_input);
481 break;
482
483 case GX_EVENT_MARK_NEXT:
484 _gx_single_line_text_input_mark_next(text_input);
485 break;
486
487 case GX_EVENT_MARK_PREVIOUS:
488 _gx_single_line_text_input_mark_previous(text_input);
489 break;
490
491 case GX_EVENT_MARK_HOME:
492 _gx_single_line_text_input_mark_home(text_input);
493 break;
494
495 case GX_EVENT_MARK_END:
496 _gx_single_line_text_input_mark_end(text_input);
497 break;
498
499 case GX_EVENT_DELETE:
500 if (text_input -> gx_widget_status & GX_STATUS_DYNAMIC_BUFFER)
501 {
502 if (!_gx_system_memory_free)
503 {
504 return GX_SYSTEM_MEMORY_ERROR;
505 }
506
507 _gx_system_memory_free(text_input -> gx_single_line_text_input_buffer);
508 text_input -> gx_single_line_text_input_buffer = GX_NULL;
509 }
510 break;
511
512 default:
513 /* Call the widget default processing. */
514 status = _gx_widget_event_process(widget, event_ptr);
515 }
516
517 /* Return completion status. */
518 return(status);
519 }
520
521