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 /** Multi Line Text Input Management (Multi 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_multi_line_text_view.h"
29 #include "gx_text_input_cursor.h"
30 #include "gx_multi_line_text_input.h"
31 #include "gx_context.h"
32 #include "gx_window.h"
33 #include "gx_widget.h"
34 #include "gx_utility.h"
35 #include "gx_canvas.h"
36 #include "gx_scrollbar.h"
37
38 /**************************************************************************/
39 /* */
40 /* FUNCTION RELEASE */
41 /* */
42 /* _gx_multi_line_text_input_draw PORTABLE C */
43 /* 6.1 */
44 /* AUTHOR */
45 /* */
46 /* Kenneth Maxwell, Microsoft Corporation */
47 /* */
48 /* DESCRIPTION */
49 /* */
50 /* This function draws the multi-line text input widget, which is a */
51 /* special type of widget. */
52 /* */
53 /* INPUT */
54 /* */
55 /* text_input Multi line text input */
56 /* control block */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* None */
61 /* */
62 /* CALLS */
63 /* */
64 /* _gx_context_font_get Retrieve font */
65 /* _gx_multi_line_text_view_draw Draw the text view area */
66 /* _gx_text_input_cursor_draw Draw the cursor */
67 /* */
68 /* CALLED BY */
69 /* */
70 /* Application Code */
71 /* GUIX Internal Code */
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_multi_line_text_input_draw(GX_MULTI_LINE_TEXT_INPUT * input)82 VOID _gx_multi_line_text_input_draw(GX_MULTI_LINE_TEXT_INPUT *input)
83 {
84 GX_TEXT_INPUT_CURSOR *cursor_ptr = &input -> gx_multi_line_text_input_cursor_instance;
85 GX_VALUE line_height = 0;
86 GX_FONT *font;
87 GX_RESOURCE_ID fill_color;
88 GX_RESOURCE_ID text_color;
89 UINT line_start_mark;
90 UINT line_end_mark;
91 UINT end_mark;
92
93 UINT index;
94 GX_STRING private_string;
95 GX_STRING draw_string;
96 INT x_pos;
97 INT y_pos;
98 GX_RECTANGLE client;
99 GX_RECTANGLE draw_area;
100 GX_CANVAS *canvas;
101 UINT first_visible_line;
102 UINT last_visible_line;
103 UINT line_start_index;
104 UINT line_end_index;
105 UINT line_cache_start;
106 GX_VALUE text_width;
107 GX_VALUE client_width;
108 GX_SCROLLBAR *scroll;
109 GX_VALUE space_width;
110
111 if (input -> gx_widget_style & GX_STYLE_ENABLED)
112 {
113 if (input -> gx_widget_style & GX_STYLE_TEXT_INPUT_READONLY)
114 {
115 fill_color = input -> gx_multi_line_text_input_readonly_fill_color;
116 text_color = input -> gx_multi_line_text_input_readonly_text_color;
117 }
118 else
119 {
120 fill_color = input -> gx_widget_normal_fill_color;
121 text_color = input -> gx_multi_line_text_view_normal_text_color;
122 }
123 }
124 else
125 {
126 fill_color = input -> gx_widget_disabled_fill_color;
127 text_color = input -> gx_multi_line_text_view_disabled_text_color;
128 }
129
130 /* Draw background. */
131 _gx_window_border_draw((GX_WINDOW *)input, fill_color);
132
133 if (input -> gx_multi_line_text_view_line_index_old)
134 {
135 /* Get visible rows. */
136 _gx_multi_line_text_view_visible_rows_compute((GX_MULTI_LINE_TEXT_VIEW *)input);
137
138 /* Calculate text total rows. */
139 _gx_multi_line_text_view_string_total_rows_compute((GX_MULTI_LINE_TEXT_VIEW *)input);
140
141 _gx_window_scrollbar_find((GX_WINDOW *)input, GX_TYPE_VERTICAL_SCROLL, &scroll);
142 if (scroll)
143 {
144 /* Reset scrollbar. */
145 _gx_scrollbar_reset(scroll, GX_NULL);
146 }
147 else
148 {
149
150 if (input -> gx_multi_line_text_view_text_total_rows >
151 input -> gx_multi_line_text_view_cache_size)
152 {
153 /* Update line cache. */
154 _gx_multi_line_text_view_line_cache_update((GX_MULTI_LINE_TEXT_VIEW *)input);
155 }
156 }
157
158 _gx_multi_line_text_input_cursor_pos_update(input, GX_TRUE);
159 }
160
161 if ((input -> gx_multi_line_text_input_start_mark == input -> gx_multi_line_text_input_end_mark) ||
162 !(input -> gx_widget_style & GX_STYLE_ENABLED))
163 {
164 /* Draw text. */
165 _gx_multi_line_text_view_text_draw((GX_MULTI_LINE_TEXT_VIEW *)input, text_color);
166
167 /* Draw the cursor. */
168 if ((input -> gx_widget_status & GX_STATUS_CURSOR_SHOW) &&
169 (input -> gx_widget_status & GX_STATUS_CURSOR_DRAW))
170 {
171 _gx_context_font_get(input -> gx_multi_line_text_view_font_id, &font);
172
173 if (font)
174 {
175 line_height = (GX_VALUE)(font -> gx_font_line_height +
176 input -> gx_multi_line_text_view_line_space);
177 }
178
179 if (!(cursor_ptr -> gx_text_input_cursor_flags & GX_CURSOR_USE_CUSTOM_HEIGHT))
180 {
181 cursor_ptr -> gx_text_input_cursor_height = line_height;
182 }
183
184 _gx_text_input_cursor_draw(cursor_ptr);
185 }
186 }
187 else
188 {
189 line_start_mark = input -> gx_multi_line_text_input_start_mark;
190 end_mark = input -> gx_multi_line_text_input_end_mark;
191
192 if (line_start_mark > end_mark)
193 {
194 GX_SWAP_VALS(line_start_mark, end_mark);
195 }
196
197 _gx_context_font_get(input -> gx_multi_line_text_view_font_id, &font);
198 _gx_context_font_set(input -> gx_multi_line_text_view_font_id);
199 _gx_context_brush_width_set(0);
200
201 /* Is there a string and font? */
202 if ((input -> gx_multi_line_text_view_text.gx_string_length) && (font))
203 {
204 /* pick up current canvas */
205 canvas = _gx_system_current_draw_context -> gx_draw_context_canvas;
206
207 /* Pick up client area. */
208 client = input -> gx_window_client;
209
210 /* Offset client area by the size of whitespace. */
211 _gx_utility_rectangle_resize(&client, (GX_VALUE)(-input -> gx_multi_line_text_view_whitespace));
212
213 _gx_utility_rectangle_overlap_detect(&_gx_system_current_draw_context -> gx_draw_context_dirty, &client, &draw_area);
214 _gx_canvas_drawing_initiate(canvas, (GX_WIDGET *)input, &draw_area);
215
216 /* Pickup text height. */
217 line_height = (GX_VALUE)(font -> gx_font_line_height + input -> gx_multi_line_text_view_line_space);
218
219 if (line_height)
220 {
221
222 first_visible_line = ((UINT)(-input -> gx_multi_line_text_view_text_scroll_shift)) / (UINT)line_height;
223 last_visible_line = first_visible_line + input -> gx_multi_line_text_view_text_visible_rows;
224
225 if (last_visible_line > input -> gx_multi_line_text_view_text_total_rows - 1)
226 {
227 last_visible_line = input -> gx_multi_line_text_view_text_total_rows - 1;
228 }
229
230 /* Compute the start displaying position of pixels in x direction and y direction. */
231 y_pos = client.gx_rectangle_top;
232 y_pos += input -> gx_multi_line_text_view_text_scroll_shift;
233 y_pos += (input -> gx_multi_line_text_view_line_space >> 1);
234 y_pos += (INT)first_visible_line * line_height;
235
236 _gx_system_private_string_get(&input -> gx_multi_line_text_view_text, &private_string, input -> gx_widget_style);
237
238 draw_string.gx_string_ptr = " ";
239 draw_string.gx_string_length = 1;
240 _gx_system_string_width_get_ext(font, &draw_string, &space_width);
241
242 for (index = first_visible_line; index <= last_visible_line; index++)
243 {
244 line_cache_start = input -> gx_multi_line_text_view_first_cache_line;
245
246 line_start_index = input -> gx_multi_line_text_view_line_index[index - line_cache_start];
247
248 if ((INT)(index - line_cache_start) >= (INT)(input -> gx_multi_line_text_view_cache_size - 1))
249 {
250 line_end_index = input -> gx_multi_line_text_view_text.gx_string_length;
251 }
252 else
253 {
254 line_end_index = input -> gx_multi_line_text_view_line_index[index - line_cache_start + 1];
255 }
256
257 switch (input -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
258 {
259 case GX_STYLE_TEXT_RIGHT:
260 draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_index;
261 draw_string.gx_string_length = line_end_index - line_start_index;
262 _gx_system_string_width_get_ext(font, &draw_string, &text_width);
263 while (text_width > (client.gx_rectangle_right - client.gx_rectangle_left - 2))
264 {
265 text_width = (GX_VALUE)(text_width - space_width);
266 }
267 x_pos = client.gx_rectangle_right - 1;
268 x_pos = (GX_VALUE)(x_pos - text_width);
269 break;
270 case GX_STYLE_TEXT_LEFT:
271 x_pos = client.gx_rectangle_left + 1;
272 break;
273 case GX_STYLE_TEXT_CENTER:
274 default:
275 draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_index;
276 draw_string.gx_string_length = line_end_index - line_start_index;
277 _gx_system_string_width_get_ext(font, &draw_string, &text_width);
278 client_width = (GX_VALUE)(client.gx_rectangle_right - client.gx_rectangle_left + 1);
279
280 while (text_width > (client_width - 3))
281 {
282 text_width = (GX_VALUE)(text_width - space_width);
283 }
284
285 x_pos = (GX_VALUE)(client.gx_rectangle_left + ((client_width - text_width) / 2));
286 break;
287 }
288
289 if ((line_start_mark < line_end_index) && (end_mark > line_start_index))
290 {
291 if (line_start_mark < line_start_index)
292 {
293 line_start_mark = line_start_index;
294 }
295
296 if (line_start_mark > line_start_index)
297 {
298 /* Draw text[line_start_index : start_mark - 1] with normal text color. */
299 _gx_context_line_color_set(text_color);
300 draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_index;
301 draw_string.gx_string_length = line_start_mark - line_start_index;
302 _gx_system_string_width_get_ext(font, &draw_string, &text_width);
303 _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &draw_string);
304 x_pos += text_width;
305 }
306
307 if (end_mark < line_end_index)
308 {
309 line_end_mark = end_mark;
310 }
311 else
312 {
313 line_end_mark = line_end_index;
314 }
315
316 /* Draw text[start_mark: end_mark - 1] with hightlight text color. */
317 _gx_context_line_color_set(input -> gx_multi_line_text_view_selected_text_color);
318 _gx_context_fill_color_set(input -> gx_widget_selected_fill_color);
319
320 draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_mark;
321 draw_string.gx_string_length = line_end_mark - line_start_mark;
322 _gx_system_string_width_get_ext(font, &draw_string, &text_width);
323
324 draw_area.gx_rectangle_left = (GX_VALUE)x_pos;
325 draw_area.gx_rectangle_right = (GX_VALUE)(x_pos + text_width - 1);
326 draw_area.gx_rectangle_top = (GX_VALUE)(y_pos - (input -> gx_multi_line_text_view_line_space >> 1));
327 draw_area.gx_rectangle_bottom = (GX_VALUE)(draw_area.gx_rectangle_top + line_height - 1);
328
329 while (draw_area.gx_rectangle_right > client.gx_rectangle_right - 1)
330 {
331 draw_area.gx_rectangle_right = (GX_VALUE)(draw_area.gx_rectangle_right - space_width);
332 }
333
334 _gx_canvas_rectangle_draw(&draw_area);
335
336 draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_mark;
337 draw_string.gx_string_length = line_end_mark - line_start_mark;
338 _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &draw_string);
339
340 x_pos += text_width;
341
342 /* Draw text[end_mark : line_end_index] width normal text color. */
343 if (line_end_mark < line_end_index)
344 {
345 _gx_context_line_color_set(text_color);
346 draw_string.gx_string_ptr = private_string.gx_string_ptr + line_end_mark;
347 draw_string.gx_string_length = line_end_index - line_end_mark;
348 _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &draw_string);
349 }
350
351 if (end_mark > line_end_index)
352 {
353 line_start_mark = line_end_index;
354 }
355 }
356 else
357 {
358 _gx_context_line_color_set(text_color);
359
360 /* Draw the text. */
361 draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_index;
362 draw_string.gx_string_length = line_end_index - line_start_index;
363 _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &draw_string);
364 }
365
366 y_pos += line_height;
367 }
368 }
369
370 _gx_canvas_drawing_complete(canvas, GX_FALSE);
371 }
372 }
373
374 /* Draw widget's children. */
375 _gx_widget_children_draw((GX_WIDGET *)input);
376 }
377
378