Blender V5.0
text_draw.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
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_listbase.h"
16#include "BLI_rect.h"
17#include "BLI_string.h"
18#include "BLI_string_utf8.h"
19
20#include "DNA_screen_types.h"
21#include "DNA_space_types.h"
22#include "DNA_text_types.h"
23
24#include "BKE_context.hh"
25#include "BKE_screen.hh"
26#include "BKE_text.h"
28
29#include "ED_text.hh"
30
31#include "GPU_immediate.hh"
32#include "GPU_state.hh"
33
34#include "UI_interface.hh"
35#include "UI_resources.hh"
36
37#include "text_format.hh"
38#include "text_intern.hh"
39
40#include "WM_api.hh"
41#include "WM_types.hh"
42
43/* -------------------------------------------------------------------- */
46
48{
49 return st->runtime->viewlines;
50}
51
53
54/* -------------------------------------------------------------------- */
57
64
66{
68 tdc->cwidth_px = 0;
69 tdc->lheight_px = st->runtime->lheight_px;
71}
72
73static void text_font_begin(const TextDrawContext *tdc)
74{
75 BLF_size(tdc->font_id, float(tdc->lheight_px));
76}
77
78static void text_font_end(const TextDrawContext * /*tdc*/) {}
79
80static int text_font_draw(const TextDrawContext *tdc, int x, int y, const char *str)
81{
82 const char tab_columns = 1; /* Tab characters aren't used here. */
83 BLF_position(tdc->font_id, x, y, 0);
84 const int columns = BLF_draw_mono(
85 tdc->font_id, str, BLF_DRAW_STR_DUMMY_MAX, tdc->cwidth_px, tab_columns);
86
87 return tdc->cwidth_px * columns;
88}
89
90static int text_font_draw_character(const TextDrawContext *tdc, int x, int y, char c)
91{
92 const char tab_columns = 1;
93 BLF_position(tdc->font_id, x, y, 0);
94 BLF_draw_mono(tdc->font_id, &c, 1, tdc->cwidth_px, tab_columns);
95
96 return tdc->cwidth_px;
97}
98
100 const TextDrawContext *tdc, int x, int y, const char *c, const int c_len)
101{
103 const char tab_columns = 1; /* Tab characters aren't used here. */
104 BLF_position(tdc->font_id, x, y, 0);
105 const int columns = BLF_draw_mono(tdc->font_id, c, c_len, tdc->cwidth_px, tab_columns);
106
107 return tdc->cwidth_px * columns;
108}
109
110#if 0
112static void txt_format_text(SpaceText *st)
113{
115
116 if (!st->text) {
117 return;
118 }
119
120 for (linep = st->text->lines.first; linep; linep = linep->next) {
121 txt_format_line(st, linep, 0);
122 }
123}
124#endif
125
127static void format_draw_color(const TextDrawContext *tdc, char formatchar)
128{
129 switch (formatchar) {
131 break;
132 case FMT_TYPE_SYMBOL:
134 break;
135 case FMT_TYPE_COMMENT:
137 break;
138 case FMT_TYPE_NUMERAL:
140 break;
141 case FMT_TYPE_STRING:
143 break;
146 break;
147 case FMT_TYPE_SPECIAL:
149 break;
152 break;
153 case FMT_TYPE_KEYWORD:
155 break;
156 case FMT_TYPE_DEFAULT:
157 default:
159 break;
160 }
161}
162
164
165/* -------------------------------------------------------------------- */
197
198int space_text_wrap_width(const SpaceText *st, const ARegion *region)
199{
200 int winx = region->winx - TXT_SCROLL_WIDTH;
201 int x, max;
202
203 x = TXT_BODY_LEFT(st);
204 max = st->runtime->cwidth_px ? (winx - x) / st->runtime->cwidth_px : 0;
205 return max > 8 ? max : 8;
206}
207
209 const SpaceText *st, const ARegion *region, TextLine *linein, int cursin, int *offl, int *offc)
210{
211 Text *text;
213 int i, j, start, end, max;
214 bool chop;
215 char ch;
216
217 *offl = *offc = 0;
218
219 if (!st->text) {
220 return;
221 }
222 if (!st->wordwrap) {
223 return;
224 }
225
226 text = st->text;
227
228 /* Move pointer to first visible line (top). */
229 linep = static_cast<TextLine *>(text->lines.first);
230 i = st->top;
231 while (i > 0 && linep) {
232 int lines = space_text_get_visible_lines(st, region, linep->line);
233
234 /* Line before top. */
235 if (linep == linein) {
236 if (lines <= i) {
237 /* No visible part of line. */
238 return;
239 }
240 }
241
242 if (i - lines < 0) {
243 break;
244 }
245
246 linep = linep->next;
247 (*offl) += lines - 1;
248 i -= lines;
249 }
250
253
254 while (linep) {
255 start = 0;
256 end = max;
257 chop = true;
258 *offc = 0;
259 for (i = 0, j = 0; linep->line[j]; j += BLI_str_utf8_size_safe(linep->line + j)) {
260 int chars;
261 const int columns = BLI_str_utf8_char_width_safe(linep->line + j); /* = 1 for tab. */
262
263 /* Mimic replacement of tabs. */
264 ch = linep->line[j];
265 if (ch == '\t') {
266 chars = st->tabnumber - i % st->tabnumber;
267 if (linep == linein && i < cursin) {
268 cursin += chars - 1;
269 }
270 ch = ' ';
271 }
272 else {
273 chars = 1;
274 }
275
276 while (chars--) {
277 if (i + columns - start > max) {
278 end = std::min(end, i);
279
280 if (chop && linep == linein && i >= cursin) {
281 if (i == cursin) {
282 (*offl)++;
283 *offc -= end - start;
284 }
285
286 return;
287 }
288
289 (*offl)++;
290 *offc -= end - start;
291
292 start = end;
293 end += max;
294 chop = true;
295 }
296 else if (ELEM(ch, ' ', '-')) {
297 end = i + 1;
298 chop = false;
299 if (linep == linein && i >= cursin) {
300 return;
301 }
302 }
303 i += columns;
304 }
305 }
306 if (linep == linein) {
307 break;
308 }
309 linep = linep->next;
310 }
311}
312
314 const SpaceText *st, const ARegion *region, TextLine *linein, int cursin, int *offl, int *offc)
315{
316 int i, j, start, end, chars, max;
317 bool chop;
318 char ch;
319
320 *offl = *offc = 0;
321
322 if (!st->text) {
323 return;
324 }
325 if (!st->wordwrap) {
326 return;
327 }
328
329 max = space_text_wrap_width(st, region);
330
331 start = 0;
332 end = max;
333 chop = true;
334 *offc = 0;
336
337 for (i = 0, j = 0; linein->line[j]; j += BLI_str_utf8_size_safe(linein->line + j)) {
338 const int columns = BLI_str_utf8_char_width_safe(linein->line + j); /* = 1 for tab. */
339
340 /* Mimic replacement of tabs. */
341 ch = linein->line[j];
342 if (ch == '\t') {
343 chars = st->tabnumber - i % st->tabnumber;
344 if (i < cursin) {
345 cursin += chars - 1;
346 }
347 ch = ' ';
348 }
349 else {
350 chars = 1;
351 }
352
353 while (chars--) {
354 if (i + columns - start > max) {
355 end = std::min(end, i);
356
357 if (chop && i >= cursin) {
358 if (i == cursin) {
359 (*offl)++;
360 *offc -= end - start;
361 }
362
363 return;
364 }
365
366 (*offl)++;
367 *offc -= end - start;
368
369 start = end;
370 end += max;
371 chop = true;
372 }
373 else if (ELEM(ch, ' ', '-')) {
374 end = i + 1;
375 chop = false;
376 if (i >= cursin) {
377 return;
378 }
379 }
380 i += columns;
381 }
382 }
383}
384
385int space_text_get_char_pos(const SpaceText *st, const char *line, int cur)
386{
387 int a = 0, i;
388
389 for (i = 0; i < cur && line[i]; i += BLI_str_utf8_size_safe(line + i)) {
390 if (line[i] == '\t') {
391 a += st->tabnumber - a % st->tabnumber;
392 }
393 else {
394 a += BLI_str_utf8_char_width_safe(line + i);
395 }
396 }
397 return a;
398}
399
400static const char *txt_utf8_forward_columns(const char *str, int columns, int *padding)
401{
402 const char *p = str;
403 while (*p) {
404 const int col = BLI_str_utf8_char_width_safe(p);
405 if (columns - col < 0) {
406 break;
407 }
408 columns -= col;
410 if (columns == 0) {
411 break;
412 }
413 }
414 if (padding) {
415 *padding = *p ? columns : 0;
416 }
417 return p;
418}
419
421 const TextDrawContext *tdc,
422 const char *str,
423 int x,
424 int y,
425 int w,
426 const char *format,
427 int skip)
428{
429 const bool use_syntax = (tdc->syntax_highlight && format);
430 FlattenString fs;
431 int basex, lines;
432 int i, wrap, end, max, columns, padding; /* Column. */
433 /* Warning, only valid when `use_syntax` is set. */
434 int a, fstart, fpos; /* UTF8 characters. */
435 int mi, ma, mstart, mend;
436 char fmt_prev = 0xff;
437 /* Don't draw lines below this. */
438 const int clip_min_y = -(st->runtime->lheight_px - 1);
439
440 flatten_string(st, &fs, str);
441 str = fs.buf;
442 max = w / st->runtime->cwidth_px;
443 max = std::max(max, 8);
444 basex = x;
445 lines = 1;
446
447 fpos = fstart = 0;
448 mstart = 0;
450 end = wrap = max - padding;
451
452 for (i = 0, mi = 0; str[mi]; i += columns, mi += BLI_str_utf8_size_safe(str + mi)) {
453 columns = BLI_str_utf8_char_width_safe(str + mi);
454 if (i + columns > end) {
455 /* Skip hidden part of line. */
456 if (skip) {
457 skip--;
458 if (use_syntax) {
459 /* Currently fpos only used when formatting. */
460 fpos += BLI_strnlen_utf8(str + mstart, mend - mstart);
461 }
462 fstart = fpos;
463 mstart = mend;
464 mend = txt_utf8_forward_columns(str + mend, max, &padding) - str;
465 end = (wrap += max - padding);
466 continue;
467 }
468
469 /* Draw the visible portion of text on the overshot line. */
470 for (a = fstart, ma = mstart; ma < mend; a++) {
471 if (use_syntax) {
472 if (fmt_prev != format[a]) {
473 format_draw_color(tdc, fmt_prev = format[a]);
474 }
475 }
476 const int c_len = BLI_str_utf8_size_safe(str + ma);
477 x += text_font_draw_character_utf8(tdc, x, y, str + ma, c_len);
478 ma += c_len;
479 fpos++;
480 }
481 y -= TXT_LINE_HEIGHT(st);
482 x = basex;
483 lines++;
484 fstart = fpos;
485 mstart = mend;
486 mend = txt_utf8_forward_columns(str + mend, max, &padding) - str;
487 end = (wrap += max - padding);
488
489 if (y <= clip_min_y) {
490 break;
491 }
492 }
493 else if (ELEM(str[mi], ' ', '-')) {
494 wrap = i + 1;
495 mend = mi + 1;
496 }
497 }
498
499 /* Draw the remaining text. */
500 for (a = fstart, ma = mstart; str[ma] && y > clip_min_y; a++) {
501 if (use_syntax) {
502 if (fmt_prev != format[a]) {
503 format_draw_color(tdc, fmt_prev = format[a]);
504 }
505 }
506
507 const int c_len = BLI_str_utf8_size_safe(str + ma);
508 x += text_font_draw_character_utf8(tdc, x, y, str + ma, c_len);
509 ma += c_len;
510 }
511
513
514 return lines;
515}
516
517static void space_text_draw(const SpaceText *st,
518 const TextDrawContext *tdc,
519 char *str,
520 int cshift,
521 int maxwidth,
522 int x,
523 int y,
524 const char *format)
525{
526 const bool use_syntax = (tdc->syntax_highlight && format);
527 FlattenString fs;
528 int n, w = 0, padding, amount = 0;
529 const char *in = nullptr;
530
531 for (n = flatten_string(st, &fs, str), str = fs.buf; n > 0; n--) {
532 const int columns = BLI_str_utf8_char_width_safe(str);
533 const int size = BLI_str_utf8_size_safe(str);
534
535 if (!in) {
536 if (w >= cshift) {
537 padding = w - cshift;
538 in = str;
539 }
540 else if (format) {
541 format++;
542 }
543 }
544 if (in) {
545 if (maxwidth && w + columns > cshift + maxwidth) {
546 break;
547 }
548 amount++;
549 }
550
551 w += columns;
552 str += size;
553 }
554 if (!in) {
556 return; /* String is shorter than shift or ends with a padding. */
557 }
558
559 x += tdc->cwidth_px * padding;
560
561 if (use_syntax) {
562 int a, str_shift = 0;
563 char fmt_prev = 0xff;
564
565 for (a = 0; a < amount; a++) {
566 if (format[a] != fmt_prev) {
567 format_draw_color(tdc, fmt_prev = format[a]);
568 }
569 const int c_len = BLI_str_utf8_size_safe(in + str_shift);
570 x += text_font_draw_character_utf8(tdc, x, y, in + str_shift, c_len);
571 str_shift += c_len;
572 }
573 }
574 else {
575 text_font_draw(tdc, x, y, in);
576 }
577
579}
580
582
583/* -------------------------------------------------------------------- */
586
587struct DrawCache {
590
591 /* This is needed to check cache relevance. */
593 short lheight;
596
598 bool update;
599 int valid_head, valid_tail; /* Amount of unchanged lines. */
600};
601
603{
604 DrawCache *drawcache = MEM_callocN<DrawCache>("text draw cache");
605
606 drawcache->winx = -1;
607 drawcache->nlines = BLI_listbase_count(&st->text->lines);
608 drawcache->text_id[0] = '\0';
609
610 st->runtime->drawcache = drawcache;
611}
612
613static void space_text_update_drawcache(SpaceText *st, const ARegion *region)
614{
615 DrawCache *drawcache;
616 bool full_update = false;
617 int nlines = 0;
618 Text *txt = st->text;
619
620 if (st->runtime->drawcache == nullptr) {
622 }
623
625
626 drawcache = static_cast<DrawCache *>(st->runtime->drawcache);
627 nlines = drawcache->nlines;
628
629 /* Check if full cache update is needed. */
630
631 /* Area was resized. */
632 full_update |= drawcache->winx != region->winx;
633 /* Word-wrapping option was toggled. */
634 full_update |= drawcache->wordwrap != st->wordwrap;
635 /* Word-wrapping option was toggled. */
636 full_update |= drawcache->showlinenrs != st->showlinenrs;
637 /* Word-wrapping option was toggled. */
638 full_update |= drawcache->tabnumber != st->tabnumber;
639 /* Word-wrapping option was toggled. */
640 full_update |= drawcache->lheight != st->runtime->lheight_px;
641 /* Word-wrapping option was toggled. */
642 full_update |= drawcache->cwidth_px != st->runtime->cwidth_px;
643 /* Text datablock was changed. */
644 full_update |= !STREQLEN(drawcache->text_id, txt->id.name, MAX_ID_NAME - 2);
645
646 if (st->wordwrap) {
647 /* Update line heights. */
648 if (full_update || !drawcache->line_height) {
649 drawcache->valid_head = 0;
650 drawcache->valid_tail = 0;
651 drawcache->update = true;
652 }
653
654 if (drawcache->update) {
655 TextLine *line = static_cast<TextLine *>(st->text->lines.first);
656 int lineno = 0, size, lines_count;
657 int *fp = drawcache->line_height, *new_tail, *old_tail;
658
659 nlines = BLI_listbase_count(&txt->lines);
660 size = sizeof(int) * nlines;
661
662 if (fp) {
663 fp = static_cast<int *>(MEM_reallocN(fp, size));
664 }
665 else {
666 fp = static_cast<int *>(MEM_callocN(size, "text drawcache line_height"));
667 }
668
669 drawcache->valid_tail = drawcache->valid_head = 0;
670 old_tail = fp + drawcache->nlines - drawcache->valid_tail;
671 new_tail = fp + nlines - drawcache->valid_tail;
672 memmove(new_tail, old_tail, drawcache->valid_tail);
673
674 drawcache->total_lines = 0;
675
676 if (st->showlinenrs) {
677 st->runtime->line_number_display_digits = integer_digits_i(nlines);
678 }
679
680 while (line) {
681 if (drawcache->valid_head) { /* We're inside valid head lines. */
682 lines_count = fp[lineno];
683 drawcache->valid_head--;
684 }
685 else if (lineno > new_tail - fp) { /* We-re inside valid tail lines. */
686 lines_count = fp[lineno];
687 }
688 else {
689 lines_count = space_text_get_visible_lines(st, region, line->line);
690 }
691
692 fp[lineno] = lines_count;
693
694 line = line->next;
695 lineno++;
696 drawcache->total_lines += lines_count;
697 }
698
699 drawcache->line_height = fp;
700 }
701 }
702 else {
703 MEM_SAFE_FREE(drawcache->line_height);
704
705 if (full_update || drawcache->update) {
706 nlines = BLI_listbase_count(&txt->lines);
707
708 if (st->showlinenrs) {
709 st->runtime->line_number_display_digits = integer_digits_i(nlines);
710 }
711 }
712
713 drawcache->total_lines = nlines;
714 }
715
716 drawcache->nlines = nlines;
717
718 /* Store settings. */
719 drawcache->winx = region->winx;
720 drawcache->wordwrap = st->wordwrap;
721 drawcache->lheight = st->runtime->lheight_px;
722 drawcache->cwidth_px = st->runtime->cwidth_px;
723 drawcache->showlinenrs = st->showlinenrs;
724 drawcache->tabnumber = st->tabnumber;
725
726 STRNCPY(drawcache->text_id, txt->id.name + 2);
727
728 /* Clear update flag. */
729 drawcache->update = false;
730 drawcache->valid_head = 0;
731 drawcache->valid_tail = 0;
732}
733
735{
736 /* This happens if text editor ops are called from Python. */
737 if (st == nullptr) {
738 return;
739 }
740
741 if (st->runtime->drawcache != nullptr) {
742 DrawCache *drawcache = static_cast<DrawCache *>(st->runtime->drawcache);
743 Text *txt = st->text;
744
745 if (drawcache->update) {
746 /* Happens when tagging update from space listener. */
747 /* Should do nothing to prevent locally tagged cache be fully recalculated. */
748 return;
749 }
750
751 if (!full) {
752 int sellno = BLI_findindex(&txt->lines, txt->sell);
753 int curlno = BLI_findindex(&txt->lines, txt->curl);
754
755 if (curlno < sellno) {
756 drawcache->valid_head = curlno;
757 drawcache->valid_tail = drawcache->nlines - sellno - 1;
758 }
759 else {
760 drawcache->valid_head = sellno;
761 drawcache->valid_tail = drawcache->nlines - curlno - 1;
762 }
763
764 /* Quick cache recalculation is also used in delete operator,
765 * which could merge lines which are adjacent to current selection lines
766 * expand recalculate area to this lines. */
767 if (drawcache->valid_head > 0) {
768 drawcache->valid_head--;
769 }
770 if (drawcache->valid_tail > 0) {
771 drawcache->valid_tail--;
772 }
773 }
774 else {
775 drawcache->valid_head = 0;
776 drawcache->valid_tail = 0;
777 }
778
779 drawcache->update = true;
780 }
781}
782
784{
785 DrawCache *drawcache = static_cast<DrawCache *>(st->runtime->drawcache);
786
787 if (drawcache) {
788 if (drawcache->line_height) {
789 MEM_freeN(drawcache->line_height);
790 }
791
792 MEM_freeN(drawcache);
793 }
794}
795
797
798/* -------------------------------------------------------------------- */
801
803static int space_text_get_visible_lines_no(const SpaceText *st, int lineno)
804{
805 const DrawCache *drawcache = static_cast<const DrawCache *>(st->runtime->drawcache);
806
807 return drawcache->line_height[lineno];
808}
809
810int space_text_get_visible_lines(const SpaceText *st, const ARegion *region, const char *str)
811{
812 int i, j, start, end, max, lines, chars;
813 char ch;
814
815 max = space_text_wrap_width(st, region);
816 lines = 1;
817 start = 0;
818 end = max;
819 for (i = 0, j = 0; str[j]; j += BLI_str_utf8_size_safe(str + j)) {
820 const int columns = BLI_str_utf8_char_width_safe(str + j); /* = 1 for tab. */
821
822 /* Mimic replacement of tabs. */
823 ch = str[j];
824 if (ch == '\t') {
825 chars = st->tabnumber - i % st->tabnumber;
826 ch = ' ';
827 }
828 else {
829 chars = 1;
830 }
831
832 while (chars--) {
833 if (i + columns - start > max) {
834 lines++;
835 start = std::min(end, i);
836 end += max;
837 }
838 else if (ELEM(ch, ' ', '-')) {
839 end = i + 1;
840 }
841
842 i += columns;
843 }
844 }
845
846 return lines;
847}
848
850 const ARegion *region,
851 const TextLine *from,
852 const TextLine *to)
853{
854 if (st->wordwrap) {
855 int ret = 0;
856 const TextLine *tmp = from;
857
858 /* Look forwards. */
859 while (tmp) {
860 if (tmp == to) {
861 return ret;
862 }
863 ret += space_text_get_visible_lines(st, region, tmp->line);
864 tmp = tmp->next;
865 }
866
867 return ret;
868 }
869 return txt_get_span(from, to);
870}
871
873{
874 DrawCache *drawcache;
875
876 space_text_update_drawcache(st, region);
877 drawcache = static_cast<DrawCache *>(st->runtime->drawcache);
878
879 return drawcache->total_lines;
880}
881
883
884/* -------------------------------------------------------------------- */
887
888static void calc_text_rcts(SpaceText *st, ARegion *region, rcti *r_scroll, rcti *r_back)
889{
890 int lhlstart, lhlend, ltexth, sell_off, curl_off;
891 short barheight, barstart, hlstart, hlend, blank_lines;
892 short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
893
894 pix_top_margin = (0.4 * U.widget_unit);
895 pix_bottom_margin = (0.4 * U.widget_unit);
896 pix_available = region->winy - pix_top_margin - pix_bottom_margin;
897 ltexth = space_text_get_total_lines(st, region);
898 blank_lines = st->runtime->viewlines / 2;
899
900 /* Nicer code: use scroll rect for entire bar. */
901 r_back->xmin = region->winx - (0.6 * U.widget_unit);
902 r_back->xmax = region->winx;
903 r_back->ymin = 0;
904 r_back->ymax = region->winy;
905
906 r_scroll->xmax = region->winx - (0.2 * U.widget_unit);
907 r_scroll->xmin = r_scroll->xmax - (0.4 * U.widget_unit);
908 r_scroll->ymin = pix_top_margin;
909 r_scroll->ymax = pix_available;
910
911 /* When re-sizing a 2D Viewport with the bar at the bottom to a greater height
912 * more blank lines will be added. */
913 if (ltexth + blank_lines < st->top + st->runtime->viewlines) {
914 blank_lines = st->top + st->runtime->viewlines - ltexth;
915 }
916
917 ltexth += blank_lines;
918
919 barheight = (ltexth > 0) ? (st->runtime->viewlines * pix_available) / ltexth : 0;
920 pix_bardiff = 0;
921 if (barheight < 20) {
922 pix_bardiff = 20 - barheight; /* Take into account the now non-linear sizing of the bar. */
923 barheight = 20;
924 }
925 barstart = (ltexth > 0) ? ((pix_available - pix_bardiff) * st->top) / ltexth : 0;
926
927 st->runtime->scroll_region_handle = *r_scroll;
928 st->runtime->scroll_region_handle.ymax -= barstart;
929 st->runtime->scroll_region_handle.ymin = st->runtime->scroll_region_handle.ymax - barheight;
930
931 CLAMP(st->runtime->scroll_region_handle.ymin, pix_bottom_margin, region->winy - pix_top_margin);
932 CLAMP(st->runtime->scroll_region_handle.ymax, pix_bottom_margin, region->winy - pix_top_margin);
933
934 st->runtime->scroll_px_per_line = (pix_available > 0) ? float(ltexth) / pix_available : 0;
935 st->runtime->scroll_px_per_line = std::max(st->runtime->scroll_px_per_line, 0.1f);
936
937 curl_off = space_text_get_span_wrap(
938 st, region, static_cast<TextLine *>(st->text->lines.first), st->text->curl);
939 sell_off = space_text_get_span_wrap(
940 st, region, static_cast<TextLine *>(st->text->lines.first), st->text->sell);
941 lhlstart = std::min(curl_off, sell_off);
942 lhlend = std::max(curl_off, sell_off);
943
944 if (ltexth > 0) {
945 hlstart = (lhlstart * pix_available) / ltexth;
946 hlend = (lhlend * pix_available) / ltexth;
947
948 /* The scroll-bar is non-linear sized. */
949 if (pix_bardiff > 0) {
950 /* The start of the highlight is in the current viewport. */
951 if (st->runtime->viewlines && lhlstart >= st->top &&
952 lhlstart <= st->top + st->runtime->viewlines)
953 {
954 /* Speed the progression of the start of the highlight through the scroll-bar. */
955 hlstart = (((pix_available - pix_bardiff) * lhlstart) / ltexth) +
956 (pix_bardiff * (lhlstart - st->top) / st->runtime->viewlines);
957 }
958 else if (lhlstart > st->top + st->runtime->viewlines && hlstart < barstart + barheight &&
959 hlstart > barstart)
960 {
961 /* Push `hlstart` down. */
962 hlstart = barstart + barheight;
963 }
964 else if (lhlend > st->top && lhlstart < st->top && hlstart > barstart) {
965 /* Fill out start. */
966 hlstart = barstart;
967 }
968
969 if (hlend <= hlstart) {
970 hlend = hlstart + 2;
971 }
972
973 /* The end of the highlight is in the current viewport. */
974 if (st->runtime->viewlines && lhlend >= st->top &&
975 lhlend <= st->top + st->runtime->viewlines)
976 {
977 /* Speed the progression of the end of the highlight through the scroll-bar. */
978 hlend = (((pix_available - pix_bardiff) * lhlend) / ltexth) +
979 (pix_bardiff * (lhlend - st->top) / st->runtime->viewlines);
980 }
981 else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
982 /* Push `hlend` up. */
983 hlend = barstart;
984 }
985 else if (lhlend > st->top + st->runtime->viewlines &&
986 lhlstart < st->top + st->runtime->viewlines && hlend < barstart + barheight)
987 {
988 /* Fill out end. */
989 hlend = barstart + barheight;
990 }
991
992 if (hlend <= hlstart) {
993 hlstart = hlend - 2;
994 }
995 }
996 }
997 else {
998 hlstart = 0;
999 hlend = 0;
1000 }
1001
1002 if (hlend - hlstart < 2) {
1003 hlend = hlstart + 2;
1004 }
1005
1006 st->runtime->scroll_region_select = *r_scroll;
1007 st->runtime->scroll_region_select.ymax = region->winy - pix_top_margin - hlstart;
1008 st->runtime->scroll_region_select.ymin = region->winy - pix_top_margin - hlend;
1009
1010 CLAMP(st->runtime->scroll_region_select.ymin, pix_bottom_margin, region->winy - pix_top_margin);
1011 CLAMP(st->runtime->scroll_region_select.ymax, pix_bottom_margin, region->winy - pix_top_margin);
1012}
1013
1014static void draw_textscroll(const SpaceText *st, const rcti *scroll, const rcti *back)
1015{
1016 bTheme *btheme = UI_GetTheme();
1017 uiWidgetColors wcol = btheme->tui.wcol_scroll;
1018 float col[4];
1019 float rad;
1020
1021 /* Background so highlights don't go behind the scroll-bar. */
1023 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1026 immRectf(pos, back->xmin, back->ymin, back->xmax, back->ymax);
1028
1030 scroll,
1031 &st->runtime->scroll_region_handle,
1033
1035 rad = 0.4f * min_ii(BLI_rcti_size_x(&st->runtime->scroll_region_select),
1036 BLI_rcti_size_y(&st->runtime->scroll_region_select));
1038 col[3] = 0.18f;
1039
1040 rctf rect;
1041 rect.xmin = st->runtime->scroll_region_select.xmin + 1;
1042 rect.xmax = st->runtime->scroll_region_select.xmax - 1;
1043 rect.ymin = st->runtime->scroll_region_select.ymin;
1044 rect.ymax = st->runtime->scroll_region_select.ymax;
1045 UI_draw_roundbox_aa(&rect, true, rad, col);
1046}
1047
1049
1050/* -------------------------------------------------------------------- */
1053
1054static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc, ARegion *region)
1055{
1056 SuggItem *item, *first, *last, *sel;
1057 char str[SUGG_LIST_WIDTH * BLI_UTF8_MAX + 1];
1058 int offl, offc, vcurl, vcurc;
1059 int w, boxw = 0, boxh, i, x, y, *top;
1060 const int lheight = TXT_LINE_HEIGHT(st);
1061 const int margin_x = 2;
1062
1063 if (!st->text) {
1064 return;
1065 }
1066 if (!texttool_text_is_active(st->text)) {
1067 return;
1068 }
1069
1070 first = texttool_suggest_first();
1071 last = texttool_suggest_last();
1072
1073 if (!first || !last) {
1074 return;
1075 }
1076
1080
1081 space_text_wrap_offset(st, region, st->text->curl, st->text->curc, &offl, &offc);
1082 vcurl = txt_get_span(static_cast<TextLine *>(st->text->lines.first), st->text->curl) - st->top +
1083 offl;
1084 vcurc = space_text_get_char_pos(st, st->text->curl->line, st->text->curc) - st->left + offc;
1085
1086 x = TXT_BODY_LEFT(st) + (vcurc * st->runtime->cwidth_px);
1087 y = region->winy - (vcurl + 1) * lheight - 2;
1088
1089 /* Offset back so the start of the text lines up with the suggestions,
1090 * not essential but makes suggestions easier to follow. */
1091 x -= st->runtime->cwidth_px *
1092 (st->text->curc - text_find_identifier_start(st->text->curl->line, st->text->curc));
1093
1094 boxw = SUGG_LIST_WIDTH * st->runtime->cwidth_px + 20;
1095 boxh = SUGG_LIST_SIZE * lheight + 8;
1096
1097 if (x + boxw > region->winx) {
1098 x = std::max(0, region->winx - boxw);
1099 }
1100
1101 /* Not needed but stands out nicer. */
1102 {
1103 rctf rect;
1104 rect.xmin = x;
1105 rect.xmax = x + boxw;
1106 rect.ymin = y - boxh;
1107 rect.ymax = y;
1108 ui_draw_dropshadow(&rect, 0.0f, 8.0f, 1.0f, 0.5f);
1109 }
1110
1112 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1114
1116 immRectf(pos, x - 1, y + 1, x + boxw + 1, y - boxh - 1);
1118 immRectf(pos, x, y, x + boxw, y - boxh);
1119
1121
1122 /* Set the top `item` of the visible list. */
1123 for (i = 0, item = first; i < *top && item->next; i++, item = item->next) {
1124 /* Pass. */
1125 }
1126
1127 for (i = 0; i < SUGG_LIST_SIZE && item; i++, item = item->next) {
1128 int len = txt_utf8_forward_columns(item->name, SUGG_LIST_WIDTH, nullptr) - item->name;
1129
1130 y -= lheight;
1131
1132 BLI_strncpy_utf8(str, item->name, len + 1);
1133
1134 w = st->runtime->cwidth_px * space_text_get_char_pos(st, str, len);
1135
1136 if (item == sel) {
1138 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1140
1142 immRectf(posi, x + margin_x, y - 3, x + margin_x + w, y + lheight - 3);
1143
1145 }
1146
1147 format_draw_color(tdc, item->type);
1148 space_text_draw(st, tdc, str, 0, 0, x + margin_x, y - 1, nullptr);
1149
1150 if (item == last) {
1151 break;
1152 }
1153 }
1154}
1155
1157
1158/* -------------------------------------------------------------------- */
1161
1162static void draw_text_decoration(SpaceText *st, ARegion *region)
1163{
1164 Text *text = st->text;
1165 int vcurl, vcurc, vsell, vselc;
1166 bool hidden = false;
1167 int offl, offc;
1168 const int lheight = TXT_LINE_HEIGHT(st);
1169
1170 /* Convert to view space character coordinates to determine if cursor is hidden. */
1171 space_text_wrap_offset(st, region, text->sell, text->selc, &offl, &offc);
1172 vsell = txt_get_span(static_cast<TextLine *>(text->lines.first), text->sell) - st->top + offl;
1173 vselc = space_text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1174
1175 if (vselc < 0) {
1176 vselc = 0;
1177 hidden = true;
1178 }
1179
1180 if (text->curl == text->sell && text->curc == text->selc && !st->line_hlight && hidden) {
1181 /* Nothing to draw here. */
1182 return;
1183 }
1184
1186 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1188
1189 /* Draw the selection. */
1190 if (text->curl != text->sell || text->curc != text->selc) {
1191 /* Convert all to view space character coordinates. */
1192 space_text_wrap_offset(st, region, text->curl, text->curc, &offl, &offc);
1193 vcurl = txt_get_span(static_cast<TextLine *>(text->lines.first), text->curl) - st->top + offl;
1194 vcurc = space_text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1195
1196 vcurc = std::max(vcurc, 0);
1197
1199
1200 int x = TXT_BODY_LEFT(st);
1201 int y = region->winy;
1202 if (st->flags & ST_SCROLL_SELECT) {
1203 y += st->runtime->scroll_ofs_px[1];
1204 }
1205
1206 if (vcurl == vsell) {
1207 y -= vcurl * lheight;
1208
1209 if (vcurc < vselc) {
1210 immRectf(pos,
1211 x + vcurc * st->runtime->cwidth_px,
1212 y,
1213 x + vselc * st->runtime->cwidth_px,
1214 y - lheight);
1215 }
1216 else {
1217 immRectf(pos,
1218 x + vselc * st->runtime->cwidth_px,
1219 y,
1220 x + vcurc * st->runtime->cwidth_px,
1221 y - lheight);
1222 }
1223 }
1224 else {
1225 int froml, fromc, tol, toc;
1226
1227 if (vcurl < vsell) {
1228 froml = vcurl;
1229 tol = vsell;
1230 fromc = vcurc;
1231 toc = vselc;
1232 }
1233 else {
1234 froml = vsell;
1235 tol = vcurl;
1236 fromc = vselc;
1237 toc = vcurc;
1238 }
1239
1240 y -= froml * lheight;
1241
1242 immRectf(
1243 pos, x + fromc * st->runtime->cwidth_px - U.pixelsize, y, region->winx, y - lheight);
1244 y -= lheight;
1245
1246 for (int i = froml + 1; i < tol; i++) {
1247 immRectf(pos, x - U.pixelsize, y, region->winx, y - lheight);
1248 y -= lheight;
1249 }
1250
1251 if (x + toc * st->runtime->cwidth_px > x) {
1252 immRectf(pos, x - U.pixelsize, y, x + toc * st->runtime->cwidth_px, y - lheight);
1253 }
1254 y -= lheight;
1255 }
1256 /* Quiet warnings. */
1257 UNUSED_VARS(x, y);
1258 }
1259
1260 if (st->line_hlight) {
1261 int y1, y2;
1262
1263 if (st->wordwrap) {
1264 int visible_lines = space_text_get_visible_lines(st, region, text->sell->line);
1265
1266 space_text_wrap_offset_in_line(st, region, text->sell, text->selc, &offl, &offc);
1267
1268 y1 = region->winy - (vsell - offl) * lheight;
1269 if (st->flags & ST_SCROLL_SELECT) {
1270 y1 += st->runtime->scroll_ofs_px[1];
1271 }
1272 y2 = y1 - (lheight * visible_lines);
1273 }
1274 else {
1275 y1 = region->winy - vsell * lheight;
1276 if (st->flags & ST_SCROLL_SELECT) {
1277 y1 += st->runtime->scroll_ofs_px[1];
1278 }
1279 y2 = y1 - (lheight);
1280 }
1281
1282 if (!(y1 < 0 || y2 > region->winy)) { /* Check we need to draw. */
1283 float highlight_color[4];
1284 UI_GetThemeColor4fv(TH_TEXT, highlight_color);
1285 highlight_color[3] = 0.1f;
1286 immUniformColor4fv(highlight_color);
1288 immRectf(pos, 0, y1, region->winx, y2);
1290 }
1291 }
1292
1293 if (!hidden) {
1294 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge). */
1295 int x = TXT_BODY_LEFT(st) + (vselc * st->runtime->cwidth_px);
1296 int y = region->winy - vsell * lheight;
1297 if (st->flags & ST_SCROLL_SELECT) {
1298 y += st->runtime->scroll_ofs_px[1];
1299 }
1300
1302
1303 if (st->overwrite) {
1304 char ch = text->sell->line[text->selc];
1305
1306 y += TXT_LINE_SPACING(st);
1307 int w = st->runtime->cwidth_px;
1308 if (ch == '\t') {
1309 w *= st->tabnumber - (vselc + st->left) % st->tabnumber;
1310 }
1311
1312 immRectf(
1313 pos, x, y - lheight - U.pixelsize, x + w + U.pixelsize, y - lheight - (3 * U.pixelsize));
1314 }
1315 else {
1316 immRectf(pos, x - U.pixelsize, y, x + U.pixelsize, y - lheight);
1317 }
1318 }
1319
1321}
1322
1324
1325/* -------------------------------------------------------------------- */
1328
1329static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegion *region)
1330{
1331 TextLine *startl, *endl, *linep;
1332 Text *text = st->text;
1333 int b, fc, find, stack, viewc, viewl, offl, offc, x, y;
1334 int startc, endc, c;
1335
1336 char ch;
1337
1338 /* Syntax_highlight must be on or else the format string will be null. */
1339 if (!text->curl || !tdc->syntax_highlight) {
1340 return;
1341 }
1342
1343 startl = text->curl;
1344 startc = text->curc;
1345 b = text_check_bracket(startl->line[startc]);
1346 if (b == 0 && startc > 0) {
1347 b = text_check_bracket(startl->line[--startc]);
1348 }
1349 if (b == 0) {
1350 return;
1351 }
1352
1353 linep = startl;
1354 c = startc;
1355 fc = BLI_str_utf8_offset_to_index(linep->line, linep->len, startc);
1356 endl = nullptr;
1357 endc = -1;
1358 find = -b;
1359 stack = 0;
1360
1361 /* Don't highlight brackets if syntax HL is off or bracket in string or comment. */
1362 if (!linep->format || linep->format[fc] == FMT_TYPE_STRING ||
1363 linep->format[fc] == FMT_TYPE_COMMENT)
1364 {
1365 return;
1366 }
1367
1368 if (b > 0) {
1369 /* Opening bracket, search forward for close. */
1370 fc++;
1371 c += BLI_str_utf8_size_safe(linep->line + c);
1372 while (linep) {
1373 while (c < linep->len) {
1374 if (linep->format && linep->format[fc] != FMT_TYPE_STRING &&
1375 linep->format[fc] != FMT_TYPE_COMMENT)
1376 {
1377 b = text_check_bracket(linep->line[c]);
1378 if (b == find) {
1379 if (stack == 0) {
1380 endl = linep;
1381 endc = c;
1382 break;
1383 }
1384 stack--;
1385 }
1386 else if (b == -find) {
1387 stack++;
1388 }
1389 }
1390 fc++;
1391 c += BLI_str_utf8_size_safe(linep->line + c);
1392 }
1393 if (endl) {
1394 break;
1395 }
1396 linep = linep->next;
1397 c = 0;
1398 fc = 0;
1399 }
1400 }
1401 else {
1402 /* Closing bracket, search backward for open. */
1403 fc--;
1404 if (c > 0) {
1405 c -= linep->line + c - BLI_str_find_prev_char_utf8(linep->line + c, linep->line);
1406 }
1407 while (linep) {
1408 while (fc >= 0) {
1409 if (linep->format && linep->format[fc] != FMT_TYPE_STRING &&
1410 linep->format[fc] != FMT_TYPE_COMMENT)
1411 {
1412 b = text_check_bracket(linep->line[c]);
1413 if (b == find) {
1414 if (stack == 0) {
1415 endl = linep;
1416 endc = c;
1417 break;
1418 }
1419 stack--;
1420 }
1421 else if (b == -find) {
1422 stack++;
1423 }
1424 }
1425 fc--;
1426 if (c > 0) {
1427 c -= linep->line + c - BLI_str_find_prev_char_utf8(linep->line + c, linep->line);
1428 }
1429 }
1430 if (endl) {
1431 break;
1432 }
1433 linep = linep->prev;
1434 if (linep) {
1435 if (linep->format) {
1436 fc = strlen(linep->format) - 1;
1437 }
1438 else {
1439 fc = -1;
1440 }
1441 if (linep->len) {
1442 c = BLI_str_find_prev_char_utf8(linep->line + linep->len, linep->line) - linep->line;
1443 }
1444 else {
1445 fc = -1;
1446 }
1447 }
1448 }
1449 }
1450
1451 if (!endl || endc == -1) {
1452 return;
1453 }
1454
1456 x = TXT_BODY_LEFT(st);
1457 y = region->winy - st->runtime->lheight_px;
1458 if (st->flags & ST_SCROLL_SELECT) {
1459 y += st->runtime->scroll_ofs_px[1];
1460 }
1461
1462 /* Draw opening bracket. */
1463 ch = startl->line[startc];
1464 space_text_wrap_offset(st, region, startl, startc, &offl, &offc);
1465 viewc = space_text_get_char_pos(st, startl->line, startc) - st->left + offc;
1466
1467 if (viewc >= 0) {
1468 viewl = txt_get_span(static_cast<TextLine *>(text->lines.first), startl) - st->top + offl;
1469
1471 tdc, x + viewc * st->runtime->cwidth_px, y - viewl * TXT_LINE_HEIGHT(st), ch);
1473 tdc, x + viewc * st->runtime->cwidth_px + 1, y - viewl * TXT_LINE_HEIGHT(st), ch);
1474 }
1475
1476 /* Draw closing bracket. */
1477 ch = endl->line[endc];
1478 space_text_wrap_offset(st, region, endl, endc, &offl, &offc);
1479 viewc = space_text_get_char_pos(st, endl->line, endc) - st->left + offc;
1480
1481 if (viewc >= 0) {
1482 viewl = txt_get_span(static_cast<TextLine *>(text->lines.first), endl) - st->top + offl;
1483
1485 tdc, x + viewc * st->runtime->cwidth_px, y - viewl * TXT_LINE_HEIGHT(st), ch);
1487 tdc, x + viewc * st->runtime->cwidth_px + 1, y - viewl * TXT_LINE_HEIGHT(st), ch);
1488 }
1489}
1490
1492
1493/* -------------------------------------------------------------------- */
1496
1498{
1499 TextDrawContext tdc = {0};
1500 Text *text = st->text;
1501 TextFormatType *tft;
1502 TextLine *tmp;
1503 rcti scroll, back;
1504 char linenr[12];
1505 int i, x, y, winx, linecount = 0, lineno = 0;
1506 int wraplinecount = 0, wrap_skip = 0;
1507 int margin_column_x;
1508
1509 /* If no text, nothing to do. */
1510 if (!text) {
1511 return;
1512 }
1513
1514 /* DPI controlled line height and font size. */
1515 st->runtime->lheight_px = (U.widget_unit * st->lheight) / 20;
1516
1517 /* Don't draw lines below this. */
1518 const int clip_min_y = -(st->runtime->lheight_px - 1);
1519
1520 st->runtime->viewlines = (st->runtime->lheight_px) ?
1521 int(region->winy - clip_min_y) / TXT_LINE_HEIGHT(st) :
1522 0;
1523
1525
1526 space_text_update_drawcache(st, region);
1527
1528 /* Make sure all the positional pointers exist. */
1529 if (!text->curl || !text->sell || !text->lines.first || !text->lines.last) {
1530 txt_clean_text(text);
1531 }
1532
1533 /* Update rectangles for scroll. */
1534 calc_text_rcts(st, region, &scroll, &back); /* Scroll will hold the entire bar size. */
1535
1536 /* Update syntax formatting if needed. */
1537 tft = ED_text_format_get(text);
1538 tmp = static_cast<TextLine *>(text->lines.first);
1539 lineno = 0;
1540 for (i = 0; i < st->top && tmp; i++) {
1541 if (tdc.syntax_highlight && !tmp->format) {
1542 tft->format_line(st, tmp, false);
1543 }
1544
1545 if (st->wordwrap) {
1546 int lines = space_text_get_visible_lines_no(st, lineno);
1547
1548 if (wraplinecount + lines > st->top) {
1549 wrap_skip = st->top - wraplinecount;
1550 break;
1551 }
1552
1553 wraplinecount += lines;
1554 tmp = tmp->next;
1555 linecount++;
1556 }
1557 else {
1558 tmp = tmp->next;
1559 linecount++;
1560 }
1561
1562 lineno++;
1563 }
1564
1565 text_font_begin(&tdc);
1566
1567 tdc.cwidth_px = max_ii(int(BLF_fixed_width(tdc.font_id)), 1);
1568 st->runtime->cwidth_px = tdc.cwidth_px;
1569
1570 /* Draw line numbers background. */
1571 if (st->showlinenrs) {
1573 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1576 immRectf(pos, 0, 0, TXT_NUMCOL_WIDTH(st), region->winy);
1578 }
1579 else {
1580 st->runtime->line_number_display_digits = 0; /* Not used. */
1581 }
1582
1583 x = TXT_BODY_LEFT(st);
1584 y = region->winy - st->runtime->lheight_px;
1585 int viewlines = st->runtime->viewlines;
1586 if (st->flags & ST_SCROLL_SELECT) {
1587 y += st->runtime->scroll_ofs_px[1];
1588 viewlines += 1;
1589 }
1590
1591 winx = region->winx - TXT_SCROLL_WIDTH;
1592
1593 /* Draw cursor, margin, selection and highlight. */
1594 draw_text_decoration(st, region);
1595
1596 /* Draw the text. */
1598
1599 for (i = 0; y > clip_min_y && i < viewlines && tmp; i++, tmp = tmp->next) {
1600 if (tdc.syntax_highlight && !tmp->format) {
1601 tft->format_line(st, tmp, false);
1602 }
1603
1604 if (st->showlinenrs && !wrap_skip) {
1605 /* Draw line number. */
1606 UI_FontThemeColor(tdc.font_id, (tmp == text->sell) ? TH_HILITE : TH_LINENUMBERS);
1607 SNPRINTF_UTF8(linenr, "%*d", st->runtime->line_number_display_digits, i + linecount + 1);
1608 text_font_draw(&tdc, TXT_NUMCOL_PAD * st->runtime->cwidth_px, y, linenr);
1609 /* Change back to text color. */
1611 }
1612
1613 if (st->wordwrap) {
1614 /* Draw word wrapped text. */
1615 int lines = space_text_draw_wrapped(
1616 st, &tdc, tmp->line, x, y, winx - x, tmp->format, wrap_skip);
1617 y -= lines * TXT_LINE_HEIGHT(st);
1618 }
1619 else {
1620 /* Draw unwrapped text. */
1622 st, &tdc, tmp->line, st->left, region->winx / st->runtime->cwidth_px, x, y, tmp->format);
1623 y -= TXT_LINE_HEIGHT(st);
1624 }
1625
1626 wrap_skip = 0;
1627 }
1628
1629 if (st->flags & ST_SHOW_MARGIN) {
1630 margin_column_x = x + st->runtime->cwidth_px * (st->margin_column - st->left);
1631 if (margin_column_x >= x) {
1633 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1635 float margin_color[4];
1636 UI_GetThemeColor4fv(TH_TEXT, margin_color);
1637 margin_color[3] = 0.2f;
1638 immUniformColor4fv(margin_color);
1640 immRectf(pos, margin_column_x, 0, margin_column_x + U.pixelsize, region->winy);
1643 }
1644 }
1645
1646 /* Draw other stuff. */
1647 draw_brackets(st, &tdc, region);
1648 draw_textscroll(st, &scroll, &back);
1649 /* Draw_documentation(st, region); - No longer supported. */
1650 draw_suggestion_list(st, &tdc, region);
1651
1652 text_font_end(&tdc);
1653}
1654
1656
1657/* -------------------------------------------------------------------- */
1660
1662{
1663 TextDrawContext tdc = {0};
1664
1666
1667 text_font_begin(&tdc);
1668 st->runtime->cwidth_px = BLF_fixed_width(tdc.font_id);
1669 st->runtime->cwidth_px = std::max(st->runtime->cwidth_px, 1);
1670 text_font_end(&tdc);
1671}
1672
1674{
1676 if (area) {
1677 SpaceText *st = static_cast<SpaceText *>(area->spacedata.first);
1679 st->text = text;
1680 if (region) {
1681 ED_space_text_scroll_to_cursor(st, region, true);
1682 }
1684 return true;
1685 }
1686
1687 return false;
1688}
1689
1690void ED_space_text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center)
1691{
1692 Text *text;
1693 int i, x, winx = region->winx;
1694
1695 if (ELEM(nullptr, st, st->text, st->text->curl)) {
1696 return;
1697 }
1698
1699 text = st->text;
1700
1702
1703 i = txt_get_span(static_cast<TextLine *>(text->lines.first), text->sell);
1704 if (st->wordwrap) {
1705 int offl, offc;
1706 space_text_wrap_offset(st, region, text->sell, text->selc, &offl, &offc);
1707 i += offl;
1708 }
1709
1710 if (center) {
1711 if (st->top + st->runtime->viewlines <= i || st->top > i) {
1712 st->top = i - st->runtime->viewlines / 2;
1713 }
1714 }
1715 else {
1716 if (st->top + st->runtime->viewlines <= i) {
1717 st->top = i - (st->runtime->viewlines - 1);
1718 }
1719 else if (st->top > i) {
1720 st->top = i;
1721 }
1722 }
1723
1724 if (st->wordwrap) {
1725 st->left = 0;
1726 }
1727 else {
1728 x = st->runtime->cwidth_px *
1729 (space_text_get_char_pos(st, text->sell->line, text->selc) - st->left);
1730 winx -= TXT_BODY_LEFT(st) + TXT_SCROLL_WIDTH;
1731
1732 if (center) {
1733 if (x <= 0 || x > winx) {
1734 st->left += (x - winx / 2) / st->runtime->cwidth_px;
1735 }
1736 }
1737 else {
1738 if (x <= 0) {
1739 st->left += ((x + 1) / st->runtime->cwidth_px) - 1;
1740 }
1741 else if (x > winx) {
1742 st->left += ((x - (winx + 1)) / st->runtime->cwidth_px) + 1;
1743 }
1744 }
1745 }
1746
1747 st->top = std::max(st->top, 0);
1748 st->left = std::max(st->left, 0);
1749
1750 st->runtime->scroll_ofs_px[0] = 0;
1751 st->runtime->scroll_ofs_px[1] = 0;
1752}
1753
1755{
1756 ARegion *region;
1757
1758 if (ELEM(nullptr, st, st->text, st->text->curl)) {
1759 return;
1760 }
1761
1763
1764 if (region) {
1765 ED_space_text_scroll_to_cursor(st, region, center);
1766 }
1767}
1768
1776
1778 const ARegion *region,
1779 const int cursor_co[2],
1780 int r_pixel_co[2])
1781{
1782 Text *text = st->text;
1783
1784 if (!text) {
1785 return false;
1786 }
1787 TextLine *line = static_cast<TextLine *>(BLI_findlink(&text->lines, cursor_co[0]));
1788 if (!line) {
1789 return false;
1790 }
1791
1792 /* Convert character index to char byte offset. */
1793 const int char_ofs = BLI_str_utf8_offset_from_index(line->line, line->len, cursor_co[1]);
1794 if (char_ofs < 0 || char_ofs > line->len) {
1795 return false;
1796 }
1797
1798 /* All values are in-range, calculate the pixel offset.
1799 * Note that !126720 provides a useful interactive test-case for this logic. */
1800 const int lheight = TXT_LINE_HEIGHT(st);
1801 const int linenr_offset = TXT_BODY_LEFT(st);
1802 /* Handle tabs as well! */
1803 const int char_pos = space_text_get_char_pos(st, line->line, char_ofs);
1804
1805 int offl, offc;
1806 space_text_wrap_offset(st, region, line, char_ofs, &offl, &offc);
1807 r_pixel_co[0] = (char_pos + offc - st->left) * st->runtime->cwidth_px + linenr_offset;
1808 r_pixel_co[1] = (region->winy - ((cursor_co[0] + offl - st->top) * lheight)) - lheight;
1809 return true;
1810}
1811
SpaceText * CTX_wm_space_text(const bContext *C)
bScreen * CTX_wm_screen(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
ScrArea * BKE_screen_find_big_area(const bScreen *screen, int spacetype, short min)
Definition screen.cc:944
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:846
void txt_clean_text(struct Text *text)
Definition text.cc:634
int text_check_bracket(char ch)
Definition text.cc:2273
int txt_get_span(const struct TextLine *from, const struct TextLine *to)
int text_find_identifier_start(const char *str, int i)
Definition text.cc:2380
short texttool_text_is_active(struct Text *text)
int * texttool_suggest_top(void)
SuggItem * texttool_suggest_first(void)
SuggItem * texttool_suggest_last(void)
SuggItem * texttool_suggest_selected(void)
void BLF_size(int fontid, float size)
Definition blf.cc:443
float BLF_fixed_width(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:815
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:440
int blf_mono_font
Definition blf.cc:48
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
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE int integer_digits_i(int i)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define BLI_UTF8_MAX
int BLI_str_utf8_offset_from_index(const char *str, size_t str_len, int index_target) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define SNPRINTF_UTF8(dst, format,...)
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
int BLI_str_utf8_char_width_safe(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
size_t size_t BLI_strnlen_utf8(const char *strc, size_t strc_maxlen) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
int BLI_str_utf8_size_safe(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_str_utf8_offset_to_index(const char *str, size_t str_len, int offset_target) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
const char * BLI_str_find_prev_char_utf8(const char *p, const char *str_start) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1
int BLI_str_utf8_offset_to_column(const char *str, size_t str_len, int offset_target) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
unsigned int uint
#define CLAMP(a, b, c)
#define UNUSED_VARS(...)
#define STREQLEN(a, b, n)
#define ELEM(...)
#define MAX_ID_NAME
Definition DNA_ID.h:373
@ RGN_TYPE_WINDOW
@ SPACE_TEXT
@ ST_SCROLL_SELECT
@ ST_SHOW_MARGIN
bool ED_text_is_syntax_highlight_supported(Text *text)
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immUniformThemeColor(int color_id)
void immUniformThemeColorShade(int color_id, int offset)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
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.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
#define C
Definition RandGen.cpp:29
void ui_draw_dropshadow(const rctf *rct, float radius, float width, float aspect, float alpha)
@ UI_SCROLL_PRESSED
void UI_draw_roundbox_corner_set(int type)
@ UI_CNR_ALL
void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state)
void UI_draw_roundbox_aa(const rctf *rect, bool filled, float rad, const float color[4])
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_SYNTAX_B
@ TH_GRID
@ TH_BACK
@ TH_SYNTAX_C
@ TH_SYNTAX_R
@ TH_SYNTAX_S
@ TH_SHADE2
@ TH_LINENUMBERS
@ TH_SHADE1
@ TH_SYNTAX_D
@ TH_HILITE
@ TH_SYNTAX_V
@ TH_SYNTAX_L
@ TH_SYNTAX_N
@ TH_TEXT
void UI_GetThemeColor4fv(int colorid, float col[4])
bTheme * UI_GetTheme()
void UI_FontThemeColor(int fontid, int colorid)
#define ND_CURSOR
Definition WM_types.hh:490
#define NC_TEXT
Definition WM_types.hh:386
#define U
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
#define str(s)
uint pos
uint col
uint top
#define in
uint padding(uint offset, uint alignment)
format
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
float wrap(float value, float max, float min)
Definition node_math.h:103
return ret
short lheight
Definition text_draw.cc:593
char cwidth_px
Definition text_draw.cc:594
int * line_height
Definition text_draw.cc:588
int valid_head
Definition text_draw.cc:599
int valid_tail
Definition text_draw.cc:599
bool update
Definition text_draw.cc:598
char text_id[MAX_ID_NAME - 2]
Definition text_draw.cc:595
int showlinenrs
Definition text_draw.cc:592
int total_lines
Definition text_draw.cc:589
int tabnumber
Definition text_draw.cc:592
char name[258]
Definition DNA_ID.h:432
void * last
void * first
ListBase spacedata
SpaceText_Runtime * runtime
short margin_column
struct Text * text
struct SuggItem * next
bool syntax_highlight
Definition text_draw.cc:62
void(* format_line)(SpaceText *st, TextLine *line, bool do_next)
char * format
char * line
struct TextLine * next
ListBase lines
TextLine * curl
TextLine * sell
uiWidgetColors wcol_scroll
ThemeUI tui
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
void text_pop_suggest_list()
static int text_font_draw(const TextDrawContext *tdc, int x, int y, const char *str)
Definition text_draw.cc:80
int ED_space_text_visible_lines_get(const SpaceText *st)
Definition text_draw.cc:47
static int space_text_get_visible_lines_no(const SpaceText *st, int lineno)
Definition text_draw.cc:803
static int text_font_draw_character(const TextDrawContext *tdc, int x, int y, char c)
Definition text_draw.cc:90
linep
Definition text_draw.cc:229
static void space_text_draw_context_init(const SpaceText *st, TextDrawContext *tdc)
Definition text_draw.cc:65
void space_text_drawcache_tag_update(SpaceText *st, const bool full)
Definition text_draw.cc:734
void ED_space_text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center)
void space_text_free_caches(SpaceText *st)
Definition text_draw.cc:783
static int text_font_draw_character_utf8(const TextDrawContext *tdc, int x, int y, const char *c, const int c_len)
Definition text_draw.cc:99
static void draw_textscroll(const SpaceText *st, const rcti *scroll, const rcti *back)
cursin
Definition text_draw.cc:252
static void text_font_end(const TextDrawContext *)
Definition text_draw.cc:78
void draw_text_main(SpaceText *st, ARegion *region)
bool ED_text_activate_in_screen(bContext *C, Text *text)
void space_text_update_cursor_moved(bContext *C)
void space_text_update_character_width(SpaceText *st)
int space_text_get_visible_lines(const SpaceText *st, const ARegion *region, const char *str)
Definition text_draw.cc:810
static void text_font_begin(const TextDrawContext *tdc)
Definition text_draw.cc:73
void space_text_wrap_offset_in_line(const SpaceText *st, const ARegion *region, TextLine *linein, int cursin, int *offl, int *offc)
Definition text_draw.cc:313
static void format_draw_color(const TextDrawContext *tdc, char formatchar)
Definition text_draw.cc:127
static const char * txt_utf8_forward_columns(const char *str, int columns, int *padding)
Definition text_draw.cc:400
i
Definition text_draw.cc:230
static int space_text_draw_wrapped(const SpaceText *st, const TextDrawContext *tdc, const char *str, int x, int y, int w, const char *format, int skip)
Definition text_draw.cc:420
max
Definition text_draw.cc:251
bool ED_space_text_region_location_from_cursor(const SpaceText *st, const ARegion *region, const int cursor_co[2], int r_pixel_co[2])
static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegion *region)
int space_text_get_span_wrap(const SpaceText *st, const ARegion *region, const TextLine *from, const TextLine *to)
Definition text_draw.cc:849
static void calc_text_rcts(SpaceText *st, ARegion *region, rcti *r_scroll, rcti *r_back)
Definition text_draw.cc:888
int space_text_get_total_lines(SpaceText *st, const ARegion *region)
Definition text_draw.cc:872
static void draw_text_decoration(SpaceText *st, ARegion *region)
static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc, ARegion *region)
static void space_text_drawcache_init(SpaceText *st)
Definition text_draw.cc:602
int space_text_get_char_pos(const SpaceText *st, const char *line, int cur)
Definition text_draw.cc:385
static void space_text_update_drawcache(SpaceText *st, const ARegion *region)
Definition text_draw.cc:613
static void space_text_draw(const SpaceText *st, const TextDrawContext *tdc, char *str, int cshift, int maxwidth, int x, int y, const char *format)
Definition text_draw.cc:517
void space_text_scroll_to_cursor_with_area(SpaceText *st, ScrArea *area, const bool center)
int flatten_string(const SpaceText *st, FlattenString *fs, const char *in)
void flatten_string_free(FlattenString *fs)
TextFormatType * ED_text_format_get(Text *text)
@ FMT_TYPE_DIRECTIVE
@ FMT_TYPE_STRING
@ FMT_TYPE_COMMENT
@ FMT_TYPE_SPECIAL
@ FMT_TYPE_DEFAULT
@ FMT_TYPE_KEYWORD
@ FMT_TYPE_WHITESPACE
@ FMT_TYPE_NUMERAL
@ FMT_TYPE_RESERVED
@ FMT_TYPE_SYMBOL
#define SUGG_LIST_WIDTH
int space_text_wrap_width(const SpaceText *st, const ARegion *region)
#define TXT_LINE_SPACING(st)
#define TXT_LINE_HEIGHT(st)
#define TXT_NUMCOL_WIDTH(st)
void space_text_wrap_offset(const SpaceText *st, const ARegion *region, TextLine *linein, int cursin, int *offl, int *offc)
#define TXT_NUMCOL_PAD
#define SUGG_LIST_SIZE
#define TXT_BODY_LEFT(st)
#define TXT_SCROLL_WIDTH
uint len
void WM_event_add_notifier(const bContext *C, uint type, void *reference)