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_context.h"
29 #include "gx_widget.h"
30 #include "gx_single_line_text_input.h"
31 #include "gx_utility.h"
32 #include "gx_text_input_cursor.h"
33
34 /**************************************************************************/
35 /* */
36 /* FUNCTION RELEASE */
37 /* */
38 /* _gx_single_line_text_input_backspace PORTABLE C */
39 /* 6.1 */
40 /* AUTHOR */
41 /* */
42 /* Kenneth Maxwell, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This service processes a backspace character. */
47 /* */
48 /* INPUT */
49 /* */
50 /* text_input Single-line text input widget */
51 /* control blcok */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* status Completion status */
56 /* */
57 /* CALLS */
58 /* */
59 /* memmove Move a block of memory */
60 /* _gx_widget_font_get Get font by specified ID */
61 /* _gx_widget_client_get Get client rectangle */
62 /* _gx_widget_border_width_get Get the widget border width */
63 /* _gx_system_string_width_get Get the width of a string */
64 /* _gx_system_dirty_partial_add Mark the partial area of a */
65 /* widget as dirty */
66 /* _gx_utility_utf8_string_character_get Parse utf8 string to */
67 /* multi-byte glyph */
68 /* _gx_single_line_text_input_position_update */
69 /* Update cursor position */
70 /* according to insert position*/
71 /* */
72 /* CALLED BY */
73 /* */
74 /* Application Code */
75 /* GUIX Internal Code */
76 /* */
77 /* RELEASE HISTORY */
78 /* */
79 /* DATE NAME DESCRIPTION */
80 /* */
81 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
82 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
83 /* resulting in version 6.1 */
84 /* */
85 /**************************************************************************/
_gx_single_line_text_input_backspace(GX_SINGLE_LINE_TEXT_INPUT * text_input)86 UINT _gx_single_line_text_input_backspace(GX_SINGLE_LINE_TEXT_INPUT *text_input)
87 {
88 GX_TEXT_INPUT_CURSOR *cursor_ptr = &text_input -> gx_single_line_text_input_cursor_instance;
89 GX_WIDGET *widget = (GX_WIDGET *)text_input;
90 UINT insert_pos;
91 GX_VALUE x_pos;
92 GX_VALUE new_x_pos;
93 GX_RECTANGLE client;
94 GX_VALUE border_width;
95 GX_VALUE delete_char_width;
96 GX_VALUE text_width;
97 GX_VALUE leftoff;
98 GX_VALUE rightoff;
99 GX_FONT *gx_font;
100 GX_CHAR *input_buffer;
101 UINT string_size;
102 GX_STRING string;
103 UINT glyph_len = 1;
104
105 if (text_input -> gx_single_line_text_input_start_mark > text_input -> gx_single_line_text_input_end_mark)
106 {
107 return _gx_single_line_text_input_character_delete(text_input);
108 }
109
110 insert_pos = text_input -> gx_single_line_text_input_insert_pos;
111
112 if (insert_pos > 0)
113 {
114 input_buffer = text_input -> gx_single_line_text_input_buffer;
115 string_size = text_input -> gx_single_line_text_input_string_size;
116
117 _gx_widget_border_width_get(widget, &border_width);
118 _gx_widget_client_get((GX_WIDGET *)text_input, border_width, &client);
119
120 if (text_input -> gx_single_line_text_input_start_mark != text_input -> gx_single_line_text_input_end_mark)
121 {
122 glyph_len = text_input -> gx_single_line_text_input_end_mark - text_input -> gx_single_line_text_input_start_mark;
123
124 if (cursor_ptr -> gx_text_input_cursor_pos.gx_point_x <= client.gx_rectangle_left ||
125 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x >= client.gx_rectangle_right)
126 {
127 _gx_single_line_text_input_right_arrow(text_input);
128 }
129
130 text_input -> gx_single_line_text_input_start_mark = 0;
131 text_input -> gx_single_line_text_input_end_mark = 0;
132 }
133 #ifdef GX_UTF8_SUPPORT
134 else
135 {
136 /* Get the glyph length of the cursor left character. */
137 string.gx_string_ptr = input_buffer;
138 string.gx_string_length = string_size;
139 _gx_utility_utf8_string_backward_character_length_get(&string, (INT)(insert_pos - 1), &glyph_len);
140 }
141 #endif
142
143 /* Pick up delete character width. */
144 _gx_widget_font_get((GX_WIDGET *)text_input, text_input -> gx_prompt_font_id, &gx_font);
145
146 string.gx_string_ptr = input_buffer + insert_pos - glyph_len;
147 string.gx_string_length = glyph_len;
148 _gx_system_string_width_get_ext(gx_font, &string, &delete_char_width);
149
150 /* Pick up text witth. */
151 string.gx_string_ptr = input_buffer;
152 string.gx_string_length = string_size;
153 _gx_system_string_width_get_ext(gx_font, &string, &text_width);
154
155 /* Delete a character from string buffer. */
156 memmove(input_buffer + insert_pos - glyph_len, input_buffer + insert_pos, string.gx_string_length - insert_pos);
157 text_input -> gx_single_line_text_input_buffer[string_size - glyph_len] = '\0';
158
159 /* Update input string size and insert position. */
160 text_input -> gx_single_line_text_input_string_size -= glyph_len;
161 text_input -> gx_single_line_text_input_insert_pos -= glyph_len;
162
163 switch (text_input -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
164 {
165 case GX_STYLE_TEXT_RIGHT:
166 x_pos = (GX_VALUE)(client.gx_rectangle_right - 1);
167 x_pos = (GX_VALUE)(x_pos - text_input -> gx_single_line_text_input_xoffset);
168
169 leftoff = (GX_VALUE)(client.gx_rectangle_left + 1 - x_pos);
170 rightoff = (GX_VALUE)(x_pos + text_width - (client.gx_rectangle_right - 1));
171
172 if ((leftoff > delete_char_width) || (rightoff == 0))
173 {
174 /* Decrease text inpit xoffset by the width of the deleted character width. */
175 text_input -> gx_single_line_text_input_xoffset = (GX_VALUE)(text_input -> gx_single_line_text_input_xoffset - delete_char_width);
176
177 /* No need to update cursor position, mark the cursor left area dirty. */
178 client.gx_rectangle_right = cursor_ptr -> gx_text_input_cursor_pos.gx_point_x;
179 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
180 }
181 else if ((leftoff == 0) && (rightoff > delete_char_width))
182 {
183 /* Decrease cursor position by the width of the deleted character width. */
184 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x - delete_char_width);
185
186 /* No need to update x offset, mark the cursor right area dirty. */
187 client.gx_rectangle_left = cursor_ptr -> gx_text_input_cursor_pos.gx_point_x;
188 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
189 }
190 else
191 {
192 rightoff = (GX_VALUE)(rightoff - (delete_char_width - leftoff));
193
194 if (rightoff > 0)
195 {
196 text_input -> gx_single_line_text_input_xoffset = (GX_VALUE)(text_input -> gx_single_line_text_input_xoffset - leftoff);
197 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x - delete_char_width + leftoff);
198 }
199 else
200 {
201 text_input -> gx_single_line_text_input_xoffset = (GX_VALUE)(text_input -> gx_single_line_text_input_xoffset - rightoff);
202 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x - delete_char_width - rightoff);
203 }
204
205 _gx_system_dirty_mark((GX_WIDGET *)text_input);
206 }
207 break;
208
209 case GX_STYLE_TEXT_CENTER:
210 x_pos = (GX_VALUE)(client.gx_rectangle_left + 1);
211 x_pos = (GX_VALUE)(x_pos + ((client.gx_rectangle_right - client.gx_rectangle_left + 1) >> 1));
212 x_pos = (GX_VALUE)(x_pos - text_input -> gx_single_line_text_input_xoffset);
213 if (text_width >= (client.gx_rectangle_right - client.gx_rectangle_left + 1 - border_width))
214 {
215 /* Dirty mark this widget. */
216 _gx_system_dirty_mark((GX_WIDGET *)text_input);
217 }
218 else
219 {
220 client.gx_rectangle_left = (GX_VALUE)(x_pos - (cursor_ptr -> gx_text_input_cursor_width >> 1));
221 client.gx_rectangle_right = (GX_VALUE)(x_pos + text_width + ((cursor_ptr -> gx_text_input_cursor_width + 1) >> 1) - 1);
222 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
223 }
224 /* Calculate the cursor position. */
225 text_input -> gx_single_line_text_input_xoffset = (GX_VALUE)((text_width - delete_char_width + 1) >> 1);
226 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x +
227 (((text_width + 1) >> 1) - text_input -> gx_single_line_text_input_xoffset) -
228 delete_char_width);
229 break;
230
231 case GX_STYLE_TEXT_LEFT:
232 default:
233 x_pos = (GX_VALUE)(client.gx_rectangle_left + 1);
234 x_pos = (GX_VALUE)(x_pos - text_input -> gx_single_line_text_input_xoffset);
235
236 new_x_pos = (GX_VALUE)(x_pos + delete_char_width);
237
238 if (new_x_pos < client.gx_rectangle_left + 1)
239 {
240 /* Decrease x offset value by the width of the deleted character. */
241 text_input -> gx_single_line_text_input_xoffset = (GX_VALUE)(text_input -> gx_single_line_text_input_xoffset - delete_char_width);
242
243 /* No need to update cursor position, mark the cursor left area dirty. */
244 client.gx_rectangle_right = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x + ((cursor_ptr -> gx_text_input_cursor_width + 1) >> 1) - 1);
245 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
246 }
247 else if (x_pos < client.gx_rectangle_left + 1)
248 {
249 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x - delete_char_width +
250 text_input -> gx_single_line_text_input_xoffset);
251
252 text_input -> gx_single_line_text_input_xoffset = 0;
253
254 _gx_system_dirty_mark((GX_WIDGET *)text_input);
255 }
256 else
257 {
258 /* Decrease cursor position by the width of the deleted character. */
259 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x - delete_char_width);
260
261 /* No need to update text input x offset, mark th ecursor right area dirty. */
262 client.gx_rectangle_left = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x - (cursor_ptr -> gx_text_input_cursor_width >> 1));
263 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
264 }
265 break;
266 }
267
268
269 /* Mark text input be changed. */
270 text_input -> gx_single_line_text_input_was_modified = GX_TRUE;
271 }
272
273 return GX_SUCCESS;
274 }
275
276