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