rich_text_label.cpp 139 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459
  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-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 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 "core/math/math_defs.h"
  32. #include "core/os/keyboard.h"
  33. #include "core/os/os.h"
  34. #include "scene/scene_string_names.h"
  35. #include "servers/display_server.h"
  36. #include "modules/modules_enabled.gen.h"
  37. #ifdef MODULE_REGEX_ENABLED
  38. #include "modules/regex/regex.h"
  39. #endif
  40. RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) const {
  41. if (p_free) {
  42. if (p_item->subitems.size()) {
  43. return p_item->subitems.front()->get();
  44. } else if (!p_item->parent) {
  45. return nullptr;
  46. } else if (p_item->E->next()) {
  47. return p_item->E->next()->get();
  48. } else {
  49. //go up until something with a next is found
  50. while (p_item->parent && !p_item->E->next()) {
  51. p_item = p_item->parent;
  52. }
  53. if (p_item->parent) {
  54. return p_item->E->next()->get();
  55. } else {
  56. return nullptr;
  57. }
  58. }
  59. } else {
  60. if (p_item->subitems.size() && p_item->type != ITEM_TABLE) {
  61. return p_item->subitems.front()->get();
  62. } else if (p_item->type == ITEM_FRAME) {
  63. return nullptr;
  64. } else if (p_item->E->next()) {
  65. return p_item->E->next()->get();
  66. } else {
  67. //go up until something with a next is found
  68. while (p_item->type != ITEM_FRAME && !p_item->E->next()) {
  69. p_item = p_item->parent;
  70. }
  71. if (p_item->type != ITEM_FRAME) {
  72. return p_item->E->next()->get();
  73. } else {
  74. return nullptr;
  75. }
  76. }
  77. }
  78. return nullptr;
  79. }
  80. RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) const {
  81. if (p_free) {
  82. if (p_item->subitems.size()) {
  83. return p_item->subitems.back()->get();
  84. } else if (!p_item->parent) {
  85. return nullptr;
  86. } else if (p_item->E->prev()) {
  87. return p_item->E->prev()->get();
  88. } else {
  89. //go back until something with a prev is found
  90. while (p_item->parent && !p_item->E->prev()) {
  91. p_item = p_item->parent;
  92. }
  93. if (p_item->parent) {
  94. return p_item->E->prev()->get();
  95. } else {
  96. return nullptr;
  97. }
  98. }
  99. } else {
  100. if (p_item->subitems.size() && p_item->type != ITEM_TABLE) {
  101. return p_item->subitems.back()->get();
  102. } else if (p_item->type == ITEM_FRAME) {
  103. return nullptr;
  104. } else if (p_item->E->prev()) {
  105. return p_item->E->prev()->get();
  106. } else {
  107. //go back until something with a prev is found
  108. while (p_item->type != ITEM_FRAME && !p_item->E->prev()) {
  109. p_item = p_item->parent;
  110. }
  111. if (p_item->type != ITEM_FRAME) {
  112. return p_item->E->prev()->get();
  113. } else {
  114. return nullptr;
  115. }
  116. }
  117. }
  118. return nullptr;
  119. }
  120. Rect2 RichTextLabel::_get_text_rect() {
  121. Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
  122. return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
  123. }
  124. RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item_from, RichTextLabel::Item *p_item_to, int p_position) {
  125. int offset = 0;
  126. for (Item *it = p_item_from; it && it != p_item_to; it = _get_next_item(it)) {
  127. switch (it->type) {
  128. case ITEM_TEXT: {
  129. ItemText *t = (ItemText *)it;
  130. offset += t->text.length();
  131. if (offset > p_position) {
  132. return it;
  133. }
  134. } break;
  135. case ITEM_NEWLINE:
  136. case ITEM_IMAGE:
  137. case ITEM_TABLE: {
  138. offset += 1;
  139. } break;
  140. default:
  141. break;
  142. }
  143. }
  144. return p_item_from;
  145. }
  146. String RichTextLabel::_roman(int p_num, bool p_capitalize) const {
  147. if (p_num > 3999) {
  148. return "ERR";
  149. };
  150. String s;
  151. if (p_capitalize) {
  152. String M[] = { "", "M", "MM", "MMM" };
  153. String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
  154. String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
  155. String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
  156. s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10];
  157. } else {
  158. String M[] = { "", "m", "mm", "mmm" };
  159. String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" };
  160. String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" };
  161. String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" };
  162. s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10];
  163. }
  164. return s;
  165. }
  166. String RichTextLabel::_letters(int p_num, bool p_capitalize) const {
  167. int64_t n = p_num;
  168. int chars = 0;
  169. do {
  170. n /= 24;
  171. chars++;
  172. } while (n);
  173. String s;
  174. s.resize(chars + 1);
  175. char32_t *c = s.ptrw();
  176. c[chars] = 0;
  177. n = p_num;
  178. do {
  179. int mod = ABS(n % 24);
  180. char a = (p_capitalize ? 'A' : 'a');
  181. c[--chars] = a + mod - 1;
  182. n /= 24;
  183. } while (n);
  184. return s;
  185. }
  186. void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) {
  187. ERR_FAIL_COND(p_frame == nullptr);
  188. ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
  189. Line &l = p_frame->lines.write[p_line];
  190. l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size);
  191. l.text_buf->set_width(p_width - l.offset.x);
  192. if (tab_size > 0) { // Align inline tabs.
  193. Vector<float> tabs;
  194. tabs.push_back(tab_size * p_base_font->get_char_size(' ', 0, p_base_font_size).width);
  195. l.text_buf->tab_align(tabs);
  196. }
  197. Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
  198. for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
  199. switch (it->type) {
  200. case ITEM_TABLE: {
  201. ItemTable *table = static_cast<ItemTable *>(it);
  202. int hseparation = get_theme_constant(SNAME("table_hseparation"));
  203. int vseparation = get_theme_constant(SNAME("table_vseparation"));
  204. int col_count = table->columns.size();
  205. for (int i = 0; i < col_count; i++) {
  206. table->columns.write[i].width = 0;
  207. }
  208. int idx = 0;
  209. for (Item *E : table->subitems) {
  210. ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
  211. ItemFrame *frame = static_cast<ItemFrame *>(E);
  212. for (int i = 0; i < frame->lines.size(); i++) {
  213. _resize_line(frame, i, p_base_font, p_base_font_size, 1);
  214. }
  215. idx++;
  216. }
  217. // Compute minimum width for each cell.
  218. const int available_width = p_width - hseparation * (col_count - 1);
  219. // Compute available width and total ratio (for expanders).
  220. int total_ratio = 0;
  221. int remaining_width = available_width;
  222. table->total_width = hseparation;
  223. for (int i = 0; i < col_count; i++) {
  224. remaining_width -= table->columns[i].min_width;
  225. if (table->columns[i].max_width > table->columns[i].min_width) {
  226. table->columns.write[i].expand = true;
  227. }
  228. if (table->columns[i].expand) {
  229. total_ratio += table->columns[i].expand_ratio;
  230. }
  231. }
  232. // Assign actual widths.
  233. for (int i = 0; i < col_count; i++) {
  234. table->columns.write[i].width = table->columns[i].min_width;
  235. if (table->columns[i].expand && total_ratio > 0) {
  236. table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
  237. }
  238. table->total_width += table->columns[i].width + hseparation;
  239. }
  240. // Resize to max_width if needed and distribute the remaining space.
  241. bool table_need_fit = true;
  242. while (table_need_fit) {
  243. table_need_fit = false;
  244. // Fit slim.
  245. for (int i = 0; i < col_count; i++) {
  246. if (!table->columns[i].expand) {
  247. continue;
  248. }
  249. int dif = table->columns[i].width - table->columns[i].max_width;
  250. if (dif > 0) {
  251. table_need_fit = true;
  252. table->columns.write[i].width = table->columns[i].max_width;
  253. table->total_width -= dif;
  254. total_ratio -= table->columns[i].expand_ratio;
  255. }
  256. }
  257. // Grow.
  258. remaining_width = available_width - table->total_width;
  259. if (remaining_width > 0 && total_ratio > 0) {
  260. for (int i = 0; i < col_count; i++) {
  261. if (table->columns[i].expand) {
  262. int dif = table->columns[i].max_width - table->columns[i].width;
  263. if (dif > 0) {
  264. int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
  265. int incr = MIN(dif, slice);
  266. table->columns.write[i].width += incr;
  267. table->total_width += incr;
  268. }
  269. }
  270. }
  271. }
  272. }
  273. // Update line width and get total height.
  274. idx = 0;
  275. table->total_height = 0;
  276. table->rows.clear();
  277. Vector2 offset;
  278. float row_height = 0.0;
  279. for (Item *E : table->subitems) {
  280. ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
  281. ItemFrame *frame = static_cast<ItemFrame *>(E);
  282. int column = idx % col_count;
  283. offset.x += frame->padding.position.x;
  284. float yofs = frame->padding.position.y;
  285. for (int i = 0; i < frame->lines.size(); i++) {
  286. frame->lines.write[i].text_buf->set_width(table->columns[column].width);
  287. table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
  288. if (i > 0) {
  289. frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
  290. } else {
  291. frame->lines.write[i].offset.y = 0;
  292. }
  293. frame->lines.write[i].offset += offset;
  294. float h = frame->lines[i].text_buf->get_size().y;
  295. if (frame->min_size_over.y > 0) {
  296. h = MAX(h, frame->min_size_over.y);
  297. }
  298. if (frame->max_size_over.y > 0) {
  299. h = MIN(h, frame->max_size_over.y);
  300. }
  301. yofs += h;
  302. }
  303. yofs += frame->padding.size.y;
  304. offset.x += table->columns[column].width + hseparation + frame->padding.size.x;
  305. row_height = MAX(yofs, row_height);
  306. if (column == col_count - 1) {
  307. offset.x = 0;
  308. row_height += vseparation;
  309. table->total_height += row_height;
  310. offset.y += row_height;
  311. table->rows.push_back(row_height);
  312. row_height = 0;
  313. }
  314. idx++;
  315. }
  316. l.text_buf->resize_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align);
  317. } break;
  318. default:
  319. break;
  320. }
  321. }
  322. if (p_line > 0) {
  323. l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
  324. } else {
  325. l.offset.y = 0;
  326. }
  327. }
  328. void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset) {
  329. ERR_FAIL_COND(p_frame == nullptr);
  330. ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
  331. Line &l = p_frame->lines.write[p_line];
  332. // Clear cache.
  333. l.text_buf->clear();
  334. l.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
  335. l.char_offset = *r_char_offset;
  336. l.char_count = 0;
  337. // Add indent.
  338. l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size);
  339. l.text_buf->set_width(p_width - l.offset.x);
  340. l.text_buf->set_align((HAlign)_find_align(l.from));
  341. l.text_buf->set_direction(_find_direction(l.from));
  342. if (tab_size > 0) { // Align inline tabs.
  343. Vector<float> tabs;
  344. tabs.push_back(tab_size * p_base_font->get_char_size(' ', 0, p_base_font_size).width);
  345. l.text_buf->tab_align(tabs);
  346. }
  347. // Shape current paragraph.
  348. String text;
  349. Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
  350. int remaining_characters = visible_characters - l.char_offset;
  351. for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
  352. if (visible_characters >= 0 && remaining_characters <= 0) {
  353. break;
  354. }
  355. switch (it->type) {
  356. case ITEM_DROPCAP: {
  357. // Add dropcap.
  358. const ItemDropcap *dc = (ItemDropcap *)it;
  359. if (dc != nullptr) {
  360. l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins);
  361. l.dc_color = dc->color;
  362. l.dc_ol_size = dc->ol_size;
  363. l.dc_ol_color = dc->ol_color;
  364. } else {
  365. l.text_buf->clear_dropcap();
  366. }
  367. } break;
  368. case ITEM_NEWLINE: {
  369. Ref<Font> font = _find_font(it);
  370. if (font.is_null()) {
  371. font = p_base_font;
  372. }
  373. int font_size = _find_font_size(it);
  374. if (font_size == -1) {
  375. font_size = p_base_font_size;
  376. }
  377. l.text_buf->add_string("\n", font, font_size, Dictionary(), "");
  378. text += "\n";
  379. l.char_count++;
  380. remaining_characters--;
  381. } break;
  382. case ITEM_TEXT: {
  383. ItemText *t = (ItemText *)it;
  384. Ref<Font> font = _find_font(it);
  385. if (font.is_null()) {
  386. font = p_base_font;
  387. }
  388. int font_size = _find_font_size(it);
  389. if (font_size == -1) {
  390. font_size = p_base_font_size;
  391. }
  392. Dictionary font_ftr = _find_font_features(it);
  393. String lang = _find_language(it);
  394. String tx = t->text;
  395. if (visible_characters >= 0 && remaining_characters >= 0) {
  396. tx = tx.substr(0, remaining_characters);
  397. }
  398. remaining_characters -= tx.length();
  399. l.text_buf->add_string(tx, font, font_size, font_ftr, lang);
  400. text += tx;
  401. l.char_count += tx.length();
  402. } break;
  403. case ITEM_IMAGE: {
  404. ItemImage *img = (ItemImage *)it;
  405. l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1);
  406. text += String::chr(0xfffc);
  407. l.char_count++;
  408. remaining_characters--;
  409. } break;
  410. case ITEM_TABLE: {
  411. ItemTable *table = static_cast<ItemTable *>(it);
  412. int hseparation = get_theme_constant(SNAME("table_hseparation"));
  413. int vseparation = get_theme_constant(SNAME("table_vseparation"));
  414. int col_count = table->columns.size();
  415. int t_char_count = 0;
  416. // Set minimums to zero.
  417. for (int i = 0; i < col_count; i++) {
  418. table->columns.write[i].min_width = 0;
  419. table->columns.write[i].max_width = 0;
  420. table->columns.write[i].width = 0;
  421. }
  422. // Compute minimum width for each cell.
  423. const int available_width = p_width - hseparation * (col_count - 1);
  424. int idx = 0;
  425. for (Item *E : table->subitems) {
  426. ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
  427. ItemFrame *frame = static_cast<ItemFrame *>(E);
  428. int column = idx % col_count;
  429. for (int i = 0; i < frame->lines.size(); i++) {
  430. int char_offset = l.char_offset + l.char_count;
  431. _shape_line(frame, i, p_base_font, p_base_font_size, 1, &char_offset);
  432. int cell_ch = (char_offset - (l.char_offset + l.char_count));
  433. l.char_count += cell_ch;
  434. t_char_count += cell_ch;
  435. remaining_characters -= cell_ch;
  436. table->columns.write[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x));
  437. table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wrapped_size().x));
  438. }
  439. idx++;
  440. }
  441. // Compute available width and total ratio (for expanders).
  442. int total_ratio = 0;
  443. int remaining_width = available_width;
  444. table->total_width = hseparation;
  445. for (int i = 0; i < col_count; i++) {
  446. remaining_width -= table->columns[i].min_width;
  447. if (table->columns[i].max_width > table->columns[i].min_width) {
  448. table->columns.write[i].expand = true;
  449. }
  450. if (table->columns[i].expand) {
  451. total_ratio += table->columns[i].expand_ratio;
  452. }
  453. }
  454. // Assign actual widths.
  455. for (int i = 0; i < col_count; i++) {
  456. table->columns.write[i].width = table->columns[i].min_width;
  457. if (table->columns[i].expand && total_ratio > 0) {
  458. table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
  459. }
  460. table->total_width += table->columns[i].width + hseparation;
  461. }
  462. // Resize to max_width if needed and distribute the remaining space.
  463. bool table_need_fit = true;
  464. while (table_need_fit) {
  465. table_need_fit = false;
  466. // Fit slim.
  467. for (int i = 0; i < col_count; i++) {
  468. if (!table->columns[i].expand) {
  469. continue;
  470. }
  471. int dif = table->columns[i].width - table->columns[i].max_width;
  472. if (dif > 0) {
  473. table_need_fit = true;
  474. table->columns.write[i].width = table->columns[i].max_width;
  475. table->total_width -= dif;
  476. total_ratio -= table->columns[i].expand_ratio;
  477. }
  478. }
  479. // Grow.
  480. remaining_width = available_width - table->total_width;
  481. if (remaining_width > 0 && total_ratio > 0) {
  482. for (int i = 0; i < col_count; i++) {
  483. if (table->columns[i].expand) {
  484. int dif = table->columns[i].max_width - table->columns[i].width;
  485. if (dif > 0) {
  486. int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
  487. int incr = MIN(dif, slice);
  488. table->columns.write[i].width += incr;
  489. table->total_width += incr;
  490. }
  491. }
  492. }
  493. }
  494. }
  495. // Update line width and get total height.
  496. idx = 0;
  497. table->total_height = 0;
  498. table->rows.clear();
  499. Vector2 offset;
  500. float row_height = 0.0;
  501. for (const List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
  502. ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
  503. ItemFrame *frame = static_cast<ItemFrame *>(E->get());
  504. int column = idx % col_count;
  505. offset.x += frame->padding.position.x;
  506. float yofs = frame->padding.position.y;
  507. for (int i = 0; i < frame->lines.size(); i++) {
  508. frame->lines.write[i].text_buf->set_width(table->columns[column].width);
  509. table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
  510. if (i > 0) {
  511. frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
  512. } else {
  513. frame->lines.write[i].offset.y = 0;
  514. }
  515. frame->lines.write[i].offset += offset;
  516. float h = frame->lines[i].text_buf->get_size().y;
  517. if (frame->min_size_over.y > 0) {
  518. h = MAX(h, frame->min_size_over.y);
  519. }
  520. if (frame->max_size_over.y > 0) {
  521. h = MIN(h, frame->max_size_over.y);
  522. }
  523. yofs += h;
  524. }
  525. yofs += frame->padding.size.y;
  526. offset.x += table->columns[column].width + hseparation + frame->padding.size.x;
  527. row_height = MAX(yofs, row_height);
  528. // Add row height after last column of the row or last cell of the table.
  529. if (column == col_count - 1 || E->next() == nullptr) {
  530. offset.x = 0;
  531. row_height += vseparation;
  532. table->total_height += row_height;
  533. offset.y += row_height;
  534. table->rows.push_back(row_height);
  535. row_height = 0;
  536. }
  537. idx++;
  538. }
  539. l.text_buf->add_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align, t_char_count);
  540. text += String::chr(0xfffc).repeat(t_char_count);
  541. } break;
  542. default:
  543. break;
  544. }
  545. }
  546. //Apply BiDi override.
  547. l.text_buf->set_bidi_override(structured_text_parser(_find_stt(l.from), st_args, text));
  548. *r_char_offset = l.char_offset + l.char_count;
  549. if (p_line > 0) {
  550. l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
  551. } else {
  552. l.offset.y = 0;
  553. }
  554. }
  555. int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) {
  556. Vector2 off;
  557. ERR_FAIL_COND_V(p_frame == nullptr, 0);
  558. ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), 0);
  559. Line &l = p_frame->lines.write[p_line];
  560. Item *it_from = l.from;
  561. Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
  562. Variant meta;
  563. if (it_from == nullptr) {
  564. return 0;
  565. }
  566. RID ci = get_canvas_item();
  567. bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
  568. bool lrtl = is_layout_rtl();
  569. Vector<int> list_index;
  570. Vector<ItemList *> list_items;
  571. _find_list(l.from, list_index, list_items);
  572. String prefix;
  573. for (int i = 0; i < list_index.size(); i++) {
  574. if (rtl) {
  575. prefix = prefix + ".";
  576. } else {
  577. prefix = "." + prefix;
  578. }
  579. String segment;
  580. if (list_items[i]->list_type == LIST_DOTS) {
  581. static const char32_t _prefix[2] = { 0x25CF, 0 };
  582. prefix = _prefix;
  583. break;
  584. } else if (list_items[i]->list_type == LIST_NUMBERS) {
  585. segment = TS->format_number(itos(list_index[i]), _find_language(l.from));
  586. } else if (list_items[i]->list_type == LIST_LETTERS) {
  587. segment = _letters(list_index[i], list_items[i]->capitalize);
  588. } else if (list_items[i]->list_type == LIST_ROMAN) {
  589. segment = _roman(list_index[i], list_items[i]->capitalize);
  590. }
  591. if (rtl) {
  592. prefix = prefix + segment;
  593. } else {
  594. prefix = segment + prefix;
  595. }
  596. }
  597. if (prefix != "") {
  598. Ref<Font> font = _find_font(l.from);
  599. if (font.is_null()) {
  600. font = get_theme_font(SNAME("normal_font"));
  601. }
  602. int font_size = _find_font_size(l.from);
  603. if (font_size == -1) {
  604. font_size = get_theme_font_size(SNAME("normal_font_size"));
  605. }
  606. if (rtl) {
  607. float offx = 0.0f;
  608. if (!lrtl && p_frame == main) { // Skip Scrollbar.
  609. offx -= scroll_w;
  610. }
  611. font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HALIGN_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color));
  612. } else {
  613. float offx = 0.0f;
  614. if (lrtl && p_frame == main) { // Skip Scrollbar.
  615. offx += scroll_w;
  616. }
  617. font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HALIGN_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color));
  618. }
  619. }
  620. // Draw dropcap.
  621. int dc_lines = l.text_buf->get_dropcap_lines();
  622. float h_off = l.text_buf->get_dropcap_size().x;
  623. if (l.dc_ol_size > 0) {
  624. l.text_buf->draw_dropcap_outline(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_ol_size, l.dc_ol_color);
  625. }
  626. l.text_buf->draw_dropcap(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color);
  627. int line_count = 0;
  628. Size2 ctrl_size = get_size();
  629. // Draw text.
  630. for (int line = 0; line < l.text_buf->get_line_count(); line++) {
  631. RID rid = l.text_buf->get_line_rid(line);
  632. if (p_ofs.y + off.y >= ctrl_size.height) {
  633. break;
  634. }
  635. if (p_ofs.y + off.y + TS->shaped_text_get_size(rid).y <= 0) {
  636. off.y += TS->shaped_text_get_size(rid).y;
  637. continue;
  638. }
  639. float width = l.text_buf->get_width();
  640. float length = TS->shaped_text_get_width(rid);
  641. // Draw line.
  642. line_count++;
  643. if (rtl) {
  644. off.x = p_width - l.offset.x - width;
  645. if (!lrtl && p_frame == main) { // Skip Scrollbar.
  646. off.x -= scroll_w;
  647. }
  648. } else {
  649. off.x = l.offset.x;
  650. if (lrtl && p_frame == main) { // Skip Scrollbar.
  651. off.x += scroll_w;
  652. }
  653. }
  654. // Draw text.
  655. switch (l.text_buf->get_align()) {
  656. case HALIGN_FILL:
  657. case HALIGN_LEFT: {
  658. if (rtl) {
  659. off.x += width - length;
  660. }
  661. } break;
  662. case HALIGN_CENTER: {
  663. off.x += Math::floor((width - length) / 2.0);
  664. } break;
  665. case HALIGN_RIGHT: {
  666. if (!rtl) {
  667. off.x += width - length;
  668. }
  669. } break;
  670. }
  671. if (line <= dc_lines) {
  672. if (rtl) {
  673. off.x -= h_off;
  674. } else {
  675. off.x += h_off;
  676. }
  677. }
  678. //draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
  679. off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top();
  680. // Draw inlined objects.
  681. Array objects = TS->shaped_text_get_objects(rid);
  682. for (int i = 0; i < objects.size(); i++) {
  683. Item *it = (Item *)(uint64_t)objects[i];
  684. if (it != nullptr) {
  685. Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
  686. //draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS
  687. switch (it->type) {
  688. case ITEM_IMAGE: {
  689. ItemImage *img = static_cast<ItemImage *>(it);
  690. img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color);
  691. } break;
  692. case ITEM_TABLE: {
  693. ItemTable *table = static_cast<ItemTable *>(it);
  694. Color odd_row_bg = get_theme_color(SNAME("table_odd_row_bg"));
  695. Color even_row_bg = get_theme_color(SNAME("table_even_row_bg"));
  696. Color border = get_theme_color(SNAME("table_border"));
  697. int hseparation = get_theme_constant(SNAME("table_hseparation"));
  698. int col_count = table->columns.size();
  699. int row_count = table->rows.size();
  700. int idx = 0;
  701. for (Item *E : table->subitems) {
  702. ItemFrame *frame = static_cast<ItemFrame *>(E);
  703. int col = idx % col_count;
  704. int row = idx / col_count;
  705. if (frame->lines.size() != 0 && row < row_count) {
  706. Vector2 coff = frame->lines[0].offset;
  707. if (rtl) {
  708. coff.x = rect.size.width - table->columns[col].width - coff.x;
  709. }
  710. if (row % 2 == 0) {
  711. draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg), true);
  712. } else {
  713. draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg), true);
  714. }
  715. draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false);
  716. }
  717. for (int j = 0; j < frame->lines.size(); j++) {
  718. _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_as_outline, p_shadow_ofs);
  719. }
  720. idx++;
  721. }
  722. } break;
  723. default:
  724. break;
  725. }
  726. }
  727. }
  728. const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
  729. int gl_size = TS->shaped_text_get_glyph_count(rid);
  730. Vector2 gloff = off;
  731. // Draw oulines and shadow.
  732. for (int i = 0; i < gl_size; i++) {
  733. Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
  734. int size = _find_outline_size(it, p_outline_size);
  735. Color font_color = _find_outline_color(it, p_outline_color);
  736. if (size <= 0) {
  737. gloff.x += glyphs[i].advance;
  738. continue;
  739. }
  740. // Get FX.
  741. ItemFade *fade = nullptr;
  742. Item *fade_item = it;
  743. while (fade_item) {
  744. if (fade_item->type == ITEM_FADE) {
  745. fade = static_cast<ItemFade *>(fade_item);
  746. break;
  747. }
  748. fade_item = fade_item->parent;
  749. }
  750. Vector<ItemFX *> fx_stack;
  751. _fetch_item_fx_stack(it, fx_stack);
  752. bool custom_fx_ok = true;
  753. Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
  754. RID frid = glyphs[i].font_rid;
  755. uint32_t gl = glyphs[i].index;
  756. uint16_t gl_fl = glyphs[i].flags;
  757. uint8_t gl_cn = glyphs[i].count;
  758. bool cprev = false;
  759. if (gl_cn == 0) { // Parts of the same cluster, always connected.
  760. cprev = true;
  761. }
  762. if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
  763. if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
  764. cprev = true;
  765. }
  766. } else {
  767. if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
  768. cprev = true;
  769. }
  770. }
  771. //Apply fx.
  772. float faded_visibility = 1.0f;
  773. if (fade) {
  774. if (glyphs[i].start >= fade->starting_index) {
  775. faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
  776. faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
  777. }
  778. font_color.a = faded_visibility;
  779. }
  780. bool visible = (font_color.a != 0);
  781. for (int j = 0; j < fx_stack.size(); j++) {
  782. ItemFX *item_fx = fx_stack[j];
  783. if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
  784. ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
  785. Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
  786. Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
  787. if (!custom_effect.is_null()) {
  788. charfx->elapsed_time = item_custom->elapsed_time;
  789. charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
  790. charfx->visibility = visible;
  791. charfx->outline = true;
  792. charfx->font = frid;
  793. charfx->glyph_index = gl;
  794. charfx->glyph_flags = gl_fl;
  795. charfx->glyph_count = gl_cn;
  796. charfx->offset = fx_offset;
  797. charfx->color = font_color;
  798. bool effect_status = custom_effect->_process_effect_impl(charfx);
  799. custom_fx_ok = effect_status;
  800. fx_offset += charfx->offset;
  801. font_color = charfx->color;
  802. frid = charfx->font;
  803. gl = charfx->glyph_index;
  804. visible &= charfx->visibility;
  805. }
  806. } else if (item_fx->type == ITEM_SHAKE) {
  807. ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
  808. if (!cprev) {
  809. uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
  810. uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
  811. uint64_t max_rand = 2147483647;
  812. double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
  813. double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
  814. double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
  815. n_time = (n_time > 1.0) ? 1.0 : n_time;
  816. item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
  817. }
  818. fx_offset += item_shake->prev_off;
  819. } else if (item_fx->type == ITEM_WAVE) {
  820. ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
  821. if (!cprev) {
  822. double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_wave->amplitude / 10.0f);
  823. item_wave->prev_off = Point2(0, 1) * value;
  824. }
  825. fx_offset += item_wave->prev_off;
  826. } else if (item_fx->type == ITEM_TORNADO) {
  827. ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
  828. if (!cprev) {
  829. double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
  830. double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
  831. item_tornado->prev_off = Point2(torn_x, torn_y);
  832. }
  833. fx_offset += item_tornado->prev_off;
  834. } else if (item_fx->type == ITEM_RAINBOW) {
  835. ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
  836. font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + gloff.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
  837. }
  838. }
  839. Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
  840. // Draw glyph outlines.
  841. for (int j = 0; j < glyphs[i].repeat; j++) {
  842. if (visible) {
  843. if (frid != RID()) {
  844. if (p_shadow_as_outline) {
  845. TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, shadow_ofs.y), gl, p_font_shadow_color);
  846. TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(shadow_ofs.x, -shadow_ofs.y), gl, p_font_shadow_color);
  847. TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, -shadow_ofs.y), gl, p_font_shadow_color);
  848. }
  849. TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
  850. }
  851. }
  852. gloff.x += glyphs[i].advance;
  853. }
  854. }
  855. Vector2 fbg_line_off = off + p_ofs;
  856. // Draw background color box
  857. Vector2i chr_range = TS->shaped_text_get_range(rid);
  858. _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 0);
  859. // Draw main text.
  860. Color selection_fg = get_theme_color(SNAME("font_selected_color"));
  861. Color selection_bg = get_theme_color(SNAME("selection_color"));
  862. int sel_start = -1;
  863. int sel_end = -1;
  864. if (selection.active && (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) <= (l.char_offset + TS->shaped_text_get_range(rid).y) && (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) >= (l.char_offset + TS->shaped_text_get_range(rid).x)) {
  865. sel_start = MAX(TS->shaped_text_get_range(rid).x, (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) - l.char_offset);
  866. sel_end = MIN(TS->shaped_text_get_range(rid).y, (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) - l.char_offset);
  867. Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_start, sel_end);
  868. for (int i = 0; i < sel.size(); i++) {
  869. Rect2 rect = Rect2(sel[i].x + p_ofs.x + off.x, p_ofs.y + off.y - TS->shaped_text_get_ascent(rid), sel[i].y - sel[i].x, TS->shaped_text_get_size(rid).y);
  870. RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_bg);
  871. }
  872. }
  873. for (int i = 0; i < gl_size; i++) {
  874. bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end);
  875. Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
  876. Color font_color = _find_color(it, p_base_color);
  877. if (_find_underline(it) || (_find_meta(it, &meta) && underline_meta)) {
  878. Color uc = font_color;
  879. uc.a *= 0.5;
  880. float y_off = TS->shaped_text_get_underline_position(rid);
  881. float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
  882. draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
  883. } else if (_find_strikethrough(it)) {
  884. Color uc = font_color;
  885. uc.a *= 0.5;
  886. float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
  887. float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
  888. draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
  889. }
  890. // Get FX.
  891. ItemFade *fade = nullptr;
  892. Item *fade_item = it;
  893. while (fade_item) {
  894. if (fade_item->type == ITEM_FADE) {
  895. fade = static_cast<ItemFade *>(fade_item);
  896. break;
  897. }
  898. fade_item = fade_item->parent;
  899. }
  900. Vector<ItemFX *> fx_stack;
  901. _fetch_item_fx_stack(it, fx_stack);
  902. bool custom_fx_ok = true;
  903. Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
  904. RID frid = glyphs[i].font_rid;
  905. uint32_t gl = glyphs[i].index;
  906. uint16_t gl_fl = glyphs[i].flags;
  907. uint8_t gl_cn = glyphs[i].count;
  908. bool cprev = false;
  909. if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected.
  910. cprev = true;
  911. }
  912. if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
  913. if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
  914. cprev = true;
  915. }
  916. } else {
  917. if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
  918. cprev = true;
  919. }
  920. }
  921. //Apply fx.
  922. float faded_visibility = 1.0f;
  923. if (fade) {
  924. if (glyphs[i].start >= fade->starting_index) {
  925. faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
  926. faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
  927. }
  928. font_color.a = faded_visibility;
  929. }
  930. bool visible = (font_color.a != 0);
  931. for (int j = 0; j < fx_stack.size(); j++) {
  932. ItemFX *item_fx = fx_stack[j];
  933. if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
  934. ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
  935. Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
  936. Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
  937. if (!custom_effect.is_null()) {
  938. charfx->elapsed_time = item_custom->elapsed_time;
  939. charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
  940. charfx->visibility = visible;
  941. charfx->outline = false;
  942. charfx->font = frid;
  943. charfx->glyph_index = gl;
  944. charfx->glyph_flags = gl_fl;
  945. charfx->glyph_count = gl_cn;
  946. charfx->offset = fx_offset;
  947. charfx->color = font_color;
  948. bool effect_status = custom_effect->_process_effect_impl(charfx);
  949. custom_fx_ok = effect_status;
  950. fx_offset += charfx->offset;
  951. font_color = charfx->color;
  952. frid = charfx->font;
  953. gl = charfx->glyph_index;
  954. visible &= charfx->visibility;
  955. }
  956. } else if (item_fx->type == ITEM_SHAKE) {
  957. ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
  958. if (!cprev) {
  959. uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
  960. uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
  961. uint64_t max_rand = 2147483647;
  962. double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
  963. double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
  964. double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
  965. n_time = (n_time > 1.0) ? 1.0 : n_time;
  966. item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
  967. }
  968. fx_offset += item_shake->prev_off;
  969. } else if (item_fx->type == ITEM_WAVE) {
  970. ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
  971. if (!cprev) {
  972. double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
  973. item_wave->prev_off = Point2(0, 1) * value;
  974. }
  975. fx_offset += item_wave->prev_off;
  976. } else if (item_fx->type == ITEM_TORNADO) {
  977. ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
  978. if (!cprev) {
  979. double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
  980. double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
  981. item_tornado->prev_off = Point2(torn_x, torn_y);
  982. }
  983. fx_offset += item_tornado->prev_off;
  984. } else if (item_fx->type == ITEM_RAINBOW) {
  985. ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
  986. font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + off.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
  987. }
  988. }
  989. if (selected) {
  990. font_color = override_selected_font_color ? selection_fg : font_color;
  991. }
  992. // Draw glyphs.
  993. for (int j = 0; j < glyphs[i].repeat; j++) {
  994. if (visible) {
  995. if (frid != RID()) {
  996. TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
  997. } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
  998. TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
  999. }
  1000. }
  1001. off.x += glyphs[i].advance;
  1002. }
  1003. }
  1004. // Draw foreground color box
  1005. _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 1);
  1006. off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom();
  1007. }
  1008. return line_count;
  1009. }
  1010. void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool *r_outside) {
  1011. if (r_click_item) {
  1012. *r_click_item = nullptr;
  1013. }
  1014. if (r_click_char != nullptr) {
  1015. *r_click_char = 0;
  1016. }
  1017. if (r_outside != nullptr) {
  1018. *r_outside = true;
  1019. }
  1020. Size2 size = get_size();
  1021. Rect2 text_rect = _get_text_rect();
  1022. int vofs = vscroll->get_value();
  1023. // Search for the first line.
  1024. int from_line = 0;
  1025. //TODO, change to binary search ?
  1026. while (from_line < main->lines.size()) {
  1027. if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) {
  1028. break;
  1029. }
  1030. from_line++;
  1031. }
  1032. if (from_line >= main->lines.size()) {
  1033. return;
  1034. }
  1035. Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
  1036. while (ofs.y < size.height && from_line < main->lines.size()) {
  1037. _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
  1038. ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
  1039. if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
  1040. if (r_outside != nullptr) {
  1041. *r_outside = false;
  1042. }
  1043. return;
  1044. }
  1045. from_line++;
  1046. }
  1047. }
  1048. float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char) {
  1049. Vector2 off;
  1050. int char_pos = -1;
  1051. Line &l = p_frame->lines.write[p_line];
  1052. bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
  1053. bool lrtl = is_layout_rtl();
  1054. bool table_hit = false;
  1055. for (int line = 0; line < l.text_buf->get_line_count(); line++) {
  1056. RID rid = l.text_buf->get_line_rid(line);
  1057. float width = l.text_buf->get_width();
  1058. float length = TS->shaped_text_get_width(rid);
  1059. if (rtl) {
  1060. off.x = p_width - l.offset.x - width;
  1061. if (!lrtl && p_frame == main) { // Skip Scrollbar.
  1062. off.x -= scroll_w;
  1063. }
  1064. } else {
  1065. off.x = l.offset.x;
  1066. if (lrtl && p_frame == main) { // Skip Scrollbar.
  1067. off.x += scroll_w;
  1068. }
  1069. }
  1070. switch (l.text_buf->get_align()) {
  1071. case HALIGN_FILL:
  1072. case HALIGN_LEFT: {
  1073. if (rtl) {
  1074. off.x += width - length;
  1075. }
  1076. } break;
  1077. case HALIGN_CENTER: {
  1078. off.x += Math::floor((width - length) / 2.0);
  1079. } break;
  1080. case HALIGN_RIGHT: {
  1081. if (!rtl) {
  1082. off.x += width - length;
  1083. }
  1084. } break;
  1085. }
  1086. off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top();
  1087. Array objects = TS->shaped_text_get_objects(rid);
  1088. for (int i = 0; i < objects.size(); i++) {
  1089. Item *it = (Item *)(uint64_t)objects[i];
  1090. if (it != nullptr) {
  1091. Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
  1092. if (rect.has_point(p_click - p_ofs - off)) {
  1093. switch (it->type) {
  1094. case ITEM_TABLE: {
  1095. int hseparation = get_theme_constant(SNAME("table_hseparation"));
  1096. int vseparation = get_theme_constant(SNAME("table_vseparation"));
  1097. ItemTable *table = static_cast<ItemTable *>(it);
  1098. table_hit = true;
  1099. int idx = 0;
  1100. int col_count = table->columns.size();
  1101. int row_count = table->rows.size();
  1102. for (Item *E : table->subitems) {
  1103. ItemFrame *frame = static_cast<ItemFrame *>(E);
  1104. int col = idx % col_count;
  1105. int row = idx / col_count;
  1106. if (frame->lines.size() != 0 && row < row_count) {
  1107. Vector2 coff = frame->lines[0].offset;
  1108. if (rtl) {
  1109. coff.x = rect.size.width - table->columns[col].width - coff.x;
  1110. }
  1111. Rect2 crect = Rect2(p_ofs + off + rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size);
  1112. if (col == col_count - 1) {
  1113. if (rtl) {
  1114. crect.size.x = crect.position.x + crect.size.x;
  1115. crect.position.x = 0;
  1116. } else {
  1117. crect.size.x = get_size().x;
  1118. }
  1119. }
  1120. if (crect.has_point(p_click)) {
  1121. for (int j = 0; j < frame->lines.size(); j++) {
  1122. _find_click_in_line(frame, j, p_ofs + off + rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
  1123. if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
  1124. return off.y;
  1125. }
  1126. }
  1127. }
  1128. }
  1129. idx++;
  1130. }
  1131. } break;
  1132. default:
  1133. break;
  1134. }
  1135. }
  1136. }
  1137. }
  1138. Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)), Size2(get_size().x, TS->shaped_text_get_size(rid).y));
  1139. if (rect.has_point(p_click) && !table_hit) {
  1140. char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
  1141. }
  1142. off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom();
  1143. }
  1144. if (char_pos >= 0) {
  1145. // Find item.
  1146. if (r_click_item != nullptr) {
  1147. Item *it = p_frame->lines[p_line].from;
  1148. Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
  1149. if (char_pos == p_frame->lines[p_line].char_count) {
  1150. // Selection after the end of line, select last item.
  1151. if (it_to != nullptr) {
  1152. *r_click_item = _get_prev_item(it_to);
  1153. } else {
  1154. for (Item *i = it; i && i != it_to; i = _get_next_item(i)) {
  1155. *r_click_item = i;
  1156. }
  1157. }
  1158. } else {
  1159. // Selection in the line.
  1160. *r_click_item = _get_item_at_pos(it, it_to, char_pos);
  1161. }
  1162. }
  1163. if (r_click_frame != nullptr) {
  1164. *r_click_frame = p_frame;
  1165. }
  1166. if (r_click_line != nullptr) {
  1167. *r_click_line = p_line;
  1168. }
  1169. if (r_click_char != nullptr) {
  1170. *r_click_char = char_pos;
  1171. }
  1172. }
  1173. return off.y;
  1174. }
  1175. void RichTextLabel::_scroll_changed(double) {
  1176. if (updating_scroll) {
  1177. return;
  1178. }
  1179. if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page())) {
  1180. scroll_following = true;
  1181. } else {
  1182. scroll_following = false;
  1183. }
  1184. scroll_updated = true;
  1185. update();
  1186. }
  1187. void RichTextLabel::_update_scroll() {
  1188. int total_height = get_content_height();
  1189. bool exceeds = total_height > get_size().height && scroll_active;
  1190. if (exceeds != scroll_visible) {
  1191. if (exceeds) {
  1192. scroll_visible = true;
  1193. scroll_w = vscroll->get_combined_minimum_size().width;
  1194. vscroll->show();
  1195. vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w);
  1196. } else {
  1197. scroll_visible = false;
  1198. scroll_w = 0;
  1199. vscroll->hide();
  1200. }
  1201. main->first_resized_line = 0; //invalidate ALL
  1202. _validate_line_caches(main);
  1203. }
  1204. }
  1205. void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, double p_delta_time) {
  1206. Item *it = p_frame;
  1207. while (it) {
  1208. ItemFX *ifx = nullptr;
  1209. if (it->type == ITEM_CUSTOMFX || it->type == ITEM_SHAKE || it->type == ITEM_WAVE || it->type == ITEM_TORNADO || it->type == ITEM_RAINBOW) {
  1210. ifx = static_cast<ItemFX *>(it);
  1211. }
  1212. if (!ifx) {
  1213. it = _get_next_item(it, true);
  1214. continue;
  1215. }
  1216. ifx->elapsed_time += p_delta_time;
  1217. ItemShake *shake = nullptr;
  1218. if (it->type == ITEM_SHAKE) {
  1219. shake = static_cast<ItemShake *>(it);
  1220. }
  1221. if (shake) {
  1222. bool cycle = (shake->elapsed_time > (1.0f / shake->rate));
  1223. if (cycle) {
  1224. shake->elapsed_time -= (1.0f / shake->rate);
  1225. shake->reroll_random();
  1226. }
  1227. }
  1228. it = _get_next_item(it, true);
  1229. }
  1230. }
  1231. void RichTextLabel::_notification(int p_what) {
  1232. switch (p_what) {
  1233. case NOTIFICATION_MOUSE_EXIT: {
  1234. if (meta_hovering) {
  1235. meta_hovering = nullptr;
  1236. emit_signal(SNAME("meta_hover_ended"), current_meta);
  1237. current_meta = false;
  1238. update();
  1239. }
  1240. } break;
  1241. case NOTIFICATION_RESIZED: {
  1242. main->first_resized_line = 0; //invalidate ALL
  1243. update();
  1244. } break;
  1245. case NOTIFICATION_THEME_CHANGED:
  1246. case NOTIFICATION_ENTER_TREE: {
  1247. if (text != "") {
  1248. set_text(text);
  1249. }
  1250. main->first_invalid_line = 0; //invalidate ALL
  1251. update();
  1252. } break;
  1253. case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
  1254. case NOTIFICATION_TRANSLATION_CHANGED: {
  1255. main->first_invalid_line = 0; //invalidate ALL
  1256. update();
  1257. } break;
  1258. case NOTIFICATION_DRAW: {
  1259. _validate_line_caches(main);
  1260. _update_scroll();
  1261. RID ci = get_canvas_item();
  1262. Size2 size = get_size();
  1263. Rect2 text_rect = _get_text_rect();
  1264. draw_style_box(get_theme_stylebox(SNAME("normal")), Rect2(Point2(), size));
  1265. if (has_focus()) {
  1266. RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
  1267. draw_style_box(get_theme_stylebox(SNAME("focus")), Rect2(Point2(), size));
  1268. RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
  1269. }
  1270. float vofs = vscroll->get_value();
  1271. // Search for the first line.
  1272. int from_line = 0;
  1273. //TODO, change to binary search ?
  1274. while (from_line < main->lines.size()) {
  1275. if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) {
  1276. break;
  1277. }
  1278. from_line++;
  1279. }
  1280. if (from_line >= main->lines.size()) {
  1281. break; //nothing to draw
  1282. }
  1283. Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
  1284. Color base_color = get_theme_color(SNAME("default_color"));
  1285. Color outline_color = get_theme_color(SNAME("font_outline_color"));
  1286. int outline_size = get_theme_constant(SNAME("outline_size"));
  1287. Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
  1288. bool use_outline = get_theme_constant(SNAME("shadow_as_outline"));
  1289. Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
  1290. visible_paragraph_count = 0;
  1291. visible_line_count = 0;
  1292. // New cache draw.
  1293. Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
  1294. while (ofs.y < size.height && from_line < main->lines.size()) {
  1295. visible_paragraph_count++;
  1296. visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, use_outline, shadow_ofs);
  1297. ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
  1298. from_line++;
  1299. }
  1300. } break;
  1301. case NOTIFICATION_INTERNAL_PROCESS: {
  1302. if (is_visible_in_tree()) {
  1303. double dt = get_process_delta_time();
  1304. _update_fx(main, dt);
  1305. update();
  1306. }
  1307. } break;
  1308. case NOTIFICATION_FOCUS_EXIT: {
  1309. if (deselect_on_focus_loss_enabled) {
  1310. selection.active = false;
  1311. update();
  1312. }
  1313. } break;
  1314. }
  1315. }
  1316. Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const {
  1317. if (!underline_meta) {
  1318. return get_default_cursor_shape();
  1319. }
  1320. if (selection.click_item) {
  1321. return CURSOR_IBEAM;
  1322. }
  1323. if (main->first_invalid_line < main->lines.size()) {
  1324. return get_default_cursor_shape(); //invalid
  1325. }
  1326. if (main->first_resized_line < main->lines.size()) {
  1327. return get_default_cursor_shape(); //invalid
  1328. }
  1329. Item *item = nullptr;
  1330. bool outside = true;
  1331. ((RichTextLabel *)(this))->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside);
  1332. if (item && !outside && ((RichTextLabel *)(this))->_find_meta(item, nullptr)) {
  1333. return CURSOR_POINTING_HAND;
  1334. }
  1335. return get_default_cursor_shape();
  1336. }
  1337. void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
  1338. ERR_FAIL_COND(p_event.is_null());
  1339. Ref<InputEventMouseButton> b = p_event;
  1340. if (b.is_valid()) {
  1341. if (main->first_invalid_line < main->lines.size()) {
  1342. return;
  1343. }
  1344. if (main->first_resized_line < main->lines.size()) {
  1345. return;
  1346. }
  1347. if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
  1348. if (b->is_pressed() && !b->is_double_click()) {
  1349. scroll_updated = false;
  1350. ItemFrame *c_frame = nullptr;
  1351. int c_line = 0;
  1352. Item *c_item = nullptr;
  1353. int c_index = 0;
  1354. bool outside;
  1355. _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
  1356. if (c_item != nullptr) {
  1357. if (selection.enabled) {
  1358. selection.click_frame = c_frame;
  1359. selection.click_item = c_item;
  1360. selection.click_line = c_line;
  1361. selection.click_char = c_index;
  1362. // Erase previous selection.
  1363. if (selection.active) {
  1364. selection.from_frame = nullptr;
  1365. selection.from_line = 0;
  1366. selection.from_item = nullptr;
  1367. selection.from_char = 0;
  1368. selection.to_frame = nullptr;
  1369. selection.to_line = 0;
  1370. selection.to_item = nullptr;
  1371. selection.to_char = 0;
  1372. selection.active = false;
  1373. update();
  1374. }
  1375. }
  1376. }
  1377. } else if (b->is_pressed() && b->is_double_click() && selection.enabled) {
  1378. //double_click: select word
  1379. ItemFrame *c_frame = nullptr;
  1380. int c_line = 0;
  1381. Item *c_item = nullptr;
  1382. int c_index = 0;
  1383. bool outside;
  1384. _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
  1385. if (c_frame) {
  1386. const Line &l = c_frame->lines[c_line];
  1387. PackedInt32Array words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid());
  1388. for (int i = 0; i < words.size(); i = i + 2) {
  1389. if (c_index >= words[i] && c_index < words[i + 1]) {
  1390. selection.from_frame = c_frame;
  1391. selection.from_line = c_line;
  1392. selection.from_item = c_item;
  1393. selection.from_char = words[i];
  1394. selection.to_frame = c_frame;
  1395. selection.to_line = c_line;
  1396. selection.to_item = c_item;
  1397. selection.to_char = words[i + 1];
  1398. selection.active = true;
  1399. if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
  1400. DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
  1401. }
  1402. update();
  1403. break;
  1404. }
  1405. }
  1406. }
  1407. } else if (!b->is_pressed()) {
  1408. if (selection.enabled && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
  1409. DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
  1410. }
  1411. selection.click_item = nullptr;
  1412. if (!b->is_double_click() && !scroll_updated) {
  1413. Item *c_item = nullptr;
  1414. bool outside = true;
  1415. _find_click(main, b->get_position(), nullptr, nullptr, &c_item, nullptr, &outside);
  1416. if (c_item) {
  1417. Variant meta;
  1418. if (!outside && _find_meta(c_item, &meta)) {
  1419. //meta clicked
  1420. emit_signal(SNAME("meta_clicked"), meta);
  1421. }
  1422. }
  1423. }
  1424. }
  1425. }
  1426. if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
  1427. if (scroll_active) {
  1428. vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
  1429. }
  1430. }
  1431. if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
  1432. if (scroll_active) {
  1433. vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
  1434. }
  1435. }
  1436. }
  1437. Ref<InputEventPanGesture> pan_gesture = p_event;
  1438. if (pan_gesture.is_valid()) {
  1439. if (scroll_active) {
  1440. vscroll->set_value(vscroll->get_value() + vscroll->get_page() * pan_gesture->get_delta().y * 0.5 / 8);
  1441. }
  1442. return;
  1443. }
  1444. Ref<InputEventKey> k = p_event;
  1445. if (k.is_valid()) {
  1446. if (k->is_pressed()) {
  1447. bool handled = false;
  1448. if (k->is_action("ui_page_up") && vscroll->is_visible_in_tree()) {
  1449. vscroll->set_value(vscroll->get_value() - vscroll->get_page());
  1450. handled = true;
  1451. }
  1452. if (k->is_action("ui_page_down") && vscroll->is_visible_in_tree()) {
  1453. vscroll->set_value(vscroll->get_value() + vscroll->get_page());
  1454. handled = true;
  1455. }
  1456. if (k->is_action("ui_up") && vscroll->is_visible_in_tree()) {
  1457. vscroll->set_value(vscroll->get_value() - get_theme_font(SNAME("normal_font"))->get_height(get_theme_font_size(SNAME("normal_font_size"))));
  1458. handled = true;
  1459. }
  1460. if (k->is_action("ui_down") && vscroll->is_visible_in_tree()) {
  1461. vscroll->set_value(vscroll->get_value() + get_theme_font(SNAME("normal_font"))->get_height(get_theme_font_size(SNAME("normal_font_size"))));
  1462. handled = true;
  1463. }
  1464. if (k->is_action("ui_home") && vscroll->is_visible_in_tree()) {
  1465. vscroll->set_value(0);
  1466. handled = true;
  1467. }
  1468. if (k->is_action("ui_end") && vscroll->is_visible_in_tree()) {
  1469. vscroll->set_value(vscroll->get_max());
  1470. handled = true;
  1471. }
  1472. if (k->is_action("ui_copy")) {
  1473. selection_copy();
  1474. handled = true;
  1475. }
  1476. if (handled) {
  1477. accept_event();
  1478. }
  1479. }
  1480. }
  1481. Ref<InputEventMouseMotion> m = p_event;
  1482. if (m.is_valid()) {
  1483. if (main->first_invalid_line < main->lines.size()) {
  1484. return;
  1485. }
  1486. if (main->first_resized_line < main->lines.size()) {
  1487. return;
  1488. }
  1489. ItemFrame *c_frame = nullptr;
  1490. int c_line = 0;
  1491. Item *c_item = nullptr;
  1492. int c_index = 0;
  1493. bool outside;
  1494. _find_click(main, m->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
  1495. if (selection.click_item && c_item) {
  1496. selection.from_frame = selection.click_frame;
  1497. selection.from_line = selection.click_line;
  1498. selection.from_item = selection.click_item;
  1499. selection.from_char = selection.click_char;
  1500. selection.to_frame = c_frame;
  1501. selection.to_line = c_line;
  1502. selection.to_item = c_item;
  1503. selection.to_char = c_index;
  1504. bool swap = false;
  1505. if (selection.from_item->index > selection.to_item->index) {
  1506. swap = true;
  1507. } else if (selection.from_item->index == selection.to_item->index) {
  1508. if (selection.from_char > selection.to_char) {
  1509. swap = true;
  1510. } else if (selection.from_char == selection.to_char) {
  1511. selection.active = false;
  1512. update();
  1513. return;
  1514. }
  1515. }
  1516. if (swap) {
  1517. SWAP(selection.from_frame, selection.to_frame);
  1518. SWAP(selection.from_line, selection.to_line);
  1519. SWAP(selection.from_item, selection.to_item);
  1520. SWAP(selection.from_char, selection.to_char);
  1521. }
  1522. selection.active = true;
  1523. update();
  1524. }
  1525. Variant meta;
  1526. ItemMeta *item_meta;
  1527. if (c_item && !outside && _find_meta(c_item, &meta, &item_meta)) {
  1528. if (meta_hovering != item_meta) {
  1529. if (meta_hovering) {
  1530. emit_signal(SNAME("meta_hover_ended"), current_meta);
  1531. }
  1532. meta_hovering = item_meta;
  1533. current_meta = meta;
  1534. emit_signal(SNAME("meta_hover_started"), meta);
  1535. }
  1536. } else if (meta_hovering) {
  1537. meta_hovering = nullptr;
  1538. emit_signal(SNAME("meta_hover_ended"), current_meta);
  1539. current_meta = false;
  1540. }
  1541. }
  1542. }
  1543. void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) {
  1544. if (r_frame != nullptr) {
  1545. *r_frame = nullptr;
  1546. }
  1547. if (r_line != nullptr) {
  1548. *r_line = 0;
  1549. }
  1550. Item *item = p_item;
  1551. while (item) {
  1552. if (item->parent != nullptr && item->parent->type == ITEM_FRAME) {
  1553. if (r_frame != nullptr) {
  1554. *r_frame = (ItemFrame *)item->parent;
  1555. }
  1556. if (r_line != nullptr) {
  1557. *r_line = item->line;
  1558. }
  1559. return;
  1560. }
  1561. item = item->parent;
  1562. }
  1563. }
  1564. Ref<Font> RichTextLabel::_find_font(Item *p_item) {
  1565. Item *fontitem = p_item;
  1566. while (fontitem) {
  1567. if (fontitem->type == ITEM_FONT) {
  1568. ItemFont *fi = static_cast<ItemFont *>(fontitem);
  1569. return fi->font;
  1570. }
  1571. fontitem = fontitem->parent;
  1572. }
  1573. return Ref<Font>();
  1574. }
  1575. int RichTextLabel::_find_font_size(Item *p_item) {
  1576. Item *sizeitem = p_item;
  1577. while (sizeitem) {
  1578. if (sizeitem->type == ITEM_FONT_SIZE) {
  1579. ItemFontSize *fi = static_cast<ItemFontSize *>(sizeitem);
  1580. return fi->font_size;
  1581. }
  1582. sizeitem = sizeitem->parent;
  1583. }
  1584. return -1;
  1585. }
  1586. int RichTextLabel::_find_outline_size(Item *p_item, int p_default) {
  1587. Item *sizeitem = p_item;
  1588. while (sizeitem) {
  1589. if (sizeitem->type == ITEM_OUTLINE_SIZE) {
  1590. ItemOutlineSize *fi = static_cast<ItemOutlineSize *>(sizeitem);
  1591. return fi->outline_size;
  1592. }
  1593. sizeitem = sizeitem->parent;
  1594. }
  1595. return p_default;
  1596. }
  1597. Dictionary RichTextLabel::_find_font_features(Item *p_item) {
  1598. Item *ffitem = p_item;
  1599. while (ffitem) {
  1600. if (ffitem->type == ITEM_FONT_FEATURES) {
  1601. ItemFontFeatures *fi = static_cast<ItemFontFeatures *>(ffitem);
  1602. return fi->opentype_features;
  1603. }
  1604. ffitem = ffitem->parent;
  1605. }
  1606. return Dictionary();
  1607. }
  1608. RichTextLabel::ItemDropcap *RichTextLabel::_find_dc_item(Item *p_item) {
  1609. Item *item = p_item;
  1610. while (item) {
  1611. if (item->type == ITEM_DROPCAP) {
  1612. return static_cast<ItemDropcap *>(item);
  1613. }
  1614. item = item->parent;
  1615. }
  1616. return nullptr;
  1617. }
  1618. RichTextLabel::ItemList *RichTextLabel::_find_list_item(Item *p_item) {
  1619. Item *item = p_item;
  1620. while (item) {
  1621. if (item->type == ITEM_LIST) {
  1622. return static_cast<ItemList *>(item);
  1623. }
  1624. item = item->parent;
  1625. }
  1626. return nullptr;
  1627. }
  1628. int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list) {
  1629. Item *item = p_item;
  1630. Item *prev_item = p_item;
  1631. int level = 0;
  1632. while (item) {
  1633. if (item->type == ITEM_LIST) {
  1634. ItemList *list = static_cast<ItemList *>(item);
  1635. ItemFrame *frame = nullptr;
  1636. int line = -1;
  1637. _find_frame(list, &frame, &line);
  1638. int index = 1;
  1639. if (frame != nullptr) {
  1640. for (int i = list->line + 1; i <= prev_item->line; i++) {
  1641. if (_find_list_item(frame->lines[i].from) == list) {
  1642. index++;
  1643. }
  1644. }
  1645. }
  1646. r_index.push_back(index);
  1647. r_list.push_back(list);
  1648. prev_item = item;
  1649. }
  1650. level++;
  1651. item = item->parent;
  1652. }
  1653. return level;
  1654. }
  1655. int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size) {
  1656. Item *item = p_item;
  1657. float margin = 0.0;
  1658. while (item) {
  1659. if (item->type == ITEM_INDENT) {
  1660. Ref<Font> font = _find_font(item);
  1661. if (font.is_null()) {
  1662. font = p_base_font;
  1663. }
  1664. int font_size = _find_font_size(item);
  1665. if (font_size == -1) {
  1666. font_size = p_base_font_size;
  1667. }
  1668. margin += tab_size * font->get_char_size(' ', 0, font_size).width;
  1669. } else if (item->type == ITEM_LIST) {
  1670. Ref<Font> font = _find_font(item);
  1671. if (font.is_null()) {
  1672. font = p_base_font;
  1673. }
  1674. int font_size = _find_font_size(item);
  1675. if (font_size == -1) {
  1676. font_size = p_base_font_size;
  1677. }
  1678. margin += tab_size * font->get_char_size(' ', 0, font_size).width;
  1679. }
  1680. item = item->parent;
  1681. }
  1682. return margin;
  1683. }
  1684. RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) {
  1685. Item *item = p_item;
  1686. while (item) {
  1687. if (item->type == ITEM_PARAGRAPH) {
  1688. ItemParagraph *p = static_cast<ItemParagraph *>(item);
  1689. return p->align;
  1690. }
  1691. item = item->parent;
  1692. }
  1693. return default_align;
  1694. }
  1695. TextServer::Direction RichTextLabel::_find_direction(Item *p_item) {
  1696. Item *item = p_item;
  1697. while (item) {
  1698. if (item->type == ITEM_PARAGRAPH) {
  1699. ItemParagraph *p = static_cast<ItemParagraph *>(item);
  1700. if (p->direction != Control::TEXT_DIRECTION_INHERITED) {
  1701. return (TextServer::Direction)p->direction;
  1702. }
  1703. }
  1704. item = item->parent;
  1705. }
  1706. if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
  1707. return is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR;
  1708. } else {
  1709. return (TextServer::Direction)text_direction;
  1710. }
  1711. }
  1712. Control::StructuredTextParser RichTextLabel::_find_stt(Item *p_item) {
  1713. Item *item = p_item;
  1714. while (item) {
  1715. if (item->type == ITEM_PARAGRAPH) {
  1716. ItemParagraph *p = static_cast<ItemParagraph *>(item);
  1717. return p->st_parser;
  1718. }
  1719. item = item->parent;
  1720. }
  1721. return st_parser;
  1722. }
  1723. String RichTextLabel::_find_language(Item *p_item) {
  1724. Item *item = p_item;
  1725. while (item) {
  1726. if (item->type == ITEM_PARAGRAPH) {
  1727. ItemParagraph *p = static_cast<ItemParagraph *>(item);
  1728. return p->language;
  1729. }
  1730. item = item->parent;
  1731. }
  1732. return language;
  1733. }
  1734. Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) {
  1735. Item *item = p_item;
  1736. while (item) {
  1737. if (item->type == ITEM_COLOR) {
  1738. ItemColor *color = static_cast<ItemColor *>(item);
  1739. return color->color;
  1740. }
  1741. item = item->parent;
  1742. }
  1743. return p_default_color;
  1744. }
  1745. Color RichTextLabel::_find_outline_color(Item *p_item, const Color &p_default_color) {
  1746. Item *item = p_item;
  1747. while (item) {
  1748. if (item->type == ITEM_OUTLINE_COLOR) {
  1749. ItemOutlineColor *color = static_cast<ItemOutlineColor *>(item);
  1750. return color->color;
  1751. }
  1752. item = item->parent;
  1753. }
  1754. return p_default_color;
  1755. }
  1756. bool RichTextLabel::_find_underline(Item *p_item) {
  1757. Item *item = p_item;
  1758. while (item) {
  1759. if (item->type == ITEM_UNDERLINE) {
  1760. return true;
  1761. }
  1762. item = item->parent;
  1763. }
  1764. return false;
  1765. }
  1766. bool RichTextLabel::_find_strikethrough(Item *p_item) {
  1767. Item *item = p_item;
  1768. while (item) {
  1769. if (item->type == ITEM_STRIKETHROUGH) {
  1770. return true;
  1771. }
  1772. item = item->parent;
  1773. }
  1774. return false;
  1775. }
  1776. void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack) {
  1777. Item *item = p_item;
  1778. while (item) {
  1779. if (item->type == ITEM_CUSTOMFX || item->type == ITEM_SHAKE || item->type == ITEM_WAVE || item->type == ITEM_TORNADO || item->type == ITEM_RAINBOW) {
  1780. r_stack.push_back(static_cast<ItemFX *>(item));
  1781. }
  1782. item = item->parent;
  1783. }
  1784. }
  1785. bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
  1786. Item *item = p_item;
  1787. while (item) {
  1788. if (item->type == ITEM_META) {
  1789. ItemMeta *meta = static_cast<ItemMeta *>(item);
  1790. if (r_meta) {
  1791. *r_meta = meta->meta;
  1792. }
  1793. if (r_item) {
  1794. *r_item = meta;
  1795. }
  1796. return true;
  1797. }
  1798. item = item->parent;
  1799. }
  1800. return false;
  1801. }
  1802. Color RichTextLabel::_find_bgcolor(Item *p_item) {
  1803. Item *item = p_item;
  1804. while (item) {
  1805. if (item->type == ITEM_BGCOLOR) {
  1806. ItemBGColor *color = static_cast<ItemBGColor *>(item);
  1807. return color->color;
  1808. }
  1809. item = item->parent;
  1810. }
  1811. return Color(0, 0, 0, 0);
  1812. }
  1813. Color RichTextLabel::_find_fgcolor(Item *p_item) {
  1814. Item *item = p_item;
  1815. while (item) {
  1816. if (item->type == ITEM_FGCOLOR) {
  1817. ItemFGColor *color = static_cast<ItemFGColor *>(item);
  1818. return color->color;
  1819. }
  1820. item = item->parent;
  1821. }
  1822. return Color(0, 0, 0, 0);
  1823. }
  1824. bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
  1825. if (from && from != to) {
  1826. if (from->type != ITEM_FONT && from->type != ITEM_COLOR && from->type != ITEM_UNDERLINE && from->type != ITEM_STRIKETHROUGH) {
  1827. return true;
  1828. }
  1829. for (Item *E : from->subitems) {
  1830. bool layout = _find_layout_subitem(E, to);
  1831. if (layout) {
  1832. return true;
  1833. }
  1834. }
  1835. }
  1836. return false;
  1837. }
  1838. void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
  1839. if (p_frame->first_invalid_line == p_frame->lines.size()) {
  1840. if (p_frame->first_resized_line == p_frame->lines.size()) {
  1841. return;
  1842. }
  1843. // Resize lines without reshaping.
  1844. Size2 size = get_size();
  1845. if (fixed_width != -1) {
  1846. size.width = fixed_width;
  1847. }
  1848. Rect2 text_rect = _get_text_rect();
  1849. Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
  1850. int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
  1851. for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) {
  1852. _resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w);
  1853. }
  1854. int total_height = 0;
  1855. if (p_frame->lines.size()) {
  1856. total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y;
  1857. }
  1858. p_frame->first_resized_line = p_frame->lines.size();
  1859. updating_scroll = true;
  1860. vscroll->set_max(total_height);
  1861. vscroll->set_page(text_rect.size.height);
  1862. if (scroll_follow && scroll_following) {
  1863. vscroll->set_value(total_height - size.height);
  1864. }
  1865. updating_scroll = false;
  1866. if (fit_content_height) {
  1867. minimum_size_changed();
  1868. }
  1869. return;
  1870. }
  1871. // Shape invalid lines.
  1872. Size2 size = get_size();
  1873. if (fixed_width != -1) {
  1874. size.width = fixed_width;
  1875. }
  1876. Rect2 text_rect = _get_text_rect();
  1877. Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
  1878. int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
  1879. int total_chars = (p_frame->first_invalid_line == 0) ? 0 : (p_frame->lines[p_frame->first_invalid_line].char_offset + p_frame->lines[p_frame->first_invalid_line].char_count);
  1880. for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) {
  1881. _shape_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, &total_chars);
  1882. }
  1883. int total_height = 0;
  1884. if (p_frame->lines.size()) {
  1885. total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y;
  1886. }
  1887. p_frame->first_invalid_line = p_frame->lines.size();
  1888. p_frame->first_resized_line = p_frame->lines.size();
  1889. updating_scroll = true;
  1890. vscroll->set_max(total_height);
  1891. vscroll->set_page(text_rect.size.height);
  1892. if (scroll_follow && scroll_following) {
  1893. vscroll->set_value(total_height - size.height);
  1894. }
  1895. updating_scroll = false;
  1896. if (fit_content_height) {
  1897. minimum_size_changed();
  1898. }
  1899. }
  1900. void RichTextLabel::_invalidate_current_line(ItemFrame *p_frame) {
  1901. if (p_frame->lines.size() - 1 <= p_frame->first_invalid_line) {
  1902. p_frame->first_invalid_line = p_frame->lines.size() - 1;
  1903. update();
  1904. }
  1905. }
  1906. void RichTextLabel::add_text(const String &p_text) {
  1907. if (current->type == ITEM_TABLE) {
  1908. return; //can't add anything here
  1909. }
  1910. int pos = 0;
  1911. while (pos < p_text.length()) {
  1912. int end = p_text.find("\n", pos);
  1913. String line;
  1914. bool eol = false;
  1915. if (end == -1) {
  1916. end = p_text.length();
  1917. } else {
  1918. eol = true;
  1919. }
  1920. if (pos == 0 && end == p_text.length()) {
  1921. line = p_text;
  1922. } else {
  1923. line = p_text.substr(pos, end - pos);
  1924. }
  1925. if (line.length() > 0) {
  1926. if (current->subitems.size() && current->subitems.back()->get()->type == ITEM_TEXT) {
  1927. //append text condition!
  1928. ItemText *ti = static_cast<ItemText *>(current->subitems.back()->get());
  1929. ti->text += line;
  1930. _invalidate_current_line(main);
  1931. } else {
  1932. //append item condition
  1933. ItemText *item = memnew(ItemText);
  1934. item->text = line;
  1935. _add_item(item, false);
  1936. }
  1937. }
  1938. if (eol) {
  1939. ItemNewline *item = memnew(ItemNewline);
  1940. item->line = current_frame->lines.size();
  1941. _add_item(item, false);
  1942. current_frame->lines.resize(current_frame->lines.size() + 1);
  1943. if (item->type != ITEM_NEWLINE) {
  1944. current_frame->lines.write[current_frame->lines.size() - 1].from = item;
  1945. }
  1946. _invalidate_current_line(current_frame);
  1947. }
  1948. pos = end + 1;
  1949. }
  1950. }
  1951. void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) {
  1952. p_item->parent = current;
  1953. p_item->E = current->subitems.push_back(p_item);
  1954. p_item->index = current_idx++;
  1955. p_item->char_ofs = current_char_ofs;
  1956. if (p_item->type == ITEM_TEXT) {
  1957. ItemText *t = (ItemText *)p_item;
  1958. current_char_ofs += t->text.length();
  1959. } else if (p_item->type == ITEM_IMAGE) {
  1960. current_char_ofs++;
  1961. }
  1962. if (p_enter) {
  1963. current = p_item;
  1964. }
  1965. if (p_ensure_newline) {
  1966. Item *from = current_frame->lines[current_frame->lines.size() - 1].from;
  1967. // only create a new line for Item types that generate content/layout, ignore those that represent formatting/styling
  1968. if (_find_layout_subitem(from, p_item)) {
  1969. _invalidate_current_line(current_frame);
  1970. current_frame->lines.resize(current_frame->lines.size() + 1);
  1971. }
  1972. }
  1973. if (current_frame->lines[current_frame->lines.size() - 1].from == nullptr) {
  1974. current_frame->lines.write[current_frame->lines.size() - 1].from = p_item;
  1975. }
  1976. p_item->line = current_frame->lines.size() - 1;
  1977. _invalidate_current_line(current_frame);
  1978. if (fixed_width != -1) {
  1979. minimum_size_changed();
  1980. }
  1981. }
  1982. void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_subitem_line) {
  1983. int size = p_item->subitems.size();
  1984. if (size == 0) {
  1985. p_item->parent->subitems.erase(p_item);
  1986. // If a newline was erased, all lines AFTER the newline need to be decremented.
  1987. if (p_item->type == ITEM_NEWLINE) {
  1988. current_frame->lines.remove(p_line);
  1989. for (int i = 0; i < current->subitems.size(); i++) {
  1990. if (current->subitems[i]->line > p_subitem_line) {
  1991. current->subitems[i]->line--;
  1992. }
  1993. }
  1994. }
  1995. } else {
  1996. // First, remove all child items for the provided item.
  1997. for (int i = 0; i < size; i++) {
  1998. _remove_item(p_item->subitems.front()->get(), p_line, p_subitem_line);
  1999. }
  2000. // Then remove the provided item itself.
  2001. p_item->parent->subitems.erase(p_item);
  2002. }
  2003. }
  2004. void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlign p_align) {
  2005. if (current->type == ITEM_TABLE) {
  2006. return;
  2007. }
  2008. ERR_FAIL_COND(p_image.is_null());
  2009. ERR_FAIL_COND(p_image->get_width() == 0);
  2010. ERR_FAIL_COND(p_image->get_height() == 0);
  2011. ItemImage *item = memnew(ItemImage);
  2012. item->image = p_image;
  2013. item->color = p_color;
  2014. item->inline_align = p_align;
  2015. if (p_width > 0) {
  2016. // custom width
  2017. item->size.width = p_width;
  2018. if (p_height > 0) {
  2019. // custom height
  2020. item->size.height = p_height;
  2021. } else {
  2022. // calculate height to keep aspect ratio
  2023. item->size.height = p_image->get_height() * p_width / p_image->get_width();
  2024. }
  2025. } else {
  2026. if (p_height > 0) {
  2027. // custom height
  2028. item->size.height = p_height;
  2029. // calculate width to keep aspect ratio
  2030. item->size.width = p_image->get_width() * p_height / p_image->get_height();
  2031. } else {
  2032. // keep original width and height
  2033. item->size = p_image->get_size();
  2034. }
  2035. }
  2036. _add_item(item, false);
  2037. }
  2038. void RichTextLabel::add_newline() {
  2039. if (current->type == ITEM_TABLE) {
  2040. return;
  2041. }
  2042. ItemNewline *item = memnew(ItemNewline);
  2043. item->line = current_frame->lines.size();
  2044. _add_item(item, false);
  2045. current_frame->lines.resize(current_frame->lines.size() + 1);
  2046. _invalidate_current_line(current_frame);
  2047. }
  2048. bool RichTextLabel::remove_line(const int p_line) {
  2049. if (p_line >= current_frame->lines.size() || p_line < 0) {
  2050. return false;
  2051. }
  2052. // Remove all subitems with the same line as that provided.
  2053. Vector<int> subitem_indices_to_remove;
  2054. for (int i = 0; i < current->subitems.size(); i++) {
  2055. if (current->subitems[i]->line == p_line) {
  2056. subitem_indices_to_remove.push_back(i);
  2057. }
  2058. }
  2059. bool had_newline = false;
  2060. // Reverse for loop to remove items from the end first.
  2061. for (int i = subitem_indices_to_remove.size() - 1; i >= 0; i--) {
  2062. int subitem_idx = subitem_indices_to_remove[i];
  2063. had_newline = had_newline || current->subitems[subitem_idx]->type == ITEM_NEWLINE;
  2064. _remove_item(current->subitems[subitem_idx], current->subitems[subitem_idx]->line, p_line);
  2065. }
  2066. if (!had_newline) {
  2067. current_frame->lines.remove(p_line);
  2068. if (current_frame->lines.size() == 0) {
  2069. current_frame->lines.resize(1);
  2070. }
  2071. }
  2072. if (p_line == 0 && current->subitems.size() > 0) {
  2073. main->lines.write[0].from = main;
  2074. }
  2075. main->first_invalid_line = 0; // p_line ???
  2076. update();
  2077. return true;
  2078. }
  2079. void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins, const Color &p_color, int p_ol_size, const Color &p_ol_color) {
  2080. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2081. ERR_FAIL_COND(p_string.is_empty());
  2082. ERR_FAIL_COND(p_font.is_null());
  2083. ERR_FAIL_COND(p_size <= 0);
  2084. ItemDropcap *item = memnew(ItemDropcap);
  2085. item->text = p_string;
  2086. item->font = p_font;
  2087. item->font_size = p_size;
  2088. item->color = p_color;
  2089. item->ol_size = p_ol_size;
  2090. item->ol_color = p_ol_color;
  2091. item->dropcap_margins = p_dropcap_margins;
  2092. _add_item(item, false);
  2093. }
  2094. void RichTextLabel::push_font(const Ref<Font> &p_font) {
  2095. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2096. ERR_FAIL_COND(p_font.is_null());
  2097. ItemFont *item = memnew(ItemFont);
  2098. item->font = p_font;
  2099. _add_item(item, true);
  2100. }
  2101. void RichTextLabel::push_normal() {
  2102. Ref<Font> normal_font = get_theme_font(SNAME("normal_font"));
  2103. ERR_FAIL_COND(normal_font.is_null());
  2104. push_font(normal_font);
  2105. }
  2106. void RichTextLabel::push_bold() {
  2107. Ref<Font> bold_font = get_theme_font(SNAME("bold_font"));
  2108. ERR_FAIL_COND(bold_font.is_null());
  2109. push_font(bold_font);
  2110. }
  2111. void RichTextLabel::push_bold_italics() {
  2112. Ref<Font> bold_italics_font = get_theme_font(SNAME("bold_italics_font"));
  2113. ERR_FAIL_COND(bold_italics_font.is_null());
  2114. push_font(bold_italics_font);
  2115. }
  2116. void RichTextLabel::push_italics() {
  2117. Ref<Font> italics_font = get_theme_font(SNAME("italics_font"));
  2118. ERR_FAIL_COND(italics_font.is_null());
  2119. push_font(italics_font);
  2120. }
  2121. void RichTextLabel::push_mono() {
  2122. Ref<Font> mono_font = get_theme_font(SNAME("mono_font"));
  2123. ERR_FAIL_COND(mono_font.is_null());
  2124. push_font(mono_font);
  2125. }
  2126. void RichTextLabel::push_font_size(int p_font_size) {
  2127. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2128. ItemFontSize *item = memnew(ItemFontSize);
  2129. item->font_size = p_font_size;
  2130. _add_item(item, true);
  2131. }
  2132. void RichTextLabel::push_font_features(const Dictionary &p_features) {
  2133. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2134. ItemFontFeatures *item = memnew(ItemFontFeatures);
  2135. item->opentype_features = p_features;
  2136. _add_item(item, true);
  2137. }
  2138. void RichTextLabel::push_outline_size(int p_font_size) {
  2139. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2140. ItemOutlineSize *item = memnew(ItemOutlineSize);
  2141. item->outline_size = p_font_size;
  2142. _add_item(item, true);
  2143. }
  2144. void RichTextLabel::push_color(const Color &p_color) {
  2145. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2146. ItemColor *item = memnew(ItemColor);
  2147. item->color = p_color;
  2148. _add_item(item, true);
  2149. }
  2150. void RichTextLabel::push_outline_color(const Color &p_color) {
  2151. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2152. ItemOutlineColor *item = memnew(ItemOutlineColor);
  2153. item->color = p_color;
  2154. _add_item(item, true);
  2155. }
  2156. void RichTextLabel::push_underline() {
  2157. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2158. ItemUnderline *item = memnew(ItemUnderline);
  2159. _add_item(item, true);
  2160. }
  2161. void RichTextLabel::push_strikethrough() {
  2162. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2163. ItemStrikethrough *item = memnew(ItemStrikethrough);
  2164. _add_item(item, true);
  2165. }
  2166. void RichTextLabel::push_paragraph(Align p_align, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) {
  2167. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2168. ItemParagraph *item = memnew(ItemParagraph);
  2169. item->align = p_align;
  2170. item->direction = p_direction;
  2171. item->language = p_language;
  2172. item->st_parser = p_st_parser;
  2173. _add_item(item, true, true);
  2174. }
  2175. void RichTextLabel::push_indent(int p_level) {
  2176. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2177. ERR_FAIL_COND(p_level < 0);
  2178. ItemIndent *item = memnew(ItemIndent);
  2179. item->level = p_level;
  2180. _add_item(item, true, true);
  2181. }
  2182. void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize) {
  2183. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2184. ERR_FAIL_COND(p_level < 0);
  2185. ItemList *item = memnew(ItemList);
  2186. item->list_type = p_list;
  2187. item->level = p_level;
  2188. item->capitalize = p_capitalize;
  2189. _add_item(item, true, true);
  2190. }
  2191. void RichTextLabel::push_meta(const Variant &p_meta) {
  2192. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2193. ItemMeta *item = memnew(ItemMeta);
  2194. item->meta = p_meta;
  2195. _add_item(item, true);
  2196. }
  2197. void RichTextLabel::push_table(int p_columns, InlineAlign p_align) {
  2198. ERR_FAIL_COND(p_columns < 1);
  2199. ItemTable *item = memnew(ItemTable);
  2200. item->columns.resize(p_columns);
  2201. item->total_width = 0;
  2202. item->inline_align = p_align;
  2203. for (int i = 0; i < item->columns.size(); i++) {
  2204. item->columns.write[i].expand = false;
  2205. item->columns.write[i].expand_ratio = 1;
  2206. }
  2207. _add_item(item, true, false);
  2208. }
  2209. void RichTextLabel::push_fade(int p_start_index, int p_length) {
  2210. ItemFade *item = memnew(ItemFade);
  2211. item->starting_index = p_start_index;
  2212. item->length = p_length;
  2213. _add_item(item, true);
  2214. }
  2215. void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
  2216. ItemShake *item = memnew(ItemShake);
  2217. item->strength = p_strength;
  2218. item->rate = p_rate;
  2219. _add_item(item, true);
  2220. }
  2221. void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) {
  2222. ItemWave *item = memnew(ItemWave);
  2223. item->frequency = p_frequency;
  2224. item->amplitude = p_amplitude;
  2225. _add_item(item, true);
  2226. }
  2227. void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) {
  2228. ItemTornado *item = memnew(ItemTornado);
  2229. item->frequency = p_frequency;
  2230. item->radius = p_radius;
  2231. _add_item(item, true);
  2232. }
  2233. void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_frequency) {
  2234. ItemRainbow *item = memnew(ItemRainbow);
  2235. item->frequency = p_frequency;
  2236. item->saturation = p_saturation;
  2237. item->value = p_value;
  2238. _add_item(item, true);
  2239. }
  2240. void RichTextLabel::push_bgcolor(const Color &p_color) {
  2241. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2242. ItemBGColor *item = memnew(ItemBGColor);
  2243. item->color = p_color;
  2244. _add_item(item, true);
  2245. }
  2246. void RichTextLabel::push_fgcolor(const Color &p_color) {
  2247. ERR_FAIL_COND(current->type == ITEM_TABLE);
  2248. ItemFGColor *item = memnew(ItemFGColor);
  2249. item->color = p_color;
  2250. _add_item(item, true);
  2251. }
  2252. void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment) {
  2253. ItemCustomFX *item = memnew(ItemCustomFX);
  2254. item->custom_effect = p_custom_effect;
  2255. item->char_fx_transform->environment = p_environment;
  2256. _add_item(item, true);
  2257. }
  2258. void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
  2259. ERR_FAIL_COND(current->type != ITEM_TABLE);
  2260. ItemTable *table = static_cast<ItemTable *>(current);
  2261. ERR_FAIL_INDEX(p_column, table->columns.size());
  2262. table->columns.write[p_column].expand = p_expand;
  2263. table->columns.write[p_column].expand_ratio = p_ratio;
  2264. }
  2265. void RichTextLabel::set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg) {
  2266. ERR_FAIL_COND(current->type != ITEM_FRAME);
  2267. ItemFrame *cell = static_cast<ItemFrame *>(current);
  2268. ERR_FAIL_COND(!cell->cell);
  2269. cell->odd_row_bg = p_odd_row_bg;
  2270. cell->even_row_bg = p_even_row_bg;
  2271. }
  2272. void RichTextLabel::set_cell_border_color(const Color &p_color) {
  2273. ERR_FAIL_COND(current->type != ITEM_FRAME);
  2274. ItemFrame *cell = static_cast<ItemFrame *>(current);
  2275. ERR_FAIL_COND(!cell->cell);
  2276. cell->border = p_color;
  2277. }
  2278. void RichTextLabel::set_cell_size_override(const Size2 &p_min_size, const Size2 &p_max_size) {
  2279. ERR_FAIL_COND(current->type != ITEM_FRAME);
  2280. ItemFrame *cell = static_cast<ItemFrame *>(current);
  2281. ERR_FAIL_COND(!cell->cell);
  2282. cell->min_size_over = p_min_size;
  2283. cell->max_size_over = p_max_size;
  2284. }
  2285. void RichTextLabel::set_cell_padding(const Rect2 &p_padding) {
  2286. ERR_FAIL_COND(current->type != ITEM_FRAME);
  2287. ItemFrame *cell = static_cast<ItemFrame *>(current);
  2288. ERR_FAIL_COND(!cell->cell);
  2289. cell->padding = p_padding;
  2290. }
  2291. void RichTextLabel::push_cell() {
  2292. ERR_FAIL_COND(current->type != ITEM_TABLE);
  2293. ItemFrame *item = memnew(ItemFrame);
  2294. item->parent_frame = current_frame;
  2295. _add_item(item, true);
  2296. current_frame = item;
  2297. item->cell = true;
  2298. item->lines.resize(1);
  2299. item->lines.write[0].from = nullptr;
  2300. item->first_invalid_line = 0; // parent frame last line ???
  2301. }
  2302. int RichTextLabel::get_current_table_column() const {
  2303. ERR_FAIL_COND_V(current->type != ITEM_TABLE, -1);
  2304. ItemTable *table = static_cast<ItemTable *>(current);
  2305. return table->subitems.size() % table->columns.size();
  2306. }
  2307. void RichTextLabel::pop() {
  2308. ERR_FAIL_COND(!current->parent);
  2309. if (current->type == ITEM_FRAME) {
  2310. current_frame = static_cast<ItemFrame *>(current)->parent_frame;
  2311. }
  2312. current = current->parent;
  2313. }
  2314. void RichTextLabel::clear() {
  2315. main->_clear_children();
  2316. current = main;
  2317. current_frame = main;
  2318. main->lines.clear();
  2319. main->lines.resize(1);
  2320. main->first_invalid_line = 0;
  2321. update();
  2322. selection.click_frame = nullptr;
  2323. selection.click_item = nullptr;
  2324. selection.active = false;
  2325. current_idx = 1;
  2326. current_char_ofs = 0;
  2327. if (scroll_follow) {
  2328. scroll_following = true;
  2329. }
  2330. if (fixed_width != -1) {
  2331. minimum_size_changed();
  2332. }
  2333. }
  2334. void RichTextLabel::set_tab_size(int p_spaces) {
  2335. tab_size = p_spaces;
  2336. main->first_resized_line = 0;
  2337. update();
  2338. }
  2339. int RichTextLabel::get_tab_size() const {
  2340. return tab_size;
  2341. }
  2342. void RichTextLabel::set_fit_content_height(bool p_enabled) {
  2343. if (p_enabled != fit_content_height) {
  2344. fit_content_height = p_enabled;
  2345. minimum_size_changed();
  2346. }
  2347. }
  2348. bool RichTextLabel::is_fit_content_height_enabled() const {
  2349. return fit_content_height;
  2350. }
  2351. void RichTextLabel::set_meta_underline(bool p_underline) {
  2352. underline_meta = p_underline;
  2353. update();
  2354. }
  2355. bool RichTextLabel::is_meta_underlined() const {
  2356. return underline_meta;
  2357. }
  2358. void RichTextLabel::set_override_selected_font_color(bool p_override_selected_font_color) {
  2359. override_selected_font_color = p_override_selected_font_color;
  2360. }
  2361. bool RichTextLabel::is_overriding_selected_font_color() const {
  2362. return override_selected_font_color;
  2363. }
  2364. void RichTextLabel::set_offset(int p_pixel) {
  2365. vscroll->set_value(p_pixel);
  2366. }
  2367. void RichTextLabel::set_scroll_active(bool p_active) {
  2368. if (scroll_active == p_active) {
  2369. return;
  2370. }
  2371. scroll_active = p_active;
  2372. vscroll->set_drag_node_enabled(p_active);
  2373. update();
  2374. }
  2375. bool RichTextLabel::is_scroll_active() const {
  2376. return scroll_active;
  2377. }
  2378. void RichTextLabel::set_scroll_follow(bool p_follow) {
  2379. scroll_follow = p_follow;
  2380. if (!vscroll->is_visible_in_tree() || vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page())) {
  2381. scroll_following = true;
  2382. }
  2383. }
  2384. bool RichTextLabel::is_scroll_following() const {
  2385. return scroll_follow;
  2386. }
  2387. void RichTextLabel::parse_bbcode(const String &p_bbcode) {
  2388. clear();
  2389. append_text(p_bbcode);
  2390. }
  2391. void RichTextLabel::append_text(const String &p_bbcode) {
  2392. int pos = 0;
  2393. List<String> tag_stack;
  2394. Ref<Font> normal_font = get_theme_font(SNAME("normal_font"));
  2395. Ref<Font> bold_font = get_theme_font(SNAME("bold_font"));
  2396. Ref<Font> italics_font = get_theme_font(SNAME("italics_font"));
  2397. Ref<Font> bold_italics_font = get_theme_font(SNAME("bold_italics_font"));
  2398. Ref<Font> mono_font = get_theme_font(SNAME("mono_font"));
  2399. Color base_color = get_theme_color(SNAME("default_color"));
  2400. int indent_level = 0;
  2401. bool in_bold = false;
  2402. bool in_italics = false;
  2403. set_process_internal(false);
  2404. while (pos < p_bbcode.length()) {
  2405. int brk_pos = p_bbcode.find("[", pos);
  2406. if (brk_pos < 0) {
  2407. brk_pos = p_bbcode.length();
  2408. }
  2409. if (brk_pos > pos) {
  2410. add_text(p_bbcode.substr(pos, brk_pos - pos));
  2411. }
  2412. if (brk_pos == p_bbcode.length()) {
  2413. break; //nothing else to add
  2414. }
  2415. int brk_end = p_bbcode.find("]", brk_pos + 1);
  2416. if (brk_end == -1) {
  2417. //no close, add the rest
  2418. add_text(p_bbcode.substr(brk_pos, p_bbcode.length() - brk_pos));
  2419. break;
  2420. }
  2421. String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
  2422. Vector<String> split_tag_block = tag.split(" ", false);
  2423. // Find optional parameters.
  2424. String bbcode_name;
  2425. typedef Map<String, String> OptionMap;
  2426. OptionMap bbcode_options;
  2427. if (!split_tag_block.is_empty()) {
  2428. bbcode_name = split_tag_block[0];
  2429. for (int i = 1; i < split_tag_block.size(); i++) {
  2430. const String &expr = split_tag_block[i];
  2431. int value_pos = expr.find("=");
  2432. if (value_pos > -1) {
  2433. bbcode_options[expr.substr(0, value_pos)] = expr.substr(value_pos + 1);
  2434. }
  2435. }
  2436. } else {
  2437. bbcode_name = tag;
  2438. }
  2439. // Find main parameter.
  2440. String bbcode_value;
  2441. int main_value_pos = bbcode_name.find("=");
  2442. if (main_value_pos > -1) {
  2443. bbcode_value = bbcode_name.substr(main_value_pos + 1);
  2444. bbcode_name = bbcode_name.substr(0, main_value_pos);
  2445. }
  2446. if (tag.begins_with("/") && tag_stack.size()) {
  2447. bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
  2448. if (tag_stack.front()->get() == "b") {
  2449. in_bold = false;
  2450. }
  2451. if (tag_stack.front()->get() == "i") {
  2452. in_italics = false;
  2453. }
  2454. if ((tag_stack.front()->get() == "indent") || (tag_stack.front()->get() == "ol") || (tag_stack.front()->get() == "ul")) {
  2455. indent_level--;
  2456. }
  2457. if (!tag_ok) {
  2458. add_text("[" + tag);
  2459. pos = brk_end;
  2460. continue;
  2461. }
  2462. tag_stack.pop_front();
  2463. pos = brk_end + 1;
  2464. if (tag != "/img" && tag != "/dropcap") {
  2465. pop();
  2466. }
  2467. } else if (tag == "b") {
  2468. //use bold font
  2469. in_bold = true;
  2470. if (in_italics) {
  2471. push_font(bold_italics_font);
  2472. } else {
  2473. push_font(bold_font);
  2474. }
  2475. pos = brk_end + 1;
  2476. tag_stack.push_front(tag);
  2477. } else if (tag == "i") {
  2478. //use italics font
  2479. in_italics = true;
  2480. if (in_bold) {
  2481. push_font(bold_italics_font);
  2482. } else {
  2483. push_font(italics_font);
  2484. }
  2485. pos = brk_end + 1;
  2486. tag_stack.push_front(tag);
  2487. } else if (tag == "code") {
  2488. //use monospace font
  2489. push_font(mono_font);
  2490. pos = brk_end + 1;
  2491. tag_stack.push_front(tag);
  2492. } else if (tag.begins_with("table=")) {
  2493. Vector<String> subtag = tag.substr(6, tag.length()).split(",");
  2494. int columns = subtag[0].to_int();
  2495. if (columns < 1) {
  2496. columns = 1;
  2497. }
  2498. int align = INLINE_ALIGN_TOP;
  2499. if (subtag.size() > 2) {
  2500. if (subtag[1] == "top" || subtag[1] == "t") {
  2501. align = INLINE_ALIGN_TOP_TO;
  2502. } else if (subtag[1] == "center" || subtag[1] == "c") {
  2503. align = INLINE_ALIGN_CENTER_TO;
  2504. } else if (subtag[1] == "bottom" || subtag[1] == "b") {
  2505. align = INLINE_ALIGN_BOTTOM_TO;
  2506. }
  2507. if (subtag[2] == "top" || subtag[2] == "t") {
  2508. align |= INLINE_ALIGN_TO_TOP;
  2509. } else if (subtag[2] == "center" || subtag[2] == "c") {
  2510. align |= INLINE_ALIGN_TO_CENTER;
  2511. } else if (subtag[2] == "baseline" || subtag[2] == "l") {
  2512. align |= INLINE_ALIGN_TO_BASELINE;
  2513. } else if (subtag[2] == "bottom" || subtag[2] == "b") {
  2514. align |= INLINE_ALIGN_TO_BOTTOM;
  2515. }
  2516. } else if (subtag.size() > 1) {
  2517. if (subtag[1] == "top" || subtag[1] == "t") {
  2518. align = INLINE_ALIGN_TOP;
  2519. } else if (subtag[1] == "center" || subtag[1] == "c") {
  2520. align = INLINE_ALIGN_CENTER;
  2521. } else if (subtag[1] == "bottom" || subtag[1] == "b") {
  2522. align = INLINE_ALIGN_BOTTOM;
  2523. }
  2524. }
  2525. push_table(columns, (InlineAlign)align);
  2526. pos = brk_end + 1;
  2527. tag_stack.push_front("table");
  2528. } else if (tag == "cell") {
  2529. push_cell();
  2530. pos = brk_end + 1;
  2531. tag_stack.push_front(tag);
  2532. } else if (tag.begins_with("cell=")) {
  2533. int ratio = tag.substr(5, tag.length()).to_int();
  2534. if (ratio < 1) {
  2535. ratio = 1;
  2536. }
  2537. set_table_column_expand(get_current_table_column(), true, ratio);
  2538. push_cell();
  2539. pos = brk_end + 1;
  2540. tag_stack.push_front("cell");
  2541. } else if (tag.begins_with("cell ")) {
  2542. Vector<String> subtag = tag.substr(5, tag.length()).split(" ");
  2543. for (int i = 0; i < subtag.size(); i++) {
  2544. Vector<String> subtag_a = subtag[i].split("=");
  2545. if (subtag_a.size() == 2) {
  2546. if (subtag_a[0] == "expand") {
  2547. int ratio = subtag_a[1].to_int();
  2548. if (ratio < 1) {
  2549. ratio = 1;
  2550. }
  2551. set_table_column_expand(get_current_table_column(), true, ratio);
  2552. }
  2553. }
  2554. }
  2555. push_cell();
  2556. const Color fallback_color = Color(0, 0, 0, 0);
  2557. for (int i = 0; i < subtag.size(); i++) {
  2558. Vector<String> subtag_a = subtag[i].split("=");
  2559. if (subtag_a.size() == 2) {
  2560. if (subtag_a[0] == "border") {
  2561. Color color = Color::from_string(subtag_a[1], fallback_color);
  2562. set_cell_border_color(color);
  2563. } else if (subtag_a[0] == "bg") {
  2564. Vector<String> subtag_b = subtag_a[1].split(",");
  2565. if (subtag_b.size() == 2) {
  2566. Color color1 = Color::from_string(subtag_b[0], fallback_color);
  2567. Color color2 = Color::from_string(subtag_b[1], fallback_color);
  2568. set_cell_row_background_color(color1, color2);
  2569. }
  2570. }
  2571. }
  2572. }
  2573. pos = brk_end + 1;
  2574. tag_stack.push_front("cell");
  2575. } else if (tag == "u") {
  2576. //use underline
  2577. push_underline();
  2578. pos = brk_end + 1;
  2579. tag_stack.push_front(tag);
  2580. } else if (tag == "s") {
  2581. //use strikethrough
  2582. push_strikethrough();
  2583. pos = brk_end + 1;
  2584. tag_stack.push_front(tag);
  2585. } else if (tag == "lrm") {
  2586. add_text(String::chr(0x200E));
  2587. pos = brk_end + 1;
  2588. } else if (tag == "rlm") {
  2589. add_text(String::chr(0x200F));
  2590. pos = brk_end + 1;
  2591. } else if (tag == "lre") {
  2592. add_text(String::chr(0x202A));
  2593. pos = brk_end + 1;
  2594. } else if (tag == "rle") {
  2595. add_text(String::chr(0x202B));
  2596. pos = brk_end + 1;
  2597. } else if (tag == "lro") {
  2598. add_text(String::chr(0x202D));
  2599. pos = brk_end + 1;
  2600. } else if (tag == "rlo") {
  2601. add_text(String::chr(0x202E));
  2602. pos = brk_end + 1;
  2603. } else if (tag == "pdf") {
  2604. add_text(String::chr(0x202C));
  2605. pos = brk_end + 1;
  2606. } else if (tag == "alm") {
  2607. add_text(String::chr(0x061c));
  2608. pos = brk_end + 1;
  2609. } else if (tag == "lri") {
  2610. add_text(String::chr(0x2066));
  2611. pos = brk_end + 1;
  2612. } else if (tag == "rli") {
  2613. add_text(String::chr(0x2027));
  2614. pos = brk_end + 1;
  2615. } else if (tag == "fsi") {
  2616. add_text(String::chr(0x2068));
  2617. pos = brk_end + 1;
  2618. } else if (tag == "pdi") {
  2619. add_text(String::chr(0x2069));
  2620. pos = brk_end + 1;
  2621. } else if (tag == "zwj") {
  2622. add_text(String::chr(0x200D));
  2623. pos = brk_end + 1;
  2624. } else if (tag == "zwnj") {
  2625. add_text(String::chr(0x200C));
  2626. pos = brk_end + 1;
  2627. } else if (tag == "wj") {
  2628. add_text(String::chr(0x2060));
  2629. pos = brk_end + 1;
  2630. } else if (tag == "shy") {
  2631. add_text(String::chr(0x00AD));
  2632. pos = brk_end + 1;
  2633. } else if (tag == "center") {
  2634. push_paragraph(ALIGN_CENTER);
  2635. pos = brk_end + 1;
  2636. tag_stack.push_front(tag);
  2637. } else if (tag == "fill") {
  2638. push_paragraph(ALIGN_FILL);
  2639. pos = brk_end + 1;
  2640. tag_stack.push_front(tag);
  2641. } else if (tag == "right") {
  2642. push_paragraph(ALIGN_RIGHT);
  2643. pos = brk_end + 1;
  2644. tag_stack.push_front(tag);
  2645. } else if (tag == "ul") {
  2646. indent_level++;
  2647. push_list(indent_level, LIST_DOTS, false);
  2648. pos = brk_end + 1;
  2649. tag_stack.push_front(tag);
  2650. } else if ((tag == "ol") || (tag == "ol type=1")) {
  2651. indent_level++;
  2652. push_list(indent_level, LIST_NUMBERS, false);
  2653. pos = brk_end + 1;
  2654. tag_stack.push_front(tag);
  2655. } else if (tag == "ol type=a") {
  2656. indent_level++;
  2657. push_list(indent_level, LIST_LETTERS, false);
  2658. pos = brk_end + 1;
  2659. tag_stack.push_front("ol");
  2660. } else if (tag == "ol type=A") {
  2661. indent_level++;
  2662. push_list(indent_level, LIST_LETTERS, true);
  2663. pos = brk_end + 1;
  2664. tag_stack.push_front("ol");
  2665. } else if (tag == "ol type=i") {
  2666. indent_level++;
  2667. push_list(indent_level, LIST_ROMAN, false);
  2668. pos = brk_end + 1;
  2669. tag_stack.push_front("ol");
  2670. } else if (tag == "ol type=I") {
  2671. indent_level++;
  2672. push_list(indent_level, LIST_ROMAN, true);
  2673. pos = brk_end + 1;
  2674. tag_stack.push_front("ol");
  2675. } else if (tag == "indent") {
  2676. indent_level++;
  2677. push_indent(indent_level);
  2678. pos = brk_end + 1;
  2679. tag_stack.push_front(tag);
  2680. } else if (tag == "p") {
  2681. push_paragraph(ALIGN_LEFT);
  2682. pos = brk_end + 1;
  2683. tag_stack.push_front("p");
  2684. } else if (tag.begins_with("p ")) {
  2685. Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
  2686. Align align = ALIGN_LEFT;
  2687. Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED;
  2688. String lang;
  2689. Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
  2690. for (int i = 0; i < subtag.size(); i++) {
  2691. Vector<String> subtag_a = subtag[i].split("=");
  2692. if (subtag_a.size() == 2) {
  2693. if (subtag_a[0] == "align") {
  2694. if (subtag_a[1] == "l" || subtag_a[1] == "left") {
  2695. align = ALIGN_LEFT;
  2696. } else if (subtag_a[1] == "c" || subtag_a[1] == "center") {
  2697. align = ALIGN_CENTER;
  2698. } else if (subtag_a[1] == "r" || subtag_a[1] == "right") {
  2699. align = ALIGN_RIGHT;
  2700. } else if (subtag_a[1] == "f" || subtag_a[1] == "fill") {
  2701. align = ALIGN_FILL;
  2702. }
  2703. } else if (subtag_a[0] == "dir" || subtag_a[0] == "direction") {
  2704. if (subtag_a[1] == "a" || subtag_a[1] == "auto") {
  2705. dir = Control::TEXT_DIRECTION_AUTO;
  2706. } else if (subtag_a[1] == "l" || subtag_a[1] == "ltr") {
  2707. dir = Control::TEXT_DIRECTION_LTR;
  2708. } else if (subtag_a[1] == "r" || subtag_a[1] == "rtl") {
  2709. dir = Control::TEXT_DIRECTION_RTL;
  2710. }
  2711. } else if (subtag_a[0] == "lang" || subtag_a[0] == "language") {
  2712. lang = subtag_a[1];
  2713. } else if (subtag_a[0] == "st" || subtag_a[0] == "bidi_override") {
  2714. if (subtag_a[1] == "d" || subtag_a[1] == "default") {
  2715. st_parser = STRUCTURED_TEXT_DEFAULT;
  2716. } else if (subtag_a[1] == "u" || subtag_a[1] == "uri") {
  2717. st_parser = STRUCTURED_TEXT_URI;
  2718. } else if (subtag_a[1] == "f" || subtag_a[1] == "file") {
  2719. st_parser = STRUCTURED_TEXT_FILE;
  2720. } else if (subtag_a[1] == "e" || subtag_a[1] == "email") {
  2721. st_parser = STRUCTURED_TEXT_EMAIL;
  2722. } else if (subtag_a[1] == "l" || subtag_a[1] == "list") {
  2723. st_parser = STRUCTURED_TEXT_LIST;
  2724. } else if (subtag_a[1] == "n" || subtag_a[1] == "none") {
  2725. st_parser = STRUCTURED_TEXT_NONE;
  2726. } else if (subtag_a[1] == "c" || subtag_a[1] == "custom") {
  2727. st_parser = STRUCTURED_TEXT_CUSTOM;
  2728. }
  2729. }
  2730. }
  2731. }
  2732. push_paragraph(align, dir, lang, st_parser);
  2733. pos = brk_end + 1;
  2734. tag_stack.push_front("p");
  2735. } else if (tag == "url") {
  2736. int end = p_bbcode.find("[", brk_end);
  2737. if (end == -1) {
  2738. end = p_bbcode.length();
  2739. }
  2740. String url = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
  2741. push_meta(url);
  2742. pos = brk_end + 1;
  2743. tag_stack.push_front(tag);
  2744. } else if (tag.begins_with("url=")) {
  2745. String url = tag.substr(4, tag.length());
  2746. push_meta(url);
  2747. pos = brk_end + 1;
  2748. tag_stack.push_front("url");
  2749. } else if (tag.begins_with("dropcap")) {
  2750. Vector<String> subtag = tag.substr(5, tag.length()).split(" ");
  2751. Ref<Font> f = get_theme_font(SNAME("normal_font"));
  2752. int fs = get_theme_font_size(SNAME("normal_font_size")) * 3;
  2753. Color color = get_theme_color(SNAME("default_color"));
  2754. Color outline_color = get_theme_color(SNAME("outline_color"));
  2755. int outline_size = get_theme_constant(SNAME("outline_size"));
  2756. Rect2 dropcap_margins = Rect2();
  2757. for (int i = 0; i < subtag.size(); i++) {
  2758. Vector<String> subtag_a = subtag[i].split("=");
  2759. if (subtag_a.size() == 2) {
  2760. if (subtag_a[0] == "font" || subtag_a[0] == "f") {
  2761. String fnt = subtag_a[1];
  2762. Ref<Font> font = ResourceLoader::load(fnt, "Font");
  2763. if (font.is_valid()) {
  2764. f = font;
  2765. }
  2766. } else if (subtag_a[0] == "font_size") {
  2767. fs = subtag_a[1].to_int();
  2768. } else if (subtag_a[0] == "margins") {
  2769. Vector<String> subtag_b = subtag_a[1].split(",");
  2770. if (subtag_b.size() == 4) {
  2771. dropcap_margins.position.x = subtag_b[0].to_float();
  2772. dropcap_margins.position.y = subtag_b[1].to_float();
  2773. dropcap_margins.size.x = subtag_b[2].to_float();
  2774. dropcap_margins.size.y = subtag_b[3].to_float();
  2775. }
  2776. } else if (subtag_a[0] == "outline_size") {
  2777. outline_size = subtag_a[1].to_int();
  2778. } else if (subtag_a[0] == "color") {
  2779. color = Color::from_string(subtag_a[1], color);
  2780. } else if (subtag_a[0] == "outline_color") {
  2781. outline_color = Color::from_string(subtag_a[1], outline_color);
  2782. }
  2783. }
  2784. }
  2785. int end = p_bbcode.find("[", brk_end);
  2786. if (end == -1) {
  2787. end = p_bbcode.length();
  2788. }
  2789. String txt = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
  2790. push_dropcap(txt, f, fs, dropcap_margins, color, outline_size, outline_color);
  2791. pos = end;
  2792. tag_stack.push_front(bbcode_name);
  2793. } else if (tag.begins_with("img")) {
  2794. int align = INLINE_ALIGN_CENTER;
  2795. if (tag.begins_with("img=")) {
  2796. Vector<String> subtag = tag.substr(4, tag.length()).split(",");
  2797. if (subtag.size() > 1) {
  2798. if (subtag[0] == "top" || subtag[0] == "t") {
  2799. align = INLINE_ALIGN_TOP_TO;
  2800. } else if (subtag[0] == "center" || subtag[0] == "c") {
  2801. align = INLINE_ALIGN_CENTER_TO;
  2802. } else if (subtag[0] == "bottom" || subtag[0] == "b") {
  2803. align = INLINE_ALIGN_BOTTOM_TO;
  2804. }
  2805. if (subtag[1] == "top" || subtag[1] == "t") {
  2806. align |= INLINE_ALIGN_TO_TOP;
  2807. } else if (subtag[1] == "center" || subtag[1] == "c") {
  2808. align |= INLINE_ALIGN_TO_CENTER;
  2809. } else if (subtag[1] == "baseline" || subtag[1] == "l") {
  2810. align |= INLINE_ALIGN_TO_BASELINE;
  2811. } else if (subtag[1] == "bottom" || subtag[1] == "b") {
  2812. align |= INLINE_ALIGN_TO_BOTTOM;
  2813. }
  2814. } else if (subtag.size() > 0) {
  2815. if (subtag[0] == "top" || subtag[0] == "t") {
  2816. align = INLINE_ALIGN_TOP;
  2817. } else if (subtag[0] == "center" || subtag[0] == "c") {
  2818. align = INLINE_ALIGN_CENTER;
  2819. } else if (subtag[0] == "bottom" || subtag[0] == "b") {
  2820. align = INLINE_ALIGN_BOTTOM;
  2821. }
  2822. }
  2823. }
  2824. int end = p_bbcode.find("[", brk_end);
  2825. if (end == -1) {
  2826. end = p_bbcode.length();
  2827. }
  2828. String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
  2829. Ref<Texture2D> texture = ResourceLoader::load(image, "Texture2D");
  2830. if (texture.is_valid()) {
  2831. Color color = Color(1.0, 1.0, 1.0);
  2832. OptionMap::Element *color_option = bbcode_options.find("color");
  2833. if (color_option) {
  2834. color = Color::from_string(color_option->value(), color);
  2835. }
  2836. int width = 0;
  2837. int height = 0;
  2838. if (!bbcode_value.is_empty()) {
  2839. int sep = bbcode_value.find("x");
  2840. if (sep == -1) {
  2841. width = bbcode_value.to_int();
  2842. } else {
  2843. width = bbcode_value.substr(0, sep).to_int();
  2844. height = bbcode_value.substr(sep + 1).to_int();
  2845. }
  2846. } else {
  2847. OptionMap::Element *width_option = bbcode_options.find("width");
  2848. if (width_option) {
  2849. width = width_option->value().to_int();
  2850. }
  2851. OptionMap::Element *height_option = bbcode_options.find("height");
  2852. if (height_option) {
  2853. height = height_option->value().to_int();
  2854. }
  2855. }
  2856. add_image(texture, width, height, color, (InlineAlign)align);
  2857. }
  2858. pos = end;
  2859. tag_stack.push_front(bbcode_name);
  2860. } else if (tag.begins_with("color=")) {
  2861. String color_str = tag.substr(6, tag.length());
  2862. Color color = Color::from_string(color_str, base_color);
  2863. push_color(color);
  2864. pos = brk_end + 1;
  2865. tag_stack.push_front("color");
  2866. } else if (tag.begins_with("outline_color=")) {
  2867. String color_str = tag.substr(14, tag.length());
  2868. Color color = Color::from_string(color_str, base_color);
  2869. push_outline_color(color);
  2870. pos = brk_end + 1;
  2871. tag_stack.push_front("outline_color");
  2872. } else if (tag.begins_with("font=")) {
  2873. String fnt = tag.substr(5, tag.length());
  2874. Ref<Font> font = ResourceLoader::load(fnt, "Font");
  2875. if (font.is_valid()) {
  2876. push_font(font);
  2877. } else {
  2878. push_font(normal_font);
  2879. }
  2880. pos = brk_end + 1;
  2881. tag_stack.push_front("font");
  2882. } else if (tag.begins_with("font_size=")) {
  2883. int fnt_size = tag.substr(10, tag.length()).to_int();
  2884. push_font_size(fnt_size);
  2885. pos = brk_end + 1;
  2886. tag_stack.push_front("font_size");
  2887. } else if (tag.begins_with("opentype_features=")) {
  2888. String fnt_ftr = tag.substr(18, tag.length());
  2889. Vector<String> subtag = fnt_ftr.split(",");
  2890. Dictionary ftrs;
  2891. for (int i = 0; i < subtag.size(); i++) {
  2892. Vector<String> subtag_a = subtag[i].split("=");
  2893. if (subtag_a.size() == 2) {
  2894. ftrs[TS->name_to_tag(subtag_a[0])] = subtag_a[1].to_int();
  2895. } else if (subtag_a.size() == 1) {
  2896. ftrs[TS->name_to_tag(subtag_a[0])] = 1;
  2897. }
  2898. }
  2899. push_font_features(ftrs);
  2900. pos = brk_end + 1;
  2901. tag_stack.push_front("opentype_features");
  2902. } else if (tag.begins_with("font ")) {
  2903. Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
  2904. for (int i = 1; i < subtag.size(); i++) {
  2905. Vector<String> subtag_a = subtag[i].split("=", true, 2);
  2906. if (subtag_a.size() == 2) {
  2907. if (subtag_a[0] == "name" || subtag_a[0] == "n") {
  2908. String fnt = subtag_a[1];
  2909. Ref<Font> font = ResourceLoader::load(fnt, "Font");
  2910. if (font.is_valid()) {
  2911. push_font(font);
  2912. } else {
  2913. push_font(normal_font);
  2914. }
  2915. } else if (subtag_a[0] == "size" || subtag_a[0] == "s") {
  2916. int fnt_size = subtag_a[1].to_int();
  2917. push_font_size(fnt_size);
  2918. }
  2919. }
  2920. }
  2921. pos = brk_end + 1;
  2922. tag_stack.push_front("font");
  2923. } else if (tag.begins_with("outline_size=")) {
  2924. int fnt_size = tag.substr(13, tag.length()).to_int();
  2925. push_outline_size(fnt_size);
  2926. pos = brk_end + 1;
  2927. tag_stack.push_front("outline_size");
  2928. } else if (bbcode_name == "fade") {
  2929. int start_index = 0;
  2930. OptionMap::Element *start_option = bbcode_options.find("start");
  2931. if (start_option) {
  2932. start_index = start_option->value().to_int();
  2933. }
  2934. int length = 10;
  2935. OptionMap::Element *length_option = bbcode_options.find("length");
  2936. if (length_option) {
  2937. length = length_option->value().to_int();
  2938. }
  2939. push_fade(start_index, length);
  2940. pos = brk_end + 1;
  2941. tag_stack.push_front("fade");
  2942. } else if (bbcode_name == "shake") {
  2943. int strength = 5;
  2944. OptionMap::Element *strength_option = bbcode_options.find("level");
  2945. if (strength_option) {
  2946. strength = strength_option->value().to_int();
  2947. }
  2948. float rate = 20.0f;
  2949. OptionMap::Element *rate_option = bbcode_options.find("rate");
  2950. if (rate_option) {
  2951. rate = rate_option->value().to_float();
  2952. }
  2953. push_shake(strength, rate);
  2954. pos = brk_end + 1;
  2955. tag_stack.push_front("shake");
  2956. set_process_internal(true);
  2957. } else if (bbcode_name == "wave") {
  2958. float amplitude = 20.0f;
  2959. OptionMap::Element *amplitude_option = bbcode_options.find("amp");
  2960. if (amplitude_option) {
  2961. amplitude = amplitude_option->value().to_float();
  2962. }
  2963. float period = 5.0f;
  2964. OptionMap::Element *period_option = bbcode_options.find("freq");
  2965. if (period_option) {
  2966. period = period_option->value().to_float();
  2967. }
  2968. push_wave(period, amplitude);
  2969. pos = brk_end + 1;
  2970. tag_stack.push_front("wave");
  2971. set_process_internal(true);
  2972. } else if (bbcode_name == "tornado") {
  2973. float radius = 10.0f;
  2974. OptionMap::Element *radius_option = bbcode_options.find("radius");
  2975. if (radius_option) {
  2976. radius = radius_option->value().to_float();
  2977. }
  2978. float frequency = 1.0f;
  2979. OptionMap::Element *frequency_option = bbcode_options.find("freq");
  2980. if (frequency_option) {
  2981. frequency = frequency_option->value().to_float();
  2982. }
  2983. push_tornado(frequency, radius);
  2984. pos = brk_end + 1;
  2985. tag_stack.push_front("tornado");
  2986. set_process_internal(true);
  2987. } else if (bbcode_name == "rainbow") {
  2988. float saturation = 0.8f;
  2989. OptionMap::Element *saturation_option = bbcode_options.find("sat");
  2990. if (saturation_option) {
  2991. saturation = saturation_option->value().to_float();
  2992. }
  2993. float value = 0.8f;
  2994. OptionMap::Element *value_option = bbcode_options.find("val");
  2995. if (value_option) {
  2996. value = value_option->value().to_float();
  2997. }
  2998. float frequency = 1.0f;
  2999. OptionMap::Element *frequency_option = bbcode_options.find("freq");
  3000. if (frequency_option) {
  3001. frequency = frequency_option->value().to_float();
  3002. }
  3003. push_rainbow(saturation, value, frequency);
  3004. pos = brk_end + 1;
  3005. tag_stack.push_front("rainbow");
  3006. set_process_internal(true);
  3007. } else if (tag.begins_with("bgcolor=")) {
  3008. String color_str = tag.substr(8, tag.length());
  3009. Color color = Color::from_string(color_str, base_color);
  3010. push_bgcolor(color);
  3011. pos = brk_end + 1;
  3012. tag_stack.push_front("bgcolor");
  3013. } else if (tag.begins_with("fgcolor=")) {
  3014. String color_str = tag.substr(8, tag.length());
  3015. Color color = Color::from_string(color_str, base_color);
  3016. push_fgcolor(color);
  3017. pos = brk_end + 1;
  3018. tag_stack.push_front("fgcolor");
  3019. } else {
  3020. Vector<String> &expr = split_tag_block;
  3021. if (expr.size() < 1) {
  3022. add_text("[");
  3023. pos = brk_pos + 1;
  3024. } else {
  3025. String identifier = expr[0];
  3026. expr.remove(0);
  3027. Dictionary properties = parse_expressions_for_values(expr);
  3028. Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier);
  3029. if (!effect.is_null()) {
  3030. push_customfx(effect, properties);
  3031. pos = brk_end + 1;
  3032. tag_stack.push_front(identifier);
  3033. set_process_internal(true);
  3034. } else {
  3035. add_text("["); //ignore
  3036. pos = brk_pos + 1;
  3037. }
  3038. }
  3039. }
  3040. }
  3041. Vector<ItemFX *> fx_items;
  3042. for (Item *E : main->subitems) {
  3043. Item *subitem = static_cast<Item *>(E);
  3044. _fetch_item_fx_stack(subitem, fx_items);
  3045. if (fx_items.size()) {
  3046. set_process_internal(true);
  3047. break;
  3048. }
  3049. }
  3050. }
  3051. void RichTextLabel::scroll_to_paragraph(int p_paragraph) {
  3052. ERR_FAIL_INDEX(p_paragraph, main->lines.size());
  3053. _validate_line_caches(main);
  3054. vscroll->set_value(main->lines[p_paragraph].offset.y);
  3055. }
  3056. int RichTextLabel::get_paragraph_count() const {
  3057. return current_frame->lines.size();
  3058. }
  3059. int RichTextLabel::get_visible_paragraph_count() const {
  3060. if (!is_visible()) {
  3061. return 0;
  3062. }
  3063. return visible_paragraph_count;
  3064. }
  3065. void RichTextLabel::scroll_to_line(int p_line) {
  3066. _validate_line_caches(main);
  3067. int line_count = 0;
  3068. for (int i = 0; i < main->lines.size(); i++) {
  3069. if ((line_count <= p_line) && (line_count + main->lines[i].text_buf->get_line_count() >= p_line)) {
  3070. float line_offset = 0.f;
  3071. for (int j = 0; j < p_line - line_count; j++) {
  3072. line_offset += main->lines[i].text_buf->get_line_size(j).y;
  3073. }
  3074. vscroll->set_value(main->lines[i].offset.y + line_offset);
  3075. return;
  3076. }
  3077. line_count += main->lines[i].text_buf->get_line_count();
  3078. }
  3079. }
  3080. int RichTextLabel::get_line_count() const {
  3081. int line_count = 0;
  3082. for (int i = 0; i < main->lines.size(); i++) {
  3083. line_count += main->lines[i].text_buf->get_line_count();
  3084. }
  3085. return line_count;
  3086. }
  3087. int RichTextLabel::get_visible_line_count() const {
  3088. if (!is_visible()) {
  3089. return 0;
  3090. }
  3091. return visible_line_count;
  3092. }
  3093. void RichTextLabel::set_selection_enabled(bool p_enabled) {
  3094. selection.enabled = p_enabled;
  3095. if (!p_enabled) {
  3096. if (selection.active) {
  3097. selection.active = false;
  3098. update();
  3099. }
  3100. set_focus_mode(FOCUS_NONE);
  3101. } else {
  3102. set_focus_mode(FOCUS_ALL);
  3103. }
  3104. }
  3105. void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) {
  3106. deselect_on_focus_loss_enabled = p_enabled;
  3107. if (p_enabled && selection.active && !has_focus()) {
  3108. selection.active = false;
  3109. update();
  3110. }
  3111. }
  3112. bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search) {
  3113. List<Item *>::Element *E = p_from;
  3114. while (E != nullptr) {
  3115. ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
  3116. ItemFrame *frame = static_cast<ItemFrame *>(E->get());
  3117. if (p_reverse_search) {
  3118. for (int i = frame->lines.size() - 1; i >= 0; i--) {
  3119. if (_search_line(frame, i, p_string, -1, p_reverse_search)) {
  3120. return true;
  3121. }
  3122. }
  3123. } else {
  3124. for (int i = 0; i < frame->lines.size(); i++) {
  3125. if (_search_line(frame, i, p_string, 0, p_reverse_search)) {
  3126. return true;
  3127. }
  3128. }
  3129. }
  3130. E = p_reverse_search ? E->prev() : E->next();
  3131. }
  3132. return false;
  3133. }
  3134. bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p_string, int p_char_idx, bool p_reverse_search) {
  3135. ERR_FAIL_COND_V(p_frame == nullptr, false);
  3136. ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), false);
  3137. Line &l = p_frame->lines.write[p_line];
  3138. String text;
  3139. Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
  3140. for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
  3141. switch (it->type) {
  3142. case ITEM_NEWLINE: {
  3143. text += "\n";
  3144. } break;
  3145. case ITEM_TEXT: {
  3146. ItemText *t = (ItemText *)it;
  3147. text += t->text;
  3148. } break;
  3149. case ITEM_IMAGE: {
  3150. text += " ";
  3151. } break;
  3152. case ITEM_TABLE: {
  3153. ItemTable *table = static_cast<ItemTable *>(it);
  3154. List<Item *>::Element *E = p_reverse_search ? table->subitems.back() : table->subitems.front();
  3155. if (_search_table(table, E, p_string, p_reverse_search)) {
  3156. return true;
  3157. }
  3158. } break;
  3159. default:
  3160. break;
  3161. }
  3162. }
  3163. int sp = -1;
  3164. if (p_reverse_search) {
  3165. sp = text.rfindn(p_string, p_char_idx);
  3166. } else {
  3167. sp = text.findn(p_string, p_char_idx);
  3168. }
  3169. if (sp != -1) {
  3170. selection.from_frame = p_frame;
  3171. selection.from_line = p_line;
  3172. selection.from_item = _get_item_at_pos(l.from, it_to, sp);
  3173. selection.from_char = sp;
  3174. selection.to_frame = p_frame;
  3175. selection.to_line = p_line;
  3176. selection.to_item = _get_item_at_pos(l.from, it_to, sp + p_string.length());
  3177. selection.to_char = sp + p_string.length();
  3178. selection.active = true;
  3179. return true;
  3180. }
  3181. return false;
  3182. }
  3183. bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) {
  3184. ERR_FAIL_COND_V(!selection.enabled, false);
  3185. if (p_string.size() == 0) {
  3186. selection.active = false;
  3187. return false;
  3188. }
  3189. int char_idx = p_search_previous ? -1 : 0;
  3190. int current_line = 0;
  3191. int ending_line = main->lines.size() - 1;
  3192. if (p_from_selection && selection.active) {
  3193. // First check to see if other results exist in current line
  3194. char_idx = p_search_previous ? selection.from_char - 1 : selection.to_char;
  3195. if (!(p_search_previous && char_idx < 0) &&
  3196. _search_line(selection.from_frame, selection.from_line, p_string, char_idx, p_search_previous)) {
  3197. scroll_to_line(selection.from_frame->line + selection.from_line);
  3198. update();
  3199. return true;
  3200. }
  3201. char_idx = p_search_previous ? -1 : 0;
  3202. // Next, check to see if the current search result is in a table
  3203. if (selection.from_frame->parent != nullptr && selection.from_frame->parent->type == ITEM_TABLE) {
  3204. // Find last search result in table
  3205. ItemTable *parent_table = static_cast<ItemTable *>(selection.from_frame->parent);
  3206. List<Item *>::Element *parent_element = p_search_previous ? parent_table->subitems.back() : parent_table->subitems.front();
  3207. while (parent_element->get() != selection.from_frame) {
  3208. parent_element = p_search_previous ? parent_element->prev() : parent_element->next();
  3209. ERR_FAIL_COND_V(parent_element == nullptr, false);
  3210. }
  3211. // Search remainder of table
  3212. if (!(p_search_previous && parent_element == parent_table->subitems.front()) &&
  3213. parent_element != parent_table->subitems.back()) {
  3214. parent_element = p_search_previous ? parent_element->prev() : parent_element->next(); // Don't want to search current item
  3215. ERR_FAIL_COND_V(parent_element == nullptr, false);
  3216. // Search for next element
  3217. if (_search_table(parent_table, parent_element, p_string, p_search_previous)) {
  3218. scroll_to_line(selection.from_frame->line + selection.from_line);
  3219. update();
  3220. return true;
  3221. }
  3222. }
  3223. }
  3224. ending_line = selection.from_frame->line + selection.from_line;
  3225. current_line = p_search_previous ? ending_line - 1 : ending_line + 1;
  3226. } else if (p_search_previous) {
  3227. current_line = ending_line;
  3228. ending_line = 0;
  3229. }
  3230. // Search remainder of the file
  3231. while (current_line != ending_line) {
  3232. // Wrap around
  3233. if (current_line < 0) {
  3234. current_line = main->lines.size() - 1;
  3235. } else if (current_line >= main->lines.size()) {
  3236. current_line = 0;
  3237. }
  3238. if (_search_line(main, current_line, p_string, char_idx, p_search_previous)) {
  3239. scroll_to_line(current_line);
  3240. update();
  3241. return true;
  3242. }
  3243. p_search_previous ? current_line-- : current_line++;
  3244. }
  3245. if (p_from_selection && selection.active) {
  3246. // Check contents of selection
  3247. return _search_line(main, current_line, p_string, char_idx, p_search_previous);
  3248. } else {
  3249. return false;
  3250. }
  3251. }
  3252. String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) const {
  3253. String text;
  3254. ERR_FAIL_COND_V(p_frame == nullptr, text);
  3255. ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), text);
  3256. Line &l = p_frame->lines.write[p_line];
  3257. Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
  3258. int end_idx = 0;
  3259. if (it_to != nullptr) {
  3260. end_idx = it_to->index;
  3261. } else {
  3262. for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
  3263. end_idx = it->index + 1;
  3264. }
  3265. }
  3266. for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
  3267. if ((p_selection.to_item != nullptr) && (p_selection.to_item->index < l.from->index)) {
  3268. break;
  3269. }
  3270. if ((p_selection.from_item != nullptr) && (p_selection.from_item->index >= end_idx)) {
  3271. break;
  3272. }
  3273. switch (it->type) {
  3274. case ITEM_NEWLINE: {
  3275. text += "\n";
  3276. } break;
  3277. case ITEM_TEXT: {
  3278. ItemText *t = (ItemText *)it;
  3279. text += t->text;
  3280. } break;
  3281. case ITEM_IMAGE: {
  3282. text += " ";
  3283. } break;
  3284. case ITEM_TABLE: {
  3285. ItemTable *table = static_cast<ItemTable *>(it);
  3286. int idx = 0;
  3287. int col_count = table->columns.size();
  3288. for (Item *E : table->subitems) {
  3289. ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
  3290. ItemFrame *frame = static_cast<ItemFrame *>(E);
  3291. int column = idx % col_count;
  3292. for (int i = 0; i < frame->lines.size(); i++) {
  3293. text += _get_line_text(frame, i, p_selection);
  3294. }
  3295. if (column == col_count - 1) {
  3296. text += "\n";
  3297. } else {
  3298. text += " ";
  3299. }
  3300. idx++;
  3301. }
  3302. } break;
  3303. default:
  3304. break;
  3305. }
  3306. }
  3307. if ((l.from != nullptr) && (p_frame == p_selection.to_frame) && (p_selection.to_item != nullptr) && (p_selection.to_item->index >= l.from->index) && (p_selection.to_item->index < end_idx)) {
  3308. text = text.substr(0, p_selection.to_char);
  3309. }
  3310. if ((l.from != nullptr) && (p_frame == p_selection.from_frame) && (p_selection.from_item != nullptr) && (p_selection.from_item->index >= l.from->index) && (p_selection.from_item->index < end_idx)) {
  3311. text = text.substr(p_selection.from_char, -1);
  3312. }
  3313. return text;
  3314. }
  3315. String RichTextLabel::get_selected_text() const {
  3316. if (!selection.active || !selection.enabled) {
  3317. return "";
  3318. }
  3319. String text;
  3320. for (int i = 0; i < main->lines.size(); i++) {
  3321. text += _get_line_text(main, i, selection);
  3322. }
  3323. return text;
  3324. }
  3325. void RichTextLabel::selection_copy() {
  3326. String text = get_selected_text();
  3327. if (text != "") {
  3328. DisplayServer::get_singleton()->clipboard_set(text);
  3329. }
  3330. }
  3331. bool RichTextLabel::is_selection_enabled() const {
  3332. return selection.enabled;
  3333. }
  3334. bool RichTextLabel::is_deselect_on_focus_loss_enabled() const {
  3335. return deselect_on_focus_loss_enabled;
  3336. }
  3337. int RichTextLabel::get_selection_from() const {
  3338. if (!selection.active || !selection.enabled) {
  3339. return -1;
  3340. }
  3341. return selection.from_frame->lines[selection.from_line].char_offset + selection.from_char;
  3342. }
  3343. int RichTextLabel::get_selection_to() const {
  3344. if (!selection.active || !selection.enabled) {
  3345. return -1;
  3346. }
  3347. return selection.to_frame->lines[selection.to_line].char_offset + selection.to_char - 1;
  3348. }
  3349. void RichTextLabel::set_text(const String &p_bbcode) {
  3350. text = p_bbcode;
  3351. if (is_inside_tree() && use_bbcode) {
  3352. parse_bbcode(p_bbcode);
  3353. } else { // raw text
  3354. clear();
  3355. add_text(p_bbcode);
  3356. }
  3357. }
  3358. String RichTextLabel::get_text() const {
  3359. return text;
  3360. }
  3361. void RichTextLabel::set_use_bbcode(bool p_enable) {
  3362. if (use_bbcode == p_enable) {
  3363. return;
  3364. }
  3365. use_bbcode = p_enable;
  3366. notify_property_list_changed();
  3367. set_text(text);
  3368. }
  3369. bool RichTextLabel::is_using_bbcode() const {
  3370. return use_bbcode;
  3371. }
  3372. String RichTextLabel::get_parsed_text() const {
  3373. String text = "";
  3374. Item *it = main;
  3375. while (it) {
  3376. if (it->type == ITEM_DROPCAP) {
  3377. const ItemDropcap *dc = (ItemDropcap *)it;
  3378. if (dc != nullptr) {
  3379. text += dc->text;
  3380. }
  3381. } else if (it->type == ITEM_TEXT) {
  3382. ItemText *t = static_cast<ItemText *>(it);
  3383. text += t->text;
  3384. } else if (it->type == ITEM_NEWLINE) {
  3385. text += "\n";
  3386. } else if (it->type == ITEM_IMAGE) {
  3387. text += " ";
  3388. } else if (it->type == ITEM_INDENT || it->type == ITEM_LIST) {
  3389. text += "\t";
  3390. }
  3391. it = _get_next_item(it, true);
  3392. }
  3393. return text;
  3394. }
  3395. void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) {
  3396. ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
  3397. if (text_direction != p_text_direction) {
  3398. text_direction = p_text_direction;
  3399. main->first_invalid_line = 0; //invalidate ALL
  3400. _validate_line_caches(main);
  3401. update();
  3402. }
  3403. }
  3404. void RichTextLabel::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
  3405. if (st_parser != p_parser) {
  3406. st_parser = p_parser;
  3407. main->first_invalid_line = 0; //invalidate ALL
  3408. _validate_line_caches(main);
  3409. update();
  3410. }
  3411. }
  3412. Control::StructuredTextParser RichTextLabel::get_structured_text_bidi_override() const {
  3413. return st_parser;
  3414. }
  3415. void RichTextLabel::set_structured_text_bidi_override_options(Array p_args) {
  3416. st_args = p_args;
  3417. main->first_invalid_line = 0; //invalidate ALL
  3418. _validate_line_caches(main);
  3419. update();
  3420. }
  3421. Array RichTextLabel::get_structured_text_bidi_override_options() const {
  3422. return st_args;
  3423. }
  3424. Control::TextDirection RichTextLabel::get_text_direction() const {
  3425. return text_direction;
  3426. }
  3427. void RichTextLabel::set_language(const String &p_language) {
  3428. if (language != p_language) {
  3429. language = p_language;
  3430. main->first_invalid_line = 0; //invalidate ALL
  3431. _validate_line_caches(main);
  3432. update();
  3433. }
  3434. }
  3435. String RichTextLabel::get_language() const {
  3436. return language;
  3437. }
  3438. void RichTextLabel::set_percent_visible(float p_percent) {
  3439. if (percent_visible != p_percent) {
  3440. if (p_percent < 0 || p_percent >= 1) {
  3441. visible_characters = -1;
  3442. percent_visible = 1;
  3443. } else {
  3444. visible_characters = get_total_character_count() * p_percent;
  3445. percent_visible = p_percent;
  3446. }
  3447. main->first_invalid_line = 0; //invalidate ALL
  3448. _validate_line_caches(main);
  3449. update();
  3450. }
  3451. }
  3452. float RichTextLabel::get_percent_visible() const {
  3453. return percent_visible;
  3454. }
  3455. void RichTextLabel::set_effects(Array p_effects) {
  3456. custom_effects = p_effects;
  3457. if ((text != "") && use_bbcode) {
  3458. parse_bbcode(text);
  3459. }
  3460. }
  3461. Array RichTextLabel::get_effects() {
  3462. return custom_effects;
  3463. }
  3464. void RichTextLabel::install_effect(const Variant effect) {
  3465. Ref<RichTextEffect> rteffect;
  3466. rteffect = effect;
  3467. if (rteffect.is_valid()) {
  3468. custom_effects.push_back(effect);
  3469. if ((text != "") && use_bbcode) {
  3470. parse_bbcode(text);
  3471. }
  3472. }
  3473. }
  3474. int RichTextLabel::get_content_height() const {
  3475. int total_height = 0;
  3476. if (main->lines.size()) {
  3477. total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y;
  3478. }
  3479. return total_height;
  3480. }
  3481. #ifndef DISABLE_DEPRECATED
  3482. // People will be very angry, if their texts get erased, because of #39148. (3.x -> 4.0)
  3483. // Altough some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty
  3484. bool RichTextLabel::_set(const StringName &p_name, const Variant &p_value) {
  3485. if (p_name == "bbcode_text" && !((String)p_value).is_empty()) {
  3486. set_text(p_value);
  3487. return true;
  3488. }
  3489. return false;
  3490. }
  3491. #endif
  3492. void RichTextLabel::_bind_methods() {
  3493. ClassDB::bind_method(D_METHOD("get_parsed_text"), &RichTextLabel::get_parsed_text);
  3494. ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);
  3495. ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text);
  3496. ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGN_CENTER));
  3497. ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
  3498. ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
  3499. ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
  3500. ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size);
  3501. ClassDB::bind_method(D_METHOD("push_font_features", "opentype_features"), &RichTextLabel::push_font_features);
  3502. ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal);
  3503. ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold);
  3504. ClassDB::bind_method(D_METHOD("push_bold_italics"), &RichTextLabel::push_bold_italics);
  3505. ClassDB::bind_method(D_METHOD("push_italics"), &RichTextLabel::push_italics);
  3506. ClassDB::bind_method(D_METHOD("push_mono"), &RichTextLabel::push_mono);
  3507. ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color);
  3508. ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size);
  3509. ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color);
  3510. ClassDB::bind_method(D_METHOD("push_paragraph", "align", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT));
  3511. ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent);
  3512. ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize"), &RichTextLabel::push_list);
  3513. ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta);
  3514. ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline);
  3515. ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough);
  3516. ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGN_TOP));
  3517. ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0)));
  3518. ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
  3519. ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color);
  3520. ClassDB::bind_method(D_METHOD("set_cell_border_color", "color"), &RichTextLabel::set_cell_border_color);
  3521. ClassDB::bind_method(D_METHOD("set_cell_size_override", "min_size", "max_size"), &RichTextLabel::set_cell_size_override);
  3522. ClassDB::bind_method(D_METHOD("set_cell_padding", "padding"), &RichTextLabel::set_cell_padding);
  3523. ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell);
  3524. ClassDB::bind_method(D_METHOD("push_fgcolor", "fgcolor"), &RichTextLabel::push_fgcolor);
  3525. ClassDB::bind_method(D_METHOD("push_bgcolor", "bgcolor"), &RichTextLabel::push_bgcolor);
  3526. ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop);
  3527. ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear);
  3528. ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &RichTextLabel::set_structured_text_bidi_override);
  3529. ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &RichTextLabel::get_structured_text_bidi_override);
  3530. ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &RichTextLabel::set_structured_text_bidi_override_options);
  3531. ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &RichTextLabel::get_structured_text_bidi_override_options);
  3532. ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &RichTextLabel::set_text_direction);
  3533. ClassDB::bind_method(D_METHOD("get_text_direction"), &RichTextLabel::get_text_direction);
  3534. ClassDB::bind_method(D_METHOD("set_language", "language"), &RichTextLabel::set_language);
  3535. ClassDB::bind_method(D_METHOD("get_language"), &RichTextLabel::get_language);
  3536. ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline);
  3537. ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined);
  3538. ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &RichTextLabel::set_override_selected_font_color);
  3539. ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &RichTextLabel::is_overriding_selected_font_color);
  3540. ClassDB::bind_method(D_METHOD("set_scroll_active", "active"), &RichTextLabel::set_scroll_active);
  3541. ClassDB::bind_method(D_METHOD("is_scroll_active"), &RichTextLabel::is_scroll_active);
  3542. ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow);
  3543. ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following);
  3544. ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll);
  3545. ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line);
  3546. ClassDB::bind_method(D_METHOD("scroll_to_paragraph", "paragraph"), &RichTextLabel::scroll_to_paragraph);
  3547. ClassDB::bind_method(D_METHOD("set_tab_size", "spaces"), &RichTextLabel::set_tab_size);
  3548. ClassDB::bind_method(D_METHOD("get_tab_size"), &RichTextLabel::get_tab_size);
  3549. ClassDB::bind_method(D_METHOD("set_fit_content_height", "enabled"), &RichTextLabel::set_fit_content_height);
  3550. ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &RichTextLabel::is_fit_content_height_enabled);
  3551. ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled);
  3552. ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled);
  3553. ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &RichTextLabel::set_deselect_on_focus_loss_enabled);
  3554. ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &RichTextLabel::is_deselect_on_focus_loss_enabled);
  3555. ClassDB::bind_method(D_METHOD("get_selection_from"), &RichTextLabel::get_selection_from);
  3556. ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to);
  3557. ClassDB::bind_method(D_METHOD("get_selected_text"), &RichTextLabel::get_selected_text);
  3558. ClassDB::bind_method(D_METHOD("parse_bbcode", "bbcode"), &RichTextLabel::parse_bbcode);
  3559. ClassDB::bind_method(D_METHOD("append_text", "bbcode"), &RichTextLabel::append_text);
  3560. ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
  3561. ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &RichTextLabel::set_visible_characters);
  3562. ClassDB::bind_method(D_METHOD("get_visible_characters"), &RichTextLabel::get_visible_characters);
  3563. ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &RichTextLabel::set_percent_visible);
  3564. ClassDB::bind_method(D_METHOD("get_percent_visible"), &RichTextLabel::get_percent_visible);
  3565. ClassDB::bind_method(D_METHOD("get_total_character_count"), &RichTextLabel::get_total_character_count);
  3566. ClassDB::bind_method(D_METHOD("set_use_bbcode", "enable"), &RichTextLabel::set_use_bbcode);
  3567. ClassDB::bind_method(D_METHOD("is_using_bbcode"), &RichTextLabel::is_using_bbcode);
  3568. ClassDB::bind_method(D_METHOD("get_line_count"), &RichTextLabel::get_line_count);
  3569. ClassDB::bind_method(D_METHOD("get_visible_line_count"), &RichTextLabel::get_visible_line_count);
  3570. ClassDB::bind_method(D_METHOD("get_paragraph_count"), &RichTextLabel::get_paragraph_count);
  3571. ClassDB::bind_method(D_METHOD("get_visible_paragraph_count"), &RichTextLabel::get_visible_paragraph_count);
  3572. ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
  3573. ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values);
  3574. ClassDB::bind_method(D_METHOD("set_effects", "effects"), &RichTextLabel::set_effects);
  3575. ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects);
  3576. ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect);
  3577. ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
  3578. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
  3579. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined");
  3580. ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size");
  3581. ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
  3582. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
  3583. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled");
  3584. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active");
  3585. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following");
  3586. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled");
  3587. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
  3588. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
  3589. ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects");
  3590. ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
  3591. ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
  3592. ADD_GROUP("Structured Text", "structured_text_");
  3593. ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
  3594. ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
  3595. ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
  3596. ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
  3597. ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
  3598. BIND_ENUM_CONSTANT(ALIGN_LEFT);
  3599. BIND_ENUM_CONSTANT(ALIGN_CENTER);
  3600. BIND_ENUM_CONSTANT(ALIGN_RIGHT);
  3601. BIND_ENUM_CONSTANT(ALIGN_FILL);
  3602. BIND_ENUM_CONSTANT(LIST_NUMBERS);
  3603. BIND_ENUM_CONSTANT(LIST_LETTERS);
  3604. BIND_ENUM_CONSTANT(LIST_ROMAN);
  3605. BIND_ENUM_CONSTANT(LIST_DOTS);
  3606. BIND_ENUM_CONSTANT(ITEM_FRAME);
  3607. BIND_ENUM_CONSTANT(ITEM_TEXT);
  3608. BIND_ENUM_CONSTANT(ITEM_IMAGE);
  3609. BIND_ENUM_CONSTANT(ITEM_NEWLINE);
  3610. BIND_ENUM_CONSTANT(ITEM_FONT);
  3611. BIND_ENUM_CONSTANT(ITEM_FONT_SIZE);
  3612. BIND_ENUM_CONSTANT(ITEM_FONT_FEATURES);
  3613. BIND_ENUM_CONSTANT(ITEM_COLOR);
  3614. BIND_ENUM_CONSTANT(ITEM_OUTLINE_SIZE);
  3615. BIND_ENUM_CONSTANT(ITEM_OUTLINE_COLOR);
  3616. BIND_ENUM_CONSTANT(ITEM_UNDERLINE);
  3617. BIND_ENUM_CONSTANT(ITEM_STRIKETHROUGH);
  3618. BIND_ENUM_CONSTANT(ITEM_PARAGRAPH);
  3619. BIND_ENUM_CONSTANT(ITEM_INDENT);
  3620. BIND_ENUM_CONSTANT(ITEM_LIST);
  3621. BIND_ENUM_CONSTANT(ITEM_TABLE);
  3622. BIND_ENUM_CONSTANT(ITEM_FADE);
  3623. BIND_ENUM_CONSTANT(ITEM_SHAKE);
  3624. BIND_ENUM_CONSTANT(ITEM_WAVE);
  3625. BIND_ENUM_CONSTANT(ITEM_TORNADO);
  3626. BIND_ENUM_CONSTANT(ITEM_RAINBOW);
  3627. BIND_ENUM_CONSTANT(ITEM_BGCOLOR);
  3628. BIND_ENUM_CONSTANT(ITEM_FGCOLOR);
  3629. BIND_ENUM_CONSTANT(ITEM_META);
  3630. BIND_ENUM_CONSTANT(ITEM_DROPCAP);
  3631. BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
  3632. }
  3633. void RichTextLabel::set_visible_characters(int p_visible) {
  3634. if (visible_characters != p_visible) {
  3635. visible_characters = p_visible;
  3636. if (p_visible == -1) {
  3637. percent_visible = 1;
  3638. } else {
  3639. int total_char_count = get_total_character_count();
  3640. if (total_char_count > 0) {
  3641. percent_visible = (float)p_visible / (float)total_char_count;
  3642. }
  3643. }
  3644. main->first_invalid_line = 0; //invalidate ALL
  3645. _validate_line_caches(main);
  3646. update();
  3647. }
  3648. }
  3649. int RichTextLabel::get_visible_characters() const {
  3650. return visible_characters;
  3651. }
  3652. int RichTextLabel::get_total_character_count() const {
  3653. // Note: Do not use line buffer "char_count", it includes only visible characters.
  3654. int tc = 0;
  3655. Item *it = main;
  3656. while (it) {
  3657. if (it->type == ITEM_TEXT) {
  3658. ItemText *t = static_cast<ItemText *>(it);
  3659. tc += t->text.length();
  3660. } else if (it->type == ITEM_NEWLINE) {
  3661. tc++;
  3662. } else if (it->type == ITEM_IMAGE) {
  3663. tc++;
  3664. }
  3665. it = _get_next_item(it, true);
  3666. }
  3667. return tc;
  3668. }
  3669. void RichTextLabel::set_fixed_size_to_width(int p_width) {
  3670. fixed_width = p_width;
  3671. minimum_size_changed();
  3672. }
  3673. Size2 RichTextLabel::get_minimum_size() const {
  3674. Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
  3675. Size2 size = style->get_minimum_size();
  3676. if (fixed_width != -1) {
  3677. size.x += fixed_width;
  3678. }
  3679. if (fixed_width != -1 || fit_content_height) {
  3680. const_cast<RichTextLabel *>(this)->_validate_line_caches(main);
  3681. size.y += get_content_height();
  3682. }
  3683. return size;
  3684. }
  3685. void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag) {
  3686. Vector2i fbg_index = Vector2i(end, start);
  3687. Color last_color = Color(0, 0, 0, 0);
  3688. bool draw_box = false;
  3689. // Draw a box based on color tags associated with glyphs
  3690. for (int i = start; i < end; i++) {
  3691. Item *it = _get_item_at_pos(it_from, it_to, i);
  3692. Color color = Color(0, 0, 0, 0);
  3693. if (fbg_flag == 0) {
  3694. color = _find_bgcolor(it);
  3695. } else {
  3696. color = _find_fgcolor(it);
  3697. }
  3698. bool change_to_color = ((color.a > 0) && ((last_color.a - 0.0) < 0.01));
  3699. bool change_from_color = (((color.a - 0.0) < 0.01) && (last_color.a > 0.0));
  3700. bool change_color = (((color.a > 0) == (last_color.a > 0)) && (color != last_color));
  3701. if (change_to_color) {
  3702. fbg_index.x = MIN(i, fbg_index.x);
  3703. fbg_index.y = MAX(i, fbg_index.y);
  3704. }
  3705. if (change_from_color || change_color) {
  3706. fbg_index.x = MIN(i, fbg_index.x);
  3707. fbg_index.y = MAX(i, fbg_index.y);
  3708. draw_box = true;
  3709. }
  3710. if (draw_box) {
  3711. Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, fbg_index.y);
  3712. for (int j = 0; j < sel.size(); j++) {
  3713. Vector2 rect_off = line_off + Vector2(sel[j].x, -TS->shaped_text_get_ascent(p_rid));
  3714. Vector2 rect_size = Vector2(sel[j].y - sel[j].x, TS->shaped_text_get_size(p_rid).y);
  3715. RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color);
  3716. }
  3717. fbg_index = Vector2i(end, start);
  3718. draw_box = false;
  3719. }
  3720. if (change_color) {
  3721. fbg_index.x = MIN(i, fbg_index.x);
  3722. fbg_index.y = MAX(i, fbg_index.y);
  3723. }
  3724. last_color = color;
  3725. }
  3726. if (last_color.a > 0) {
  3727. Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, end);
  3728. for (int i = 0; i < sel.size(); i++) {
  3729. Vector2 rect_off = line_off + Vector2(sel[i].x, -TS->shaped_text_get_ascent(p_rid));
  3730. Vector2 rect_size = Vector2(sel[i].y - sel[i].x, TS->shaped_text_get_size(p_rid).y);
  3731. RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color);
  3732. }
  3733. }
  3734. }
  3735. Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
  3736. for (int i = 0; i < custom_effects.size(); i++) {
  3737. Ref<RichTextEffect> effect = custom_effects[i];
  3738. if (!effect.is_valid()) {
  3739. continue;
  3740. }
  3741. if (effect->get_bbcode() == p_bbcode_identifier) {
  3742. return effect;
  3743. }
  3744. }
  3745. return Ref<RichTextEffect>();
  3746. }
  3747. Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) {
  3748. Dictionary d = Dictionary();
  3749. for (int i = 0; i < p_expressions.size(); i++) {
  3750. String expression = p_expressions[i];
  3751. Array a = Array();
  3752. Vector<String> parts = expression.split("=", true);
  3753. String key = parts[0];
  3754. if (parts.size() != 2) {
  3755. return d;
  3756. }
  3757. Vector<String> values = parts[1].split(",", false);
  3758. #ifdef MODULE_REGEX_ENABLED
  3759. RegEx color = RegEx();
  3760. color.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
  3761. RegEx nodepath = RegEx();
  3762. nodepath.compile("^\\$");
  3763. RegEx boolean = RegEx();
  3764. boolean.compile("^(true|false)$");
  3765. RegEx decimal = RegEx();
  3766. decimal.compile("^-?^.?\\d+(\\.\\d+?)?$");
  3767. RegEx numerical = RegEx();
  3768. numerical.compile("^\\d+$");
  3769. for (int j = 0; j < values.size(); j++) {
  3770. if (!color.search(values[j]).is_null()) {
  3771. a.append(Color::html(values[j]));
  3772. } else if (!nodepath.search(values[j]).is_null()) {
  3773. if (values[j].begins_with("$")) {
  3774. String v = values[j].substr(1, values[j].length());
  3775. a.append(NodePath(v));
  3776. }
  3777. } else if (!boolean.search(values[j]).is_null()) {
  3778. if (values[j] == "true") {
  3779. a.append(true);
  3780. } else if (values[j] == "false") {
  3781. a.append(false);
  3782. }
  3783. } else if (!decimal.search(values[j]).is_null()) {
  3784. a.append(values[j].to_float());
  3785. } else if (!numerical.search(values[j]).is_null()) {
  3786. a.append(values[j].to_int());
  3787. } else {
  3788. a.append(values[j]);
  3789. }
  3790. }
  3791. #endif
  3792. if (values.size() > 1) {
  3793. d[key] = a;
  3794. } else if (values.size() == 1) {
  3795. d[key] = a[0];
  3796. }
  3797. }
  3798. return d;
  3799. }
  3800. RichTextLabel::RichTextLabel() {
  3801. main = memnew(ItemFrame);
  3802. main->index = 0;
  3803. current = main;
  3804. main->lines.resize(1);
  3805. main->lines.write[0].from = main;
  3806. main->first_invalid_line = 0;
  3807. main->first_resized_line = 0;
  3808. current_frame = main;
  3809. vscroll = memnew(VScrollBar);
  3810. add_child(vscroll, false, INTERNAL_MODE_FRONT);
  3811. vscroll->set_drag_node(String(".."));
  3812. vscroll->set_step(1);
  3813. vscroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
  3814. vscroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
  3815. vscroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
  3816. vscroll->connect("value_changed", callable_mp(this, &RichTextLabel::_scroll_changed));
  3817. vscroll->set_step(1);
  3818. vscroll->hide();
  3819. set_clip_contents(true);
  3820. }
  3821. RichTextLabel::~RichTextLabel() {
  3822. memdelete(main);
  3823. }