rich_text_label.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764
  1. /*************************************************************************/
  2. /* rich_text_label.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* http://www.godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
  9. /* */
  10. /* Permission is hereby granted, free of charge, to any person obtaining */
  11. /* a copy of this software and associated documentation files (the */
  12. /* "Software"), to deal in the Software without restriction, including */
  13. /* without limitation the rights to use, copy, modify, merge, publish, */
  14. /* distribute, sublicense, and/or sell copies of the Software, and to */
  15. /* permit persons to whom the Software is furnished to do so, subject to */
  16. /* the following conditions: */
  17. /* */
  18. /* The above copyright notice and this permission notice shall be */
  19. /* included in all copies or substantial portions of the Software. */
  20. /* */
  21. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  22. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  23. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  24. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  25. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  26. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  27. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  28. /*************************************************************************/
  29. #include "rich_text_label.h"
  30. #include "scene/scene_string_names.h"
  31. #include "os/keyboard.h"
  32. #include "os/os.h"
  33. RichTextLabel::Item *RichTextLabel::_get_next_item(Item* p_item) {
  34. if (p_item->subitems.size()) {
  35. return p_item->subitems.front()->get();
  36. } else if (!p_item->parent) {
  37. return NULL;
  38. } else if (p_item->E->next()) {
  39. return p_item->E->next()->get();
  40. } else {
  41. //go up until something with a next is found
  42. while (p_item->parent && !p_item->E->next()) {
  43. p_item=p_item->parent;
  44. }
  45. if (p_item && p_item->parent)
  46. return p_item->E->next()->get();
  47. else
  48. return NULL;
  49. }
  50. return NULL;
  51. }
  52. void RichTextLabel::_process_line(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) {
  53. RID ci;
  54. if (r_outside)
  55. *r_outside=false;
  56. if (p_mode==PROCESS_DRAW) {
  57. ci=get_canvas_item();
  58. if (r_click_item)
  59. *r_click_item=NULL;
  60. }
  61. Line &l = lines[p_line];
  62. Item *it = l.from;
  63. int line_ofs=0;
  64. int margin=_find_margin(it,p_base_font);
  65. Align align=_find_align(it);;
  66. int line=0;
  67. int spaces=0;
  68. if (p_mode!=PROCESS_CACHE) {
  69. ERR_FAIL_INDEX(line,l.offset_caches.size());
  70. line_ofs = l.offset_caches[line];
  71. }
  72. if (p_mode==PROCESS_CACHE) {
  73. l.offset_caches.clear();
  74. l.height_caches.clear();
  75. }
  76. int wofs=margin;
  77. int spaces_size=0;
  78. if (p_mode!=PROCESS_CACHE && align!=ALIGN_FILL)
  79. wofs+=line_ofs;
  80. int begin=wofs;
  81. Ref<Font> cfont = _find_font(it);
  82. if (cfont.is_null())
  83. cfont=p_base_font;
  84. //line height should be the font height for the first time, this ensures that an empty line will never have zero height and succesive newlines are displayed
  85. int line_height=cfont->get_height();
  86. Variant meta;
  87. #define NEW_LINE \
  88. {\
  89. if (p_mode!=PROCESS_CACHE) {\
  90. line++;\
  91. if (line < l.offset_caches.size())\
  92. line_ofs=l.offset_caches[line];\
  93. wofs=margin;\
  94. if (align!=ALIGN_FILL)\
  95. wofs+=line_ofs;\
  96. } else {\
  97. int used=wofs-margin;\
  98. switch(align) {\
  99. case ALIGN_LEFT: l.offset_caches.push_back(0); break;\
  100. case ALIGN_CENTER: l.offset_caches.push_back(((p_width-margin)-used)/2); break;\
  101. case ALIGN_RIGHT: l.offset_caches.push_back(((p_width-margin)-used)); break;\
  102. case ALIGN_FILL: l.offset_caches.push_back((p_width-margin)-used+spaces_size); break;\
  103. }\
  104. l.height_caches.push_back(line_height);\
  105. l.space_caches.push_back(spaces);\
  106. }\
  107. y+=line_height+get_constant(SceneStringNames::get_singleton()->line_separation);\
  108. line_height=0;\
  109. spaces=0;\
  110. spaces_size=0;\
  111. wofs=begin;\
  112. if (p_mode!=PROCESS_CACHE) {\
  113. lh=line<l.height_caches.size()?l.height_caches[line]:1;\
  114. }\
  115. if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh && p_click_pos.x<wofs) {\
  116. if (r_outside) *r_outside=true;\
  117. *r_click_item=it;\
  118. *r_click_char=rchar;\
  119. return;\
  120. }\
  121. }
  122. #define ENSURE_WIDTH(m_width) \
  123. if (wofs + m_width > p_width) {\
  124. if (p_mode==PROCESS_CACHE) {\
  125. if (spaces>0) \
  126. spaces-=1;\
  127. }\
  128. if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh && p_click_pos.x>wofs) {\
  129. if (r_outside) *r_outside=true; \
  130. *r_click_item=it;\
  131. *r_click_char=rchar;\
  132. return;\
  133. }\
  134. NEW_LINE\
  135. }
  136. #define ADVANCE(m_width) \
  137. {\
  138. if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh && p_click_pos.x>=wofs && p_click_pos.x<wofs+m_width) {\
  139. if (r_outside) *r_outside=false; \
  140. *r_click_item=it;\
  141. *r_click_char=rchar;\
  142. return;\
  143. }\
  144. wofs+=m_width;\
  145. }
  146. #define CHECK_HEIGHT( m_height ) \
  147. if (m_height > line_height) {\
  148. line_height=m_height;\
  149. }
  150. Color selection_fg;
  151. Color selection_bg;
  152. if (p_mode==PROCESS_DRAW) {
  153. selection_fg = get_color("font_color_selected");
  154. selection_bg = get_color("selection_color");
  155. }
  156. int rchar=0;
  157. int lh=0;
  158. while (it) {
  159. switch(it->type) {
  160. case ITEM_TEXT: {
  161. ItemText *text = static_cast<ItemText*>(it);
  162. Ref<Font> font=_find_font(it);
  163. if (font.is_null())
  164. font=p_base_font;
  165. const CharType *c = text->text.c_str();
  166. const CharType *cf=c;
  167. int fh=font->get_height();
  168. int ascent = font->get_ascent();
  169. Color color;
  170. bool underline=false;
  171. if (p_mode==PROCESS_DRAW) {
  172. color=_find_color(text,p_base_color);
  173. underline=_find_underline(text);
  174. if (_find_meta(text,&meta)) {
  175. underline=true;
  176. }
  177. }
  178. rchar=0;
  179. while(*c) {
  180. int end=0;
  181. int w=0;
  182. int fw=0;
  183. lh=0;
  184. if (p_mode!=PROCESS_CACHE) {
  185. lh=line<l.height_caches.size()?l.height_caches[line]:1;
  186. }
  187. bool found_space=false;
  188. while (c[end]!=0 && !(end && c[end-1]==' ' && c[end]!=' ')) {
  189. int cw = font->get_char_size(c[end],c[end+1]).width;
  190. if (c[end]=='\t') {
  191. cw=tab_size*font->get_char_size(' ').width;
  192. }
  193. w+=cw;
  194. if (c[end]==' ') {
  195. if (p_mode==PROCESS_CACHE) {
  196. fw+=cw;
  197. } else if (align==ALIGN_FILL && line<l.space_caches.size() && l.space_caches[line]>0) {
  198. //print_line(String(c,end)+": "+itos(l.offset_caches[line])+"/"+itos(l.space_caches[line]));
  199. //sub_space=cw;
  200. found_space=true;
  201. } else {
  202. fw+=cw;
  203. }
  204. } else {
  205. fw+=cw;
  206. }
  207. end++;
  208. }
  209. ENSURE_WIDTH(w);
  210. //print_line("END: "+String::chr(c[end])+".");
  211. if (end && c[end-1]==' ') {
  212. spaces++;
  213. if (p_mode==PROCESS_CACHE) {
  214. spaces_size+=font->get_char_size(' ').width;
  215. }
  216. if (found_space) {
  217. int ln = MIN(l.offset_caches.size()-1,line);
  218. fw+=l.offset_caches[ln]/l.space_caches[ln];
  219. }
  220. }
  221. {
  222. int ofs=0;
  223. for(int i=0;i<end;i++) {
  224. int pofs=wofs+ofs;
  225. if (p_mode==PROCESS_POINTER && r_click_char && p_click_pos.y>=y && p_click_pos.y<=y+lh) {
  226. //int o = (wofs+w)-p_click_pos.x;
  227. int cw=font->get_char_size(c[i],c[i+1]).x;
  228. if (c[i]=='\t') {
  229. cw=tab_size*font->get_char_size(' ').width;
  230. }
  231. if (p_click_pos.x-cw/2>pofs) {
  232. rchar=int((&c[i])-cf);
  233. //print_line("GOT: "+itos(rchar));
  234. //if (i==end-1 && p_click_pos.x+cw/2 > pofs)
  235. // rchar++;
  236. //int o = (wofs+w)-p_click_pos.x;
  237. // if (o>cw/2)
  238. // rchar++;
  239. }
  240. ofs+=cw;
  241. } else if (p_mode==PROCESS_DRAW) {
  242. bool selected=false;
  243. if (selection.active) {
  244. int cofs = (&c[i])-cf;
  245. 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))) {
  246. selected=true;
  247. }
  248. }
  249. int cw;
  250. if (selected) {
  251. cw = font->get_char_size(c[i],c[i+1]).x;
  252. draw_rect(Rect2(pofs,y,cw,lh),selection_bg);
  253. font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],selection_fg);
  254. } else {
  255. cw=font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],color);
  256. }
  257. if (c[i]=='\t') {
  258. cw=tab_size*font->get_char_size(' ').width;
  259. }
  260. //print_line("draw char: "+String::chr(c[i]));
  261. if (underline) {
  262. Color uc=color;
  263. uc.a*=0.5;
  264. //VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,y+ascent+2),Point2(pofs+cw,y+ascent+2),uc);
  265. int uy = y+lh-fh+ascent+2;
  266. VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,uy),Point2(pofs+cw,uy),uc);
  267. }
  268. ofs+=cw;
  269. }
  270. }
  271. }
  272. ADVANCE(fw);
  273. CHECK_HEIGHT(fh); //must be done somewhere
  274. c=&c[end];
  275. }
  276. } break;
  277. case ITEM_IMAGE: {
  278. lh=0;
  279. if (p_mode!=PROCESS_CACHE)
  280. lh = line<l.height_caches.size()?l.height_caches[line]:1;
  281. ItemImage *img = static_cast<ItemImage*>(it);
  282. Ref<Font> font=_find_font(it);
  283. if (font.is_null())
  284. font=p_base_font;
  285. if (p_mode==PROCESS_POINTER && r_click_char)
  286. *r_click_char=0;
  287. ENSURE_WIDTH( img->image->get_width() );
  288. if (p_mode==PROCESS_DRAW) {
  289. img->image->draw(ci,Point2(wofs,y+lh-font->get_descent()-img->image->get_height()));
  290. }
  291. ADVANCE( img->image->get_width() );
  292. CHECK_HEIGHT( (img->image->get_height()+font->get_descent()) );
  293. } break;
  294. case ITEM_NEWLINE: {
  295. lh=0;
  296. if (p_mode!=PROCESS_CACHE)
  297. lh = line<l.height_caches.size()?l.height_caches[line]:1;
  298. #if 0
  299. if (p_mode==PROCESS_POINTER && r_click_item ) {
  300. //previous last "wrapped" line
  301. int pl = line-1;
  302. if (pl<0 || lines[pl].height_caches.size()==0)
  303. break;
  304. int py=lines[pl].offset_caches[ lines[pl].offset_caches.size() -1 ];
  305. int ph=lines[pl].height_caches[ lines[pl].height_caches.size() -1 ];
  306. print_line("py: "+itos(py));
  307. print_line("ph: "+itos(ph));
  308. rchar=0;
  309. if (p_click_pos.y>=py && p_click_pos.y<=py+ph) {
  310. if (r_outside) *r_outside=true;
  311. *r_click_item=it;
  312. *r_click_char=rchar;
  313. return;
  314. }
  315. }
  316. #endif
  317. } break;
  318. default: {}
  319. }
  320. Item *itp = it;
  321. it = _get_next_item(it);
  322. if (p_mode == PROCESS_POINTER && r_click_item && itp && !it && p_click_pos.y>y+lh) {
  323. //at the end of all, return this
  324. if (r_outside) *r_outside=true;
  325. *r_click_item=itp;
  326. *r_click_char=rchar;
  327. return;
  328. }
  329. if (it && (p_line+1 < lines.size()) && lines[p_line+1].from==it) {
  330. if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh) {
  331. //went to next line, but pointer was on the previous one
  332. if (r_outside) *r_outside=true;
  333. *r_click_item=itp;
  334. *r_click_char=rchar;
  335. return;
  336. }
  337. break;
  338. }
  339. }
  340. NEW_LINE;
  341. #undef NEW_LINE
  342. #undef ENSURE_WIDTH
  343. #undef ADVANCE
  344. #undef CHECK_HEIGHT
  345. }
  346. void RichTextLabel::_scroll_changed(double) {
  347. if (updating_scroll)
  348. return;
  349. if (scroll_follow && vscroll->get_val()>=(vscroll->get_max()-vscroll->get_page()))
  350. scroll_following=true;
  351. else
  352. scroll_following=false;
  353. update();
  354. }
  355. void RichTextLabel::_update_scroll() {
  356. int total_height=0;
  357. if (lines.size())
  358. total_height=lines[lines.size()-1].height_accum_cache;
  359. bool exceeds = total_height > get_size().height && scroll_active;
  360. if (exceeds!=scroll_visible) {
  361. if (exceeds) {
  362. scroll_visible=true;
  363. first_invalid_line=0;
  364. scroll_w=vscroll->get_combined_minimum_size().width;
  365. vscroll->show();
  366. vscroll->set_anchor_and_margin( MARGIN_LEFT, ANCHOR_END,scroll_w);
  367. _validate_line_caches();
  368. } else {
  369. scroll_visible=false;
  370. vscroll->hide();
  371. scroll_w=0;
  372. _validate_line_caches();
  373. }
  374. }
  375. }
  376. void RichTextLabel::_notification(int p_what) {
  377. switch (p_what) {
  378. case NOTIFICATION_RESIZED: {
  379. first_invalid_line=0; //invalidate ALL
  380. update();
  381. } break;
  382. case NOTIFICATION_ENTER_TREE: {
  383. if (use_bbcode)
  384. parse_bbcode(bbcode);
  385. first_invalid_line=0; //invalidate ALL
  386. update();
  387. } break;
  388. case NOTIFICATION_THEME_CHANGED: {
  389. if (is_inside_tree() && use_bbcode) {
  390. parse_bbcode(bbcode);
  391. //first_invalid_line=0; //invalidate ALL
  392. //update();
  393. }
  394. } break;
  395. case NOTIFICATION_DRAW: {
  396. _validate_line_caches();
  397. _update_scroll();
  398. RID ci=get_canvas_item();
  399. Size2 size = get_size();
  400. VisualServer::get_singleton()->canvas_item_set_clip(ci,true);
  401. if (has_focus()) {
  402. VisualServer::get_singleton()->canvas_item_add_clip_ignore(ci,true);
  403. draw_style_box(get_stylebox("focus"),Rect2(Point2(),size));
  404. VisualServer::get_singleton()->canvas_item_add_clip_ignore(ci,false);
  405. }
  406. int ofs = vscroll->get_val();
  407. //todo, change to binary search
  408. int from_line = 0;
  409. while (from_line<lines.size()) {
  410. if (lines[from_line].height_accum_cache>=ofs)
  411. break;
  412. from_line++;
  413. }
  414. if (from_line>=lines.size())
  415. break; //nothing to draw
  416. int y = (lines[from_line].height_accum_cache - lines[from_line].height_cache) - ofs;
  417. Ref<Font> base_font=get_font("normal_font");
  418. Color base_color=get_color("default_color");
  419. while (y<size.height && from_line<lines.size()) {
  420. _process_line(y,size.width-scroll_w,from_line,PROCESS_DRAW,base_font,base_color);
  421. from_line++;
  422. }
  423. }
  424. }
  425. }
  426. void RichTextLabel::_find_click(const Point2i& p_click,Item **r_click_item,int *r_click_char,bool *r_outside) {
  427. if (r_click_item)
  428. *r_click_item=NULL;
  429. Size2 size = get_size();
  430. int ofs = vscroll->get_val();
  431. //todo, change to binary search
  432. int from_line = 0;
  433. while (from_line<lines.size()) {
  434. if (lines[from_line].height_accum_cache>=ofs)
  435. break;
  436. from_line++;
  437. }
  438. if (from_line>=lines.size())
  439. return;
  440. int y = (lines[from_line].height_accum_cache - lines[from_line].height_cache) - ofs;
  441. Ref<Font> base_font=get_font("normal_font");
  442. Color base_color=get_color("default_color");
  443. while (y<size.height && from_line<lines.size()) {
  444. _process_line(y,size.width-scroll_w,from_line,PROCESS_POINTER,base_font,base_color,p_click,r_click_item,r_click_char,r_outside);
  445. if (r_click_item && *r_click_item)
  446. return;
  447. from_line++;
  448. }
  449. }
  450. Control::CursorShape RichTextLabel::get_cursor_shape(const Point2& p_pos) const {
  451. if (!underline_meta || selection.click)
  452. return CURSOR_ARROW;
  453. if (first_invalid_line<lines.size())
  454. return CURSOR_ARROW; //invalid
  455. int line=0;
  456. Item *item=NULL;
  457. ((RichTextLabel*)(this))->_find_click(p_pos,&item,&line);
  458. if (item && ((RichTextLabel*)(this))->_find_meta(item,NULL))
  459. return CURSOR_POINTING_HAND;
  460. return CURSOR_ARROW;
  461. }
  462. void RichTextLabel::_input_event(InputEvent p_event) {
  463. switch(p_event.type) {
  464. case InputEvent::MOUSE_BUTTON: {
  465. if (first_invalid_line<lines.size())
  466. return;
  467. const InputEventMouseButton& b = p_event.mouse_button;
  468. if (b.button_index==BUTTON_LEFT) {
  469. if (true) {
  470. if (b.pressed && !b.doubleclick) {
  471. int line=0;
  472. Item *item=NULL;
  473. bool outside;
  474. _find_click(Point2i(b.x,b.y),&item,&line,&outside);
  475. if (item) {
  476. Variant meta;
  477. if (!outside && _find_meta(item,&meta)) {
  478. //meta clicked
  479. emit_signal("meta_clicked",meta);
  480. } else if (selection.enabled) {
  481. selection.click=item;
  482. selection.click_char=line;
  483. }
  484. }
  485. } else if (!b.pressed) {
  486. selection.click=NULL;
  487. }
  488. }
  489. }
  490. if (b.button_index==BUTTON_WHEEL_UP) {
  491. if (scroll_active)
  492. vscroll->set_val( vscroll->get_val()-vscroll->get_page()/8 );
  493. }
  494. if (b.button_index==BUTTON_WHEEL_DOWN) {
  495. if (scroll_active)
  496. vscroll->set_val( vscroll->get_val()+vscroll->get_page()/8 );
  497. }
  498. } break;
  499. case InputEvent::KEY: {
  500. const InputEventKey &k=p_event.key;
  501. if (k.pressed) {
  502. bool handled=true;
  503. switch(k.scancode) {
  504. case KEY_PAGEUP: {
  505. if (vscroll->is_visible())
  506. vscroll->set_val( vscroll->get_val() - vscroll->get_page() );
  507. } break;
  508. case KEY_PAGEDOWN: {
  509. if (vscroll->is_visible())
  510. vscroll->set_val( vscroll->get_val() + vscroll->get_page() );
  511. } break;
  512. case KEY_UP: {
  513. if (vscroll->is_visible())
  514. vscroll->set_val( vscroll->get_val() - get_font("normal_font")->get_height() );
  515. } break;
  516. case KEY_DOWN: {
  517. if (vscroll->is_visible())
  518. vscroll->set_val( vscroll->get_val() + get_font("normal_font")->get_height() );
  519. } break;
  520. case KEY_HOME: {
  521. if (vscroll->is_visible())
  522. vscroll->set_val( 0 );
  523. } break;
  524. case KEY_END: {
  525. if (vscroll->is_visible())
  526. vscroll->set_val( vscroll->get_max() );
  527. } break;
  528. case KEY_INSERT:
  529. case KEY_C: {
  530. if (k.mod.command) {
  531. selection_copy();
  532. } else {
  533. handled=false;
  534. }
  535. } break;
  536. default: handled=false;
  537. }
  538. if (handled)
  539. accept_event();
  540. }
  541. } break;
  542. case InputEvent::MOUSE_MOTION: {
  543. if (first_invalid_line<lines.size())
  544. return;
  545. const InputEventMouseMotion& m = p_event.mouse_motion;
  546. if (selection.click) {
  547. int line=0;
  548. Item *item=NULL;
  549. _find_click(Point2i(m.x,m.y),&item,&line);
  550. if (!item)
  551. return; // do not update
  552. selection.from=selection.click;
  553. selection.from_char=selection.click_char;
  554. selection.to=item;
  555. selection.to_char=line;
  556. bool swap=false;
  557. if (selection.from->index > selection.to->index )
  558. swap=true;
  559. else if (selection.from->index == selection.to->index) {
  560. if (selection.from_char > selection.to_char)
  561. swap=true;
  562. else if (selection.from_char == selection.to_char) {
  563. selection.active=false;
  564. return;
  565. }
  566. }
  567. if (swap) {
  568. SWAP( selection.from, selection.to );
  569. SWAP( selection.from_char, selection.to_char );
  570. }
  571. selection.active=true;
  572. update();
  573. }
  574. } break;
  575. }
  576. }
  577. Ref<Font> RichTextLabel::_find_font(Item *p_item) {
  578. Item *fontitem=p_item;
  579. while(fontitem) {
  580. if (fontitem->type==ITEM_FONT) {
  581. ItemFont *fi = static_cast<ItemFont*>(fontitem);
  582. return fi->font;
  583. }
  584. fontitem=fontitem->parent;
  585. }
  586. return Ref<Font>();
  587. }
  588. int RichTextLabel::_find_margin(Item *p_item,const Ref<Font>& p_base_font) {
  589. Item *item=p_item;
  590. int margin=0;
  591. while(item) {
  592. if (item->type==ITEM_INDENT) {
  593. Ref<Font> font=_find_font(item);
  594. if (font.is_null())
  595. font=p_base_font;
  596. ItemIndent *indent = static_cast<ItemIndent*>(item);
  597. margin+=indent->level*tab_size*font->get_char_size(' ').width;
  598. } else if (item->type==ITEM_LIST) {
  599. Ref<Font> font=_find_font(item);
  600. if (font.is_null())
  601. font=p_base_font;
  602. }
  603. item=item->parent;
  604. }
  605. return margin;
  606. }
  607. RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) {
  608. Item *item=p_item;
  609. while(item) {
  610. if (item->type==ITEM_ALIGN) {
  611. ItemAlign *align = static_cast<ItemAlign*>(item);
  612. return align->align;
  613. }
  614. item=item->parent;
  615. }
  616. return default_align;
  617. }
  618. Color RichTextLabel::_find_color(Item *p_item,const Color& p_default_color) {
  619. Item *item=p_item;
  620. while(item) {
  621. if (item->type==ITEM_COLOR) {
  622. ItemColor *color = static_cast<ItemColor*>(item);
  623. return color->color;
  624. }
  625. item=item->parent;
  626. }
  627. return p_default_color;
  628. }
  629. bool RichTextLabel::_find_underline(Item *p_item) {
  630. Item *item=p_item;
  631. while(item) {
  632. if (item->type==ITEM_UNDERLINE) {
  633. return true;
  634. }
  635. item=item->parent;
  636. }
  637. return false;
  638. }
  639. bool RichTextLabel::_find_meta(Item *p_item,Variant *r_meta) {
  640. Item *item=p_item;
  641. while(item) {
  642. if (item->type==ITEM_META) {
  643. ItemMeta *meta = static_cast<ItemMeta*>(item);
  644. if (r_meta)
  645. *r_meta=meta->meta;
  646. return true;
  647. }
  648. item=item->parent;
  649. }
  650. return false;
  651. }
  652. void RichTextLabel::_validate_line_caches() {
  653. if (first_invalid_line==lines.size())
  654. return;
  655. //validate invalid lines!s
  656. Size2 size = get_size();
  657. Ref<Font> base_font=get_font("normal_font");
  658. for(int i=first_invalid_line;i<lines.size();i++) {
  659. int y=0;
  660. _process_line(y,size.width-scroll_w,i,PROCESS_CACHE,base_font,Color());
  661. lines[i].height_cache=y;
  662. lines[i].height_accum_cache=y;
  663. if (i>0)
  664. lines[i].height_accum_cache+=lines[i-1].height_accum_cache;
  665. }
  666. int total_height=0;
  667. if (lines.size())
  668. total_height=lines[lines.size()-1].height_accum_cache;
  669. first_invalid_line=lines.size();
  670. updating_scroll=true;
  671. vscroll->set_max(total_height);
  672. vscroll->set_page(size.height);
  673. if (scroll_follow && scroll_following)
  674. vscroll->set_val(total_height-size.height);
  675. updating_scroll=false;
  676. }
  677. void RichTextLabel::_invalidate_current_line() {
  678. if (lines.size()-1 <= first_invalid_line) {
  679. first_invalid_line=lines.size()-1;
  680. update();
  681. }
  682. }
  683. void RichTextLabel::add_text(const String& p_text) {
  684. int pos=0;
  685. while (pos<p_text.length()) {
  686. int end=p_text.find("\n",pos);
  687. String line;
  688. bool eol=false;
  689. if (end==-1) {
  690. end=p_text.length();
  691. } else {
  692. eol=true;
  693. }
  694. if (pos==0 && end==p_text.length())
  695. line=p_text;
  696. else
  697. line=p_text.substr(pos,end-pos);
  698. if (line.length()>0) {
  699. if (current->subitems.size() && current->subitems.back()->get()->type==ITEM_TEXT) {
  700. //append text condition!
  701. ItemText *ti = static_cast<ItemText*>(current->subitems.back()->get());
  702. ti->text+=line;
  703. _invalidate_current_line();
  704. } else {
  705. //append item condition
  706. ItemText *item = memnew( ItemText );
  707. item->text=line;
  708. _add_item(item,false);
  709. }
  710. }
  711. if (eol) {
  712. ItemNewline *item = memnew( ItemNewline );
  713. item->line=lines.size();
  714. _add_item(item,false);
  715. lines.resize(lines.size()+1);
  716. lines[lines.size()-1].from=item;
  717. _invalidate_current_line();
  718. }
  719. pos=end+1;
  720. }
  721. }
  722. void RichTextLabel::_add_item(Item *p_item, bool p_enter) {
  723. p_item->parent=current;
  724. p_item->E=current->subitems.push_back(p_item);
  725. p_item->index=current_idx++;
  726. if (p_enter)
  727. current=p_item;
  728. if (lines[lines.size()-1].from==NULL) {
  729. lines[lines.size()-1].from=p_item;
  730. }
  731. _invalidate_current_line();
  732. }
  733. void RichTextLabel::add_image(const Ref<Texture>& p_image) {
  734. ERR_FAIL_COND(p_image.is_null());
  735. ItemImage *item = memnew( ItemImage );
  736. item->image=p_image;
  737. _add_item(item,false);
  738. }
  739. void RichTextLabel::add_newline() {
  740. ItemNewline *item = memnew( ItemNewline );
  741. item->line=lines.size();
  742. lines.resize(lines.size()+1);
  743. _add_item(item,false);
  744. }
  745. void RichTextLabel::push_font(const Ref<Font>& p_font) {
  746. ERR_FAIL_COND(p_font.is_null());
  747. ItemFont *item = memnew( ItemFont );
  748. item->font=p_font;
  749. _add_item(item,true);
  750. }
  751. void RichTextLabel::push_color(const Color& p_color) {
  752. ItemColor *item = memnew( ItemColor );
  753. item->color=p_color;
  754. _add_item(item,true);
  755. }
  756. void RichTextLabel::push_underline() {
  757. ItemUnderline *item = memnew( ItemUnderline );
  758. _add_item(item,true);
  759. }
  760. void RichTextLabel::push_align(Align p_align) {
  761. lines.resize(lines.size()+1);
  762. ItemAlign *item = memnew( ItemAlign );
  763. item->align=p_align;
  764. _add_item(item,true);
  765. ItemNewline *itemnl = memnew( ItemNewline );
  766. itemnl->line=lines.size()-1;
  767. _add_item(itemnl,false);
  768. }
  769. void RichTextLabel::push_indent(int p_level) {
  770. ERR_FAIL_COND(p_level<0);
  771. lines.resize(lines.size()+1);
  772. ItemIndent *item = memnew( ItemIndent );
  773. item->level=p_level;
  774. _add_item(item,true);
  775. ItemNewline *itemnl = memnew( ItemNewline );
  776. itemnl->line=lines.size()-1;
  777. _add_item(itemnl,false);
  778. }
  779. void RichTextLabel::push_list(ListType p_list) {
  780. ERR_FAIL_INDEX(p_list,3);
  781. ItemList *item = memnew( ItemList );
  782. item->list_type=p_list;
  783. _add_item(item,true);
  784. }
  785. void RichTextLabel::push_meta(const Variant& p_meta) {
  786. ItemMeta *item = memnew( ItemMeta );
  787. item->meta=p_meta;
  788. _add_item(item,true);
  789. }
  790. void RichTextLabel::pop() {
  791. ERR_FAIL_COND(!current->parent);
  792. current=current->parent;
  793. }
  794. void RichTextLabel::clear() {
  795. main->_clear_children();
  796. current=main;
  797. lines.clear();
  798. lines.resize(1);
  799. first_invalid_line=0;
  800. update();
  801. selection.click=NULL;
  802. selection.active=false;
  803. current_idx=1;
  804. }
  805. void RichTextLabel::set_tab_size(int p_spaces) {
  806. tab_size=p_spaces;
  807. first_invalid_line=0;
  808. update();
  809. }
  810. int RichTextLabel::get_tab_size() const {
  811. return tab_size;
  812. }
  813. void RichTextLabel::set_meta_underline(bool p_underline) {
  814. underline_meta=p_underline;
  815. update();
  816. }
  817. bool RichTextLabel::is_meta_underlined() const {
  818. return underline_meta;
  819. }
  820. void RichTextLabel::set_offset(int p_pixel) {
  821. vscroll->set_val(p_pixel);
  822. }
  823. void RichTextLabel::set_scroll_active(bool p_active) {
  824. if (scroll_active==p_active)
  825. return;
  826. scroll_active=p_active;
  827. update();
  828. }
  829. bool RichTextLabel::is_scroll_active() const {
  830. return scroll_active;
  831. }
  832. void RichTextLabel::set_scroll_follow(bool p_follow) {
  833. scroll_follow=p_follow;
  834. if (!vscroll->is_visible() || vscroll->get_val()>=(vscroll->get_max()-vscroll->get_page()))
  835. scroll_following=true;
  836. }
  837. bool RichTextLabel::is_scroll_following() const {
  838. return scroll_follow;
  839. }
  840. Error RichTextLabel::parse_bbcode(const String& p_bbcode) {
  841. clear();
  842. return append_bbcode(p_bbcode);
  843. }
  844. Error RichTextLabel::append_bbcode(const String& p_bbcode) {
  845. int pos = 0;
  846. List<String> tag_stack;
  847. Ref<Font> normal_font=get_font("normal_font");
  848. Ref<Font> bold_font=get_font("bold_font");
  849. Ref<Font> italics_font=get_font("italics_font");
  850. Ref<Font> bold_italics_font=get_font("bold_italics_font");
  851. Ref<Font> mono_font=get_font("mono_font");
  852. Color base_color=get_color("default_color");
  853. int indent_level=0;
  854. bool in_bold=false;
  855. bool in_italics=false;
  856. while(pos < p_bbcode.length()) {
  857. int brk_pos = p_bbcode.find("[",pos);
  858. if (brk_pos<0)
  859. brk_pos=p_bbcode.length();
  860. if (brk_pos > pos) {
  861. add_text(p_bbcode.substr(pos,brk_pos-pos));
  862. }
  863. if (brk_pos==p_bbcode.length())
  864. break; //nothing else o add
  865. int brk_end = p_bbcode.find("]",brk_pos+1);
  866. if (brk_end==-1) {
  867. //no close, add the rest
  868. add_text(p_bbcode.substr(brk_pos,p_bbcode.length()-brk_pos));
  869. break;
  870. }
  871. String tag = p_bbcode.substr(brk_pos+1,brk_end-brk_pos-1);
  872. if (tag.begins_with("/") && tag_stack.size()) {
  873. bool tag_ok = tag_stack.size() && tag_stack.front()->get()==tag.substr(1,tag.length());
  874. if (tag_stack.front()->get()=="b")
  875. in_bold=false;
  876. if (tag_stack.front()->get()=="i")
  877. in_italics=false;
  878. if (tag_stack.front()->get()=="indent")
  879. indent_level--;
  880. if (!tag_ok) {
  881. add_text("[");
  882. pos++;
  883. continue;
  884. }
  885. tag_stack.pop_front();
  886. pos=brk_end+1;
  887. if (tag!="/img")
  888. pop();
  889. } else if (tag=="b") {
  890. //use bold font
  891. in_bold=true;
  892. if (in_italics)
  893. push_font(bold_italics_font);
  894. else
  895. push_font(bold_font);
  896. pos=brk_end+1;
  897. tag_stack.push_front(tag);
  898. } else if (tag=="i") {
  899. //use italics font
  900. in_italics=true;
  901. if (in_bold)
  902. push_font(bold_italics_font);
  903. else
  904. push_font(italics_font);
  905. pos=brk_end+1;
  906. tag_stack.push_front(tag);
  907. } else if (tag=="code") {
  908. //use monospace font
  909. push_font(mono_font);
  910. pos=brk_end+1;
  911. tag_stack.push_front(tag);
  912. } else if (tag=="u") {
  913. //use underline
  914. push_underline();
  915. pos=brk_end+1;
  916. tag_stack.push_front(tag);
  917. } else if (tag=="s") {
  918. //use strikethrough (not supported underline instead)
  919. push_underline();
  920. pos=brk_end+1;
  921. tag_stack.push_front(tag);
  922. } else if (tag=="center") {
  923. //use underline
  924. push_align(ALIGN_CENTER);
  925. pos=brk_end+1;
  926. tag_stack.push_front(tag);
  927. } else if (tag=="fill") {
  928. //use underline
  929. push_align(ALIGN_FILL);
  930. pos=brk_end+1;
  931. tag_stack.push_front(tag);
  932. } else if (tag=="right") {
  933. //use underline
  934. push_align(ALIGN_RIGHT);
  935. pos=brk_end+1;
  936. tag_stack.push_front(tag);
  937. } else if (tag=="ul") {
  938. //use underline
  939. push_list(LIST_DOTS);
  940. pos=brk_end+1;
  941. tag_stack.push_front(tag);
  942. } else if (tag=="ol") {
  943. //use underline
  944. push_list(LIST_NUMBERS);
  945. pos=brk_end+1;
  946. tag_stack.push_front(tag);
  947. } else if (tag=="indent") {
  948. //use underline
  949. indent_level++;
  950. push_indent(indent_level);
  951. pos=brk_end+1;
  952. tag_stack.push_front(tag);
  953. } else if (tag=="url") {
  954. //use strikethrough (not supported underline instead)
  955. int end=p_bbcode.find("[",brk_end);
  956. if (end==-1)
  957. end=p_bbcode.length();
  958. String url = p_bbcode.substr(brk_end+1,end-brk_end-1);
  959. push_meta(url);
  960. pos=brk_end+1;
  961. tag_stack.push_front(tag);
  962. } else if (tag.begins_with("url=")) {
  963. String url = tag.substr(4,tag.length());
  964. push_meta(url);
  965. pos=brk_end+1;
  966. tag_stack.push_front("url");
  967. } else if (tag=="img") {
  968. //use strikethrough (not supported underline instead)
  969. int end=p_bbcode.find("[",brk_end);
  970. if (end==-1)
  971. end=p_bbcode.length();
  972. String image = p_bbcode.substr(brk_end+1,end-brk_end-1);
  973. Ref<Texture> texture = ResourceLoader::load(image,"Texture");
  974. if (texture.is_valid())
  975. add_image(texture);
  976. pos=end;
  977. tag_stack.push_front(tag);
  978. } else if (tag.begins_with("color=")) {
  979. String col = tag.substr(6,tag.length());
  980. Color color;
  981. if (col.begins_with("#"))
  982. color=Color::html(col);
  983. else if (col=="aqua")
  984. color=Color::html("#00FFFF");
  985. else if (col=="black")
  986. color=Color::html("#000000");
  987. else if (col=="blue")
  988. color=Color::html("#0000FF");
  989. else if (col=="fuchsia")
  990. color=Color::html("#FF00FF");
  991. else if (col=="gray" || col=="grey")
  992. color=Color::html("#808080");
  993. else if (col=="green")
  994. color=Color::html("#008000");
  995. else if (col=="lime")
  996. color=Color::html("#00FF00");
  997. else if (col=="maroon")
  998. color=Color::html("#800000");
  999. else if (col=="navy")
  1000. color=Color::html("#000080");
  1001. else if (col=="olive")
  1002. color=Color::html("#808000");
  1003. else if (col=="purple")
  1004. color=Color::html("#800080");
  1005. else if (col=="red")
  1006. color=Color::html("#FF0000");
  1007. else if (col=="silver")
  1008. color=Color::html("#C0C0C0");
  1009. else if (col=="teal")
  1010. color=Color::html("#008008");
  1011. else if (col=="white")
  1012. color=Color::html("#FFFFFF");
  1013. else if (col=="yellow")
  1014. color=Color::html("#FFFF00");
  1015. else
  1016. color=base_color;
  1017. push_color(color);
  1018. pos=brk_end+1;
  1019. tag_stack.push_front("color");
  1020. } else if (tag.begins_with("font=")) {
  1021. String fnt = tag.substr(5,tag.length());
  1022. Ref<Font> font = ResourceLoader::load(fnt,"Font");
  1023. if (font.is_valid())
  1024. push_font(font);
  1025. else
  1026. push_font(normal_font);
  1027. pos=brk_end+1;
  1028. tag_stack.push_front("font");
  1029. } else {
  1030. add_text("["); //ignore
  1031. pos=brk_pos+1;
  1032. }
  1033. }
  1034. return OK;
  1035. }
  1036. void RichTextLabel::scroll_to_line(int p_line) {
  1037. ERR_FAIL_INDEX(p_line,lines.size());
  1038. _validate_line_caches();
  1039. vscroll->set_val(lines[p_line].height_accum_cache);
  1040. }
  1041. int RichTextLabel::get_line_count() const {
  1042. return lines.size();
  1043. }
  1044. void RichTextLabel::set_selection_enabled(bool p_enabled) {
  1045. selection.enabled=p_enabled;
  1046. if (!p_enabled) {
  1047. if (selection.active) {
  1048. selection.active=false;
  1049. update();
  1050. }
  1051. set_focus_mode(FOCUS_NONE);
  1052. } else {
  1053. set_focus_mode(FOCUS_ALL);
  1054. }
  1055. }
  1056. bool RichTextLabel::search(const String& p_string,bool p_from_selection) {
  1057. ERR_FAIL_COND_V(!selection.enabled,false);
  1058. Item *it=main;
  1059. int charidx=0;
  1060. if (p_from_selection && selection.active && selection.enabled) {
  1061. it=selection.to;
  1062. charidx=selection.to_char+1;
  1063. }
  1064. int line=-1;
  1065. while(it) {
  1066. if (it->type==ITEM_TEXT) {
  1067. ItemText *t = static_cast<ItemText*>(it);
  1068. int sp = t->text.find(p_string,charidx);
  1069. if (sp!=-1) {
  1070. selection.from=it;
  1071. selection.from_char=sp;
  1072. selection.to=it;
  1073. selection.to_char=sp+p_string.length()-1;
  1074. selection.active=true;
  1075. update();
  1076. if (line==-1) {
  1077. while(it) {
  1078. if (it->type==ITEM_NEWLINE) {
  1079. line=static_cast<ItemNewline*>(it)->line;
  1080. break;
  1081. }
  1082. it=_get_next_item(it);
  1083. }
  1084. if (!it)
  1085. line=lines.size()-1;
  1086. }
  1087. scroll_to_line(line-2);
  1088. return true;
  1089. }
  1090. } else if (it->type==ITEM_NEWLINE) {
  1091. line=static_cast<ItemNewline*>(it)->line;
  1092. }
  1093. it=_get_next_item(it);
  1094. charidx=0;
  1095. }
  1096. return false;
  1097. }
  1098. void RichTextLabel::selection_copy() {
  1099. if (!selection.enabled)
  1100. return;
  1101. String text;
  1102. RichTextLabel::Item *item=selection.from;
  1103. while(item) {
  1104. if (item->type==ITEM_TEXT) {
  1105. String itext = static_cast<ItemText*>(item)->text;
  1106. if (item==selection.from && item==selection.to) {
  1107. text+=itext.substr(selection.from_char,selection.to_char-selection.from_char+1);
  1108. } else if (item==selection.from) {
  1109. text+=itext.substr(selection.from_char,itext.size());
  1110. } else if (item==selection.to) {
  1111. text+=itext.substr(0,selection.to_char+1);
  1112. } else {
  1113. text+=itext;
  1114. }
  1115. } else if (item->type==ITEM_NEWLINE) {
  1116. text+="\n";
  1117. }
  1118. if (item==selection.to)
  1119. break;
  1120. item=_get_next_item(item);
  1121. }
  1122. if (text!="") {
  1123. OS::get_singleton()->set_clipboard(text);
  1124. //print_line("COPY: "+text);
  1125. }
  1126. }
  1127. bool RichTextLabel::is_selection_enabled() const {
  1128. return selection.enabled;
  1129. }
  1130. void RichTextLabel::set_bbcode(const String& p_bbcode) {
  1131. bbcode=p_bbcode;
  1132. if (is_inside_tree() && use_bbcode)
  1133. parse_bbcode(p_bbcode);
  1134. }
  1135. String RichTextLabel::get_bbcode() const {
  1136. return bbcode;
  1137. }
  1138. void RichTextLabel::set_use_bbcode(bool p_enable) {
  1139. if (use_bbcode==p_enable)
  1140. return;
  1141. use_bbcode=p_enable;
  1142. if (is_inside_tree() && use_bbcode)
  1143. parse_bbcode(bbcode);
  1144. }
  1145. bool RichTextLabel::is_using_bbcode() const {
  1146. return use_bbcode;
  1147. }
  1148. void RichTextLabel::_bind_methods() {
  1149. ObjectTypeDB::bind_method(_MD("_input_event"),&RichTextLabel::_input_event);
  1150. ObjectTypeDB::bind_method(_MD("_scroll_changed"),&RichTextLabel::_scroll_changed);
  1151. ObjectTypeDB::bind_method(_MD("add_text","text"),&RichTextLabel::add_text);
  1152. ObjectTypeDB::bind_method(_MD("add_image","image:Texture"),&RichTextLabel::add_image);
  1153. ObjectTypeDB::bind_method(_MD("newline"),&RichTextLabel::add_newline);
  1154. ObjectTypeDB::bind_method(_MD("push_font","font"),&RichTextLabel::push_font);
  1155. ObjectTypeDB::bind_method(_MD("push_color","color"),&RichTextLabel::push_color);
  1156. ObjectTypeDB::bind_method(_MD("push_align","align"),&RichTextLabel::push_align);
  1157. ObjectTypeDB::bind_method(_MD("push_indent","level"),&RichTextLabel::push_indent);
  1158. ObjectTypeDB::bind_method(_MD("push_list","type"),&RichTextLabel::push_list);
  1159. ObjectTypeDB::bind_method(_MD("push_meta","data"),&RichTextLabel::push_meta);
  1160. ObjectTypeDB::bind_method(_MD("push_underline"),&RichTextLabel::push_underline);
  1161. ObjectTypeDB::bind_method(_MD("pop"),&RichTextLabel::pop);
  1162. ObjectTypeDB::bind_method(_MD("clear"),&RichTextLabel::clear);
  1163. ObjectTypeDB::bind_method(_MD("set_meta_underline","enable"),&RichTextLabel::set_meta_underline);
  1164. ObjectTypeDB::bind_method(_MD("is_meta_underlined"),&RichTextLabel::is_meta_underlined);
  1165. ObjectTypeDB::bind_method(_MD("set_scroll_active","active"),&RichTextLabel::set_scroll_active);
  1166. ObjectTypeDB::bind_method(_MD("is_scroll_active"),&RichTextLabel::is_scroll_active);
  1167. ObjectTypeDB::bind_method(_MD("set_scroll_follow","follow"),&RichTextLabel::set_scroll_follow);
  1168. ObjectTypeDB::bind_method(_MD("is_scroll_following"),&RichTextLabel::is_scroll_following);
  1169. ObjectTypeDB::bind_method(_MD("get_v_scroll"),&RichTextLabel::get_v_scroll);
  1170. ObjectTypeDB::bind_method(_MD("set_tab_size","spaces"),&RichTextLabel::set_tab_size);
  1171. ObjectTypeDB::bind_method(_MD("get_tab_size"),&RichTextLabel::get_tab_size);
  1172. ObjectTypeDB::bind_method(_MD("set_selection_enabled","enabled"),&RichTextLabel::set_selection_enabled);
  1173. ObjectTypeDB::bind_method(_MD("is_selection_enabled"),&RichTextLabel::is_selection_enabled);
  1174. ObjectTypeDB::bind_method(_MD("parse_bbcode", "bbcode"),&RichTextLabel::parse_bbcode);
  1175. ObjectTypeDB::bind_method(_MD("append_bbcode", "bbcode"),&RichTextLabel::append_bbcode);
  1176. ObjectTypeDB::bind_method(_MD("set_bbcode","text"),&RichTextLabel::set_bbcode);
  1177. ObjectTypeDB::bind_method(_MD("get_bbcode"),&RichTextLabel::get_bbcode);
  1178. ObjectTypeDB::bind_method(_MD("set_use_bbcode","enable"),&RichTextLabel::set_use_bbcode);
  1179. ObjectTypeDB::bind_method(_MD("is_using_bbcode"),&RichTextLabel::is_using_bbcode);
  1180. ADD_PROPERTY(PropertyInfo(Variant::BOOL,"bbcode/enabled"),_SCS("set_use_bbcode"),_SCS("is_using_bbcode"));
  1181. ADD_PROPERTY(PropertyInfo(Variant::STRING,"bbcode/bbcode",PROPERTY_HINT_MULTILINE_TEXT),_SCS("set_bbcode"),_SCS("get_bbcode"));
  1182. ADD_SIGNAL( MethodInfo("meta_clicked",PropertyInfo(Variant::NIL,"meta")));
  1183. BIND_CONSTANT( ALIGN_LEFT );
  1184. BIND_CONSTANT( ALIGN_CENTER );
  1185. BIND_CONSTANT( ALIGN_RIGHT );
  1186. BIND_CONSTANT( ALIGN_FILL );
  1187. BIND_CONSTANT( LIST_NUMBERS );
  1188. BIND_CONSTANT( LIST_LETTERS );
  1189. BIND_CONSTANT( LIST_DOTS );
  1190. BIND_CONSTANT( ITEM_MAIN );
  1191. BIND_CONSTANT( ITEM_TEXT );
  1192. BIND_CONSTANT( ITEM_IMAGE );
  1193. BIND_CONSTANT( ITEM_NEWLINE );
  1194. BIND_CONSTANT( ITEM_FONT );
  1195. BIND_CONSTANT( ITEM_COLOR );
  1196. BIND_CONSTANT( ITEM_UNDERLINE );
  1197. BIND_CONSTANT( ITEM_ALIGN );
  1198. BIND_CONSTANT( ITEM_INDENT );
  1199. BIND_CONSTANT( ITEM_LIST );
  1200. BIND_CONSTANT( ITEM_META );
  1201. }
  1202. RichTextLabel::RichTextLabel() {
  1203. main = memnew( ItemMain );
  1204. main->index=0;
  1205. current=main;
  1206. lines.resize(1);
  1207. lines[0].from=main;
  1208. first_invalid_line=0;
  1209. tab_size=4;
  1210. default_align=ALIGN_LEFT;
  1211. underline_meta=true;
  1212. scroll_visible=false;
  1213. scroll_follow=false;
  1214. scroll_following=false;
  1215. updating_scroll=false;
  1216. scroll_active=true;
  1217. scroll_w=0;
  1218. vscroll = memnew( VScrollBar );
  1219. add_child(vscroll);
  1220. vscroll->set_drag_slave(String(".."));
  1221. vscroll->set_step(1);
  1222. vscroll->set_anchor_and_margin( MARGIN_TOP, ANCHOR_BEGIN, 0);
  1223. vscroll->set_anchor_and_margin( MARGIN_BOTTOM, ANCHOR_END, 0);
  1224. vscroll->set_anchor_and_margin( MARGIN_RIGHT, ANCHOR_END, 0);
  1225. vscroll->connect("value_changed",this,"_scroll_changed");
  1226. vscroll->set_step(1);
  1227. vscroll->hide();
  1228. current_idx=1;
  1229. use_bbcode=false;
  1230. selection.click=NULL;
  1231. selection.active=false;
  1232. selection.enabled=false;
  1233. }
  1234. RichTextLabel::~RichTextLabel() {
  1235. memdelete( main );
  1236. }