rich_text_label.cpp 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061
  1. /*************************************************************************/
  2. /* rich_text_label.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "rich_text_label.h"
  31. #include "os/keyboard.h"
  32. #include "os/os.h"
  33. #include "scene/scene_string_names.h"
  34. RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) {
  35. if (p_free) {
  36. if (p_item->subitems.size()) {
  37. return p_item->subitems.front()->get();
  38. } else if (!p_item->parent) {
  39. return NULL;
  40. } else if (p_item->E->next()) {
  41. return p_item->E->next()->get();
  42. } else {
  43. //go up until something with a next is found
  44. while (p_item->parent && !p_item->E->next()) {
  45. p_item = p_item->parent;
  46. }
  47. if (p_item->parent)
  48. return p_item->E->next()->get();
  49. else
  50. return NULL;
  51. }
  52. } else {
  53. if (p_item->subitems.size() && p_item->type != ITEM_TABLE) {
  54. return p_item->subitems.front()->get();
  55. } else if (p_item->type == ITEM_FRAME) {
  56. return NULL;
  57. } else if (p_item->E->next()) {
  58. return p_item->E->next()->get();
  59. } else {
  60. //go up until something with a next is found
  61. while (p_item->type != ITEM_FRAME && !p_item->E->next()) {
  62. p_item = p_item->parent;
  63. }
  64. if (p_item->type != ITEM_FRAME)
  65. return p_item->E->next()->get();
  66. else
  67. return NULL;
  68. }
  69. }
  70. return NULL;
  71. }
  72. Rect2 RichTextLabel::_get_text_rect() {
  73. Ref<StyleBox> style = get_stylebox("normal");
  74. return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
  75. }
  76. int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
  77. RID ci;
  78. if (r_outside)
  79. *r_outside = false;
  80. if (p_mode == PROCESS_DRAW) {
  81. ci = get_canvas_item();
  82. if (r_click_item)
  83. *r_click_item = NULL;
  84. }
  85. Line &l = p_frame->lines[p_line];
  86. Item *it = l.from;
  87. int line_ofs = 0;
  88. int margin = _find_margin(it, p_base_font);
  89. Align align = _find_align(it);
  90. int line = 0;
  91. int spaces = 0;
  92. int height = get_size().y;
  93. if (p_mode != PROCESS_CACHE) {
  94. ERR_FAIL_INDEX_V(line, l.offset_caches.size(), 0);
  95. line_ofs = l.offset_caches[line];
  96. }
  97. if (p_mode == PROCESS_CACHE) {
  98. l.offset_caches.clear();
  99. l.height_caches.clear();
  100. l.char_count = 0;
  101. l.minimum_width = 0;
  102. }
  103. int wofs = margin;
  104. int spaces_size = 0;
  105. int align_ofs = 0;
  106. if (p_mode != PROCESS_CACHE && align != ALIGN_FILL)
  107. wofs += line_ofs;
  108. int begin = wofs;
  109. Ref<Font> cfont = _find_font(it);
  110. if (cfont.is_null())
  111. cfont = p_base_font;
  112. //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed
  113. int line_height = cfont->get_height();
  114. int nonblank_line_count = 0; //number of nonblank lines as counted during PROCESS_DRAW
  115. Variant meta;
  116. #define RETURN return nonblank_line_count
  117. #define NEW_LINE \
  118. { \
  119. if (p_mode != PROCESS_CACHE) { \
  120. line++; \
  121. if (!line_is_blank) { \
  122. nonblank_line_count++; \
  123. } \
  124. line_is_blank = true; \
  125. if (line < l.offset_caches.size()) \
  126. line_ofs = l.offset_caches[line]; \
  127. wofs = margin; \
  128. if (align != ALIGN_FILL) \
  129. wofs += line_ofs; \
  130. } else { \
  131. int used = wofs - margin; \
  132. switch (align) { \
  133. case ALIGN_LEFT: l.offset_caches.push_back(0); break; \
  134. case ALIGN_CENTER: l.offset_caches.push_back(((p_width - margin) - used) / 2); break; \
  135. case ALIGN_RIGHT: l.offset_caches.push_back(((p_width - margin) - used)); break; \
  136. case ALIGN_FILL: l.offset_caches.push_back((p_width - margin) - used /*+spaces_size*/); break; \
  137. } \
  138. l.height_caches.push_back(line_height); \
  139. l.space_caches.push_back(spaces); \
  140. } \
  141. y += line_height + get_constant(SceneStringNames::get_singleton()->line_separation); \
  142. line_height = 0; \
  143. spaces = 0; \
  144. spaces_size = 0; \
  145. wofs = begin; \
  146. align_ofs = 0; \
  147. if (p_mode != PROCESS_CACHE) { \
  148. lh = line < l.height_caches.size() ? l.height_caches[line] : 1; \
  149. } \
  150. if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x < p_ofs.x + wofs) { \
  151. if (r_outside) *r_outside = true; \
  152. *r_click_item = it; \
  153. *r_click_char = rchar; \
  154. RETURN; \
  155. } \
  156. }
  157. #define ENSURE_WIDTH(m_width) \
  158. if (p_mode == PROCESS_CACHE) { \
  159. l.minimum_width = MAX(l.minimum_width, wofs + m_width); \
  160. } \
  161. if (wofs + m_width > p_width) { \
  162. if (p_mode == PROCESS_CACHE) { \
  163. if (spaces > 0) \
  164. spaces -= 1; \
  165. } \
  166. if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x > p_ofs.x + wofs) { \
  167. if (r_outside) *r_outside = true; \
  168. *r_click_item = it; \
  169. *r_click_char = rchar; \
  170. RETURN; \
  171. } \
  172. NEW_LINE \
  173. }
  174. #define ADVANCE(m_width) \
  175. { \
  176. if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x >= p_ofs.x + wofs && p_click_pos.x < p_ofs.x + wofs + m_width) { \
  177. if (r_outside) *r_outside = false; \
  178. *r_click_item = it; \
  179. *r_click_char = rchar; \
  180. RETURN; \
  181. } \
  182. wofs += m_width; \
  183. }
  184. #define CHECK_HEIGHT(m_height) \
  185. if (m_height > line_height) { \
  186. line_height = m_height; \
  187. }
  188. #define YRANGE_VISIBLE(m_top, m_height) \
  189. (m_height > 0 && ((m_top >= 0 && m_top < height) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < height)))
  190. Color selection_fg;
  191. Color selection_bg;
  192. if (p_mode == PROCESS_DRAW) {
  193. selection_fg = get_color("font_color_selected");
  194. selection_bg = get_color("selection_color");
  195. }
  196. int rchar = 0;
  197. int lh = 0;
  198. bool line_is_blank = true;
  199. while (it) {
  200. switch (it->type) {
  201. case ITEM_TEXT: {
  202. ItemText *text = static_cast<ItemText *>(it);
  203. Ref<Font> font = _find_font(it);
  204. if (font.is_null())
  205. font = p_base_font;
  206. const CharType *c = text->text.c_str();
  207. const CharType *cf = c;
  208. int fh = font->get_height();
  209. int ascent = font->get_ascent();
  210. Color color;
  211. bool underline = false;
  212. if (p_mode == PROCESS_DRAW) {
  213. color = _find_color(text, p_base_color);
  214. underline = _find_underline(text);
  215. if (_find_meta(text, &meta)) {
  216. underline = true;
  217. }
  218. } else if (p_mode == PROCESS_CACHE) {
  219. l.char_count += text->text.length();
  220. }
  221. rchar = 0;
  222. while (*c) {
  223. int end = 0;
  224. int w = 0;
  225. int fw = 0;
  226. lh = 0;
  227. if (p_mode != PROCESS_CACHE) {
  228. lh = line < l.height_caches.size() ? l.height_caches[line] : 1;
  229. }
  230. while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) {
  231. int cw = font->get_char_size(c[end], c[end + 1]).width;
  232. if (c[end] == '\t') {
  233. cw = tab_size * font->get_char_size(' ').width;
  234. }
  235. if (end > 0 && w + cw + begin > p_width) {
  236. break; //don't allow lines longer than assigned width
  237. }
  238. w += cw;
  239. fw += cw;
  240. end++;
  241. }
  242. ENSURE_WIDTH(w);
  243. if (end && c[end - 1] == ' ') {
  244. if (p_mode == PROCESS_CACHE) {
  245. spaces_size += font->get_char_size(' ').width;
  246. } else if (align == ALIGN_FILL) {
  247. int ln = MIN(l.offset_caches.size() - 1, line);
  248. if (l.space_caches[ln]) {
  249. align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln];
  250. }
  251. }
  252. spaces++;
  253. }
  254. {
  255. int ofs = 0;
  256. for (int i = 0; i < end; i++) {
  257. int pofs = wofs + ofs;
  258. if (p_mode == PROCESS_POINTER && r_click_char && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) {
  259. //int o = (wofs+w)-p_click_pos.x;
  260. int cw = font->get_char_size(c[i], c[i + 1]).x;
  261. if (c[i] == '\t') {
  262. cw = tab_size * font->get_char_size(' ').width;
  263. }
  264. if (p_click_pos.x - cw / 2 > p_ofs.x + align_ofs + pofs) {
  265. rchar = int((&c[i]) - cf);
  266. }
  267. ofs += cw;
  268. } else if (p_mode == PROCESS_DRAW) {
  269. bool selected = false;
  270. if (selection.active) {
  271. int cofs = (&c[i]) - cf;
  272. if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >= selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <= selection.to_char))) {
  273. selected = true;
  274. }
  275. }
  276. int cw = 0;
  277. bool visible = visible_characters < 0 || p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - (fh - 0 * ascent), fh); //getting rid of ascent seems to work??
  278. if (visible)
  279. line_is_blank = false;
  280. if (c[i] == '\t')
  281. visible = false;
  282. if (selected) {
  283. cw = font->get_char_size(c[i], c[i + 1]).x;
  284. draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
  285. if (visible)
  286. font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - (fh - ascent)), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
  287. } else {
  288. if (visible)
  289. cw = font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - (fh - ascent)), c[i], c[i + 1], color);
  290. }
  291. p_char_count++;
  292. if (c[i] == '\t') {
  293. cw = tab_size * font->get_char_size(' ').width;
  294. }
  295. if (underline) {
  296. Color uc = color;
  297. uc.a *= 0.5;
  298. int uy = y + lh - fh + ascent + 2;
  299. VS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + pofs, uy), p_ofs + Point2(align_ofs + pofs + cw, uy), uc);
  300. }
  301. ofs += cw;
  302. }
  303. }
  304. }
  305. ADVANCE(fw);
  306. CHECK_HEIGHT(fh); //must be done somewhere
  307. c = &c[end];
  308. }
  309. } break;
  310. case ITEM_IMAGE: {
  311. lh = 0;
  312. if (p_mode != PROCESS_CACHE)
  313. lh = line < l.height_caches.size() ? l.height_caches[line] : 1;
  314. else
  315. l.char_count += 1; //images count as chars too
  316. ItemImage *img = static_cast<ItemImage *>(it);
  317. Ref<Font> font = _find_font(it);
  318. if (font.is_null())
  319. font = p_base_font;
  320. if (p_mode == PROCESS_POINTER && r_click_char)
  321. *r_click_char = 0;
  322. ENSURE_WIDTH(img->image->get_width());
  323. bool visible = visible_characters < 0 || p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->image->get_height(), img->image->get_height());
  324. if (visible)
  325. line_is_blank = false;
  326. if (p_mode == PROCESS_DRAW && visible) {
  327. img->image->draw(ci, p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->image->get_height()));
  328. }
  329. p_char_count++;
  330. ADVANCE(img->image->get_width());
  331. CHECK_HEIGHT((img->image->get_height() + font->get_descent()));
  332. } break;
  333. case ITEM_NEWLINE: {
  334. lh = 0;
  335. if (p_mode != PROCESS_CACHE) {
  336. lh = line < l.height_caches.size() ? l.height_caches[line] : 1;
  337. line_is_blank = true;
  338. }
  339. } break;
  340. case ITEM_TABLE: {
  341. lh = 0;
  342. ItemTable *table = static_cast<ItemTable *>(it);
  343. int hseparation = get_constant("table_hseparation");
  344. int vseparation = get_constant("table_vseparation");
  345. Color ccolor = _find_color(table, p_base_color);
  346. Vector2 draw_ofs = Point2(wofs, y);
  347. if (p_mode == PROCESS_CACHE) {
  348. int idx = 0;
  349. //set minimums to zero
  350. for (int i = 0; i < table->columns.size(); i++) {
  351. table->columns[i].min_width = 0;
  352. table->columns[i].width = 0;
  353. }
  354. //compute minimum width for each cell
  355. for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
  356. ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames
  357. ItemFrame *frame = static_cast<ItemFrame *>(E->get());
  358. int column = idx % table->columns.size();
  359. int ly = 0;
  360. for (int i = 0; i < frame->lines.size(); i++) {
  361. _process_line(frame, Point2(), ly, p_width, i, PROCESS_CACHE, cfont, Color());
  362. table->columns[column].min_width = MAX(table->columns[i].min_width, frame->lines[i].minimum_width);
  363. }
  364. idx++;
  365. }
  366. //compute available width and total ratio (for expanders)
  367. int total_ratio = 0;
  368. int available_width = p_width - hseparation * (table->columns.size() - 1);
  369. table->total_width = hseparation;
  370. for (int i = 0; i < table->columns.size(); i++) {
  371. available_width -= table->columns[i].min_width;
  372. if (table->columns[i].expand)
  373. total_ratio += table->columns[i].expand_ratio;
  374. }
  375. //assign actual widths
  376. for (int i = 0; i < table->columns.size(); i++) {
  377. table->columns[i].width = table->columns[i].min_width;
  378. if (table->columns[i].expand)
  379. table->columns[i].width += table->columns[i].expand_ratio * available_width / total_ratio;
  380. table->total_width += table->columns[i].width + hseparation;
  381. }
  382. //compute caches properly again with the right width
  383. idx = 0;
  384. for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
  385. ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames
  386. ItemFrame *frame = static_cast<ItemFrame *>(E->get());
  387. int column = idx % table->columns.size();
  388. for (int i = 0; i < frame->lines.size(); i++) {
  389. int ly = 0;
  390. _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color());
  391. frame->lines[i].height_cache = ly; //actual height
  392. frame->lines[i].height_accum_cache = ly; //actual height
  393. }
  394. idx++;
  395. }
  396. }
  397. Point2 offset(align_ofs + hseparation, vseparation);
  398. int row_height = 0;
  399. //draw using computed caches
  400. int idx = 0;
  401. for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
  402. ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames
  403. ItemFrame *frame = static_cast<ItemFrame *>(E->get());
  404. int column = idx % table->columns.size();
  405. int ly = 0;
  406. int yofs = 0;
  407. int lines_h = frame->lines[frame->lines.size() - 1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache);
  408. int lines_ofs = p_ofs.y + offset.y + draw_ofs.y;
  409. bool visible = lines_ofs < get_size().height && lines_ofs + lines_h >= 0;
  410. if (visible)
  411. line_is_blank = false;
  412. for (int i = 0; i < frame->lines.size(); i++) {
  413. if (visible) {
  414. if (p_mode == PROCESS_DRAW) {
  415. nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor);
  416. } else if (p_mode == PROCESS_POINTER) {
  417. _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, p_click_pos, r_click_item, r_click_char, r_outside);
  418. if (r_click_item && *r_click_item) {
  419. RETURN; // exit early
  420. }
  421. }
  422. }
  423. yofs += frame->lines[i].height_cache;
  424. if (p_mode == PROCESS_CACHE) {
  425. frame->lines[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache;
  426. }
  427. }
  428. row_height = MAX(yofs, row_height);
  429. offset.x += table->columns[column].width + hseparation;
  430. if (column == table->columns.size() - 1) {
  431. offset.y += row_height + vseparation;
  432. offset.x = hseparation;
  433. row_height = 0;
  434. }
  435. idx++;
  436. }
  437. int total_height = offset.y;
  438. if (row_height) {
  439. total_height = row_height + vseparation;
  440. }
  441. ADVANCE(table->total_width);
  442. CHECK_HEIGHT(total_height);
  443. } break;
  444. default: {}
  445. }
  446. Item *itp = it;
  447. it = _get_next_item(it);
  448. if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) {
  449. if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) {
  450. //went to next line, but pointer was on the previous one
  451. if (r_outside) *r_outside = true;
  452. *r_click_item = itp;
  453. *r_click_char = rchar;
  454. RETURN;
  455. }
  456. break;
  457. }
  458. }
  459. NEW_LINE;
  460. RETURN;
  461. #undef RETURN
  462. #undef NEW_LINE
  463. #undef ENSURE_WIDTH
  464. #undef ADVANCE
  465. #undef CHECK_HEIGHT
  466. }
  467. void RichTextLabel::_scroll_changed(double) {
  468. if (updating_scroll)
  469. return;
  470. if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page()))
  471. scroll_following = true;
  472. else
  473. scroll_following = false;
  474. update();
  475. }
  476. void RichTextLabel::_update_scroll() {
  477. int total_height = 0;
  478. if (main->lines.size())
  479. total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_stylebox("normal")->get_minimum_size().height;
  480. bool exceeds = total_height > get_size().height && scroll_active;
  481. if (exceeds != scroll_visible) {
  482. if (exceeds) {
  483. scroll_visible = true;
  484. main->first_invalid_line = 0;
  485. scroll_w = vscroll->get_combined_minimum_size().width;
  486. vscroll->show();
  487. vscroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -scroll_w);
  488. _validate_line_caches(main);
  489. } else {
  490. scroll_visible = false;
  491. vscroll->hide();
  492. scroll_w = 0;
  493. _validate_line_caches(main);
  494. }
  495. }
  496. }
  497. void RichTextLabel::_notification(int p_what) {
  498. switch (p_what) {
  499. case NOTIFICATION_RESIZED: {
  500. main->first_invalid_line = 0; //invalidate ALL
  501. update();
  502. } break;
  503. case NOTIFICATION_ENTER_TREE: {
  504. if (bbcode != "")
  505. set_bbcode(bbcode);
  506. main->first_invalid_line = 0; //invalidate ALL
  507. update();
  508. } break;
  509. case NOTIFICATION_THEME_CHANGED: {
  510. if (is_inside_tree() && use_bbcode) {
  511. parse_bbcode(bbcode);
  512. //first_invalid_line=0; //invalidate ALL
  513. //update();
  514. }
  515. } break;
  516. case NOTIFICATION_DRAW: {
  517. _validate_line_caches(main);
  518. _update_scroll();
  519. RID ci = get_canvas_item();
  520. Size2 size = get_size();
  521. Rect2 text_rect = _get_text_rect();
  522. draw_style_box(get_stylebox("normal"), Rect2(Point2(), size));
  523. if (has_focus()) {
  524. VisualServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
  525. draw_style_box(get_stylebox("focus"), Rect2(Point2(), size));
  526. VisualServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
  527. }
  528. int ofs = vscroll->get_value();
  529. //todo, change to binary search
  530. int from_line = 0;
  531. int total_chars = 0;
  532. while (from_line < main->lines.size()) {
  533. if (main->lines[from_line].height_accum_cache + _get_text_rect().get_position().y >= ofs)
  534. break;
  535. total_chars += main->lines[from_line].char_count;
  536. from_line++;
  537. }
  538. if (from_line >= main->lines.size())
  539. break; //nothing to draw
  540. int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs;
  541. Ref<Font> base_font = get_font("normal_font");
  542. Color base_color = get_color("default_color");
  543. visible_line_count = 0;
  544. while (y < size.height && from_line < main->lines.size()) {
  545. visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, Point2i(), NULL, NULL, NULL, total_chars);
  546. total_chars += main->lines[from_line].char_count;
  547. from_line++;
  548. }
  549. }
  550. }
  551. }
  552. void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item, int *r_click_char, bool *r_outside) {
  553. if (r_click_item)
  554. *r_click_item = NULL;
  555. Size2 size = get_size();
  556. Rect2 text_rect = _get_text_rect();
  557. int ofs = vscroll->get_value();
  558. //todo, change to binary search
  559. int from_line = 0;
  560. while (from_line < p_frame->lines.size()) {
  561. if (p_frame->lines[from_line].height_accum_cache >= ofs)
  562. break;
  563. from_line++;
  564. }
  565. if (from_line >= p_frame->lines.size())
  566. return;
  567. int y = (p_frame->lines[from_line].height_accum_cache - p_frame->lines[from_line].height_cache) - ofs;
  568. Ref<Font> base_font = get_font("normal_font");
  569. Color base_color = get_color("default_color");
  570. while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) {
  571. _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, p_click, r_click_item, r_click_char, r_outside);
  572. if (r_click_item && *r_click_item)
  573. return;
  574. from_line++;
  575. }
  576. }
  577. Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const {
  578. if (!underline_meta || selection.click)
  579. return CURSOR_ARROW;
  580. if (main->first_invalid_line < main->lines.size())
  581. return CURSOR_ARROW; //invalid
  582. int line = 0;
  583. Item *item = NULL;
  584. ((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line);
  585. if (item && ((RichTextLabel *)(this))->_find_meta(item, NULL))
  586. return CURSOR_POINTING_HAND;
  587. return CURSOR_ARROW;
  588. }
  589. void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
  590. Ref<InputEventMouseButton> b = p_event;
  591. if (b.is_valid()) {
  592. if (main->first_invalid_line < main->lines.size())
  593. return;
  594. if (b->get_button_index() == BUTTON_LEFT) {
  595. if (true) {
  596. if (b->is_pressed() && !b->is_doubleclick()) {
  597. int line = 0;
  598. Item *item = NULL;
  599. bool outside;
  600. _find_click(main, b->get_position(), &item, &line, &outside);
  601. if (item) {
  602. Variant meta;
  603. if (!outside && _find_meta(item, &meta)) {
  604. //meta clicked
  605. emit_signal("meta_clicked", meta);
  606. } else if (selection.enabled) {
  607. selection.click = item;
  608. selection.click_char = line;
  609. }
  610. }
  611. } else if (!b->is_pressed()) {
  612. selection.click = NULL;
  613. }
  614. }
  615. }
  616. if (b->get_button_index() == BUTTON_WHEEL_UP) {
  617. if (scroll_active)
  618. vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
  619. }
  620. if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
  621. if (scroll_active)
  622. vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
  623. }
  624. }
  625. Ref<InputEventKey> k = p_event;
  626. if (k.is_valid()) {
  627. if (k->is_pressed() && !k->get_alt() && !k->get_shift()) {
  628. bool handled = true;
  629. switch (k->get_scancode()) {
  630. case KEY_PAGEUP: {
  631. if (vscroll->is_visible_in_tree())
  632. vscroll->set_value(vscroll->get_value() - vscroll->get_page());
  633. } break;
  634. case KEY_PAGEDOWN: {
  635. if (vscroll->is_visible_in_tree())
  636. vscroll->set_value(vscroll->get_value() + vscroll->get_page());
  637. } break;
  638. case KEY_UP: {
  639. if (vscroll->is_visible_in_tree())
  640. vscroll->set_value(vscroll->get_value() - get_font("normal_font")->get_height());
  641. } break;
  642. case KEY_DOWN: {
  643. if (vscroll->is_visible_in_tree())
  644. vscroll->set_value(vscroll->get_value() + get_font("normal_font")->get_height());
  645. } break;
  646. case KEY_HOME: {
  647. if (vscroll->is_visible_in_tree())
  648. vscroll->set_value(0);
  649. } break;
  650. case KEY_END: {
  651. if (vscroll->is_visible_in_tree())
  652. vscroll->set_value(vscroll->get_max());
  653. } break;
  654. case KEY_INSERT:
  655. case KEY_C: {
  656. if (k->get_command()) {
  657. selection_copy();
  658. } else {
  659. handled = false;
  660. }
  661. } break;
  662. default: handled = false;
  663. }
  664. if (handled)
  665. accept_event();
  666. }
  667. }
  668. Ref<InputEventMouseMotion> m = p_event;
  669. if (m.is_valid()) {
  670. if (main->first_invalid_line < main->lines.size())
  671. return;
  672. if (selection.click) {
  673. int line = 0;
  674. Item *item = NULL;
  675. _find_click(main, m->get_position(), &item, &line);
  676. if (!item)
  677. return; // do not update
  678. selection.from = selection.click;
  679. selection.from_char = selection.click_char;
  680. selection.to = item;
  681. selection.to_char = line;
  682. bool swap = false;
  683. if (selection.from->index > selection.to->index)
  684. swap = true;
  685. else if (selection.from->index == selection.to->index) {
  686. if (selection.from_char > selection.to_char)
  687. swap = true;
  688. else if (selection.from_char == selection.to_char) {
  689. selection.active = false;
  690. return;
  691. }
  692. }
  693. if (swap) {
  694. SWAP(selection.from, selection.to);
  695. SWAP(selection.from_char, selection.to_char);
  696. }
  697. selection.active = true;
  698. update();
  699. }
  700. }
  701. }
  702. Ref<Font> RichTextLabel::_find_font(Item *p_item) {
  703. Item *fontitem = p_item;
  704. while (fontitem) {
  705. if (fontitem->type == ITEM_FONT) {
  706. ItemFont *fi = static_cast<ItemFont *>(fontitem);
  707. return fi->font;
  708. }
  709. fontitem = fontitem->parent;
  710. }
  711. return Ref<Font>();
  712. }
  713. int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font) {
  714. Item *item = p_item;
  715. int margin = 0;
  716. while (item) {
  717. if (item->type == ITEM_INDENT) {
  718. Ref<Font> font = _find_font(item);
  719. if (font.is_null())
  720. font = p_base_font;
  721. ItemIndent *indent = static_cast<ItemIndent *>(item);
  722. margin += indent->level * tab_size * font->get_char_size(' ').width;
  723. } else if (item->type == ITEM_LIST) {
  724. Ref<Font> font = _find_font(item);
  725. if (font.is_null())
  726. font = p_base_font;
  727. }
  728. item = item->parent;
  729. }
  730. return margin;
  731. }
  732. RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) {
  733. Item *item = p_item;
  734. while (item) {
  735. if (item->type == ITEM_ALIGN) {
  736. ItemAlign *align = static_cast<ItemAlign *>(item);
  737. return align->align;
  738. }
  739. item = item->parent;
  740. }
  741. return default_align;
  742. }
  743. Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) {
  744. Item *item = p_item;
  745. while (item) {
  746. if (item->type == ITEM_COLOR) {
  747. ItemColor *color = static_cast<ItemColor *>(item);
  748. return color->color;
  749. }
  750. item = item->parent;
  751. }
  752. return p_default_color;
  753. }
  754. bool RichTextLabel::_find_underline(Item *p_item) {
  755. Item *item = p_item;
  756. while (item) {
  757. if (item->type == ITEM_UNDERLINE) {
  758. return true;
  759. }
  760. item = item->parent;
  761. }
  762. return false;
  763. }
  764. bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta) {
  765. Item *item = p_item;
  766. while (item) {
  767. if (item->type == ITEM_META) {
  768. ItemMeta *meta = static_cast<ItemMeta *>(item);
  769. if (r_meta)
  770. *r_meta = meta->meta;
  771. return true;
  772. }
  773. item = item->parent;
  774. }
  775. return false;
  776. }
  777. void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
  778. if (p_frame->first_invalid_line == p_frame->lines.size())
  779. return;
  780. //validate invalid lines
  781. Size2 size = get_size();
  782. Rect2 text_rect = _get_text_rect();
  783. Ref<Font> base_font = get_font("normal_font");
  784. for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) {
  785. int y = 0;
  786. _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color());
  787. p_frame->lines[i].height_cache = y;
  788. p_frame->lines[i].height_accum_cache = y;
  789. if (i > 0)
  790. p_frame->lines[i].height_accum_cache += p_frame->lines[i - 1].height_accum_cache;
  791. }
  792. int total_height = 0;
  793. if (p_frame->lines.size())
  794. total_height = p_frame->lines[p_frame->lines.size() - 1].height_accum_cache + get_stylebox("normal")->get_minimum_size().height;
  795. main->first_invalid_line = p_frame->lines.size();
  796. updating_scroll = true;
  797. vscroll->set_max(total_height);
  798. vscroll->set_page(size.height);
  799. if (scroll_follow && scroll_following)
  800. vscroll->set_value(total_height - size.height);
  801. updating_scroll = false;
  802. }
  803. void RichTextLabel::_invalidate_current_line(ItemFrame *p_frame) {
  804. if (p_frame->lines.size() - 1 <= p_frame->first_invalid_line) {
  805. p_frame->first_invalid_line = p_frame->lines.size() - 1;
  806. update();
  807. }
  808. }
  809. void RichTextLabel::add_text(const String &p_text) {
  810. if (current->type == ITEM_TABLE)
  811. return; //can't add anything here
  812. int pos = 0;
  813. while (pos < p_text.length()) {
  814. int end = p_text.find("\n", pos);
  815. String line;
  816. bool eol = false;
  817. if (end == -1) {
  818. end = p_text.length();
  819. } else {
  820. eol = true;
  821. }
  822. if (pos == 0 && end == p_text.length())
  823. line = p_text;
  824. else
  825. line = p_text.substr(pos, end - pos);
  826. if (line.length() > 0) {
  827. if (current->subitems.size() && current->subitems.back()->get()->type == ITEM_TEXT) {
  828. //append text condition!
  829. ItemText *ti = static_cast<ItemText *>(current->subitems.back()->get());
  830. ti->text += line;
  831. _invalidate_current_line(main);
  832. } else {
  833. //append item condition
  834. ItemText *item = memnew(ItemText);
  835. item->text = line;
  836. _add_item(item, false);
  837. }
  838. }
  839. if (eol) {
  840. ItemNewline *item = memnew(ItemNewline);
  841. item->line = current_frame->lines.size();
  842. _add_item(item, false);
  843. current_frame->lines.resize(current_frame->lines.size() + 1);
  844. if (item->type != ITEM_NEWLINE)
  845. current_frame->lines[current_frame->lines.size() - 1].from = item;
  846. _invalidate_current_line(current_frame);
  847. }
  848. pos = end + 1;
  849. }
  850. }
  851. void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) {
  852. p_item->parent = current;
  853. p_item->E = current->subitems.push_back(p_item);
  854. p_item->index = current_idx++;
  855. if (p_enter)
  856. current = p_item;
  857. if (p_ensure_newline && current_frame->lines[current_frame->lines.size() - 1].from) {
  858. _invalidate_current_line(current_frame);
  859. current_frame->lines.resize(current_frame->lines.size() + 1);
  860. }
  861. if (current_frame->lines[current_frame->lines.size() - 1].from == NULL) {
  862. current_frame->lines[current_frame->lines.size() - 1].from = p_item;
  863. }
  864. p_item->line = current_frame->lines.size() - 1;
  865. _invalidate_current_line(current_frame);
  866. }
  867. void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_subitem_line) {
  868. int size = p_item->subitems.size();
  869. if (size == 0) {
  870. p_item->parent->subitems.erase(p_item);
  871. if (p_item->type == ITEM_NEWLINE) {
  872. current_frame->lines.remove(p_line);
  873. for (int i = p_subitem_line; i < current->subitems.size(); i++) {
  874. if (current->subitems[i]->line > 0)
  875. current->subitems[i]->line--;
  876. }
  877. }
  878. } else {
  879. for (int i = 0; i < size; i++) {
  880. _remove_item(p_item->subitems.front()->get(), p_line, p_subitem_line);
  881. }
  882. }
  883. }
  884. void RichTextLabel::add_image(const Ref<Texture> &p_image) {
  885. if (current->type == ITEM_TABLE)
  886. return;
  887. ERR_FAIL_COND(p_image.is_null());
  888. ItemImage *item = memnew(ItemImage);
  889. item->image = p_image;
  890. _add_item(item, false);
  891. }
  892. void RichTextLabel::add_newline() {
  893. if (current->type == ITEM_TABLE)
  894. return;
  895. ItemNewline *item = memnew(ItemNewline);
  896. item->line = current_frame->lines.size();
  897. _add_item(item, false);
  898. current_frame->lines.resize(current_frame->lines.size() + 1);
  899. _invalidate_current_line(current_frame);
  900. }
  901. bool RichTextLabel::remove_line(const int p_line) {
  902. if (p_line >= current_frame->lines.size() || p_line < 0)
  903. return false;
  904. int lines = p_line * 2;
  905. if (current->subitems[lines]->type != ITEM_NEWLINE)
  906. _remove_item(current->subitems[lines], current->subitems[lines]->line, lines);
  907. _remove_item(current->subitems[lines], current->subitems[lines]->line, lines);
  908. if (p_line == 0) {
  909. main->lines[0].from = main;
  910. }
  911. main->first_invalid_line = 0;
  912. return true;
  913. }
  914. void RichTextLabel::push_font(const Ref<Font> &p_font) {
  915. ERR_FAIL_COND(current->type == ITEM_TABLE);
  916. ERR_FAIL_COND(p_font.is_null());
  917. ItemFont *item = memnew(ItemFont);
  918. item->font = p_font;
  919. _add_item(item, true);
  920. }
  921. void RichTextLabel::push_color(const Color &p_color) {
  922. ERR_FAIL_COND(current->type == ITEM_TABLE);
  923. ItemColor *item = memnew(ItemColor);
  924. item->color = p_color;
  925. _add_item(item, true);
  926. }
  927. void RichTextLabel::push_underline() {
  928. ERR_FAIL_COND(current->type == ITEM_TABLE);
  929. ItemUnderline *item = memnew(ItemUnderline);
  930. _add_item(item, true);
  931. }
  932. void RichTextLabel::push_align(Align p_align) {
  933. ERR_FAIL_COND(current->type == ITEM_TABLE);
  934. ItemAlign *item = memnew(ItemAlign);
  935. item->align = p_align;
  936. _add_item(item, true, true);
  937. }
  938. void RichTextLabel::push_indent(int p_level) {
  939. ERR_FAIL_COND(current->type == ITEM_TABLE);
  940. ERR_FAIL_COND(p_level < 0);
  941. ItemIndent *item = memnew(ItemIndent);
  942. item->level = p_level;
  943. _add_item(item, true, true);
  944. }
  945. void RichTextLabel::push_list(ListType p_list) {
  946. ERR_FAIL_COND(current->type == ITEM_TABLE);
  947. ERR_FAIL_INDEX(p_list, 3);
  948. ItemList *item = memnew(ItemList);
  949. item->list_type = p_list;
  950. _add_item(item, true, true);
  951. }
  952. void RichTextLabel::push_meta(const Variant &p_meta) {
  953. ERR_FAIL_COND(current->type == ITEM_TABLE);
  954. ItemMeta *item = memnew(ItemMeta);
  955. item->meta = p_meta;
  956. _add_item(item, true);
  957. }
  958. void RichTextLabel::push_table(int p_columns) {
  959. ERR_FAIL_COND(p_columns < 1);
  960. ItemTable *item = memnew(ItemTable);
  961. item->columns.resize(p_columns);
  962. item->total_width = 0;
  963. for (int i = 0; i < item->columns.size(); i++) {
  964. item->columns[i].expand = false;
  965. item->columns[i].expand_ratio = 1;
  966. }
  967. _add_item(item, true, true);
  968. }
  969. void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
  970. ERR_FAIL_COND(current->type != ITEM_TABLE);
  971. ItemTable *table = static_cast<ItemTable *>(current);
  972. ERR_FAIL_INDEX(p_column, table->columns.size());
  973. table->columns[p_column].expand = p_expand;
  974. table->columns[p_column].expand_ratio = p_ratio;
  975. }
  976. void RichTextLabel::push_cell() {
  977. ERR_FAIL_COND(current->type != ITEM_TABLE);
  978. ItemFrame *item = memnew(ItemFrame);
  979. item->parent_frame = current_frame;
  980. _add_item(item, true);
  981. current_frame = item;
  982. item->cell = true;
  983. item->parent_line = item->parent_frame->lines.size() - 1;
  984. item->lines.resize(1);
  985. item->lines[0].from = NULL;
  986. item->first_invalid_line = 0;
  987. }
  988. int RichTextLabel::get_current_table_column() const {
  989. ERR_FAIL_COND_V(current->type != ITEM_TABLE, -1);
  990. ItemTable *table = static_cast<ItemTable *>(current);
  991. return table->subitems.size() % table->columns.size();
  992. }
  993. void RichTextLabel::pop() {
  994. ERR_FAIL_COND(!current->parent);
  995. if (current->type == ITEM_FRAME) {
  996. current_frame = static_cast<ItemFrame *>(current)->parent_frame;
  997. }
  998. current = current->parent;
  999. }
  1000. void RichTextLabel::clear() {
  1001. main->_clear_children();
  1002. current = main;
  1003. current_frame = main;
  1004. main->lines.clear();
  1005. main->lines.resize(1);
  1006. main->first_invalid_line = 0;
  1007. update();
  1008. selection.click = NULL;
  1009. selection.active = false;
  1010. current_idx = 1;
  1011. }
  1012. void RichTextLabel::set_tab_size(int p_spaces) {
  1013. tab_size = p_spaces;
  1014. main->first_invalid_line = 0;
  1015. update();
  1016. }
  1017. int RichTextLabel::get_tab_size() const {
  1018. return tab_size;
  1019. }
  1020. void RichTextLabel::set_meta_underline(bool p_underline) {
  1021. underline_meta = p_underline;
  1022. update();
  1023. }
  1024. bool RichTextLabel::is_meta_underlined() const {
  1025. return underline_meta;
  1026. }
  1027. void RichTextLabel::set_override_selected_font_color(bool p_override_selected_font_color) {
  1028. override_selected_font_color = p_override_selected_font_color;
  1029. }
  1030. bool RichTextLabel::is_overriding_selected_font_color() const {
  1031. return override_selected_font_color;
  1032. }
  1033. void RichTextLabel::set_offset(int p_pixel) {
  1034. vscroll->set_value(p_pixel);
  1035. }
  1036. void RichTextLabel::set_scroll_active(bool p_active) {
  1037. if (scroll_active == p_active)
  1038. return;
  1039. scroll_active = p_active;
  1040. update();
  1041. }
  1042. bool RichTextLabel::is_scroll_active() const {
  1043. return scroll_active;
  1044. }
  1045. void RichTextLabel::set_scroll_follow(bool p_follow) {
  1046. scroll_follow = p_follow;
  1047. if (!vscroll->is_visible_in_tree() || vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page()))
  1048. scroll_following = true;
  1049. }
  1050. bool RichTextLabel::is_scroll_following() const {
  1051. return scroll_follow;
  1052. }
  1053. Error RichTextLabel::parse_bbcode(const String &p_bbcode) {
  1054. clear();
  1055. return append_bbcode(p_bbcode);
  1056. }
  1057. Error RichTextLabel::append_bbcode(const String &p_bbcode) {
  1058. int pos = 0;
  1059. List<String> tag_stack;
  1060. Ref<Font> normal_font = get_font("normal_font");
  1061. Ref<Font> bold_font = get_font("bold_font");
  1062. Ref<Font> italics_font = get_font("italics_font");
  1063. Ref<Font> bold_italics_font = get_font("bold_italics_font");
  1064. Ref<Font> mono_font = get_font("mono_font");
  1065. Color base_color = get_color("default_color");
  1066. int indent_level = 0;
  1067. bool in_bold = false;
  1068. bool in_italics = false;
  1069. while (pos < p_bbcode.length()) {
  1070. int brk_pos = p_bbcode.find("[", pos);
  1071. if (brk_pos < 0)
  1072. brk_pos = p_bbcode.length();
  1073. if (brk_pos > pos) {
  1074. add_text(p_bbcode.substr(pos, brk_pos - pos));
  1075. }
  1076. if (brk_pos == p_bbcode.length())
  1077. break; //nothing else o add
  1078. int brk_end = p_bbcode.find("]", brk_pos + 1);
  1079. if (brk_end == -1) {
  1080. //no close, add the rest
  1081. add_text(p_bbcode.substr(brk_pos, p_bbcode.length() - brk_pos));
  1082. break;
  1083. }
  1084. String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
  1085. if (tag.begins_with("/") && tag_stack.size()) {
  1086. bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
  1087. if (tag_stack.front()->get() == "b")
  1088. in_bold = false;
  1089. if (tag_stack.front()->get() == "i")
  1090. in_italics = false;
  1091. if (tag_stack.front()->get() == "indent")
  1092. indent_level--;
  1093. if (!tag_ok) {
  1094. add_text("[");
  1095. pos++;
  1096. continue;
  1097. }
  1098. tag_stack.pop_front();
  1099. pos = brk_end + 1;
  1100. if (tag != "/img")
  1101. pop();
  1102. } else if (tag == "b") {
  1103. //use bold font
  1104. in_bold = true;
  1105. if (in_italics)
  1106. push_font(bold_italics_font);
  1107. else
  1108. push_font(bold_font);
  1109. pos = brk_end + 1;
  1110. tag_stack.push_front(tag);
  1111. } else if (tag == "i") {
  1112. //use italics font
  1113. in_italics = true;
  1114. if (in_bold)
  1115. push_font(bold_italics_font);
  1116. else
  1117. push_font(italics_font);
  1118. pos = brk_end + 1;
  1119. tag_stack.push_front(tag);
  1120. } else if (tag == "code") {
  1121. //use monospace font
  1122. push_font(mono_font);
  1123. pos = brk_end + 1;
  1124. tag_stack.push_front(tag);
  1125. } else if (tag.begins_with("table=")) {
  1126. int columns = tag.substr(6, tag.length()).to_int();
  1127. if (columns < 1)
  1128. columns = 1;
  1129. //use monospace font
  1130. push_table(columns);
  1131. pos = brk_end + 1;
  1132. tag_stack.push_front("table");
  1133. } else if (tag == "cell") {
  1134. push_cell();
  1135. pos = brk_end + 1;
  1136. tag_stack.push_front(tag);
  1137. } else if (tag.begins_with("cell=")) {
  1138. int ratio = tag.substr(6, tag.length()).to_int();
  1139. if (ratio < 1)
  1140. ratio = 1;
  1141. //use monospace font
  1142. set_table_column_expand(get_current_table_column(), true, ratio);
  1143. push_cell();
  1144. pos = brk_end + 1;
  1145. tag_stack.push_front("cell");
  1146. } else if (tag == "u") {
  1147. //use underline
  1148. push_underline();
  1149. pos = brk_end + 1;
  1150. tag_stack.push_front(tag);
  1151. } else if (tag == "s") {
  1152. //use strikethrough (not supported underline instead)
  1153. push_underline();
  1154. pos = brk_end + 1;
  1155. tag_stack.push_front(tag);
  1156. } else if (tag == "center") {
  1157. //use underline
  1158. push_align(ALIGN_CENTER);
  1159. pos = brk_end + 1;
  1160. tag_stack.push_front(tag);
  1161. } else if (tag == "fill") {
  1162. //use underline
  1163. push_align(ALIGN_FILL);
  1164. pos = brk_end + 1;
  1165. tag_stack.push_front(tag);
  1166. } else if (tag == "right") {
  1167. //use underline
  1168. push_align(ALIGN_RIGHT);
  1169. pos = brk_end + 1;
  1170. tag_stack.push_front(tag);
  1171. } else if (tag == "ul") {
  1172. //use underline
  1173. push_list(LIST_DOTS);
  1174. pos = brk_end + 1;
  1175. tag_stack.push_front(tag);
  1176. } else if (tag == "ol") {
  1177. //use underline
  1178. push_list(LIST_NUMBERS);
  1179. pos = brk_end + 1;
  1180. tag_stack.push_front(tag);
  1181. } else if (tag == "indent") {
  1182. //use underline
  1183. indent_level++;
  1184. push_indent(indent_level);
  1185. pos = brk_end + 1;
  1186. tag_stack.push_front(tag);
  1187. } else if (tag == "url") {
  1188. //use strikethrough (not supported underline instead)
  1189. int end = p_bbcode.find("[", brk_end);
  1190. if (end == -1)
  1191. end = p_bbcode.length();
  1192. String url = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
  1193. push_meta(url);
  1194. pos = brk_end + 1;
  1195. tag_stack.push_front(tag);
  1196. } else if (tag.begins_with("url=")) {
  1197. String url = tag.substr(4, tag.length());
  1198. push_meta(url);
  1199. pos = brk_end + 1;
  1200. tag_stack.push_front("url");
  1201. } else if (tag == "img") {
  1202. //use strikethrough (not supported underline instead)
  1203. int end = p_bbcode.find("[", brk_end);
  1204. if (end == -1)
  1205. end = p_bbcode.length();
  1206. String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
  1207. Ref<Texture> texture = ResourceLoader::load(image, "Texture");
  1208. if (texture.is_valid())
  1209. add_image(texture);
  1210. pos = end;
  1211. tag_stack.push_front(tag);
  1212. } else if (tag.begins_with("color=")) {
  1213. String col = tag.substr(6, tag.length());
  1214. Color color;
  1215. if (col.begins_with("#"))
  1216. color = Color::html(col);
  1217. else if (col == "aqua")
  1218. color = Color::html("#00FFFF");
  1219. else if (col == "black")
  1220. color = Color::html("#000000");
  1221. else if (col == "blue")
  1222. color = Color::html("#0000FF");
  1223. else if (col == "fuchsia")
  1224. color = Color::html("#FF00FF");
  1225. else if (col == "gray" || col == "grey")
  1226. color = Color::html("#808080");
  1227. else if (col == "green")
  1228. color = Color::html("#008000");
  1229. else if (col == "lime")
  1230. color = Color::html("#00FF00");
  1231. else if (col == "maroon")
  1232. color = Color::html("#800000");
  1233. else if (col == "navy")
  1234. color = Color::html("#000080");
  1235. else if (col == "olive")
  1236. color = Color::html("#808000");
  1237. else if (col == "purple")
  1238. color = Color::html("#800080");
  1239. else if (col == "red")
  1240. color = Color::html("#FF0000");
  1241. else if (col == "silver")
  1242. color = Color::html("#C0C0C0");
  1243. else if (col == "teal")
  1244. color = Color::html("#008008");
  1245. else if (col == "white")
  1246. color = Color::html("#FFFFFF");
  1247. else if (col == "yellow")
  1248. color = Color::html("#FFFF00");
  1249. else
  1250. color = base_color;
  1251. push_color(color);
  1252. pos = brk_end + 1;
  1253. tag_stack.push_front("color");
  1254. } else if (tag.begins_with("font=")) {
  1255. String fnt = tag.substr(5, tag.length());
  1256. Ref<Font> font = ResourceLoader::load(fnt, "Font");
  1257. if (font.is_valid())
  1258. push_font(font);
  1259. else
  1260. push_font(normal_font);
  1261. pos = brk_end + 1;
  1262. tag_stack.push_front("font");
  1263. } else {
  1264. add_text("["); //ignore
  1265. pos = brk_pos + 1;
  1266. }
  1267. }
  1268. return OK;
  1269. }
  1270. void RichTextLabel::scroll_to_line(int p_line) {
  1271. ERR_FAIL_INDEX(p_line, main->lines.size());
  1272. _validate_line_caches(main);
  1273. vscroll->set_value(main->lines[p_line].height_accum_cache - main->lines[p_line].height_cache);
  1274. }
  1275. int RichTextLabel::get_line_count() const {
  1276. return current_frame->lines.size();
  1277. }
  1278. int RichTextLabel::get_visible_line_count() const {
  1279. if (!is_visible())
  1280. return 0;
  1281. return visible_line_count;
  1282. }
  1283. void RichTextLabel::set_selection_enabled(bool p_enabled) {
  1284. selection.enabled = p_enabled;
  1285. if (!p_enabled) {
  1286. if (selection.active) {
  1287. selection.active = false;
  1288. update();
  1289. }
  1290. set_focus_mode(FOCUS_NONE);
  1291. } else {
  1292. set_focus_mode(FOCUS_ALL);
  1293. }
  1294. }
  1295. bool RichTextLabel::search(const String &p_string, bool p_from_selection) {
  1296. ERR_FAIL_COND_V(!selection.enabled, false);
  1297. Item *it = main;
  1298. int charidx = 0;
  1299. if (p_from_selection && selection.active && selection.enabled) {
  1300. it = selection.to;
  1301. charidx = selection.to_char + 1;
  1302. }
  1303. while (it) {
  1304. if (it->type == ITEM_TEXT) {
  1305. ItemText *t = static_cast<ItemText *>(it);
  1306. int sp = t->text.find(p_string, charidx);
  1307. if (sp != -1) {
  1308. selection.from = it;
  1309. selection.from_char = sp;
  1310. selection.to = it;
  1311. selection.to_char = sp + p_string.length() - 1;
  1312. selection.active = true;
  1313. update();
  1314. _validate_line_caches(main);
  1315. int fh = _find_font(t).is_valid() ? _find_font(t)->get_height() : get_font("normal_font")->get_height();
  1316. float offset = 0;
  1317. int line = t->line;
  1318. Item *item = t;
  1319. while (item) {
  1320. if (item->type == ITEM_FRAME) {
  1321. ItemFrame *frame = static_cast<ItemFrame *>(item);
  1322. if (line >= 0 && line < frame->lines.size()) {
  1323. offset += frame->lines[line].height_accum_cache - frame->lines[line].height_cache;
  1324. line = frame->line;
  1325. }
  1326. }
  1327. item = item->parent;
  1328. }
  1329. vscroll->set_value(offset - fh);
  1330. return true;
  1331. }
  1332. }
  1333. it = _get_next_item(it, true);
  1334. charidx = 0;
  1335. }
  1336. return false;
  1337. }
  1338. void RichTextLabel::selection_copy() {
  1339. if (!selection.enabled)
  1340. return;
  1341. String text;
  1342. RichTextLabel::Item *item = selection.from;
  1343. while (item) {
  1344. if (item->type == ITEM_TEXT) {
  1345. String itext = static_cast<ItemText *>(item)->text;
  1346. if (item == selection.from && item == selection.to) {
  1347. text += itext.substr(selection.from_char, selection.to_char - selection.from_char + 1);
  1348. } else if (item == selection.from) {
  1349. text += itext.substr(selection.from_char, itext.size());
  1350. } else if (item == selection.to) {
  1351. text += itext.substr(0, selection.to_char + 1);
  1352. } else {
  1353. text += itext;
  1354. }
  1355. } else if (item->type == ITEM_NEWLINE) {
  1356. text += "\n";
  1357. }
  1358. if (item == selection.to)
  1359. break;
  1360. item = _get_next_item(item, true);
  1361. }
  1362. if (text != "") {
  1363. OS::get_singleton()->set_clipboard(text);
  1364. //print_line("COPY: "+text);
  1365. }
  1366. }
  1367. bool RichTextLabel::is_selection_enabled() const {
  1368. return selection.enabled;
  1369. }
  1370. void RichTextLabel::set_bbcode(const String &p_bbcode) {
  1371. bbcode = p_bbcode;
  1372. if (is_inside_tree() && use_bbcode)
  1373. parse_bbcode(p_bbcode);
  1374. else { // raw text
  1375. clear();
  1376. add_text(p_bbcode);
  1377. }
  1378. }
  1379. String RichTextLabel::get_bbcode() const {
  1380. return bbcode;
  1381. }
  1382. void RichTextLabel::set_use_bbcode(bool p_enable) {
  1383. if (use_bbcode == p_enable)
  1384. return;
  1385. use_bbcode = p_enable;
  1386. set_bbcode(bbcode);
  1387. }
  1388. bool RichTextLabel::is_using_bbcode() const {
  1389. return use_bbcode;
  1390. }
  1391. String RichTextLabel::get_text() {
  1392. String text = "";
  1393. Item *it = main;
  1394. while (it) {
  1395. if (it->type == ITEM_TEXT) {
  1396. ItemText *t = static_cast<ItemText *>(it);
  1397. text += t->text;
  1398. } else if (it->type == ITEM_NEWLINE) {
  1399. text += "\n";
  1400. } else if (it->type == ITEM_INDENT) {
  1401. text += "\t";
  1402. }
  1403. it = _get_next_item(it, true);
  1404. }
  1405. return text;
  1406. }
  1407. void RichTextLabel::set_text(const String &p_string) {
  1408. clear();
  1409. add_text(p_string);
  1410. }
  1411. void RichTextLabel::set_percent_visible(float p_percent) {
  1412. if (p_percent < 0 || p_percent >= 1) {
  1413. visible_characters = -1;
  1414. percent_visible = 1;
  1415. } else {
  1416. visible_characters = get_total_character_count() * p_percent;
  1417. percent_visible = p_percent;
  1418. }
  1419. update();
  1420. }
  1421. float RichTextLabel::get_percent_visible() const {
  1422. return percent_visible;
  1423. }
  1424. void RichTextLabel::_bind_methods() {
  1425. ClassDB::bind_method(D_METHOD("_gui_input"), &RichTextLabel::_gui_input);
  1426. ClassDB::bind_method(D_METHOD("_scroll_changed"), &RichTextLabel::_scroll_changed);
  1427. ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
  1428. ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);
  1429. ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text);
  1430. ClassDB::bind_method(D_METHOD("add_image", "image"), &RichTextLabel::add_image);
  1431. ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
  1432. ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
  1433. ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
  1434. ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color);
  1435. ClassDB::bind_method(D_METHOD("push_align", "align"), &RichTextLabel::push_align);
  1436. ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent);
  1437. ClassDB::bind_method(D_METHOD("push_list", "type"), &RichTextLabel::push_list);
  1438. ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta);
  1439. ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline);
  1440. ClassDB::bind_method(D_METHOD("push_table", "columns"), &RichTextLabel::push_table);
  1441. ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
  1442. ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell);
  1443. ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop);
  1444. ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear);
  1445. ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline);
  1446. ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined);
  1447. ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &RichTextLabel::set_override_selected_font_color);
  1448. ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &RichTextLabel::is_overriding_selected_font_color);
  1449. ClassDB::bind_method(D_METHOD("set_scroll_active", "active"), &RichTextLabel::set_scroll_active);
  1450. ClassDB::bind_method(D_METHOD("is_scroll_active"), &RichTextLabel::is_scroll_active);
  1451. ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow);
  1452. ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following);
  1453. ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll);
  1454. ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line);
  1455. ClassDB::bind_method(D_METHOD("set_tab_size", "spaces"), &RichTextLabel::set_tab_size);
  1456. ClassDB::bind_method(D_METHOD("get_tab_size"), &RichTextLabel::get_tab_size);
  1457. ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled);
  1458. ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled);
  1459. ClassDB::bind_method(D_METHOD("parse_bbcode", "bbcode"), &RichTextLabel::parse_bbcode);
  1460. ClassDB::bind_method(D_METHOD("append_bbcode", "bbcode"), &RichTextLabel::append_bbcode);
  1461. ClassDB::bind_method(D_METHOD("set_bbcode", "text"), &RichTextLabel::set_bbcode);
  1462. ClassDB::bind_method(D_METHOD("get_bbcode"), &RichTextLabel::get_bbcode);
  1463. ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &RichTextLabel::set_visible_characters);
  1464. ClassDB::bind_method(D_METHOD("get_visible_characters"), &RichTextLabel::get_visible_characters);
  1465. ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &RichTextLabel::set_percent_visible);
  1466. ClassDB::bind_method(D_METHOD("get_percent_visible"), &RichTextLabel::get_percent_visible);
  1467. ClassDB::bind_method(D_METHOD("get_total_character_count"), &RichTextLabel::get_total_character_count);
  1468. ClassDB::bind_method(D_METHOD("set_use_bbcode", "enable"), &RichTextLabel::set_use_bbcode);
  1469. ClassDB::bind_method(D_METHOD("is_using_bbcode"), &RichTextLabel::is_using_bbcode);
  1470. ClassDB::bind_method(D_METHOD("get_line_count"), &RichTextLabel::get_line_count);
  1471. ClassDB::bind_method(D_METHOD("get_visible_line_count"), &RichTextLabel::get_visible_line_count);
  1472. ADD_GROUP("BBCode", "bbcode_");
  1473. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
  1474. ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode");
  1475. ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
  1476. ADD_PROPERTY(PropertyInfo(Variant::REAL, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
  1477. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
  1478. ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
  1479. BIND_ENUM_CONSTANT(ALIGN_LEFT);
  1480. BIND_ENUM_CONSTANT(ALIGN_CENTER);
  1481. BIND_ENUM_CONSTANT(ALIGN_RIGHT);
  1482. BIND_ENUM_CONSTANT(ALIGN_FILL);
  1483. BIND_ENUM_CONSTANT(LIST_NUMBERS);
  1484. BIND_ENUM_CONSTANT(LIST_LETTERS);
  1485. BIND_ENUM_CONSTANT(LIST_DOTS);
  1486. BIND_ENUM_CONSTANT(ITEM_FRAME);
  1487. BIND_ENUM_CONSTANT(ITEM_TEXT);
  1488. BIND_ENUM_CONSTANT(ITEM_IMAGE);
  1489. BIND_ENUM_CONSTANT(ITEM_NEWLINE);
  1490. BIND_ENUM_CONSTANT(ITEM_FONT);
  1491. BIND_ENUM_CONSTANT(ITEM_COLOR);
  1492. BIND_ENUM_CONSTANT(ITEM_UNDERLINE);
  1493. BIND_ENUM_CONSTANT(ITEM_ALIGN);
  1494. BIND_ENUM_CONSTANT(ITEM_INDENT);
  1495. BIND_ENUM_CONSTANT(ITEM_LIST);
  1496. BIND_ENUM_CONSTANT(ITEM_TABLE);
  1497. BIND_ENUM_CONSTANT(ITEM_META);
  1498. }
  1499. void RichTextLabel::set_visible_characters(int p_visible) {
  1500. visible_characters = p_visible;
  1501. update();
  1502. }
  1503. int RichTextLabel::get_visible_characters() const {
  1504. return visible_characters;
  1505. }
  1506. int RichTextLabel::get_total_character_count() const {
  1507. int tc = 0;
  1508. for (int i = 0; i < current_frame->lines.size(); i++)
  1509. tc += current_frame->lines[i].char_count;
  1510. return tc;
  1511. }
  1512. RichTextLabel::RichTextLabel() {
  1513. main = memnew(ItemFrame);
  1514. main->index = 0;
  1515. current = main;
  1516. main->lines.resize(1);
  1517. main->lines[0].from = main;
  1518. main->first_invalid_line = 0;
  1519. current_frame = main;
  1520. tab_size = 4;
  1521. default_align = ALIGN_LEFT;
  1522. underline_meta = true;
  1523. override_selected_font_color = false;
  1524. scroll_visible = false;
  1525. scroll_follow = false;
  1526. scroll_following = false;
  1527. updating_scroll = false;
  1528. scroll_active = true;
  1529. scroll_w = 0;
  1530. vscroll = memnew(VScrollBar);
  1531. add_child(vscroll);
  1532. vscroll->set_drag_slave(String(".."));
  1533. vscroll->set_step(1);
  1534. vscroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0);
  1535. vscroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0);
  1536. vscroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0);
  1537. vscroll->connect("value_changed", this, "_scroll_changed");
  1538. vscroll->set_step(1);
  1539. vscroll->hide();
  1540. current_idx = 1;
  1541. use_bbcode = false;
  1542. selection.click = NULL;
  1543. selection.active = false;
  1544. selection.enabled = false;
  1545. visible_characters = -1;
  1546. percent_visible = 1;
  1547. visible_line_count = 0;
  1548. set_clip_contents(true);
  1549. }
  1550. RichTextLabel::~RichTextLabel() {
  1551. memdelete(main);
  1552. }