Blender V5.0
textview.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLF_api.hh"
14
15#include "BLI_math_color.h"
16#include "BLI_math_vector.h"
17#include "BLI_string_utf8.h"
18#include "BLI_utildefines.h"
19
20#include "GPU_immediate.hh"
21#include "GPU_state.hh"
22
23#include "DNA_userdef_types.h" /* For 'UI_SCALE_FAC' */
24
25#include "UI_interface.hh"
26#include "UI_interface_icons.hh"
27
28#include "textview.hh"
29
30static void textview_font_begin(const int font_id, const int lheight)
31{
32 /* Font size in relation to line height. */
33 BLF_size(font_id, 0.8f * lheight);
34}
35
38 int cwidth;
41 int lofs;
50 int *xy; // [2]
51 int *sel; // [2]
52 /* Bottom of view == 0, top of file == combine chars, end of line is lower than start. */
54 const int *mval; // [2]
55 bool do_draw;
56};
57
59{
60 tds->sel[0] += step;
61 tds->sel[1] += step;
62}
63
64static void textview_draw_sel(const char *str,
65 const int xy[2],
66 const int str_len_draw,
68 const uchar bg_sel[4])
69{
70 const int sel[2] = {tds->sel[0], tds->sel[1]};
71 const int cwidth = tds->cwidth;
72 const int lheight = tds->lheight;
73
74 if (sel[0] <= str_len_draw && sel[1] >= 0) {
76 str, str_len_draw, max_ii(sel[0], 0), TVC_TAB_COLUMNS);
78 str, str_len_draw, min_ii(sel[1], str_len_draw), TVC_TAB_COLUMNS);
79
81
83 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
85
86 immUniformColor4ubv(bg_sel);
87 immRectf(pos, xy[0] + (cwidth * sta), xy[1] + lheight, xy[0] + (cwidth * end), xy[1]);
88
90
92 }
93}
94
100 const char *str, const int str_len, const int width, int *r_lines, int **r_offsets)
101{
102 int i, end; /* Offset as unicode code-point. */
103 int j; /* Offset as bytes. */
104 const int tab_columns = TVC_TAB_COLUMNS;
105 const int column_width_max = std::max(tab_columns, BLI_UTF8_WIDTH_MAX);
106
107 *r_lines = 1;
108
109 *r_offsets = MEM_calloc_arrayN<int>(
110 (str_len * column_width_max / std::max(1, width - (column_width_max - 1)) + 1), __func__);
111 (*r_offsets)[0] = 0;
112
113 for (i = 0, end = width, j = 0; j < str_len && str[j]; j += BLI_str_utf8_size_safe(str + j)) {
114 int columns = UNLIKELY(*(str + j) == '\t') ? (tab_columns - (i % tab_columns)) :
116
117 if (i + columns > end) {
118 (*r_offsets)[*r_lines] = j;
119 (*r_lines)++;
120
121 end = i + width;
122 }
123 i += columns;
124 }
125 return j;
126}
127
133 const char *str,
134 int str_len,
135 const uchar fg[4],
136 const uchar bg[4],
137 int icon,
138 const uchar icon_fg[4],
139 const uchar icon_bg[4],
140 const uchar bg_sel[4])
141{
142 int tot_lines; /* Total number of lines for wrapping. */
143 int *offsets; /* Offsets of line beginnings for wrapping. */
144
145 str_len = textview_wrap_offsets(str, str_len, tds->columns, &tot_lines, &offsets);
146
147 int line_height = (tot_lines * tds->lheight) + (tds->row_vpadding * 2);
148 int line_bottom = tds->xy[1];
149 int line_top = line_bottom + line_height;
150
151 int y_next = line_top;
152
153 /* Just advance the height. */
154 if (tds->do_draw == false) {
155 if (tds->mval_pick_offset && tds->mval[1] != INT_MAX && line_bottom <= tds->mval[1]) {
156 if (y_next >= tds->mval[1]) {
157 int ofs = 0;
158
159 /* Wrap. */
160 if (tot_lines > 1) {
161 int iofs = int(float(y_next - tds->mval[1]) / tds->lheight);
162 ofs += offsets[std::min(iofs, tot_lines - 1)];
163 }
164
165 /* Last part. */
167 str + ofs,
168 str_len - ofs,
169 int(floor(float(tds->mval[0]) / tds->cwidth)),
171
172 CLAMP(ofs, 0, str_len);
173 *tds->mval_pick_offset += str_len - ofs;
174 }
175 else {
176 *tds->mval_pick_offset += str_len + 1;
177 }
178 }
179
180 tds->xy[1] = y_next;
181 MEM_freeN(offsets);
182 return true;
183 }
184 if (y_next < tds->scroll_ymin) {
185 /* Have not reached the drawable area so don't break. */
186 tds->xy[1] = y_next;
187
188 /* Adjust selection even if not drawing. */
189 if (tds->sel[0] != tds->sel[1]) {
190 textview_step_sel(tds, -(str_len + 1));
191 }
192
193 MEM_freeN(offsets);
194 return true;
195 }
196
197 size_t len;
198 const char *s;
199 int i;
200
201 int sel_orig[2];
202 copy_v2_v2_int(sel_orig, tds->sel);
203
204 /* Invert and swap for wrapping. */
205 tds->sel[0] = str_len - sel_orig[1];
206 tds->sel[1] = str_len - sel_orig[0];
207
208 if (bg) {
210 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
213 immRectf(pos, tds->draw_rect_outer->xmin, line_bottom, tds->draw_rect_outer->xmax, line_top);
215 }
216
217 if (icon_bg) {
218 float col[4];
219 int bg_size = UI_ICON_SIZE * 1.2;
220 float vpadding = (tds->lheight + (tds->row_vpadding * 2) - bg_size) / 2;
221 float hpadding = tds->draw_rect->xmin - (bg_size * 1.2f);
222
223 rgba_uchar_to_float(col, icon_bg);
225
226 rctf roundbox_rect;
227 roundbox_rect.xmin = hpadding;
228 roundbox_rect.xmax = bg_size + hpadding;
229 roundbox_rect.ymin = line_top - bg_size - vpadding;
230 roundbox_rect.ymax = line_top - vpadding;
231
232 UI_draw_roundbox_4fv(&roundbox_rect, true, 4 * UI_SCALE_FAC, col);
233 }
234
235 if (icon) {
236 int vpadding = (tds->lheight + (tds->row_vpadding * 2) - UI_ICON_SIZE) / 2;
237 int hpadding = tds->draw_rect->xmin - (UI_ICON_SIZE * 1.3f);
238
240 UI_icon_draw_ex(hpadding,
241 line_top - UI_ICON_SIZE - vpadding,
242 icon,
243 (16 / UI_ICON_SIZE),
244 1.0f,
245 0.0f,
246 icon_fg,
247 false,
250 }
251
252 tds->xy[1] += tds->row_vpadding;
253
254 /* Last part needs no clipping. */
255 const int final_offset = offsets[tot_lines - 1];
256 len = str_len - final_offset;
257 s = str + final_offset;
258
259 if (tds->sel[0] != tds->sel[1]) {
260 textview_step_sel(tds, -final_offset);
261 const int pos[2] = {tds->xy[0], line_bottom};
262 textview_draw_sel(s, pos, len, tds, bg_sel);
263 }
264
265 BLF_position(tds->font_id, tds->xy[0], tds->lofs + line_bottom + tds->row_vpadding, 0);
266 BLF_color4ubv(tds->font_id, fg);
268
269 tds->xy[1] += tds->lheight;
270
271 BLF_color4ubv(tds->font_id, fg);
272
273 for (i = tot_lines - 1; i > 0; i--) {
274 len = offsets[i] - offsets[i - 1];
275 s = str + offsets[i - 1];
276
277 if (tds->sel[0] != tds->sel[1]) {
279 textview_draw_sel(s, tds->xy, len, tds, bg_sel);
280 }
281
282 BLF_position(tds->font_id, tds->xy[0], tds->lofs + tds->xy[1], 0);
284
285 tds->xy[1] += tds->lheight;
286
287 /* Check if we're out of view bounds. */
288 if (tds->xy[1] > tds->scroll_ymax) {
289 MEM_freeN(offsets);
290 return false;
291 }
292 }
293
294 tds->xy[1] = y_next;
295
296 copy_v2_v2_int(tds->sel, sel_orig);
297 textview_step_sel(tds, -(str_len + 1));
298
299 MEM_freeN(offsets);
300 return true;
301}
302
304 const bool do_draw,
305 const int mval_init[2],
306 void **r_mval_pick_item,
307 int *r_mval_pick_offset)
308{
309 TextViewDrawState tds = {0};
310
311 const int x_orig = tvc->draw_rect.xmin, y_orig = tvc->draw_rect.ymin;
312 int xy[2];
313 /* Disable selection by. */
314 int sel[2] = {-1, -1};
315 uchar fg[4], bg[4], icon_fg[4], icon_bg[4];
316 int icon = 0;
317 const int font_id = blf_mono_font;
318
319 textview_font_begin(font_id, tvc->lheight);
320
321 xy[0] = x_orig;
322 xy[1] = y_orig;
323
324 /* Offset and clamp the results,
325 * clamping so moving the cursor out of the bounds doesn't wrap onto the other lines. */
326 const int mval[2] = {
327 (mval_init[0] == INT_MAX) ?
328 INT_MAX :
329 std::clamp(mval_init[0], tvc->draw_rect.xmin, tvc->draw_rect.xmax) - tvc->draw_rect.xmin,
330 (mval_init[1] == INT_MAX) ?
331 INT_MAX :
332 std::clamp(mval_init[1], tvc->draw_rect.ymin, tvc->draw_rect.ymax) + tvc->scroll_ymin,
333 };
334
335 if (r_mval_pick_offset != nullptr) {
336 *r_mval_pick_offset = 0;
337 }
338
339 /* Constants for the text-view context. */
340 tds.font_id = font_id;
341 tds.cwidth = int(BLF_fixed_width(font_id));
342 BLI_assert(tds.cwidth > 0);
343 tds.lheight = tvc->lheight;
344 tds.row_vpadding = tvc->row_vpadding;
345 tds.lofs = -BLF_descender(font_id);
346 /* NOTE: scroll bar must be already subtracted. */
347 tds.columns = (tvc->draw_rect.xmax - tvc->draw_rect.xmin) / tds.cwidth;
348 /* Avoid divide by zero on small windows. */
349 tds.columns = std::max(tds.columns, 1);
350 tds.draw_rect = &tvc->draw_rect;
352 tds.scroll_ymin = tvc->scroll_ymin;
353 tds.scroll_ymax = tvc->scroll_ymax;
354 tds.xy = xy;
355 tds.sel = sel;
356 tds.mval_pick_offset = r_mval_pick_offset;
357 tds.mval = mval;
358 tds.do_draw = do_draw;
359
360 if (tvc->sel_start != tvc->sel_end) {
361 sel[0] = tvc->sel_start;
362 sel[1] = tvc->sel_end;
363 }
364
365 if (tvc->begin(tvc)) {
366 uchar bg_sel[4] = {0};
367
368 if (do_draw && tvc->const_colors) {
369 tvc->const_colors(tvc, bg_sel);
370 }
371
372 int iter_index = 0;
373 do {
374 const char *ext_line;
375 int ext_len;
376 int data_flag = 0;
377
378 const int y_prev = xy[1];
379
380 if (do_draw) {
381 data_flag = tvc->line_data(tvc, fg, bg, &icon, icon_fg, icon_bg);
382 }
383
384 tvc->line_get(tvc, &ext_line, &ext_len);
385
386 const bool is_out_of_view_y = !textview_draw_string(
387 &tds,
388 ext_line,
389 ext_len,
390 (data_flag & TVC_LINE_FG) ? fg : nullptr,
391 (data_flag & TVC_LINE_BG) ? bg : nullptr,
392 (data_flag & TVC_LINE_ICON) ? icon : 0,
393 (data_flag & TVC_LINE_ICON_FG) ? icon_fg : nullptr,
394 (data_flag & TVC_LINE_ICON_BG) ? icon_bg : nullptr,
395 bg_sel);
396
397 if (do_draw) {
398 /* We always want the cursor to draw. */
399 if (tvc->draw_cursor && iter_index == 0) {
400 tvc->draw_cursor(tvc, tds.cwidth, tds.columns);
401 }
402
403 /* When drawing, if we pass v2d->cur.ymax, then quit. */
404 if (is_out_of_view_y) {
405 break;
406 }
407 }
408
409 if ((mval[1] != INT_MAX) && (mval[1] >= y_prev && mval[1] <= xy[1])) {
410 *r_mval_pick_item = (void *)tvc->iter;
411 break;
412 }
413
414 iter_index++;
415
416 } while (tvc->step(tvc));
417 }
418
419 tvc->end(tvc);
420
421 /* Sanity checks (bugs here can be tricky to track down). */
422 BLI_assert(tds.lheight == tvc->lheight);
424 BLI_assert(tds.do_draw == do_draw);
425
426 xy[1] += tvc->lheight * 2;
427
428 return xy[1] - y_orig;
429}
void BLF_size(int fontid, float size)
Definition blf.cc:443
int BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:872
float BLF_fixed_width(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:815
int blf_mono_font
Definition blf.cc:48
void BLF_color4ubv(int fontid, const unsigned char rgba[4])
Definition blf.cc:452
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:388
int BLF_draw_mono(int fontid, const char *str, size_t str_len, int cwidth, int tab_columns) ATTR_NONNULL(2)
Definition blf.cc:607
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
#define BLI_UTF8_WIDTH_MAX
int BLI_str_utf8_char_width_safe(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_str_utf8_size_safe(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_str_utf8_offset_from_column_with_tabs(const char *str, size_t str_len, int column_target, int tab_width) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_str_utf8_offset_to_column_with_tabs(const char *str, size_t str_len, int offset_target, int tab_width) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
unsigned char uchar
unsigned int uint
#define CLAMP(a, b, c)
#define UNLIKELY(x)
#define UI_SCALE_FAC
#define UI_ICON_SIZE
void immUniformColor4ubv(const unsigned char rgba[4])
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immRectf(uint pos, float x1, float y1, float x2, float y2)
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
Read Guarded memory(de)allocation.
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
void UI_draw_roundbox_corner_set(int type)
@ UI_CNR_ALL
#define UI_NO_ICON_OVERLAY_TEXT
void UI_icon_draw_ex(float x, float y, int icon_id, float aspect, float alpha, float desaturate, const uchar mono_color[4], bool mono_border, const IconTextOverlay *text_overlay, const bool inverted=false)
#define str(s)
uint pos
uint col
#define floor
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
format
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
enum eTextViewContext_LineFlag(* line_data)(TextViewContext *tvc, uchar fg[4], uchar bg[4], int *r_icon, uchar r_icon_fg[4], uchar r_icon_bg[4])
Definition textview.hh:51
const void * iter
Definition textview.hh:60
void(* end)(TextViewContext *tvc)
Definition textview.hh:44
void(* const_colors)(TextViewContext *tvc, unsigned char bg_sel[4])
Definition textview.hh:59
int(* begin)(TextViewContext *tvc)
Definition textview.hh:43
void(* line_get)(TextViewContext *tvc, const char **r_line, int *r_len)
Definition textview.hh:50
int(* step)(TextViewContext *tvc)
Definition textview.hh:49
rcti draw_rect_outer
Definition textview.hh:37
void(* draw_cursor)(TextViewContext *tvc, int cwidth, int columns)
Definition textview.hh:57
const rcti * draw_rect
Definition textview.cc:46
const rcti * draw_rect_outer
Definition textview.cc:48
const int * mval
Definition textview.cc:54
int * mval_pick_offset
Definition textview.cc:53
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
i
Definition text_draw.cc:230
static bool textview_draw_string(TextViewDrawState *tds, const char *str, int str_len, const uchar fg[4], const uchar bg[4], int icon, const uchar icon_fg[4], const uchar icon_bg[4], const uchar bg_sel[4])
Definition textview.cc:132
static void textview_draw_sel(const char *str, const int xy[2], const int str_len_draw, TextViewDrawState *tds, const uchar bg_sel[4])
Definition textview.cc:64
static void textview_font_begin(const int font_id, const int lheight)
Definition textview.cc:30
int textview_draw(TextViewContext *tvc, const bool do_draw, const int mval_init[2], void **r_mval_pick_item, int *r_mval_pick_offset)
Definition textview.cc:303
BLI_INLINE void textview_step_sel(TextViewDrawState *tds, const int step)
Definition textview.cc:58
static int textview_wrap_offsets(const char *str, const int str_len, const int width, int *r_lines, int **r_offsets)
Definition textview.cc:99
@ TVC_LINE_ICON
Definition textview.hh:20
@ TVC_LINE_ICON_FG
Definition textview.hh:21
@ TVC_LINE_ICON_BG
Definition textview.hh:22
@ TVC_LINE_BG
Definition textview.hh:19
@ TVC_LINE_FG
Definition textview.hh:18
#define TVC_TAB_COLUMNS
Definition textview.hh:15
uint len
int xy[2]
Definition wm_draw.cc:178