Blender V4.3
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
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_string_utf8.h"
17#include "BLI_utildefines.h"
18
19#include "GPU_immediate.hh"
20#include "GPU_state.hh"
21
22#include "DNA_userdef_types.h" /* For 'UI_SCALE_FAC' */
23
24#include "UI_interface.hh"
25#include "UI_interface_icons.hh"
26
27#include "textview.hh"
28
29static void textview_font_begin(const int font_id, const int lheight)
30{
31 /* Font size in relation to line height. */
32 BLF_size(font_id, 0.8f * lheight);
33}
34
37 int cwidth;
40 int lofs;
49 int *xy; // [2]
50 int *sel; // [2]
51 /* Bottom of view == 0, top of file == combine chars, end of line is lower than start. */
53 const int *mval; // [2]
54 bool do_draw;
55};
56
58{
59 tds->sel[0] += step;
60 tds->sel[1] += step;
61}
62
63static void textview_draw_sel(const char *str,
64 const int xy[2],
65 const int str_len_draw,
67 const uchar bg_sel[4])
68{
69 const int sel[2] = {tds->sel[0], tds->sel[1]};
70 const int cwidth = tds->cwidth;
71 const int lheight = tds->lheight;
72
73 if (sel[0] <= str_len_draw && sel[1] >= 0) {
75 str, str_len_draw, max_ii(sel[0], 0), TVC_TAB_COLUMNS);
77 str, str_len_draw, min_ii(sel[1], str_len_draw), TVC_TAB_COLUMNS);
78
80
84
85 immUniformColor4ubv(bg_sel);
86 immRecti(pos, xy[0] + (cwidth * sta), xy[1] + lheight, xy[0] + (cwidth * end), xy[1]);
87
89
91 }
92}
93
99 const char *str, const int str_len, const int width, int *r_lines, int **r_offsets)
100{
101 int i, end; /* Offset as unicode code-point. */
102 int j; /* Offset as bytes. */
103 const int tab_columns = TVC_TAB_COLUMNS;
104 const int column_width_max = std::max(tab_columns, BLI_UTF8_WIDTH_MAX);
105
106 *r_lines = 1;
107
108 *r_offsets = static_cast<int *>(MEM_callocN(
109 sizeof(**r_offsets) *
110 (str_len * column_width_max / std::max(1, width - (column_width_max - 1)) + 1),
111 __func__));
112 (*r_offsets)[0] = 0;
113
114 for (i = 0, end = width, j = 0; j < str_len && str[j]; j += BLI_str_utf8_size_safe(str + j)) {
115 int columns = UNLIKELY(*(str + j) == '\t') ? (tab_columns - (i % tab_columns)) :
117
118 if (i + columns > end) {
119 (*r_offsets)[*r_lines] = j;
120 (*r_lines)++;
121
122 end = i + width;
123 }
124 i += columns;
125 }
126 return j;
127}
128
134 const char *str,
135 int str_len,
136 const uchar fg[4],
137 const uchar bg[4],
138 int icon,
139 const uchar icon_fg[4],
140 const uchar icon_bg[4],
141 const uchar bg_sel[4])
142{
143 int tot_lines; /* Total number of lines for wrapping. */
144 int *offsets; /* Offsets of line beginnings for wrapping. */
145
146 str_len = textview_wrap_offsets(str, str_len, tds->columns, &tot_lines, &offsets);
147
148 int line_height = (tot_lines * tds->lheight) + (tds->row_vpadding * 2);
149 int line_bottom = tds->xy[1];
150 int line_top = line_bottom + line_height;
151
152 int y_next = line_top;
153
154 /* Just advance the height. */
155 if (tds->do_draw == false) {
156 if (tds->mval_pick_offset && tds->mval[1] != INT_MAX && line_bottom <= tds->mval[1]) {
157 if (y_next >= tds->mval[1]) {
158 int ofs = 0;
159
160 /* Wrap. */
161 if (tot_lines > 1) {
162 int iofs = int(float(y_next - tds->mval[1]) / tds->lheight);
163 ofs += offsets[std::min(iofs, tot_lines - 1)];
164 }
165
166 /* Last part. */
168 str + ofs,
169 str_len - ofs,
170 int(floor(float(tds->mval[0]) / tds->cwidth)),
172
173 CLAMP(ofs, 0, str_len);
174 *tds->mval_pick_offset += str_len - ofs;
175 }
176 else {
177 *tds->mval_pick_offset += str_len + 1;
178 }
179 }
180
181 tds->xy[1] = y_next;
182 MEM_freeN(offsets);
183 return true;
184 }
185 if (y_next < tds->scroll_ymin) {
186 /* Have not reached the drawable area so don't break. */
187 tds->xy[1] = y_next;
188
189 /* Adjust selection even if not drawing. */
190 if (tds->sel[0] != tds->sel[1]) {
191 textview_step_sel(tds, -(str_len + 1));
192 }
193
194 MEM_freeN(offsets);
195 return true;
196 }
197
198 size_t len;
199 const char *s;
200 int i;
201
202 int sel_orig[2];
203 copy_v2_v2_int(sel_orig, tds->sel);
204
205 /* Invert and swap for wrapping. */
206 tds->sel[0] = str_len - sel_orig[1];
207 tds->sel[1] = str_len - sel_orig[0];
208
209 if (bg) {
214 immRecti(pos, tds->draw_rect_outer->xmin, line_bottom, tds->draw_rect_outer->xmax, line_top);
216 }
217
218 if (icon_bg) {
219 float col[4];
220 int bg_size = UI_ICON_SIZE * 1.2;
221 float vpadding = (tds->lheight + (tds->row_vpadding * 2) - bg_size) / 2;
222 float hpadding = tds->draw_rect->xmin - (bg_size * 1.2f);
223
224 rgba_uchar_to_float(col, icon_bg);
226
227 rctf roundbox_rect;
228 roundbox_rect.xmin = hpadding;
229 roundbox_rect.xmax = bg_size + hpadding;
230 roundbox_rect.ymin = line_top - bg_size - vpadding;
231 roundbox_rect.ymax = line_top - vpadding;
232
233 UI_draw_roundbox_4fv(&roundbox_rect, true, 4 * UI_SCALE_FAC, col);
234 }
235
236 if (icon) {
237 int vpadding = (tds->lheight + (tds->row_vpadding * 2) - UI_ICON_SIZE) / 2;
238 int hpadding = tds->draw_rect->xmin - (UI_ICON_SIZE * 1.3f);
239
241 UI_icon_draw_ex(hpadding,
242 line_top - UI_ICON_SIZE - vpadding,
243 icon,
244 (16 / UI_ICON_SIZE),
245 1.0f,
246 0.0f,
247 icon_fg,
248 false,
251 }
252
253 tds->xy[1] += tds->row_vpadding;
254
255 /* Last part needs no clipping. */
256 const int final_offset = offsets[tot_lines - 1];
257 len = str_len - final_offset;
258 s = str + final_offset;
259
260 if (tds->sel[0] != tds->sel[1]) {
261 textview_step_sel(tds, -final_offset);
262 const int pos[2] = {tds->xy[0], line_bottom};
263 textview_draw_sel(s, pos, len, tds, bg_sel);
264 }
265
266 BLF_position(tds->font_id, tds->xy[0], tds->lofs + line_bottom + tds->row_vpadding, 0);
267 BLF_color4ubv(tds->font_id, fg);
269
270 tds->xy[1] += tds->lheight;
271
272 BLF_color4ubv(tds->font_id, fg);
273
274 for (i = tot_lines - 1; i > 0; i--) {
275 len = offsets[i] - offsets[i - 1];
276 s = str + offsets[i - 1];
277
278 if (tds->sel[0] != tds->sel[1]) {
280 textview_draw_sel(s, tds->xy, len, tds, bg_sel);
281 }
282
283 BLF_position(tds->font_id, tds->xy[0], tds->lofs + tds->xy[1], 0);
285
286 tds->xy[1] += tds->lheight;
287
288 /* Check if we're out of view bounds. */
289 if (tds->xy[1] > tds->scroll_ymax) {
290 MEM_freeN(offsets);
291 return false;
292 }
293 }
294
295 tds->xy[1] = y_next;
296
297 copy_v2_v2_int(tds->sel, sel_orig);
298 textview_step_sel(tds, -(str_len + 1));
299
300 MEM_freeN(offsets);
301 return true;
302}
303
305 const bool do_draw,
306 const int mval_init[2],
307 void **r_mval_pick_item,
308 int *r_mval_pick_offset)
309{
310 TextViewDrawState tds = {0};
311
312 const int x_orig = tvc->draw_rect.xmin, y_orig = tvc->draw_rect.ymin;
313 int xy[2];
314 /* Disable selection by. */
315 int sel[2] = {-1, -1};
316 uchar fg[4], bg[4], icon_fg[4], icon_bg[4];
317 int icon = 0;
318 const int font_id = blf_mono_font;
319
320 textview_font_begin(font_id, tvc->lheight);
321
322 xy[0] = x_orig;
323 xy[1] = y_orig;
324
325 /* Offset and clamp the results,
326 * clamping so moving the cursor out of the bounds doesn't wrap onto the other lines. */
327 const int mval[2] = {
328 (mval_init[0] == INT_MAX) ?
329 INT_MAX :
330 std::clamp(mval_init[0], tvc->draw_rect.xmin, tvc->draw_rect.xmax) - tvc->draw_rect.xmin,
331 (mval_init[1] == INT_MAX) ?
332 INT_MAX :
333 std::clamp(mval_init[1], tvc->draw_rect.ymin, tvc->draw_rect.ymax) + tvc->scroll_ymin,
334 };
335
336 if (r_mval_pick_offset != nullptr) {
337 *r_mval_pick_offset = 0;
338 }
339
340 /* Constants for the text-view context. */
341 tds.font_id = font_id;
342 tds.cwidth = int(BLF_fixed_width(font_id));
343 BLI_assert(tds.cwidth > 0);
344 tds.lheight = tvc->lheight;
345 tds.row_vpadding = tvc->row_vpadding;
346 tds.lofs = -BLF_descender(font_id);
347 /* NOTE: scroll bar must be already subtracted. */
348 tds.columns = (tvc->draw_rect.xmax - tvc->draw_rect.xmin) / tds.cwidth;
349 /* Avoid divide by zero on small windows. */
350 if (tds.columns < 1) {
351 tds.columns = 1;
352 }
353 tds.draw_rect = &tvc->draw_rect;
355 tds.scroll_ymin = tvc->scroll_ymin;
356 tds.scroll_ymax = tvc->scroll_ymax;
357 tds.xy = xy;
358 tds.sel = sel;
359 tds.mval_pick_offset = r_mval_pick_offset;
360 tds.mval = mval;
361 tds.do_draw = do_draw;
362
363 if (tvc->sel_start != tvc->sel_end) {
364 sel[0] = tvc->sel_start;
365 sel[1] = tvc->sel_end;
366 }
367
368 if (tvc->begin(tvc)) {
369 uchar bg_sel[4] = {0};
370
371 if (do_draw && tvc->const_colors) {
372 tvc->const_colors(tvc, bg_sel);
373 }
374
375 int iter_index = 0;
376 do {
377 const char *ext_line;
378 int ext_len;
379 int data_flag = 0;
380
381 const int y_prev = xy[1];
382
383 if (do_draw) {
384 data_flag = tvc->line_data(tvc, fg, bg, &icon, icon_fg, icon_bg);
385 }
386
387 tvc->line_get(tvc, &ext_line, &ext_len);
388
389 const bool is_out_of_view_y = !textview_draw_string(
390 &tds,
391 ext_line,
392 ext_len,
393 (data_flag & TVC_LINE_FG) ? fg : nullptr,
394 (data_flag & TVC_LINE_BG) ? bg : nullptr,
395 (data_flag & TVC_LINE_ICON) ? icon : 0,
396 (data_flag & TVC_LINE_ICON_FG) ? icon_fg : nullptr,
397 (data_flag & TVC_LINE_ICON_BG) ? icon_bg : nullptr,
398 bg_sel);
399
400 if (do_draw) {
401 /* We always want the cursor to draw. */
402 if (tvc->draw_cursor && iter_index == 0) {
403 tvc->draw_cursor(tvc, tds.cwidth, tds.columns);
404 }
405
406 /* When drawing, if we pass v2d->cur.ymax, then quit. */
407 if (is_out_of_view_y) {
408 break;
409 }
410 }
411
412 if ((mval[1] != INT_MAX) && (mval[1] >= y_prev && mval[1] <= xy[1])) {
413 *r_mval_pick_item = (void *)tvc->iter;
414 break;
415 }
416
417 iter_index++;
418
419 } while (tvc->step(tvc));
420 }
421
422 tvc->end(tvc);
423
424 /* Sanity checks (bugs here can be tricky to track down). */
425 BLI_assert(tds.lheight == tvc->lheight);
427 BLI_assert(tds.do_draw == do_draw);
428
429 xy[1] += tvc->lheight * 2;
430
431 return xy[1] - y_orig;
432}
void BLF_size(int fontid, float size)
Definition blf.cc:426
int BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:850
float BLF_fixed_width(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:804
int blf_mono_font
Definition blf.cc:51
void BLF_color4ubv(int fontid, const unsigned char rgba[4])
Definition blf.cc:435
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:371
int BLF_draw_mono(int fontid, const char *str, size_t str_len, int cwidth, int tab_columns) ATTR_NONNULL(2)
Definition blf.cc:594
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
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(eGPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immRecti(uint pos, int x1, int y1, int x2, int 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(eGPUBlend blend)
Definition gpu_state.cc:42
@ GPU_FETCH_INT_TO_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_I32
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)
int len
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
uint col
format
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float2 floor(const float2 a)
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:47
const void * iter
Definition textview.hh:56
void(* end)(TextViewContext *tvc)
Definition textview.hh:40
void(* const_colors)(TextViewContext *tvc, unsigned char bg_sel[4])
Definition textview.hh:55
int(* begin)(TextViewContext *tvc)
Definition textview.hh:39
void(* line_get)(TextViewContext *tvc, const char **r_line, int *r_len)
Definition textview.hh:46
int(* step)(TextViewContext *tvc)
Definition textview.hh:45
rcti draw_rect_outer
Definition textview.hh:33
void(* draw_cursor)(TextViewContext *tvc, int cwidth, int columns)
Definition textview.hh:53
const rcti * draw_rect
Definition textview.cc:45
const rcti * draw_rect_outer
Definition textview.cc:47
const int * mval
Definition textview.cc:53
int * mval_pick_offset
Definition textview.cc:52
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
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:133
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:63
static void textview_font_begin(const int font_id, const int lheight)
Definition textview.cc:29
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:304
BLI_INLINE void textview_step_sel(TextViewDrawState *tds, const int step)
Definition textview.cc:57
static int textview_wrap_offsets(const char *str, const int str_len, const int width, int *r_lines, int **r_offsets)
Definition textview.cc:98
@ TVC_LINE_ICON
Definition textview.hh:16
@ TVC_LINE_ICON_FG
Definition textview.hh:17
@ TVC_LINE_ICON_BG
Definition textview.hh:18
@ TVC_LINE_BG
Definition textview.hh:15
@ TVC_LINE_FG
Definition textview.hh:14
#define TVC_TAB_COLUMNS
Definition textview.hh:11
int xy[2]
Definition wm_draw.cc:170