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 View Management (Multi Line Text View) */
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_canvas.h"
29 #include "gx_context.h"
30 #include "gx_multi_line_text_view.h"
31 #include "gx_utility.h"
32 #include "gx_widget.h"
33 #include "gx_window.h"
34 #include "gx_scrollbar.h"
35
36 /**************************************************************************/
37 /* */
38 /* FUNCTION RELEASE */
39 /* */
40 /* _gx_multi_line_text_view_draw PORTABLE C */
41 /* 6.1 */
42 /* AUTHOR */
43 /* */
44 /* Kenneth Maxwell, Microsoft Corporation */
45 /* */
46 /* DESCRIPTION */
47 /* */
48 /* This function draws text for a multi-line-text-view widget. */
49 /* */
50 /* INPUT */
51 /* */
52 /* text_view Multi-line_text_view widget */
53 /* control block */
54 /* text_color ID of text color */
55 /* */
56 /* OUTPUT */
57 /* */
58 /* None */
59 /* */
60 /* CALLS */
61 /* */
62 /* _gx_context_line_color_set Set the context color */
63 /* _gx_context_font_get Get font associated with the */
64 /* specified ID */
65 /* _gx_context_font_set Set the context font */
66 /* _gx_context_brush_width_set Set the brush width */
67 /* _gx_multi_line_text_view_visible_rows_compute */
68 /* Calculate visible rows */
69 /* _gx_multi_line_text_view_string_total_rows_compute */
70 /* Calculate total rows for input*/
71 /* string */
72 /* _gx_utility_rectangle_resize Offset rectangle by specified */
73 /* value */
74 /* _gx_utility_rectangle_overlap_detect Detect rectangle overlaps */
75 /* _gx_utility_string_length_check Test string length */
76 /* _gx_system_string_get Get string by specified id */
77 /* _gx_system_private_string_get Get string pointer in */
78 /* dynamically copied string */
79 /* buffer */
80 /* _gx_multi_line_text_view_paragraph_start_get */
81 /* Get start index of a paragraph*/
82 /* _gx_utility_bidi_paragraph_reorder Reorder bidi text for display */
83 /* _gx_system_string_width_get Get string width */
84 /* _gx_canvas_text_draw Draw the text */
85 /* _gx_canvas_drawing_initiate Initiate a drawing context */
86 /* _gx_canvas_drawing_complete Complete a drawing */
87 /* */
88 /* CALLED BY */
89 /* */
90 /* Application Code */
91 /* GUIX Internal Code */
92 /* */
93 /* RELEASE HISTORY */
94 /* */
95 /* DATE NAME DESCRIPTION */
96 /* */
97 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
98 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
99 /* support runtime Arabic */
100 /* line breaking, */
101 /* modified logic of dynamic */
102 /* bidi text draw, */
103 /* resulting in version 6.1 */
104 /* */
105 /**************************************************************************/
_gx_multi_line_text_view_text_draw(GX_MULTI_LINE_TEXT_VIEW * text_view,GX_RESOURCE_ID text_color)106 VOID _gx_multi_line_text_view_text_draw(GX_MULTI_LINE_TEXT_VIEW *text_view, GX_RESOURCE_ID text_color)
107 {
108 INT index;
109 INT line_height;
110 GX_STRING string;
111 GX_STRING line_string;
112 INT x_pos;
113 INT y_pos;
114 GX_RECTANGLE client;
115 GX_RECTANGLE draw_area;
116 GX_CANVAS *canvas;
117 INT first_visible_line;
118 INT last_visible_line;
119 UINT line_start_index;
120 UINT line_end_index;
121 UINT line_cache_start;
122 GX_VALUE text_width;
123 GX_VALUE space_width;
124 GX_VALUE client_width;
125 GX_FONT *font;
126 GX_SCROLLBAR *scroll;
127
128 #if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
129 GX_BIDI_RESOLVED_TEXT_INFO *next = GX_NULL;
130 UINT bidi_text_line_index = 0;
131 #endif
132
133 _gx_context_line_color_set(text_color);
134 _gx_context_font_get(text_view -> gx_multi_line_text_view_font_id, &font);
135 _gx_context_font_set(text_view -> gx_multi_line_text_view_font_id);
136 _gx_context_brush_width_set(1);
137
138 _gx_window_scrollbar_find((GX_WINDOW *)text_view, GX_TYPE_VERTICAL_SCROLL, &scroll);
139
140 if (text_view -> gx_multi_line_text_view_line_index_old)
141 {
142
143 /* Get visible rows. */
144 _gx_multi_line_text_view_visible_rows_compute(text_view);
145
146 /* Calculate text total rows. */
147 _gx_multi_line_text_view_string_total_rows_compute(text_view);
148
149 if (scroll)
150 {
151 /* Reset scrollbar. */
152 _gx_scrollbar_reset(scroll, GX_NULL);
153 }
154 else
155 {
156 if (text_view -> gx_multi_line_text_view_text_total_rows >
157 text_view -> gx_multi_line_text_view_cache_size)
158 {
159 /* Update line cache. */
160 _gx_multi_line_text_view_line_cache_update(text_view);
161 }
162 }
163 }
164
165 /* Is there a string and font? */
166 if ((text_view -> gx_multi_line_text_view_text.gx_string_length <= 0) ||
167 font == GX_NULL)
168 {
169 return;
170 }
171
172 /* Pickup text height. */
173 line_height = font -> gx_font_line_height + text_view -> gx_multi_line_text_view_line_space;
174
175 if (!line_height)
176 {
177 return;
178 }
179
180 /* pick up current canvas */
181 canvas = _gx_system_current_draw_context -> gx_draw_context_canvas;
182
183 /* Pick up client area. */
184 client = text_view -> gx_window_client;
185
186 /* Offset client area by the size of whitespace. */
187 _gx_utility_rectangle_resize(&client, (GX_VALUE)(-text_view -> gx_multi_line_text_view_whitespace));
188
189 /* check for auto-scrolling vertically centered text */
190 if (text_view -> gx_widget_type == GX_TYPE_MULTI_LINE_TEXT_VIEW &&
191 scroll == GX_NULL &&
192 (text_view -> gx_widget_style & GX_STYLE_VALIGN_CENTER) == GX_STYLE_VALIGN_CENTER)
193 {
194 space_width = (GX_VALUE)(client.gx_rectangle_bottom - client.gx_rectangle_top);
195 space_width = (GX_VALUE)((INT)space_width - (INT)((INT)text_view -> gx_multi_line_text_view_text_total_rows * line_height));
196 text_view -> gx_multi_line_text_view_text_scroll_shift = (space_width >> 1);
197 }
198
199 _gx_utility_rectangle_overlap_detect(&_gx_system_current_draw_context -> gx_draw_context_dirty, &client, &draw_area);
200 _gx_canvas_drawing_initiate(canvas, (GX_WIDGET *)text_view, &draw_area);
201
202 /* Compute the start displaying position of pixels in x direction and y direction. */
203 y_pos = client.gx_rectangle_top;
204 y_pos += text_view -> gx_multi_line_text_view_text_scroll_shift;
205 y_pos += (text_view -> gx_multi_line_text_view_line_space >> 1);
206
207 line_string.gx_string_ptr = " ";
208 line_string.gx_string_length = 1;
209
210 _gx_system_string_width_get_ext(font, &line_string, &space_width);
211
212 first_visible_line = ((-text_view -> gx_multi_line_text_view_text_scroll_shift)) / line_height;
213
214 if (first_visible_line < 0)
215 {
216 first_visible_line = 0;
217 }
218
219 last_visible_line = first_visible_line + (INT)(text_view -> gx_multi_line_text_view_text_visible_rows);
220
221 if (last_visible_line > (INT)(text_view -> gx_multi_line_text_view_text_total_rows - 1))
222 {
223 last_visible_line = (INT)(text_view -> gx_multi_line_text_view_text_total_rows - 1);
224 }
225
226 if (text_view -> gx_multi_line_text_view_text_id)
227 {
228 _gx_widget_string_get_ext((GX_WIDGET *)text_view, text_view -> gx_multi_line_text_view_text_id, &string);
229 }
230 else
231 {
232 _gx_system_private_string_get(&text_view -> gx_multi_line_text_view_text, &string, text_view -> gx_widget_style);
233 }
234
235 y_pos += (INT)(first_visible_line * line_height);
236
237 for (index = first_visible_line; index <= last_visible_line; index++)
238 {
239 #if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
240 if (_gx_system_bidi_text_enabled)
241 {
242 line_string.gx_string_ptr = GX_NULL;
243 line_string.gx_string_length = 0;
244
245 if (!next)
246 {
247 next = text_view -> gx_multi_line_text_view_bidi_resolved_text_info;
248 }
249
250 while (next)
251 {
252 if (bidi_text_line_index + next -> gx_bidi_resolved_text_info_total_lines > (UINT)index)
253 {
254 /* Get line string. */
255 if (next -> gx_bidi_resolved_text_info_text)
256 {
257 line_string = next -> gx_bidi_resolved_text_info_text[(UINT)index - bidi_text_line_index];
258 }
259 break;
260 }
261
262 bidi_text_line_index += next -> gx_bidi_resolved_text_info_total_lines;
263 next = next -> gx_bidi_resolved_text_info_next;
264 }
265 }
266 else
267 {
268 #endif /* defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT) */
269 line_cache_start = text_view -> gx_multi_line_text_view_first_cache_line;
270 line_start_index = text_view -> gx_multi_line_text_view_line_index[index - (INT)line_cache_start];
271
272 if ((INT)(index - (INT)line_cache_start) >= (INT)(text_view -> gx_multi_line_text_view_cache_size - 1))
273 {
274 line_end_index = text_view -> gx_multi_line_text_view_text.gx_string_length;
275 }
276 else
277 {
278 line_end_index = text_view -> gx_multi_line_text_view_line_index[index - (INT)(line_cache_start) + 1];
279 }
280
281 /* Get line string. */
282 line_string.gx_string_ptr = string.gx_string_ptr + line_start_index;
283 line_string.gx_string_length = line_end_index - line_start_index;
284 #if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
285 }
286 #endif /* defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT) */
287
288
289 switch (text_view -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
290 {
291 case GX_STYLE_TEXT_RIGHT:
292 _gx_system_string_width_get_ext(font, &line_string, &text_width);
293 while (text_width > (client.gx_rectangle_right - client.gx_rectangle_left - 2))
294 {
295 text_width = (GX_VALUE)(text_width - space_width);
296 }
297 x_pos = client.gx_rectangle_right - 1;
298 x_pos = (GX_VALUE)(x_pos - text_width);
299 break;
300 case GX_STYLE_TEXT_LEFT:
301 x_pos = client.gx_rectangle_left + 1;
302 break;
303 case GX_STYLE_TEXT_CENTER:
304 default:
305 _gx_system_string_width_get_ext(font, &line_string, &text_width);
306 client_width = (GX_VALUE)(client.gx_rectangle_right - client.gx_rectangle_left + 1);
307 while (text_width > (client_width - 3))
308 {
309 text_width = (GX_VALUE)(text_width - space_width);
310 }
311 x_pos = (GX_VALUE)(client.gx_rectangle_left + ((client_width - text_width) / 2));
312 break;
313 }
314
315 /* Draw the text. */
316 _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &line_string);
317 y_pos += line_height;
318 }
319
320 _gx_canvas_drawing_complete(canvas, GX_FALSE);
321 }
322
323