Blender V4.3
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
9#include <algorithm>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLF_api.hh"
14
15#include "BLI_blenlib.h"
16
17#include "DNA_screen_types.h"
18#include "DNA_space_types.h"
19#include "DNA_text_types.h"
20
21#include "BKE_context.hh"
22#include "BKE_screen.hh"
23#include "BKE_text.h"
25
26#include "ED_text.hh"
27
28#include "GPU_immediate.hh"
29#include "GPU_state.hh"
30
31#include "UI_interface.hh"
32#include "UI_resources.hh"
33
34#include "text_format.hh"
35#include "text_intern.hh"
36
37#include "WM_api.hh"
38#include "WM_types.hh"
39
40/* -------------------------------------------------------------------- */
45{
46 return st->runtime->viewlines;
47}
48
51/* -------------------------------------------------------------------- */
61
63{
65 tdc->cwidth_px = 0;
66 tdc->lheight_px = st->runtime->lheight_px;
68}
69
70static void text_font_begin(const TextDrawContext *tdc)
71{
72 BLF_size(tdc->font_id, float(tdc->lheight_px));
73}
74
75static void text_font_end(const TextDrawContext * /*tdc*/) {}
76
77static int text_font_draw(const TextDrawContext *tdc, int x, int y, const char *str)
78{
79 const char tab_columns = 1; /* Tab characters aren't used here. */
80 BLF_position(tdc->font_id, x, y, 0);
81 const int columns = BLF_draw_mono(
82 tdc->font_id, str, BLF_DRAW_STR_DUMMY_MAX, tdc->cwidth_px, tab_columns);
83
84 return tdc->cwidth_px * columns;
85}
86
87static int text_font_draw_character(const TextDrawContext *tdc, int x, int y, char c)
88{
89 const char tab_columns = 1;
90 BLF_position(tdc->font_id, x, y, 0);
91 BLF_draw_mono(tdc->font_id, &c, 1, tdc->cwidth_px, tab_columns);
92
93 return tdc->cwidth_px;
94}
95
97 const TextDrawContext *tdc, int x, int y, const char *c, const int c_len)
98{
100 const char tab_columns = 1; /* Tab characters aren't used here. */
101 BLF_position(tdc->font_id, x, y, 0);
102 const int columns = BLF_draw_mono(tdc->font_id, c, c_len, tdc->cwidth_px, tab_columns);
103
104 return tdc->cwidth_px * columns;
105}
106
107#if 0
108/* Formats every line of the current text */
109static void txt_format_text(SpaceText *st)
110{
111 TextLine *linep;
112
113 if (!st->text) {
114 return;
115 }
116
117 for (linep = st->text->lines.first; linep; linep = linep->next) {
118 txt_format_line(st, linep, 0);
119 }
120}
121#endif
122
123/* Sets the current drawing color based on the format character specified */
124static void format_draw_color(const TextDrawContext *tdc, char formatchar)
125{
126 switch (formatchar) {
128 break;
129 case FMT_TYPE_SYMBOL:
131 break;
132 case FMT_TYPE_COMMENT:
134 break;
135 case FMT_TYPE_NUMERAL:
137 break;
138 case FMT_TYPE_STRING:
140 break;
143 break;
144 case FMT_TYPE_SPECIAL:
146 break;
149 break;
150 case FMT_TYPE_KEYWORD:
152 break;
153 case FMT_TYPE_DEFAULT:
154 default:
156 break;
157 }
158}
159
162/* -------------------------------------------------------------------- */
195int space_text_wrap_width(const SpaceText *st, const ARegion *region)
196{
197 int winx = region->winx - TXT_SCROLL_WIDTH;
198 int x, max;
199
200 x = TXT_BODY_LEFT(st);
201 max = st->runtime->cwidth_px ? (winx - x) / st->runtime->cwidth_px : 0;
202 return max > 8 ? max : 8;
203}
204
206 const SpaceText *st, const ARegion *region, TextLine *linein, int cursin, int *offl, int *offc)
207{
208 Text *text;
209 TextLine *linep;
210 int i, j, start, end, max;
211 bool chop;
212 char ch;
213
214 *offl = *offc = 0;
215
216 if (!st->text) {
217 return;
218 }
219 if (!st->wordwrap) {
220 return;
221 }
222
223 text = st->text;
224
225 /* Move pointer to first visible line (top) */
226 linep = static_cast<TextLine *>(text->lines.first);
227 i = st->top;
228 while (i > 0 && linep) {
229 int lines = space_text_get_visible_lines(st, region, linep->line);
230
231 /* Line before top */
232 if (linep == linein) {
233 if (lines <= i) {
234 /* no visible part of line */
235 return;
236 }
237 }
238
239 if (i - lines < 0) {
240 break;
241 }
242
243 linep = linep->next;
244 (*offl) += lines - 1;
245 i -= lines;
246 }
247
248 max = space_text_wrap_width(st, region);
249 cursin = BLI_str_utf8_offset_to_column(linein->line, linein->len, cursin);
250
251 while (linep) {
252 start = 0;
253 end = max;
254 chop = true;
255 *offc = 0;
256 for (i = 0, j = 0; linep->line[j]; j += BLI_str_utf8_size_safe(linep->line + j)) {
257 int chars;
258 const int columns = BLI_str_utf8_char_width_safe(linep->line + j); /* = 1 for tab */
259
260 /* Mimic replacement of tabs */
261 ch = linep->line[j];
262 if (ch == '\t') {
263 chars = st->tabnumber - i % st->tabnumber;
264 if (linep == linein && i < cursin) {
265 cursin += chars - 1;
266 }
267 ch = ' ';
268 }
269 else {
270 chars = 1;
271 }
272
273 while (chars--) {
274 if (i + columns - start > max) {
275 end = std::min(end, i);
276
277 if (chop && linep == linein && i >= cursin) {
278 if (i == cursin) {
279 (*offl)++;
280 *offc -= end - start;
281 }
282
283 return;
284 }
285
286 (*offl)++;
287 *offc -= end - start;
288
289 start = end;
290 end += max;
291 chop = true;
292 }
293 else if (ELEM(ch, ' ', '-')) {
294 end = i + 1;
295 chop = false;
296 if (linep == linein && i >= cursin) {
297 return;
298 }
299 }
300 i += columns;
301 }
302 }
303 if (linep == linein) {
304 break;
305 }
306 linep = linep->next;
307 }
308}
309
311 const SpaceText *st, const ARegion *region, TextLine *linein, int cursin, int *offl, int *offc)
312{
313 int i, j, start, end, chars, max;
314 bool chop;
315 char ch;
316
317 *offl = *offc = 0;
318
319 if (!st->text) {
320 return;
321 }
322 if (!st->wordwrap) {
323 return;
324 }
325
326 max = space_text_wrap_width(st, region);
327
328 start = 0;
329 end = max;
330 chop = true;
331 *offc = 0;
332 cursin = BLI_str_utf8_offset_to_column(linein->line, linein->len, cursin);
333
334 for (i = 0, j = 0; linein->line[j]; j += BLI_str_utf8_size_safe(linein->line + j)) {
335 const int columns = BLI_str_utf8_char_width_safe(linein->line + j); /* = 1 for tab */
336
337 /* Mimic replacement of tabs */
338 ch = linein->line[j];
339 if (ch == '\t') {
340 chars = st->tabnumber - i % st->tabnumber;
341 if (i < cursin) {
342 cursin += chars - 1;
343 }
344 ch = ' ';
345 }
346 else {
347 chars = 1;
348 }
349
350 while (chars--) {
351 if (i + columns - start > max) {
352 end = std::min(end, i);
353
354 if (chop && i >= cursin) {
355 if (i == cursin) {
356 (*offl)++;
357 *offc -= end - start;
358 }
359
360 return;
361 }
362
363 (*offl)++;
364 *offc -= end - start;
365
366 start = end;
367 end += max;
368 chop = true;
369 }
370 else if (ELEM(ch, ' ', '-')) {
371 end = i + 1;
372 chop = false;
373 if (i >= cursin) {
374 return;
375 }
376 }
377 i += columns;
378 }
379 }
380}
381
382int space_text_get_char_pos(const SpaceText *st, const char *line, int cur)
383{
384 int a = 0, i;
385
386 for (i = 0; i < cur && line[i]; i += BLI_str_utf8_size_safe(line + i)) {
387 if (line[i] == '\t') {
388 a += st->tabnumber - a % st->tabnumber;
389 }
390 else {
391 a += BLI_str_utf8_char_width_safe(line + i);
392 }
393 }
394 return a;
395}
396
397static const char *txt_utf8_forward_columns(const char *str, int columns, int *padding)
398{
399 const char *p = str;
400 while (*p) {
401 const int col = BLI_str_utf8_char_width_safe(p);
402 if (columns - col < 0) {
403 break;
404 }
405 columns -= col;
407 if (columns == 0) {
408 break;
409 }
410 }
411 if (padding) {
412 *padding = *p ? columns : 0;
413 }
414 return p;
415}
416
417static int space_text_draw_wrapped(const SpaceText *st,
418 const TextDrawContext *tdc,
419 const char *str,
420 int x,
421 int y,
422 int w,
423 const char *format,
424 int skip)
425{
426 const bool use_syntax = (tdc->syntax_highlight && format);
427 FlattenString fs;
428 int basex, lines;
429 int i, wrap, end, max, columns, padding; /* column */
430 /* warning, only valid when 'use_syntax' is set */
431 int a, fstart, fpos; /* utf8 chars */
432 int mi, ma, mstart, mend; /* mem */
433 char fmt_prev = 0xff;
434 /* don't draw lines below this */
435 const int clip_min_y = -int(st->runtime->lheight_px - 1);
436
437 flatten_string(st, &fs, str);
438 str = fs.buf;
439 max = w / st->runtime->cwidth_px;
440 if (max < 8) {
441 max = 8;
442 }
443 basex = x;
444 lines = 1;
445
446 fpos = fstart = 0;
447 mstart = 0;
448 mend = txt_utf8_forward_columns(str, max, &padding) - str;
449 end = wrap = max - padding;
450
451 for (i = 0, mi = 0; str[mi]; i += columns, mi += BLI_str_utf8_size_safe(str + mi)) {
452 columns = BLI_str_utf8_char_width_safe(str + mi);
453 if (i + columns > end) {
454 /* skip hidden part of line */
455 if (skip) {
456 skip--;
457 if (use_syntax) {
458 /* currently fpos only used when formatting */
459 fpos += BLI_strnlen_utf8(str + mstart, mend - mstart);
460 }
461 fstart = fpos;
462 mstart = mend;
463 mend = txt_utf8_forward_columns(str + mend, max, &padding) - str;
464 end = (wrap += max - padding);
465 continue;
466 }
467
468 /* Draw the visible portion of text on the overshot line */
469 for (a = fstart, ma = mstart; ma < mend; a++) {
470 if (use_syntax) {
471 if (fmt_prev != format[a]) {
472 format_draw_color(tdc, fmt_prev = format[a]);
473 }
474 }
475 const int c_len = BLI_str_utf8_size_safe(str + ma);
476 x += text_font_draw_character_utf8(tdc, x, y, str + ma, c_len);
477 ma += c_len;
478 fpos++;
479 }
480 y -= TXT_LINE_HEIGHT(st);
481 x = basex;
482 lines++;
483 fstart = fpos;
484 mstart = mend;
485 mend = txt_utf8_forward_columns(str + mend, max, &padding) - str;
486 end = (wrap += max - padding);
487
488 if (y <= clip_min_y) {
489 break;
490 }
491 }
492 else if (ELEM(str[mi], ' ', '-')) {
493 wrap = i + 1;
494 mend = mi + 1;
495 }
496 }
497
498 /* Draw the remaining text */
499 for (a = fstart, ma = mstart; str[ma] && y > clip_min_y; a++) {
500 if (use_syntax) {
501 if (fmt_prev != format[a]) {
502 format_draw_color(tdc, fmt_prev = format[a]);
503 }
504 }
505
506 const int c_len = BLI_str_utf8_size_safe(str + ma);
507 x += text_font_draw_character_utf8(tdc, x, y, str + ma, c_len);
508 ma += c_len;
509 }
510
512
513 return lines;
514}
515
516static void space_text_draw(const SpaceText *st,
517 const TextDrawContext *tdc,
518 char *str,
519 int cshift,
520 int maxwidth,
521 int x,
522 int y,
523 const char *format)
524{
525 const bool use_syntax = (tdc->syntax_highlight && format);
526 FlattenString fs;
527 int n, w = 0, padding, amount = 0;
528 const char *in = nullptr;
529
530 for (n = flatten_string(st, &fs, str), str = fs.buf; n > 0; n--) {
531 const int columns = BLI_str_utf8_char_width_safe(str);
532 const int size = BLI_str_utf8_size_safe(str);
533
534 if (!in) {
535 if (w >= cshift) {
536 padding = w - cshift;
537 in = str;
538 }
539 else if (format) {
540 format++;
541 }
542 }
543 if (in) {
544 if (maxwidth && w + columns > cshift + maxwidth) {
545 break;
546 }
547 amount++;
548 }
549
550 w += columns;
551 str += size;
552 }
553 if (!in) {
555 return; /* String is shorter than shift or ends with a padding */
556 }
557
558 x += tdc->cwidth_px * padding;
559
560 if (use_syntax) {
561 int a, str_shift = 0;
562 char fmt_prev = 0xff;
563
564 for (a = 0; a < amount; a++) {
565 if (format[a] != fmt_prev) {
566 format_draw_color(tdc, fmt_prev = format[a]);
567 }
568 const int c_len = BLI_str_utf8_size_safe(in + str_shift);
569 x += text_font_draw_character_utf8(tdc, x, y, in + str_shift, c_len);
570 str_shift += c_len;
571 }
572 }
573 else {
574 text_font_draw(tdc, x, y, in);
575 }
576
578}
579
582/* -------------------------------------------------------------------- */
586struct DrawCache {
587 int *line_height;
588 int total_lines, nlines;
589
590 /* this is needed to check cache relevance */
591 int winx, wordwrap, showlinenrs, tabnumber;
592 short lheight;
593 char cwidth_px;
594 char text_id[MAX_ID_NAME];
595
597 bool update;
598 int valid_head, valid_tail; /* amount of unchanged lines */
599};
600
601static void space_text_drawcache_init(SpaceText *st)
602{
603 DrawCache *drawcache = static_cast<DrawCache *>(
604 MEM_callocN(sizeof(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) {
621 space_text_drawcache_init(st);
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);
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);
727
728 /* clear update flag */
729 drawcache->update = false;
730 drawcache->valid_head = 0;
731 drawcache->valid_tail = 0;
732}
733
734void space_text_drawcache_tag_update(SpaceText *st, const bool full)
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
798/* -------------------------------------------------------------------- */
802/* cache should be updated in caller */
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
872int space_text_get_total_lines(SpaceText *st, const ARegion *region)
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
884/* -------------------------------------------------------------------- */
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 if (st->runtime->scroll_px_per_line < 0.1f) {
936 st->runtime->scroll_px_per_line = 0.1f;
937 }
938
939 curl_off = space_text_get_span_wrap(
940 st, region, static_cast<TextLine *>(st->text->lines.first), st->text->curl);
941 sell_off = space_text_get_span_wrap(
942 st, region, static_cast<TextLine *>(st->text->lines.first), st->text->sell);
943 lhlstart = std::min(curl_off, sell_off);
944 lhlend = std::max(curl_off, sell_off);
945
946 if (ltexth > 0) {
947 hlstart = (lhlstart * pix_available) / ltexth;
948 hlend = (lhlend * pix_available) / ltexth;
949
950 /* The scroll-bar is non-linear sized. */
951 if (pix_bardiff > 0) {
952 /* the start of the highlight is in the current viewport */
953 if (st->runtime->viewlines && lhlstart >= st->top &&
954 lhlstart <= st->top + st->runtime->viewlines)
955 {
956 /* Speed the progression of the start of the highlight through the scroll-bar. */
957 hlstart = (((pix_available - pix_bardiff) * lhlstart) / ltexth) +
958 (pix_bardiff * (lhlstart - st->top) / st->runtime->viewlines);
959 }
960 else if (lhlstart > st->top + st->runtime->viewlines && hlstart < barstart + barheight &&
961 hlstart > barstart)
962 {
963 /* Push `hlstart` down. */
964 hlstart = barstart + barheight;
965 }
966 else if (lhlend > st->top && lhlstart < st->top && hlstart > barstart) {
967 /* Fill out start. */
968 hlstart = barstart;
969 }
970
971 if (hlend <= hlstart) {
972 hlend = hlstart + 2;
973 }
974
975 /* the end of the highlight is in the current viewport */
976 if (st->runtime->viewlines && lhlend >= st->top &&
977 lhlend <= st->top + st->runtime->viewlines)
978 {
979 /* Speed the progression of the end of the highlight through the scroll-bar. */
980 hlend = (((pix_available - pix_bardiff) * lhlend) / ltexth) +
981 (pix_bardiff * (lhlend - st->top) / st->runtime->viewlines);
982 }
983 else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
984 /* Push `hlend` up. */
985 hlend = barstart;
986 }
987 else if (lhlend > st->top + st->runtime->viewlines &&
988 lhlstart < st->top + st->runtime->viewlines && hlend < barstart + barheight)
989 {
990 /* Fill out end. */
991 hlend = barstart + barheight;
992 }
993
994 if (hlend <= hlstart) {
995 hlstart = hlend - 2;
996 }
997 }
998 }
999 else {
1000 hlstart = 0;
1001 hlend = 0;
1002 }
1003
1004 if (hlend - hlstart < 2) {
1005 hlend = hlstart + 2;
1006 }
1007
1008 st->runtime->scroll_region_select = *r_scroll;
1009 st->runtime->scroll_region_select.ymax = region->winy - pix_top_margin - hlstart;
1010 st->runtime->scroll_region_select.ymin = region->winy - pix_top_margin - hlend;
1011
1012 CLAMP(st->runtime->scroll_region_select.ymin, pix_bottom_margin, region->winy - pix_top_margin);
1013 CLAMP(st->runtime->scroll_region_select.ymax, pix_bottom_margin, region->winy - pix_top_margin);
1014}
1015
1016static void draw_textscroll(const SpaceText *st, const rcti *scroll, const rcti *back)
1017{
1018 bTheme *btheme = UI_GetTheme();
1019 uiWidgetColors wcol = btheme->tui.wcol_scroll;
1020 float col[4];
1021 float rad;
1022
1023 /* Background so highlights don't go behind the scroll-bar. */
1028 immRecti(pos, back->xmin, back->ymin, back->xmax, back->ymax);
1030
1032 scroll,
1033 &st->runtime->scroll_region_handle,
1035
1037 rad = 0.4f * min_ii(BLI_rcti_size_x(&st->runtime->scroll_region_select),
1038 BLI_rcti_size_y(&st->runtime->scroll_region_select));
1040 col[3] = 0.18f;
1041
1042 rctf rect;
1043 rect.xmin = st->runtime->scroll_region_select.xmin + 1;
1044 rect.xmax = st->runtime->scroll_region_select.xmax - 1;
1045 rect.ymin = st->runtime->scroll_region_select.ymin;
1046 rect.ymax = st->runtime->scroll_region_select.ymax;
1047 UI_draw_roundbox_aa(&rect, true, rad, col);
1048}
1049
1052/* -------------------------------------------------------------------- */
1056/* -------------------------------------------------------------------- */
1060static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc, ARegion *region)
1061{
1062 SuggItem *item, *first, *last, *sel;
1063 char str[SUGG_LIST_WIDTH * BLI_UTF8_MAX + 1];
1064 int offl, offc, vcurl, vcurc;
1065 int w, boxw = 0, boxh, i, x, y, *top;
1066 const int lheight = TXT_LINE_HEIGHT(st);
1067 const int margin_x = 2;
1068
1069 if (!st->text) {
1070 return;
1071 }
1072 if (!texttool_text_is_active(st->text)) {
1073 return;
1074 }
1075
1076 first = texttool_suggest_first();
1077 last = texttool_suggest_last();
1078
1079 if (!first || !last) {
1080 return;
1081 }
1082
1085 top = texttool_suggest_top();
1086
1087 space_text_wrap_offset(st, region, st->text->curl, st->text->curc, &offl, &offc);
1088 vcurl = txt_get_span(static_cast<TextLine *>(st->text->lines.first), st->text->curl) - st->top +
1089 offl;
1090 vcurc = space_text_get_char_pos(st, st->text->curl->line, st->text->curc) - st->left + offc;
1091
1092 x = TXT_BODY_LEFT(st) + (vcurc * st->runtime->cwidth_px);
1093 y = region->winy - (vcurl + 1) * lheight - 2;
1094
1095 /* offset back so the start of the text lines up with the suggestions,
1096 * not essential but makes suggestions easier to follow */
1097 x -= st->runtime->cwidth_px *
1098 (st->text->curc - text_find_identifier_start(st->text->curl->line, st->text->curc));
1099
1100 boxw = SUGG_LIST_WIDTH * st->runtime->cwidth_px + 20;
1101 boxh = SUGG_LIST_SIZE * lheight + 8;
1102
1103 if (x + boxw > region->winx) {
1104 x = std::max(0, region->winx - boxw);
1105 }
1106
1107 /* not needed but stands out nicer */
1108 {
1109 rctf rect;
1110 rect.xmin = x;
1111 rect.xmax = x + boxw;
1112 rect.ymin = y - boxh;
1113 rect.ymax = y;
1114 ui_draw_dropshadow(&rect, 0.0f, 8.0f, 1.0f, 0.5f);
1115 }
1116
1120
1122 immRecti(pos, x - 1, y + 1, x + boxw + 1, y - boxh - 1);
1124 immRecti(pos, x, y, x + boxw, y - boxh);
1125
1127
1128 /* Set the top 'item' of the visible list */
1129 for (i = 0, item = first; i < *top && item->next; i++, item = item->next) {
1130 /* pass */
1131 }
1132
1133 for (i = 0; i < SUGG_LIST_SIZE && item; i++, item = item->next) {
1134 int len = txt_utf8_forward_columns(item->name, SUGG_LIST_WIDTH, nullptr) - item->name;
1135
1136 y -= lheight;
1137
1138 BLI_strncpy(str, item->name, len + 1);
1139
1140 w = st->runtime->cwidth_px * space_text_get_char_pos(st, str, len);
1141
1142 if (item == sel) {
1146
1148 immRecti(posi, x + margin_x, y - 3, x + margin_x + w, y + lheight - 3);
1149
1151 }
1152
1153 format_draw_color(tdc, item->type);
1154 space_text_draw(st, tdc, str, 0, 0, x + margin_x, y - 1, nullptr);
1155
1156 if (item == last) {
1157 break;
1158 }
1159 }
1160}
1161
1164/* -------------------------------------------------------------------- */
1168static void draw_text_decoration(SpaceText *st, ARegion *region)
1169{
1170 Text *text = st->text;
1171 int vcurl, vcurc, vsell, vselc;
1172 bool hidden = false;
1173 int offl, offc;
1174 const int lheight = TXT_LINE_HEIGHT(st);
1175
1176 /* Convert to view space character coordinates to determine if cursor is hidden */
1177 space_text_wrap_offset(st, region, text->sell, text->selc, &offl, &offc);
1178 vsell = txt_get_span(static_cast<TextLine *>(text->lines.first), text->sell) - st->top + offl;
1179 vselc = space_text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1180
1181 if (vselc < 0) {
1182 vselc = 0;
1183 hidden = true;
1184 }
1185
1186 if (text->curl == text->sell && text->curc == text->selc && !st->line_hlight && hidden) {
1187 /* Nothing to draw here */
1188 return;
1189 }
1190
1194
1195 /* Draw the selection */
1196 if (text->curl != text->sell || text->curc != text->selc) {
1197 /* Convert all to view space character coordinates */
1198 space_text_wrap_offset(st, region, text->curl, text->curc, &offl, &offc);
1199 vcurl = txt_get_span(static_cast<TextLine *>(text->lines.first), text->curl) - st->top + offl;
1200 vcurc = space_text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1201
1202 if (vcurc < 0) {
1203 vcurc = 0;
1204 }
1205
1207
1208 int x = TXT_BODY_LEFT(st);
1209 int y = region->winy;
1210 if (st->flags & ST_SCROLL_SELECT) {
1211 y += st->runtime->scroll_ofs_px[1];
1212 }
1213
1214 if (vcurl == vsell) {
1215 y -= vcurl * lheight;
1216
1217 if (vcurc < vselc) {
1218 immRecti(pos,
1219 x + vcurc * st->runtime->cwidth_px,
1220 y,
1221 x + vselc * st->runtime->cwidth_px,
1222 y - lheight);
1223 }
1224 else {
1225 immRecti(pos,
1226 x + vselc * st->runtime->cwidth_px,
1227 y,
1228 x + vcurc * st->runtime->cwidth_px,
1229 y - lheight);
1230 }
1231 }
1232 else {
1233 int froml, fromc, tol, toc;
1234
1235 if (vcurl < vsell) {
1236 froml = vcurl;
1237 tol = vsell;
1238 fromc = vcurc;
1239 toc = vselc;
1240 }
1241 else {
1242 froml = vsell;
1243 tol = vcurl;
1244 fromc = vselc;
1245 toc = vcurc;
1246 }
1247
1248 y -= froml * lheight;
1249
1250 immRecti(
1251 pos, x + fromc * st->runtime->cwidth_px - U.pixelsize, y, region->winx, y - lheight);
1252 y -= lheight;
1253
1254 for (int i = froml + 1; i < tol; i++) {
1255 immRecti(pos, x - U.pixelsize, y, region->winx, y - lheight);
1256 y -= lheight;
1257 }
1258
1259 if (x + toc * st->runtime->cwidth_px > x) {
1260 immRecti(pos, x - U.pixelsize, y, x + toc * st->runtime->cwidth_px, y - lheight);
1261 }
1262 y -= lheight;
1263 }
1264 /* Quiet warnings. */
1265 UNUSED_VARS(x, y);
1266 }
1267
1268 if (st->line_hlight) {
1269 int y1, y2;
1270
1271 if (st->wordwrap) {
1272 int visible_lines = space_text_get_visible_lines(st, region, text->sell->line);
1273
1274 space_text_wrap_offset_in_line(st, region, text->sell, text->selc, &offl, &offc);
1275
1276 y1 = region->winy - (vsell - offl) * lheight;
1277 if (st->flags & ST_SCROLL_SELECT) {
1278 y1 += st->runtime->scroll_ofs_px[1];
1279 }
1280 y2 = y1 - (lheight * visible_lines);
1281 }
1282 else {
1283 y1 = region->winy - vsell * lheight;
1284 if (st->flags & ST_SCROLL_SELECT) {
1285 y1 += st->runtime->scroll_ofs_px[1];
1286 }
1287 y2 = y1 - (lheight);
1288 }
1289
1290 if (!(y1 < 0 || y2 > region->winy)) { /* check we need to draw */
1291 float highlight_color[4];
1292 UI_GetThemeColor4fv(TH_TEXT, highlight_color);
1293 highlight_color[3] = 0.1f;
1294 immUniformColor4fv(highlight_color);
1296 immRecti(pos, 0, y1, region->winx, y2);
1298 }
1299 }
1300
1301 if (!hidden) {
1302 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
1303 int x = TXT_BODY_LEFT(st) + (vselc * st->runtime->cwidth_px);
1304 int y = region->winy - vsell * lheight;
1305 if (st->flags & ST_SCROLL_SELECT) {
1306 y += st->runtime->scroll_ofs_px[1];
1307 }
1308
1310
1311 if (st->overwrite) {
1312 char ch = text->sell->line[text->selc];
1313
1314 y += TXT_LINE_SPACING(st);
1315 int w = st->runtime->cwidth_px;
1316 if (ch == '\t') {
1317 w *= st->tabnumber - (vselc + st->left) % st->tabnumber;
1318 }
1319
1320 immRecti(
1321 pos, x, y - lheight - U.pixelsize, x + w + U.pixelsize, y - lheight - (3 * U.pixelsize));
1322 }
1323 else {
1324 immRecti(pos, x - U.pixelsize, y, x + U.pixelsize, y - lheight);
1325 }
1326 }
1327
1329}
1330
1333/* -------------------------------------------------------------------- */
1337static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegion *region)
1338{
1339 TextLine *startl, *endl, *linep;
1340 Text *text = st->text;
1341 int b, fc, find, stack, viewc, viewl, offl, offc, x, y;
1342 int startc, endc, c;
1343
1344 char ch;
1345
1346 /* syntax_highlight must be on or else the format string will be null */
1347 if (!text->curl || !tdc->syntax_highlight) {
1348 return;
1349 }
1350
1351 startl = text->curl;
1352 startc = text->curc;
1353 b = text_check_bracket(startl->line[startc]);
1354 if (b == 0 && startc > 0) {
1355 b = text_check_bracket(startl->line[--startc]);
1356 }
1357 if (b == 0) {
1358 return;
1359 }
1360
1361 linep = startl;
1362 c = startc;
1363 fc = BLI_str_utf8_offset_to_index(linep->line, linep->len, startc);
1364 endl = nullptr;
1365 endc = -1;
1366 find = -b;
1367 stack = 0;
1368
1369 /* Don't highlight brackets if syntax HL is off or bracket in string or comment. */
1370 if (!linep->format || linep->format[fc] == FMT_TYPE_STRING ||
1371 linep->format[fc] == FMT_TYPE_COMMENT)
1372 {
1373 return;
1374 }
1375
1376 if (b > 0) {
1377 /* opening bracket, search forward for close */
1378 fc++;
1379 c += BLI_str_utf8_size_safe(linep->line + c);
1380 while (linep) {
1381 while (c < linep->len) {
1382 if (linep->format && linep->format[fc] != FMT_TYPE_STRING &&
1383 linep->format[fc] != FMT_TYPE_COMMENT)
1384 {
1385 b = text_check_bracket(linep->line[c]);
1386 if (b == find) {
1387 if (stack == 0) {
1388 endl = linep;
1389 endc = c;
1390 break;
1391 }
1392 stack--;
1393 }
1394 else if (b == -find) {
1395 stack++;
1396 }
1397 }
1398 fc++;
1399 c += BLI_str_utf8_size_safe(linep->line + c);
1400 }
1401 if (endl) {
1402 break;
1403 }
1404 linep = linep->next;
1405 c = 0;
1406 fc = 0;
1407 }
1408 }
1409 else {
1410 /* closing bracket, search backward for open */
1411 fc--;
1412 if (c > 0) {
1413 c -= linep->line + c - BLI_str_find_prev_char_utf8(linep->line + c, linep->line);
1414 }
1415 while (linep) {
1416 while (fc >= 0) {
1417 if (linep->format && linep->format[fc] != FMT_TYPE_STRING &&
1418 linep->format[fc] != FMT_TYPE_COMMENT)
1419 {
1420 b = text_check_bracket(linep->line[c]);
1421 if (b == find) {
1422 if (stack == 0) {
1423 endl = linep;
1424 endc = c;
1425 break;
1426 }
1427 stack--;
1428 }
1429 else if (b == -find) {
1430 stack++;
1431 }
1432 }
1433 fc--;
1434 if (c > 0) {
1435 c -= linep->line + c - BLI_str_find_prev_char_utf8(linep->line + c, linep->line);
1436 }
1437 }
1438 if (endl) {
1439 break;
1440 }
1441 linep = linep->prev;
1442 if (linep) {
1443 if (linep->format) {
1444 fc = strlen(linep->format) - 1;
1445 }
1446 else {
1447 fc = -1;
1448 }
1449 if (linep->len) {
1450 c = BLI_str_find_prev_char_utf8(linep->line + linep->len, linep->line) - linep->line;
1451 }
1452 else {
1453 fc = -1;
1454 }
1455 }
1456 }
1457 }
1458
1459 if (!endl || endc == -1) {
1460 return;
1461 }
1462
1464 x = TXT_BODY_LEFT(st);
1465 y = region->winy - st->runtime->lheight_px;
1466 if (st->flags & ST_SCROLL_SELECT) {
1467 y += st->runtime->scroll_ofs_px[1];
1468 }
1469
1470 /* draw opening bracket */
1471 ch = startl->line[startc];
1472 space_text_wrap_offset(st, region, startl, startc, &offl, &offc);
1473 viewc = space_text_get_char_pos(st, startl->line, startc) - st->left + offc;
1474
1475 if (viewc >= 0) {
1476 viewl = txt_get_span(static_cast<TextLine *>(text->lines.first), startl) - st->top + offl;
1477
1479 tdc, x + viewc * st->runtime->cwidth_px, y - viewl * TXT_LINE_HEIGHT(st), ch);
1481 tdc, x + viewc * st->runtime->cwidth_px + 1, y - viewl * TXT_LINE_HEIGHT(st), ch);
1482 }
1483
1484 /* draw closing bracket */
1485 ch = endl->line[endc];
1486 space_text_wrap_offset(st, region, endl, endc, &offl, &offc);
1487 viewc = space_text_get_char_pos(st, endl->line, endc) - st->left + offc;
1488
1489 if (viewc >= 0) {
1490 viewl = txt_get_span(static_cast<TextLine *>(text->lines.first), endl) - st->top + offl;
1491
1493 tdc, x + viewc * st->runtime->cwidth_px, y - viewl * TXT_LINE_HEIGHT(st), ch);
1495 tdc, x + viewc * st->runtime->cwidth_px + 1, y - viewl * TXT_LINE_HEIGHT(st), ch);
1496 }
1497}
1498
1501/* -------------------------------------------------------------------- */
1505void draw_text_main(SpaceText *st, ARegion *region)
1506{
1507 TextDrawContext tdc = {0};
1508 Text *text = st->text;
1509 TextFormatType *tft;
1510 TextLine *tmp;
1511 rcti scroll, back;
1512 char linenr[12];
1513 int i, x, y, winx, linecount = 0, lineno = 0;
1514 int wraplinecount = 0, wrap_skip = 0;
1515 int margin_column_x;
1516
1517 /* if no text, nothing to do */
1518 if (!text) {
1519 return;
1520 }
1521
1522 /* DPI controlled line height and font size. */
1523 st->runtime->lheight_px = (U.widget_unit * st->lheight) / 20;
1524
1525 /* don't draw lines below this */
1526 const int clip_min_y = -int(st->runtime->lheight_px - 1);
1527
1528 st->runtime->viewlines = (st->runtime->lheight_px) ?
1529 int(region->winy - clip_min_y) / TXT_LINE_HEIGHT(st) :
1530 0;
1531
1533
1534 space_text_update_drawcache(st, region);
1535
1536 /* make sure all the positional pointers exist */
1537 if (!text->curl || !text->sell || !text->lines.first || !text->lines.last) {
1538 txt_clean_text(text);
1539 }
1540
1541 /* Update rectangles for scroll. */
1542 calc_text_rcts(st, region, &scroll, &back); /* scroll will hold the entire bar size */
1543
1544 /* update syntax formatting if needed */
1545 tft = ED_text_format_get(text);
1546 tmp = static_cast<TextLine *>(text->lines.first);
1547 lineno = 0;
1548 for (i = 0; i < st->top && tmp; i++) {
1549 if (tdc.syntax_highlight && !tmp->format) {
1550 tft->format_line(st, tmp, false);
1551 }
1552
1553 if (st->wordwrap) {
1554 int lines = space_text_get_visible_lines_no(st, lineno);
1555
1556 if (wraplinecount + lines > st->top) {
1557 wrap_skip = st->top - wraplinecount;
1558 break;
1559 }
1560
1561 wraplinecount += lines;
1562 tmp = tmp->next;
1563 linecount++;
1564 }
1565 else {
1566 tmp = tmp->next;
1567 linecount++;
1568 }
1569
1570 lineno++;
1571 }
1572
1573 text_font_begin(&tdc);
1574
1575 tdc.cwidth_px = max_ii(int(BLF_fixed_width(tdc.font_id)), 1);
1576 st->runtime->cwidth_px = tdc.cwidth_px;
1577
1578 /* draw line numbers background */
1579 if (st->showlinenrs) {
1584 immRecti(pos, 0, 0, TXT_NUMCOL_WIDTH(st), region->winy);
1586 }
1587 else {
1588 st->runtime->line_number_display_digits = 0; /* not used */
1589 }
1590
1591 x = TXT_BODY_LEFT(st);
1592 y = region->winy - st->runtime->lheight_px;
1593 int viewlines = st->runtime->viewlines;
1594 if (st->flags & ST_SCROLL_SELECT) {
1595 y += st->runtime->scroll_ofs_px[1];
1596 viewlines += 1;
1597 }
1598
1599 winx = region->winx - TXT_SCROLL_WIDTH;
1600
1601 /* draw cursor, margin, selection and highlight */
1602 draw_text_decoration(st, region);
1603
1604 /* draw the text */
1606
1607 for (i = 0; y > clip_min_y && i < viewlines && tmp; i++, tmp = tmp->next) {
1608 if (tdc.syntax_highlight && !tmp->format) {
1609 tft->format_line(st, tmp, false);
1610 }
1611
1612 if (st->showlinenrs && !wrap_skip) {
1613 /* Draw line number. */
1614 UI_FontThemeColor(tdc.font_id, (tmp == text->sell) ? TH_HILITE : TH_LINENUMBERS);
1615 SNPRINTF(linenr, "%*d", st->runtime->line_number_display_digits, i + linecount + 1);
1616 text_font_draw(&tdc, TXT_NUMCOL_PAD * st->runtime->cwidth_px, y, linenr);
1617 /* Change back to text color. */
1619 }
1620
1621 if (st->wordwrap) {
1622 /* draw word wrapped text */
1623 int lines = space_text_draw_wrapped(
1624 st, &tdc, tmp->line, x, y, winx - x, tmp->format, wrap_skip);
1625 y -= lines * TXT_LINE_HEIGHT(st);
1626 }
1627 else {
1628 /* draw unwrapped text */
1629 space_text_draw(
1630 st, &tdc, tmp->line, st->left, region->winx / st->runtime->cwidth_px, x, y, tmp->format);
1631 y -= TXT_LINE_HEIGHT(st);
1632 }
1633
1634 wrap_skip = 0;
1635 }
1636
1637 if (st->flags & ST_SHOW_MARGIN) {
1638 margin_column_x = x + st->runtime->cwidth_px * (st->margin_column - st->left);
1639 if (margin_column_x >= x) {
1643 float margin_color[4];
1644 UI_GetThemeColor4fv(TH_TEXT, margin_color);
1645 margin_color[3] = 0.2f;
1646 immUniformColor4fv(margin_color);
1648 immRecti(pos, margin_column_x, 0, margin_column_x + U.pixelsize, region->winy);
1651 }
1652 }
1653
1654 /* draw other stuff */
1655 draw_brackets(st, &tdc, region);
1656 draw_textscroll(st, &scroll, &back);
1657 /* draw_documentation(st, region); - No longer supported */
1658 draw_suggestion_list(st, &tdc, region);
1659
1660 text_font_end(&tdc);
1661}
1662
1665/* -------------------------------------------------------------------- */
1670{
1671 TextDrawContext tdc = {0};
1672
1674
1675 text_font_begin(&tdc);
1676 st->runtime->cwidth_px = BLF_fixed_width(tdc.font_id);
1677 st->runtime->cwidth_px = std::max(st->runtime->cwidth_px, 1);
1678 text_font_end(&tdc);
1679}
1680
1682{
1684 if (area) {
1685 SpaceText *st = static_cast<SpaceText *>(area->spacedata.first);
1687 st->text = text;
1688 if (region) {
1689 ED_space_text_scroll_to_cursor(st, region, true);
1690 }
1692 return true;
1693 }
1694
1695 return false;
1696}
1697
1698void ED_space_text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center)
1699{
1700 Text *text;
1701 int i, x, winx = region->winx;
1702
1703 if (ELEM(nullptr, st, st->text, st->text->curl)) {
1704 return;
1705 }
1706
1707 text = st->text;
1708
1710
1711 i = txt_get_span(static_cast<TextLine *>(text->lines.first), text->sell);
1712 if (st->wordwrap) {
1713 int offl, offc;
1714 space_text_wrap_offset(st, region, text->sell, text->selc, &offl, &offc);
1715 i += offl;
1716 }
1717
1718 if (center) {
1719 if (st->top + st->runtime->viewlines <= i || st->top > i) {
1720 st->top = i - st->runtime->viewlines / 2;
1721 }
1722 }
1723 else {
1724 if (st->top + st->runtime->viewlines <= i) {
1725 st->top = i - (st->runtime->viewlines - 1);
1726 }
1727 else if (st->top > i) {
1728 st->top = i;
1729 }
1730 }
1731
1732 if (st->wordwrap) {
1733 st->left = 0;
1734 }
1735 else {
1736 x = st->runtime->cwidth_px *
1737 (space_text_get_char_pos(st, text->sell->line, text->selc) - st->left);
1738 winx -= TXT_BODY_LEFT(st) + TXT_SCROLL_WIDTH;
1739
1740 if (center) {
1741 if (x <= 0 || x > winx) {
1742 st->left += (x - winx / 2) / st->runtime->cwidth_px;
1743 }
1744 }
1745 else {
1746 if (x <= 0) {
1747 st->left += ((x + 1) / st->runtime->cwidth_px) - 1;
1748 }
1749 else if (x > winx) {
1750 st->left += ((x - (winx + 1)) / st->runtime->cwidth_px) + 1;
1751 }
1752 }
1753 }
1754
1755 if (st->top < 0) {
1756 st->top = 0;
1757 }
1758 if (st->left < 0) {
1759 st->left = 0;
1760 }
1761
1762 st->runtime->scroll_ofs_px[0] = 0;
1763 st->runtime->scroll_ofs_px[1] = 0;
1764}
1765
1766void space_text_scroll_to_cursor_with_area(SpaceText *st, ScrArea *area, const bool center)
1767{
1768 ARegion *region;
1769
1770 if (ELEM(nullptr, st, st->text, st->text->curl)) {
1771 return;
1772 }
1773
1775
1776 if (region) {
1777 ED_space_text_scroll_to_cursor(st, region, center);
1778 }
1779}
1780
1782{
1783 ScrArea *area = CTX_wm_area(C);
1785
1787}
1788
1790 const ARegion *region,
1791 const int cursor_co[2],
1792 int r_pixel_co[2])
1793{
1794 Text *text = st->text;
1795
1796 if (!text) {
1797 return false;
1798 }
1799 TextLine *line = static_cast<TextLine *>(BLI_findlink(&text->lines, cursor_co[0]));
1800 if (!line) {
1801 return false;
1802 }
1803
1804 /* Convert character index to char byte offset. */
1805 const int char_ofs = BLI_str_utf8_offset_from_index(line->line, line->len, cursor_co[1]);
1806 if (char_ofs < 0 || char_ofs > line->len) {
1807 return false;
1808 }
1809
1810 /* All values are in-range, calculate the pixel offset.
1811 * Note that !126720 provides a useful interactive test-case for this logic. */
1812 const int lheight = TXT_LINE_HEIGHT(st);
1813 const int linenr_offset = TXT_BODY_LEFT(st);
1814 /* Handle tabs as well! */
1815 const int char_pos = space_text_get_char_pos(st, line->line, char_ofs);
1816
1817 int offl, offc;
1818 space_text_wrap_offset(st, region, line, char_ofs, &offl, &offc);
1819 r_pixel_co[0] = (char_pos + offc - st->left) * st->runtime->cwidth_px + linenr_offset;
1820 r_pixel_co[1] = (region->winy - ((cursor_co[0] + offl - st->top) * lheight)) - lheight;
1821 return true;
1822}
1823
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:903
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:815
void txt_clean_text(struct Text *text)
Definition text.cc:633
int text_check_bracket(char ch)
Definition text.cc:2270
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:2377
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:426
float BLF_fixed_width(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:804
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:393
int blf_mono_font
Definition blf.cc:51
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
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
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:193
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#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)
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)
size_t size_t size_t 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:377
@ RGN_TYPE_WINDOW
@ SPACE_TEXT
@ ST_SCROLL_SELECT
@ ST_SHOW_MARGIN
bool ED_text_activate_in_screen(bContext *C, Text *text)
void ED_space_text_scroll_to_cursor(SpaceText *st, ARegion *region, bool center)
bool ED_space_text_region_location_from_cursor(const SpaceText *st, const ARegion *region, const int cursor_co[2], int r_pixel_co[2])
bool ED_text_is_syntax_highlight_supported(Text *text)
void immUnbindProgram()
void immUniformThemeColor(int color_id)
void immUniformThemeColorShade(int color_id, int offset)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
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.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
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)
void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state)
@ UI_CNR_ALL
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:457
#define NC_TEXT
Definition WM_types.hh:353
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
unsigned int U
Definition btGjkEpa3.h:78
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
local_group_size(16, 16) .push_constant(Type b
StackEntry * from
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
uint top
uint padding(uint offset, uint alignment)
format
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
static void update(bNodeTree *ntree)
float wrap(float value, float max, float min)
Definition node_math.h:71
return ret
char name[66]
Definition DNA_ID.h:425
void * first
SpaceText_Runtime * runtime
struct Text * text
struct SuggItem * next
bool syntax_highlight
Definition text_draw.cc:59
void(* format_line)(SpaceText *st, TextLine *line, bool do_next)
char * format
char * line
struct TextLine * prev
struct TextLine * next
ListBase lines
TextLine * curl
TextLine * sell
uiWidgetColors wcol_scroll
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:77
int ED_space_text_visible_lines_get(const SpaceText *st)
Definition text_draw.cc:44
static int text_font_draw_character(const TextDrawContext *tdc, int x, int y, char c)
Definition text_draw.cc:87
static void space_text_draw_context_init(const SpaceText *st, TextDrawContext *tdc)
Definition text_draw.cc:62
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:96
static void text_font_end(const TextDrawContext *)
Definition text_draw.cc:75
static void text_font_begin(const TextDrawContext *tdc)
Definition text_draw.cc:70
static void format_draw_color(const TextDrawContext *tdc, char formatchar)
Definition text_draw.cc:124
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
void space_text_drawcache_tag_update(SpaceText *st, bool full)
#define SUGG_LIST_WIDTH
int space_text_wrap_width(const SpaceText *st, const ARegion *region)
#define TXT_LINE_SPACING(st)
void space_text_free_caches(SpaceText *st)
#define TXT_LINE_HEIGHT(st)
void draw_text_main(SpaceText *st, ARegion *region)
#define TXT_NUMCOL_WIDTH(st)
void space_text_update_cursor_moved(bContext *C)
void space_text_wrap_offset(const SpaceText *st, const ARegion *region, TextLine *linein, int cursin, int *offl, int *offc)
void space_text_update_character_width(SpaceText *st)
int space_text_get_visible_lines(const SpaceText *st, const ARegion *region, const char *str)
void space_text_wrap_offset_in_line(const SpaceText *st, const ARegion *region, TextLine *linein, int cursin, int *offl, int *offc)
#define TXT_NUMCOL_PAD
void space_text_scroll_to_cursor_with_area(SpaceText *st, ScrArea *area, bool center)
int space_text_get_span_wrap(const SpaceText *st, const ARegion *region, const TextLine *from, const TextLine *to)
#define SUGG_LIST_SIZE
#define TXT_BODY_LEFT(st)
int space_text_get_total_lines(SpaceText *st, const ARegion *region)
#define TXT_SCROLL_WIDTH
int space_text_get_char_pos(const SpaceText *st, const char *line, int cur)
float max
void WM_event_add_notifier(const bContext *C, uint type, void *reference)