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_context.h"
28 #include "gx_system.h"
29 #include "gx_widget.h"
30 #include "gx_single_line_text_input.h"
31 #include "gx_text_input_cursor.h"
32 #include "gx_utility.h"
33 
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _gx_single_line_text_input_left_arrow               PORTABLE C      */
39 /*                                                           6.1          */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    Kenneth Maxwell, Microsoft Corporation                              */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This service moves the text input cursor one character position to  */
47 /*      the left.                                                         */
48 /*                                                                        */
49 /*  INPUT                                                                 */
50 /*                                                                        */
51 /*    text_input                            Single-line text input widget */
52 /*                                            control block               */
53 /*                                                                        */
54 /*  OUTPUT                                                                */
55 /*                                                                        */
56 /*    None                                                                */
57 /*                                                                        */
58 /*  CALLS                                                                 */
59 /*                                                                        */
60 /*    _gx_utility_utf8_string_character_get Parse utf8 string to          */
61 /*                                            multi-byte glyph            */
62 /*    _gx_widget_border_width_get           Get the widget border width   */
63 /*    _gx_widget_client_get                 Get widget client rectangle   */
64 /*    _gx_widget_font_get                   Get font by specified ID      */
65 /*    _gx_system_string_width_get           Get the width of a string     */
66 /*    _gx_system_dirty_mark                 Mark the area of the widget   */
67 /*                                            as dirty                    */
68 /*    _gx_system_dirty_partial_add          Mark the partial area of a    */
69 /*                                            widget as dirty             */
70 /*    _gx_text_input_cursor_dirty_rectangle_get                           */
71 /*                                          Get cursor rectangle          */
72 /*                                                                        */
73 /*  CALLED BY                                                             */
74 /*                                                                        */
75 /*    Application Code                                                    */
76 /*    GUIX Internal Code                                                  */
77 /*                                                                        */
78 /*  RELEASE HISTORY                                                       */
79 /*                                                                        */
80 /*    DATE              NAME                      DESCRIPTION             */
81 /*                                                                        */
82 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
83 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
84 /*                                            resulting in version 6.1    */
85 /*                                                                        */
86 /**************************************************************************/
_gx_single_line_text_input_left_arrow(GX_SINGLE_LINE_TEXT_INPUT * text_input)87 UINT _gx_single_line_text_input_left_arrow(GX_SINGLE_LINE_TEXT_INPUT *text_input)
88 {
89 GX_TEXT_INPUT_CURSOR *cursor_ptr = &text_input -> gx_single_line_text_input_cursor_instance;
90 UINT                  insert_pos;
91 GX_RECTANGLE          client;
92 GX_RECTANGLE          cursor_rect;
93 GX_VALUE              old_cursor_pos;
94 GX_VALUE              cursor_pos;
95 GX_VALUE              width;
96 GX_FONT              *gx_font;
97 UINT                  glyph_len = 1;
98 UINT                  start_mark = text_input -> gx_single_line_text_input_start_mark;
99 UINT                  end_mark = text_input -> gx_single_line_text_input_end_mark;
100 GX_BOOL               mark_all = GX_FALSE;
101 GX_STRING             string;
102 
103     string.gx_string_ptr = text_input -> gx_single_line_text_input_buffer;
104     string.gx_string_length = text_input -> gx_single_line_text_input_string_size;
105 
106 
107     /* Move the cursor to the left by a character width.  */
108 
109     /* Pick up widget border width. */
110     _gx_widget_border_width_get((GX_WIDGET *)text_input, &width);
111 
112     /* Get widget client rectangle. */
113     _gx_widget_client_get((GX_WIDGET *)text_input, width, &client);
114 
115     cursor_pos = cursor_ptr -> gx_text_input_cursor_pos.gx_point_x;
116     old_cursor_pos = cursor_pos;
117 
118     if (start_mark != end_mark)
119     {
120         text_input -> gx_single_line_text_input_start_mark = 0;
121         text_input -> gx_single_line_text_input_end_mark = 0;
122 
123         if (end_mark < start_mark)
124         {
125             if (((cursor_pos > client.gx_rectangle_left) &&
126                  (cursor_pos < client.gx_rectangle_right)) ||
127                 ((text_input -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK) == GX_STYLE_TEXT_CENTER))
128             {
129                 /* No need to update cursor position, just need mark
130                     cursor and highlight area as dirty. */
131                 _gx_text_input_cursor_dirty_rectangle_get(cursor_ptr, &cursor_rect);
132 
133                 _gx_single_line_text_input_text_rectangle_get(text_input, (INT)(start_mark - end_mark), &client);
134 
135                 /* Combine cursor rect and highlight area. */
136                 _gx_utility_rectangle_combine(&client, &cursor_rect);
137 
138                 return _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
139             }
140         }
141     }
142 
143     insert_pos = text_input -> gx_single_line_text_input_insert_pos;
144 
145     if ((insert_pos > 0) || (start_mark > end_mark))
146     {
147         if (end_mark >= start_mark)
148         {
149             /* Calculate new cursor position. */
150             if (end_mark != start_mark)
151             {
152                 glyph_len = end_mark - start_mark;
153             }
154 #ifdef GX_UTF8_SUPPORT
155             else
156             {
157                 /* Get the glyph length of the cursor left character. */
158                 _gx_utility_utf8_string_backward_character_length_get(&string, (INT)(insert_pos - 1), &glyph_len);
159             }
160 #endif
161             /* Reset the value of cursor positin. */
162             text_input -> gx_single_line_text_input_insert_pos -= glyph_len;
163 
164             /* Pick up text font. */
165             _gx_widget_font_get((GX_WIDGET *)text_input, text_input -> gx_prompt_font_id, &gx_font);
166 
167             /* Get text length before current cursor position to new cursor position. */
168             string.gx_string_ptr += (insert_pos - glyph_len);
169             string.gx_string_length = glyph_len;
170             _gx_system_string_width_get_ext(gx_font, &string, &width);
171 
172             cursor_pos = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_x - width);
173         }
174 
175         switch (text_input -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
176         {
177         case GX_STYLE_TEXT_CENTER:
178             if (start_mark == end_mark)
179             {
180                 /* Mark old cursor area dirty. */
181                 _gx_text_input_cursor_dirty_rectangle_get(cursor_ptr, &client);
182                 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
183             }
184 
185             cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = cursor_pos;
186 
187             /* Mark new cursor area dirty. */
188             _gx_text_input_cursor_dirty_rectangle_get(cursor_ptr, &client);
189             _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
190             break;
191 
192         case GX_STYLE_TEXT_RIGHT:
193         case GX_STYLE_TEXT_LEFT:
194         default:
195             if (cursor_pos < client.gx_rectangle_left + 1)
196             {
197                 /* Update text input x offset. */
198                 text_input -> gx_single_line_text_input_xoffset = (GX_VALUE)(text_input -> gx_single_line_text_input_xoffset -
199                                                                              client.gx_rectangle_left - 1 + cursor_pos);
200                 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = (GX_VALUE)(client.gx_rectangle_left + 1);
201 
202                 mark_all = GX_TRUE;
203             }
204             else if (cursor_pos > client.gx_rectangle_right - 1)
205             {
206                 /* Cursor position is out of client rectangle, update x offset. */
207                 text_input -> gx_single_line_text_input_xoffset = (GX_VALUE)(text_input -> gx_single_line_text_input_xoffset +
208                                                                              cursor_pos - client.gx_rectangle_right + 1);
209                 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = (GX_VALUE)(client.gx_rectangle_right - 1);
210 
211                 /* Should mark all widget as dirty.  */
212                 mark_all = GX_TRUE;
213             }
214             else
215             {
216                 if (start_mark == end_mark)
217                 {
218                     /* Mark old cursor area dirty. */
219                     _gx_text_input_cursor_dirty_rectangle_get(cursor_ptr, &client);
220                     _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
221                 }
222                 cursor_ptr -> gx_text_input_cursor_pos.gx_point_x = cursor_pos;
223 
224                 /* Mark new cursor area dirty. */
225                 _gx_text_input_cursor_dirty_rectangle_get(cursor_ptr, &client);
226                 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
227             }
228             break;
229         }
230 
231         if ((!mark_all) && (start_mark != end_mark))
232         {
233             /* Mark highlight area as dirty. */
234             client.gx_rectangle_left = cursor_pos;
235             client.gx_rectangle_right = (GX_VALUE)(old_cursor_pos - 1);
236         }
237 
238         _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &client);
239     }
240 
241     return GX_SUCCESS;
242 }
243 
244