text_edit.cpp 125 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844
  1. /*************************************************************************/
  2. /* text_edit.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* http://www.godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "text_edit.h"
  31. #include "os/input.h"
  32. #include "os/keyboard.h"
  33. #include "os/os.h"
  34. #include "message_queue.h"
  35. #include "project_settings.h"
  36. #include "scene/main/viewport.h"
  37. #define TAB_PIXELS
  38. static bool _is_text_char(CharType c) {
  39. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
  40. }
  41. static bool _is_symbol(CharType c) {
  42. return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
  43. }
  44. static bool _is_whitespace(CharType c) {
  45. return c == '\t' || c == ' ';
  46. }
  47. static bool _is_char(CharType c) {
  48. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
  49. }
  50. static bool _is_number(CharType c) {
  51. return (c >= '0' && c <= '9');
  52. }
  53. static bool _is_hex_symbol(CharType c) {
  54. return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
  55. }
  56. static bool _is_pair_right_symbol(CharType c) {
  57. return c == '"' ||
  58. c == '\'' ||
  59. c == ')' ||
  60. c == ']' ||
  61. c == '}';
  62. }
  63. static bool _is_pair_left_symbol(CharType c) {
  64. return c == '"' ||
  65. c == '\'' ||
  66. c == '(' ||
  67. c == '[' ||
  68. c == '{';
  69. }
  70. static bool _is_pair_symbol(CharType c) {
  71. return _is_pair_left_symbol(c) || _is_pair_right_symbol(c);
  72. }
  73. static CharType _get_right_pair_symbol(CharType c) {
  74. if (c == '"')
  75. return '"';
  76. if (c == '\'')
  77. return '\'';
  78. if (c == '(')
  79. return ')';
  80. if (c == '[')
  81. return ']';
  82. if (c == '{')
  83. return '}';
  84. return 0;
  85. }
  86. void TextEdit::Text::set_font(const Ref<Font> &p_font) {
  87. font = p_font;
  88. }
  89. void TextEdit::Text::set_indent_size(int p_indent_size) {
  90. indent_size = p_indent_size;
  91. }
  92. void TextEdit::Text::_update_line_cache(int p_line) const {
  93. int w = 0;
  94. int tab_w = font->get_char_size(' ').width * indent_size;
  95. int len = text[p_line].data.length();
  96. const CharType *str = text[p_line].data.c_str();
  97. //update width
  98. for (int i = 0; i < len; i++) {
  99. if (str[i] == '\t') {
  100. int left = w % tab_w;
  101. if (left == 0)
  102. w += tab_w;
  103. else
  104. w += tab_w - w % tab_w; // is right...
  105. } else {
  106. w += font->get_char_size(str[i], str[i + 1]).width;
  107. }
  108. }
  109. text[p_line].width_cache = w;
  110. //update regions
  111. text[p_line].region_info.clear();
  112. for (int i = 0; i < len; i++) {
  113. if (!_is_symbol(str[i]))
  114. continue;
  115. if (str[i] == '\\') {
  116. i++; //skip quoted anything
  117. continue;
  118. }
  119. int left = len - i;
  120. for (int j = 0; j < color_regions->size(); j++) {
  121. const ColorRegion &cr = color_regions->operator[](j);
  122. /* BEGIN */
  123. int lr = cr.begin_key.length();
  124. if (lr == 0 || lr > left)
  125. continue;
  126. const CharType *kc = cr.begin_key.c_str();
  127. bool match = true;
  128. for (int k = 0; k < lr; k++) {
  129. if (kc[k] != str[i + k]) {
  130. match = false;
  131. break;
  132. }
  133. }
  134. if (match) {
  135. ColorRegionInfo cri;
  136. cri.end = false;
  137. cri.region = j;
  138. text[p_line].region_info[i] = cri;
  139. i += lr - 1;
  140. break;
  141. }
  142. /* END */
  143. lr = cr.end_key.length();
  144. if (lr == 0 || lr > left)
  145. continue;
  146. kc = cr.end_key.c_str();
  147. match = true;
  148. for (int k = 0; k < lr; k++) {
  149. if (kc[k] != str[i + k]) {
  150. match = false;
  151. break;
  152. }
  153. }
  154. if (match) {
  155. ColorRegionInfo cri;
  156. cri.end = true;
  157. cri.region = j;
  158. text[p_line].region_info[i] = cri;
  159. i += lr - 1;
  160. break;
  161. }
  162. }
  163. }
  164. }
  165. const Map<int, TextEdit::Text::ColorRegionInfo> &TextEdit::Text::get_color_region_info(int p_line) {
  166. Map<int, ColorRegionInfo> *cri = NULL;
  167. ERR_FAIL_INDEX_V(p_line, text.size(), *cri); //enjoy your crash
  168. if (text[p_line].width_cache == -1) {
  169. _update_line_cache(p_line);
  170. }
  171. return text[p_line].region_info;
  172. }
  173. int TextEdit::Text::get_line_width(int p_line) const {
  174. ERR_FAIL_INDEX_V(p_line, text.size(), -1);
  175. if (text[p_line].width_cache == -1) {
  176. _update_line_cache(p_line);
  177. }
  178. return text[p_line].width_cache;
  179. }
  180. void TextEdit::Text::clear_caches() {
  181. for (int i = 0; i < text.size(); i++)
  182. text[i].width_cache = -1;
  183. }
  184. void TextEdit::Text::clear() {
  185. text.clear();
  186. insert(0, "");
  187. }
  188. int TextEdit::Text::get_max_width() const {
  189. //quite some work.. but should be fast enough.
  190. int max = 0;
  191. for (int i = 0; i < text.size(); i++)
  192. max = MAX(max, get_line_width(i));
  193. return max;
  194. }
  195. void TextEdit::Text::set(int p_line, const String &p_text) {
  196. ERR_FAIL_INDEX(p_line, text.size());
  197. text[p_line].width_cache = -1;
  198. text[p_line].data = p_text;
  199. }
  200. void TextEdit::Text::insert(int p_at, const String &p_text) {
  201. Line line;
  202. line.marked = false;
  203. line.breakpoint = false;
  204. line.width_cache = -1;
  205. line.data = p_text;
  206. text.insert(p_at, line);
  207. }
  208. void TextEdit::Text::remove(int p_at) {
  209. text.remove(p_at);
  210. }
  211. void TextEdit::_update_scrollbars() {
  212. Size2 size = get_size();
  213. Size2 hmin = h_scroll->get_combined_minimum_size();
  214. Size2 vmin = v_scroll->get_combined_minimum_size();
  215. v_scroll->set_begin(Point2(size.width - vmin.width, cache.style_normal->get_margin(MARGIN_TOP)));
  216. v_scroll->set_end(Point2(size.width, size.height - cache.style_normal->get_margin(MARGIN_TOP) - cache.style_normal->get_margin(MARGIN_BOTTOM)));
  217. h_scroll->set_begin(Point2(0, size.height - hmin.height));
  218. h_scroll->set_end(Point2(size.width - vmin.width, size.height));
  219. int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1;
  220. int visible_rows = get_visible_rows();
  221. int total_rows = text.size();
  222. if (scroll_past_end_of_file_enabled) {
  223. total_rows += get_visible_rows() - 1;
  224. }
  225. int vscroll_pixels = v_scroll->get_combined_minimum_size().width;
  226. int visible_width = size.width - cache.style_normal->get_minimum_size().width;
  227. int total_width = text.get_max_width() + vmin.x;
  228. if (line_numbers)
  229. total_width += cache.line_number_w;
  230. if (draw_breakpoint_gutter) {
  231. total_width += cache.breakpoint_gutter_width;
  232. }
  233. bool use_hscroll = true;
  234. bool use_vscroll = true;
  235. if (total_rows <= visible_rows && total_width <= visible_width) {
  236. //thanks yessopie for this clever bit of logic
  237. use_hscroll = false;
  238. use_vscroll = false;
  239. } else {
  240. if (total_rows > visible_rows && total_width <= visible_width - vscroll_pixels) {
  241. //thanks yessopie for this clever bit of logic
  242. use_hscroll = false;
  243. }
  244. if (total_rows <= visible_rows - hscroll_rows && total_width > visible_width) {
  245. //thanks yessopie for this clever bit of logic
  246. use_vscroll = false;
  247. }
  248. }
  249. updating_scrolls = true;
  250. if (use_vscroll) {
  251. v_scroll->show();
  252. v_scroll->set_max(total_rows);
  253. v_scroll->set_page(visible_rows);
  254. if (fabs(v_scroll->get_value() - (double)cursor.line_ofs) >= 1) {
  255. v_scroll->set_value(cursor.line_ofs);
  256. }
  257. } else {
  258. cursor.line_ofs = 0;
  259. v_scroll->hide();
  260. }
  261. if (use_hscroll) {
  262. h_scroll->show();
  263. h_scroll->set_max(total_width);
  264. h_scroll->set_page(visible_width);
  265. if (fabs(h_scroll->get_value() - (double)cursor.x_ofs) >= 1) {
  266. h_scroll->set_value(cursor.x_ofs);
  267. }
  268. } else {
  269. h_scroll->hide();
  270. }
  271. updating_scrolls = false;
  272. }
  273. void TextEdit::_click_selection_held() {
  274. if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && selection.selecting_mode != Selection::MODE_NONE) {
  275. Point2 mp = Input::get_singleton()->get_mouse_position() - get_global_position();
  276. int row, col;
  277. _get_mouse_pos(Point2i(mp.x, mp.y), row, col);
  278. select(selection.selecting_line, selection.selecting_column, row, col);
  279. cursor_set_line(row);
  280. cursor_set_column(col);
  281. update();
  282. click_select_held->start();
  283. } else {
  284. click_select_held->stop();
  285. }
  286. }
  287. void TextEdit::_notification(int p_what) {
  288. switch (p_what) {
  289. case NOTIFICATION_ENTER_TREE: {
  290. _update_caches();
  291. if (cursor_changed_dirty)
  292. MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit");
  293. if (text_changed_dirty)
  294. MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
  295. } break;
  296. case NOTIFICATION_RESIZED: {
  297. cache.size = get_size();
  298. adjust_viewport_to_cursor();
  299. } break;
  300. case NOTIFICATION_THEME_CHANGED: {
  301. _update_caches();
  302. } break;
  303. case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
  304. window_has_focus = true;
  305. draw_caret = true;
  306. update();
  307. } break;
  308. case MainLoop::NOTIFICATION_WM_FOCUS_OUT: {
  309. window_has_focus = false;
  310. draw_caret = false;
  311. update();
  312. } break;
  313. case NOTIFICATION_DRAW: {
  314. if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
  315. draw_caret = false;
  316. }
  317. if (draw_breakpoint_gutter) {
  318. breakpoint_gutter_width = (get_row_height() * 55) / 100;
  319. cache.breakpoint_gutter_width = breakpoint_gutter_width;
  320. } else {
  321. cache.breakpoint_gutter_width = 0;
  322. }
  323. int line_number_char_count = 0;
  324. {
  325. int lc = text.size() + 1;
  326. cache.line_number_w = 0;
  327. while (lc) {
  328. cache.line_number_w += 1;
  329. lc /= 10;
  330. };
  331. if (line_numbers) {
  332. line_number_char_count = cache.line_number_w;
  333. cache.line_number_w = (cache.line_number_w + 1) * cache.font->get_char_size('0').width;
  334. } else {
  335. cache.line_number_w = 0;
  336. }
  337. }
  338. _update_scrollbars();
  339. RID ci = get_canvas_item();
  340. int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width;
  341. int xmargin_end = cache.size.width - cache.style_normal->get_margin(MARGIN_RIGHT);
  342. //let's do it easy for now:
  343. cache.style_normal->draw(ci, Rect2(Point2(), cache.size));
  344. if (has_focus())
  345. cache.style_focus->draw(ci, Rect2(Point2(), cache.size));
  346. int ascent = cache.font->get_ascent();
  347. int visible_rows = get_visible_rows();
  348. int tab_w = cache.font->get_char_size(' ').width * indent_size;
  349. Color color = cache.font_color;
  350. int in_region = -1;
  351. if (syntax_coloring) {
  352. if (cache.background_color.a > 0.01) {
  353. Point2i ofs = Point2i(cache.style_normal->get_offset()) / 2.0;
  354. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs, get_size() - cache.style_normal->get_minimum_size() + ofs), cache.background_color);
  355. }
  356. //compute actual region to start (may be inside say, a comment).
  357. //slow in very large documments :( but ok for source!
  358. for (int i = 0; i < cursor.line_ofs; i++) {
  359. const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i);
  360. if (in_region >= 0 && color_regions[in_region].line_only) {
  361. in_region = -1; //reset regions that end at end of line
  362. }
  363. for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
  364. const Text::ColorRegionInfo &cri = E->get();
  365. if (in_region == -1) {
  366. if (!cri.end) {
  367. in_region = cri.region;
  368. }
  369. } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
  370. if (cri.end || color_regions[cri.region].eq) {
  371. in_region = -1;
  372. }
  373. }
  374. }
  375. }
  376. }
  377. int brace_open_match_line = -1;
  378. int brace_open_match_column = -1;
  379. bool brace_open_matching = false;
  380. bool brace_open_mismatch = false;
  381. int brace_close_match_line = -1;
  382. int brace_close_match_column = -1;
  383. bool brace_close_matching = false;
  384. bool brace_close_mismatch = false;
  385. if (brace_matching_enabled) {
  386. if (cursor.column < text[cursor.line].length()) {
  387. //check for open
  388. CharType c = text[cursor.line][cursor.column];
  389. CharType closec = 0;
  390. if (c == '[') {
  391. closec = ']';
  392. } else if (c == '{') {
  393. closec = '}';
  394. } else if (c == '(') {
  395. closec = ')';
  396. }
  397. if (closec != 0) {
  398. int stack = 1;
  399. for (int i = cursor.line; i < text.size(); i++) {
  400. int from = i == cursor.line ? cursor.column + 1 : 0;
  401. for (int j = from; j < text[i].length(); j++) {
  402. CharType cc = text[i][j];
  403. //ignore any brackets inside a string
  404. if (cc == '"' || cc == '\'') {
  405. CharType quotation = cc;
  406. do {
  407. j++;
  408. if (!(j < text[i].length())) {
  409. break;
  410. }
  411. cc = text[i][j];
  412. //skip over escaped quotation marks inside strings
  413. if (cc == '\\') {
  414. bool escaped = true;
  415. while (j + 1 < text[i].length() && text[i][j + 1] == '\\') {
  416. escaped = !escaped;
  417. j++;
  418. }
  419. if (escaped) {
  420. j++;
  421. continue;
  422. }
  423. }
  424. } while (cc != quotation);
  425. } else if (cc == c)
  426. stack++;
  427. else if (cc == closec)
  428. stack--;
  429. if (stack == 0) {
  430. brace_open_match_line = i;
  431. brace_open_match_column = j;
  432. brace_open_matching = true;
  433. break;
  434. }
  435. }
  436. if (brace_open_match_line != -1)
  437. break;
  438. }
  439. if (!brace_open_matching)
  440. brace_open_mismatch = true;
  441. }
  442. }
  443. if (cursor.column > 0) {
  444. CharType c = text[cursor.line][cursor.column - 1];
  445. CharType closec = 0;
  446. if (c == ']') {
  447. closec = '[';
  448. } else if (c == '}') {
  449. closec = '{';
  450. } else if (c == ')') {
  451. closec = '(';
  452. }
  453. if (closec != 0) {
  454. int stack = 1;
  455. for (int i = cursor.line; i >= 0; i--) {
  456. int from = i == cursor.line ? cursor.column - 2 : text[i].length() - 1;
  457. for (int j = from; j >= 0; j--) {
  458. CharType cc = text[i][j];
  459. //ignore any brackets inside a string
  460. if (cc == '"' || cc == '\'') {
  461. CharType quotation = cc;
  462. do {
  463. j--;
  464. if (!(j >= 0)) {
  465. break;
  466. }
  467. cc = text[i][j];
  468. //skip over escaped quotation marks inside strings
  469. if (cc == quotation) {
  470. bool escaped = false;
  471. while (j - 1 >= 0 && text[i][j - 1] == '\\') {
  472. escaped = !escaped;
  473. j--;
  474. }
  475. if (escaped) {
  476. j--;
  477. cc = '\\';
  478. continue;
  479. }
  480. }
  481. } while (cc != quotation);
  482. } else if (cc == c)
  483. stack++;
  484. else if (cc == closec)
  485. stack--;
  486. if (stack == 0) {
  487. brace_close_match_line = i;
  488. brace_close_match_column = j;
  489. brace_close_matching = true;
  490. break;
  491. }
  492. }
  493. if (brace_close_match_line != -1)
  494. break;
  495. }
  496. if (!brace_close_matching)
  497. brace_close_mismatch = true;
  498. }
  499. }
  500. }
  501. int deregion = 0; //force it to clear inrgion
  502. Point2 cursor_pos;
  503. // get the highlighted words
  504. String highlighted_text = get_selection_text();
  505. String line_num_padding = line_numbers_zero_padded ? "0" : " ";
  506. for (int i = 0; i < visible_rows; i++) {
  507. int line = i + cursor.line_ofs;
  508. if (line < 0 || line >= (int)text.size())
  509. continue;
  510. const String &str = text[line];
  511. int char_margin = xmargin_beg - cursor.x_ofs;
  512. int char_ofs = 0;
  513. int ofs_y = i * get_row_height() + cache.line_spacing / 2;
  514. bool prev_is_char = false;
  515. bool prev_is_number = false;
  516. bool in_keyword = false;
  517. bool underlined = false;
  518. bool in_word = false;
  519. bool in_function_name = false;
  520. bool in_member_variable = false;
  521. bool is_hex_notation = false;
  522. Color keyword_color;
  523. // check if line contains highlighted word
  524. int highlighted_text_col = -1;
  525. int search_text_col = -1;
  526. if (!search_text.empty())
  527. search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
  528. if (highlighted_text.length() != 0 && highlighted_text != search_text)
  529. highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
  530. const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(line);
  531. if (text.is_marked(line)) {
  532. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color);
  533. }
  534. if (text.is_breakpoint(line)) {
  535. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
  536. }
  537. if (line == cursor.line) {
  538. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
  539. }
  540. // draw breakpoint marker
  541. if (text.is_breakpoint(line)) {
  542. if (draw_breakpoint_gutter) {
  543. int vertical_gap = (get_row_height() * 40) / 100;
  544. int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
  545. int marker_height = get_row_height() - (vertical_gap * 2);
  546. int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2);
  547. // no transparency on marker
  548. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b));
  549. }
  550. }
  551. if (cache.line_number_w) {
  552. String fc = String::num(line + 1);
  553. while (fc.length() < line_number_char_count) {
  554. fc = line_num_padding + fc;
  555. }
  556. cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
  557. }
  558. for (int j = 0; j < str.length(); j++) {
  559. //look for keyword
  560. if (deregion > 0) {
  561. deregion--;
  562. if (deregion == 0)
  563. in_region = -1;
  564. }
  565. if (syntax_coloring && deregion == 0) {
  566. color = cache.font_color; //reset
  567. //find keyword
  568. bool is_char = _is_text_char(str[j]);
  569. bool is_symbol = _is_symbol(str[j]);
  570. bool is_number = _is_number(str[j]);
  571. if (j == 0 && in_region >= 0 && color_regions[in_region].line_only) {
  572. in_region = -1; //reset regions that end at end of line
  573. }
  574. // allow ABCDEF in hex notation
  575. if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
  576. is_number = true;
  577. } else {
  578. is_hex_notation = false;
  579. }
  580. // check for dot or 'x' for hex notation in floating point number
  581. if ((str[j] == '.' || str[j] == 'x') && !in_word && prev_is_number && !is_number) {
  582. is_number = true;
  583. is_symbol = false;
  584. if (str[j] == 'x' && str[j - 1] == '0') {
  585. is_hex_notation = true;
  586. }
  587. }
  588. if (!in_word && _is_char(str[j])) {
  589. in_word = true;
  590. }
  591. if ((in_keyword || in_word) && !is_hex_notation) {
  592. is_number = false;
  593. }
  594. if (is_symbol && str[j] != '.' && in_word) {
  595. in_word = false;
  596. }
  597. if (is_symbol && cri_map.has(j)) {
  598. const Text::ColorRegionInfo &cri = cri_map[j];
  599. if (in_region == -1) {
  600. if (!cri.end) {
  601. in_region = cri.region;
  602. }
  603. } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
  604. if (cri.end || color_regions[cri.region].eq) {
  605. deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
  606. }
  607. }
  608. }
  609. if (!is_char) {
  610. in_keyword = false;
  611. underlined = false;
  612. }
  613. if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
  614. int to = j;
  615. while (to < str.length() && _is_text_char(str[to]))
  616. to++;
  617. uint32_t hash = String::hash(&str[j], to - j);
  618. StrRange range(&str[j], to - j);
  619. const Color *col = keywords.custom_getptr(range, hash);
  620. if (col) {
  621. in_keyword = true;
  622. keyword_color = *col;
  623. }
  624. if (select_identifiers_enabled && highlighted_word != String()) {
  625. if (highlighted_word == range) {
  626. underlined = true;
  627. }
  628. }
  629. }
  630. if (!in_function_name && in_word && !in_keyword) {
  631. int k = j;
  632. while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
  633. k++;
  634. }
  635. // check for space between name and bracket
  636. while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
  637. k++;
  638. }
  639. if (str[k] == '(') {
  640. in_function_name = true;
  641. }
  642. }
  643. if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
  644. int k = j;
  645. while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
  646. k--;
  647. }
  648. if (str[k] == '.') {
  649. in_member_variable = true;
  650. }
  651. }
  652. if (is_symbol) {
  653. in_function_name = false;
  654. in_member_variable = false;
  655. }
  656. if (in_region >= 0)
  657. color = color_regions[in_region].color;
  658. else if (in_keyword)
  659. color = keyword_color;
  660. else if (in_member_variable)
  661. color = cache.member_variable_color;
  662. else if (in_function_name)
  663. color = cache.function_color;
  664. else if (is_symbol)
  665. color = cache.symbol_color;
  666. else if (is_number)
  667. color = cache.number_color;
  668. prev_is_char = is_char;
  669. prev_is_number = is_number;
  670. }
  671. int char_w;
  672. //handle tabulator
  673. if (str[j] == '\t') {
  674. int left = char_ofs % tab_w;
  675. if (left == 0)
  676. char_w = tab_w;
  677. else
  678. char_w = tab_w - char_ofs % tab_w; // is right...
  679. } else {
  680. char_w = cache.font->get_char_size(str[j], str[j + 1]).width;
  681. }
  682. if ((char_ofs + char_margin) < xmargin_beg) {
  683. char_ofs += char_w;
  684. continue;
  685. }
  686. if ((char_ofs + char_margin + char_w) >= xmargin_end) {
  687. if (syntax_coloring)
  688. continue;
  689. else
  690. break;
  691. }
  692. bool in_search_result = false;
  693. if (search_text_col != -1) {
  694. // if we are at the end check for new search result on same line
  695. if (j >= search_text_col + search_text.length())
  696. search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j);
  697. in_search_result = j >= search_text_col && j < search_text_col + search_text.length();
  698. if (in_search_result) {
  699. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.search_result_color);
  700. }
  701. }
  702. bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || j >= selection.from_column) && (line < selection.to_line || j < selection.to_column));
  703. if (in_selection) {
  704. //inside selection!
  705. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color);
  706. }
  707. if (in_search_result) {
  708. Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color;
  709. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, 1)), border_color);
  710. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color);
  711. if (j == search_text_col)
  712. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(1, get_row_height())), border_color);
  713. if (j == search_text_col + search_text.length() - 1)
  714. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w - 1, ofs_y), Size2i(1, get_row_height())), border_color);
  715. }
  716. if (highlight_all_occurrences) {
  717. if (highlighted_text_col != -1) {
  718. // if we are at the end check for new word on same line
  719. if (j > highlighted_text_col + highlighted_text.length()) {
  720. highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
  721. }
  722. bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length());
  723. /* if this is the original highlighted text we don't want to highlight it again */
  724. if (cursor.line == line && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) {
  725. in_highlighted_word = false;
  726. }
  727. if (in_highlighted_word) {
  728. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color);
  729. }
  730. }
  731. }
  732. if (brace_matching_enabled) {
  733. if ((brace_open_match_line == line && brace_open_match_column == j) ||
  734. (cursor.column == j && cursor.line == line && (brace_open_matching || brace_open_mismatch))) {
  735. if (brace_open_mismatch)
  736. color = cache.brace_mismatch_color;
  737. cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection ? cache.font_selected_color : color);
  738. }
  739. if (
  740. (brace_close_match_line == line && brace_close_match_column == j) ||
  741. (cursor.column == j + 1 && cursor.line == line && (brace_close_matching || brace_close_mismatch))) {
  742. if (brace_close_mismatch)
  743. color = cache.brace_mismatch_color;
  744. cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection ? cache.font_selected_color : color);
  745. }
  746. }
  747. if (cursor.column == j && cursor.line == line) {
  748. cursor_pos = Point2i(char_ofs + char_margin, ofs_y);
  749. if (insert_mode) {
  750. cursor_pos.y += (get_row_height() - 3);
  751. }
  752. int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w;
  753. if (ime_text.length() > 0) {
  754. int ofs = 0;
  755. while (true) {
  756. if (ofs >= ime_text.length())
  757. break;
  758. CharType cchar = ime_text[ofs];
  759. CharType next = ime_text[ofs + 1];
  760. int im_char_width = cache.font->get_char_size(cchar, next).width;
  761. if ((char_ofs + char_margin + im_char_width) >= xmargin_end)
  762. break;
  763. bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
  764. if (selected) {
  765. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color);
  766. } else {
  767. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
  768. }
  769. cache.font->draw_char(ci, Point2(char_ofs + char_margin, ofs_y + ascent), cchar, next, color);
  770. char_ofs += im_char_width;
  771. ofs++;
  772. }
  773. }
  774. if (ime_text.length() == 0) {
  775. if (draw_caret) {
  776. if (insert_mode) {
  777. int caret_h = (block_caret) ? 4 : 1;
  778. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color);
  779. } else {
  780. caret_w = (block_caret) ? caret_w : 1;
  781. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color);
  782. }
  783. }
  784. }
  785. }
  786. if (cursor.column == j && cursor.line == line && block_caret && draw_caret && !insert_mode) {
  787. color = cache.caret_background_color;
  788. }
  789. if (str[j] >= 32) {
  790. int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), str[j], str[j + 1], in_selection ? cache.font_selected_color : color);
  791. if (underlined) {
  792. draw_rect(Rect2(char_ofs + char_margin, ofs_y + ascent + 2, w, 1), in_selection ? cache.font_selected_color : color);
  793. }
  794. }
  795. else if (draw_tabs && str[j] == '\t') {
  796. int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
  797. cache.tab_icon->draw(ci, Point2(char_ofs + char_margin, ofs_y + yofs), in_selection ? cache.font_selected_color : color);
  798. }
  799. char_ofs += char_w;
  800. }
  801. if (cursor.column == str.length() && cursor.line == line && (char_ofs + char_margin) >= xmargin_beg) {
  802. cursor_pos = Point2i(char_ofs + char_margin, ofs_y);
  803. if (insert_mode) {
  804. cursor_pos.y += (get_row_height() - 3);
  805. }
  806. if (ime_text.length() > 0) {
  807. int ofs = 0;
  808. while (true) {
  809. if (ofs >= ime_text.length())
  810. break;
  811. CharType cchar = ime_text[ofs];
  812. CharType next = ime_text[ofs + 1];
  813. int im_char_width = cache.font->get_char_size(cchar, next).width;
  814. if ((char_ofs + char_margin + im_char_width) >= xmargin_end)
  815. break;
  816. bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
  817. if (selected) {
  818. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color);
  819. } else {
  820. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
  821. }
  822. cache.font->draw_char(ci, Point2(char_ofs + char_margin, ofs_y + ascent), cchar, next, color);
  823. char_ofs += im_char_width;
  824. ofs++;
  825. }
  826. }
  827. if (ime_text.length() == 0) {
  828. if (draw_caret) {
  829. if (insert_mode) {
  830. int char_w = cache.font->get_char_size(' ').width;
  831. int caret_h = (block_caret) ? 4 : 1;
  832. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color);
  833. } else {
  834. int char_w = cache.font->get_char_size(' ').width;
  835. int caret_w = (block_caret) ? char_w : 1;
  836. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color);
  837. }
  838. }
  839. }
  840. }
  841. }
  842. if (line_length_guideline) {
  843. int x = xmargin_beg + cache.font->get_char_size('0').width * line_length_guideline_col - cursor.x_ofs;
  844. if (x > xmargin_beg && x < xmargin_end) {
  845. VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(x, 0), Point2(x, cache.size.height), cache.line_length_guideline_color);
  846. }
  847. }
  848. bool completion_below = false;
  849. if (completion_active) {
  850. // code completion box
  851. Ref<StyleBox> csb = get_stylebox("completion");
  852. int maxlines = get_constant("completion_lines");
  853. int cmax_width = get_constant("completion_max_width") * cache.font->get_char_size('x').x;
  854. int scrollw = get_constant("completion_scroll_width");
  855. Color scrollc = get_color("completion_scroll_color");
  856. int lines = MIN(completion_options.size(), maxlines);
  857. int w = 0;
  858. int h = lines * get_row_height();
  859. int nofs = cache.font->get_string_size(completion_base).width;
  860. if (completion_options.size() < 50) {
  861. for (int i = 0; i < completion_options.size(); i++) {
  862. int w2 = MIN(cache.font->get_string_size(completion_options[i]).x, cmax_width);
  863. if (w2 > w)
  864. w = w2;
  865. }
  866. } else {
  867. w = cmax_width;
  868. }
  869. int th = h + csb->get_minimum_size().y;
  870. if (cursor_pos.y + get_row_height() + th > get_size().height) {
  871. completion_rect.position.y = cursor_pos.y - th;
  872. } else {
  873. completion_rect.position.y = cursor_pos.y + get_row_height() + csb->get_offset().y;
  874. completion_below = true;
  875. }
  876. if (cursor_pos.x - nofs + w + scrollw > get_size().width) {
  877. completion_rect.position.x = get_size().width - w - scrollw;
  878. } else {
  879. completion_rect.position.x = cursor_pos.x - nofs;
  880. }
  881. completion_rect.size.width = w + 2;
  882. completion_rect.size.height = h;
  883. if (completion_options.size() <= maxlines)
  884. scrollw = 0;
  885. draw_style_box(csb, Rect2(completion_rect.position - csb->get_offset(), completion_rect.size + csb->get_minimum_size() + Size2(scrollw, 0)));
  886. if (cache.completion_background_color.a > 0.01) {
  887. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(completion_rect.position, completion_rect.size + Size2(scrollw, 0)), cache.completion_background_color);
  888. }
  889. int line_from = CLAMP(completion_index - lines / 2, 0, completion_options.size() - lines);
  890. VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(completion_rect.position.x, completion_rect.position.y + (completion_index - line_from) * get_row_height()), Size2(completion_rect.size.width, get_row_height())), cache.completion_selected_color);
  891. draw_rect(Rect2(completion_rect.position, Size2(nofs, completion_rect.size.height)), cache.completion_existing_color);
  892. for (int i = 0; i < lines; i++) {
  893. int l = line_from + i;
  894. ERR_CONTINUE(l < 0 || l >= completion_options.size());
  895. Color text_color = cache.completion_font_color;
  896. for (int j = 0; j < color_regions.size(); j++) {
  897. if (completion_options[l].begins_with(color_regions[j].begin_key)) {
  898. text_color = color_regions[j].color;
  899. }
  900. }
  901. draw_string(cache.font, Point2(completion_rect.position.x, completion_rect.position.y + i * get_row_height() + cache.font->get_ascent()), completion_options[l], text_color, completion_rect.size.width);
  902. }
  903. if (scrollw) {
  904. //draw a small scroll rectangle to show a position in the options
  905. float r = maxlines / (float)completion_options.size();
  906. float o = line_from / (float)completion_options.size();
  907. draw_rect(Rect2(completion_rect.position.x + completion_rect.size.width, completion_rect.position.y + o * completion_rect.size.y, scrollw, completion_rect.size.y * r), scrollc);
  908. }
  909. completion_line_ofs = line_from;
  910. }
  911. // check to see if the hint should be drawn
  912. bool show_hint = false;
  913. if (completion_hint != "") {
  914. if (completion_active) {
  915. if (completion_below && !callhint_below) {
  916. show_hint = true;
  917. } else if (!completion_below && callhint_below) {
  918. show_hint = true;
  919. }
  920. } else {
  921. show_hint = true;
  922. }
  923. }
  924. if (show_hint) {
  925. Ref<StyleBox> sb = get_stylebox("panel", "TooltipPanel");
  926. Ref<Font> font = cache.font;
  927. Color font_color = get_color("font_color", "TooltipLabel");
  928. int max_w = 0;
  929. int sc = completion_hint.get_slice_count("\n");
  930. int offset = 0;
  931. int spacing = 0;
  932. for (int i = 0; i < sc; i++) {
  933. String l = completion_hint.get_slice("\n", i);
  934. int len = font->get_string_size(l).x;
  935. max_w = MAX(len, max_w);
  936. if (i == 0) {
  937. offset = font->get_string_size(l.substr(0, l.find(String::chr(0xFFFF)))).x;
  938. } else {
  939. spacing += cache.line_spacing;
  940. }
  941. }
  942. Size2 size = Size2(max_w, sc * font->get_height() + spacing);
  943. Size2 minsize = size + sb->get_minimum_size();
  944. if (completion_hint_offset == -0xFFFF) {
  945. completion_hint_offset = cursor_pos.x - offset;
  946. }
  947. Point2 hint_ofs = Vector2(completion_hint_offset, cursor_pos.y) + callhint_offset;
  948. if (callhint_below) {
  949. hint_ofs.y += get_row_height() + sb->get_offset().y;
  950. } else {
  951. hint_ofs.y -= minsize.y + sb->get_offset().y;
  952. }
  953. draw_style_box(sb, Rect2(hint_ofs, minsize));
  954. spacing = 0;
  955. for (int i = 0; i < sc; i++) {
  956. int begin = 0;
  957. int end = 0;
  958. String l = completion_hint.get_slice("\n", i);
  959. if (l.find(String::chr(0xFFFF)) != -1) {
  960. begin = font->get_string_size(l.substr(0, l.find(String::chr(0xFFFF)))).x;
  961. end = font->get_string_size(l.substr(0, l.rfind(String::chr(0xFFFF)))).x;
  962. }
  963. draw_string(font, hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font->get_height() * i + spacing), l.replace(String::chr(0xFFFF), ""), font_color);
  964. if (end > 0) {
  965. Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font->get_height() + font->get_height() * i + spacing - 1);
  966. draw_line(b, b + Vector2(end - begin, 0), font_color);
  967. }
  968. spacing += cache.line_spacing;
  969. }
  970. }
  971. if (has_focus()) {
  972. OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()));
  973. OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
  974. }
  975. } break;
  976. case NOTIFICATION_FOCUS_ENTER: {
  977. if (!caret_blink_enabled) {
  978. draw_caret = true;
  979. }
  980. Point2 cursor_pos = Point2(cursor_get_column(), cursor_get_line()) * get_row_height();
  981. OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos);
  982. OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
  983. if (OS::get_singleton()->has_virtual_keyboard())
  984. OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect());
  985. if (raised_from_completion) {
  986. VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1);
  987. }
  988. } break;
  989. case NOTIFICATION_FOCUS_EXIT: {
  990. OS::get_singleton()->set_ime_position(Point2());
  991. OS::get_singleton()->set_ime_intermediate_text_callback(NULL, NULL);
  992. ime_text = "";
  993. ime_selection = Point2();
  994. if (OS::get_singleton()->has_virtual_keyboard())
  995. OS::get_singleton()->hide_virtual_keyboard();
  996. if (raised_from_completion) {
  997. VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0);
  998. }
  999. } break;
  1000. }
  1001. }
  1002. void TextEdit::_ime_text_callback(void *p_self, String p_text, Point2 p_selection) {
  1003. TextEdit *self = (TextEdit *)p_self;
  1004. self->ime_text = p_text;
  1005. self->ime_selection = p_selection;
  1006. self->update();
  1007. }
  1008. void TextEdit::_consume_pair_symbol(CharType ch) {
  1009. int cursor_position_to_move = cursor_get_column() + 1;
  1010. CharType ch_single[2] = { ch, 0 };
  1011. CharType ch_single_pair[2] = { _get_right_pair_symbol(ch), 0 };
  1012. CharType ch_pair[3] = { ch, _get_right_pair_symbol(ch), 0 };
  1013. if (is_selection_active()) {
  1014. int new_column, new_line;
  1015. begin_complex_operation();
  1016. _insert_text(get_selection_from_line(), get_selection_from_column(),
  1017. ch_single,
  1018. &new_line, &new_column);
  1019. int to_col_offset = 0;
  1020. if (get_selection_from_line() == get_selection_to_line())
  1021. to_col_offset = 1;
  1022. _insert_text(get_selection_to_line(),
  1023. get_selection_to_column() + to_col_offset,
  1024. ch_single_pair,
  1025. &new_line, &new_column);
  1026. end_complex_operation();
  1027. cursor_set_line(get_selection_to_line());
  1028. cursor_set_column(get_selection_to_column() + to_col_offset);
  1029. deselect();
  1030. update();
  1031. return;
  1032. }
  1033. if ((ch == '\'' || ch == '"') &&
  1034. cursor_get_column() > 0 &&
  1035. _is_text_char(text[cursor.line][cursor_get_column() - 1])) {
  1036. insert_text_at_cursor(ch_single);
  1037. cursor_set_column(cursor_position_to_move);
  1038. return;
  1039. }
  1040. if (cursor_get_column() < text[cursor.line].length()) {
  1041. if (_is_text_char(text[cursor.line][cursor_get_column()])) {
  1042. insert_text_at_cursor(ch_single);
  1043. cursor_set_column(cursor_position_to_move);
  1044. return;
  1045. }
  1046. if (_is_pair_right_symbol(ch) &&
  1047. text[cursor.line][cursor_get_column()] == ch) {
  1048. cursor_set_column(cursor_position_to_move);
  1049. return;
  1050. }
  1051. }
  1052. insert_text_at_cursor(ch_pair);
  1053. cursor_set_column(cursor_position_to_move);
  1054. return;
  1055. }
  1056. void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column) {
  1057. bool remove_right_symbol = false;
  1058. if (cursor.column < text[cursor.line].length() && cursor.column > 0) {
  1059. CharType left_char = text[cursor.line][cursor.column - 1];
  1060. CharType right_char = text[cursor.line][cursor.column];
  1061. if (right_char == _get_right_pair_symbol(left_char)) {
  1062. remove_right_symbol = true;
  1063. }
  1064. }
  1065. if (remove_right_symbol) {
  1066. _remove_text(prev_line, prev_column, cursor.line, cursor.column + 1);
  1067. } else {
  1068. _remove_text(prev_line, prev_column, cursor.line, cursor.column);
  1069. }
  1070. }
  1071. void TextEdit::backspace_at_cursor() {
  1072. if (readonly)
  1073. return;
  1074. if (cursor.column == 0 && cursor.line == 0)
  1075. return;
  1076. int prev_line = cursor.column ? cursor.line : cursor.line - 1;
  1077. int prev_column = cursor.column ? (cursor.column - 1) : (text[cursor.line - 1].length());
  1078. if (auto_brace_completion_enabled &&
  1079. cursor.column > 0 &&
  1080. _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) {
  1081. _consume_backspace_for_pair_symbol(prev_line, prev_column);
  1082. } else {
  1083. // handle space indentation
  1084. if (cursor.column - indent_size >= 0 && indent_using_spaces) {
  1085. // if there is enough spaces to count as a tab
  1086. bool unindent = true;
  1087. for (int i = 1; i <= indent_size; i++) {
  1088. if (text[cursor.line][cursor.column - i] != ' ') {
  1089. unindent = false;
  1090. break;
  1091. }
  1092. }
  1093. // and it is before the first character
  1094. int i = 0;
  1095. while (i < cursor.column && i < text[cursor.line].length()) {
  1096. if (text[cursor.line][i] != ' ' && text[cursor.line][i] != '\t') {
  1097. unindent = false;
  1098. break;
  1099. }
  1100. i++;
  1101. }
  1102. // then we can remove it as a single character.
  1103. if (unindent) {
  1104. _remove_text(cursor.line, cursor.column - indent_size, cursor.line, cursor.column);
  1105. prev_column = cursor.column - indent_size;
  1106. } else {
  1107. _remove_text(prev_line, prev_column, cursor.line, cursor.column);
  1108. }
  1109. } else {
  1110. _remove_text(prev_line, prev_column, cursor.line, cursor.column);
  1111. }
  1112. }
  1113. cursor_set_line(prev_line);
  1114. cursor_set_column(prev_column);
  1115. }
  1116. void TextEdit::indent_selection_right() {
  1117. if (!is_selection_active()) {
  1118. return;
  1119. }
  1120. begin_complex_operation();
  1121. int start_line = get_selection_from_line();
  1122. int end_line = get_selection_to_line();
  1123. // ignore if the cursor is not past the first column
  1124. if (get_selection_to_column() == 0) {
  1125. end_line--;
  1126. }
  1127. for (int i = start_line; i <= end_line; i++) {
  1128. String line_text = get_line(i);
  1129. if (indent_using_spaces) {
  1130. line_text = space_indent + line_text;
  1131. } else {
  1132. line_text = '\t' + line_text;
  1133. }
  1134. set_line(i, line_text);
  1135. }
  1136. // fix selection being off by one on the last line
  1137. selection.to_column++;
  1138. end_complex_operation();
  1139. update();
  1140. }
  1141. void TextEdit::indent_selection_left() {
  1142. if (!is_selection_active()) {
  1143. return;
  1144. }
  1145. begin_complex_operation();
  1146. int start_line = get_selection_from_line();
  1147. int end_line = get_selection_to_line();
  1148. // ignore if the cursor is not past the first column
  1149. if (get_selection_to_column() == 0) {
  1150. end_line--;
  1151. }
  1152. String last_line_text = get_line(end_line);
  1153. for (int i = start_line; i <= end_line; i++) {
  1154. String line_text = get_line(i);
  1155. if (line_text.begins_with("\t")) {
  1156. line_text = line_text.substr(1, line_text.length());
  1157. set_line(i, line_text);
  1158. } else if (line_text.begins_with(space_indent)) {
  1159. line_text = line_text.substr(indent_size, line_text.length());
  1160. set_line(i, line_text);
  1161. }
  1162. }
  1163. // fix selection being off by one on the last line
  1164. if (last_line_text != get_line(end_line) && selection.to_column > 0) {
  1165. selection.to_column--;
  1166. }
  1167. end_complex_operation();
  1168. update();
  1169. }
  1170. void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const {
  1171. float rows = p_mouse.y;
  1172. rows -= cache.style_normal->get_margin(MARGIN_TOP);
  1173. rows /= get_row_height();
  1174. int row = cursor.line_ofs + rows;
  1175. if (row < 0)
  1176. row = 0;
  1177. int col = 0;
  1178. if (row >= text.size()) {
  1179. row = text.size() - 1;
  1180. col = text[row].size();
  1181. } else {
  1182. col = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width);
  1183. col += cursor.x_ofs;
  1184. col = get_char_pos_for(col, get_line(row));
  1185. }
  1186. r_row = row;
  1187. r_col = col;
  1188. }
  1189. void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
  1190. Ref<InputEventMouseButton> mb = p_gui_input;
  1191. if (mb.is_valid()) {
  1192. if (completion_active && completion_rect.has_point(mb->get_position())) {
  1193. if (!mb->is_pressed())
  1194. return;
  1195. if (mb->get_button_index() == BUTTON_WHEEL_UP) {
  1196. if (completion_index > 0) {
  1197. completion_index--;
  1198. completion_current = completion_options[completion_index];
  1199. update();
  1200. }
  1201. }
  1202. if (mb->get_button_index() == BUTTON_WHEEL_DOWN) {
  1203. if (completion_index < completion_options.size() - 1) {
  1204. completion_index++;
  1205. completion_current = completion_options[completion_index];
  1206. update();
  1207. }
  1208. }
  1209. if (mb->get_button_index() == BUTTON_LEFT) {
  1210. completion_index = CLAMP(completion_line_ofs + (mb->get_position().y - completion_rect.position.y) / get_row_height(), 0, completion_options.size() - 1);
  1211. completion_current = completion_options[completion_index];
  1212. update();
  1213. if (mb->is_doubleclick())
  1214. _confirm_completion();
  1215. }
  1216. return;
  1217. } else {
  1218. _cancel_completion();
  1219. _cancel_code_hint();
  1220. }
  1221. if (mb->is_pressed()) {
  1222. if (mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) {
  1223. v_scroll->set_value(v_scroll->get_value() - (3 * mb->get_factor()));
  1224. }
  1225. if (mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) {
  1226. v_scroll->set_value(v_scroll->get_value() + (3 * mb->get_factor()));
  1227. }
  1228. if (mb->get_button_index() == BUTTON_WHEEL_LEFT) {
  1229. h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
  1230. }
  1231. if (mb->get_button_index() == BUTTON_WHEEL_RIGHT) {
  1232. h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
  1233. }
  1234. if (mb->get_button_index() == BUTTON_LEFT) {
  1235. _reset_caret_blink_timer();
  1236. int row, col;
  1237. _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col);
  1238. if (mb->get_command() && highlighted_word != String()) {
  1239. emit_signal("symbol_lookup", highlighted_word, row, col);
  1240. return;
  1241. }
  1242. // toggle breakpoint on gutter click
  1243. if (draw_breakpoint_gutter) {
  1244. int gutter = cache.style_normal->get_margin(MARGIN_LEFT);
  1245. if (mb->get_position().x > gutter && mb->get_position().x <= gutter + cache.breakpoint_gutter_width + 3) {
  1246. set_line_as_breakpoint(row, !is_line_set_as_breakpoint(row));
  1247. emit_signal("breakpoint_toggled", row);
  1248. return;
  1249. }
  1250. }
  1251. int prev_col = cursor.column;
  1252. int prev_line = cursor.line;
  1253. cursor_set_line(row);
  1254. cursor_set_column(col);
  1255. if (mb->get_shift() && (cursor.column != prev_col || cursor.line != prev_line)) {
  1256. if (!selection.active) {
  1257. selection.active = true;
  1258. selection.selecting_mode = Selection::MODE_POINTER;
  1259. selection.from_column = prev_col;
  1260. selection.from_line = prev_line;
  1261. selection.to_column = cursor.column;
  1262. selection.to_line = cursor.line;
  1263. if (selection.from_line > selection.to_line || (selection.from_line == selection.to_line && selection.from_column > selection.to_column)) {
  1264. SWAP(selection.from_column, selection.to_column);
  1265. SWAP(selection.from_line, selection.to_line);
  1266. selection.shiftclick_left = false;
  1267. } else {
  1268. selection.shiftclick_left = true;
  1269. }
  1270. selection.selecting_line = prev_line;
  1271. selection.selecting_column = prev_col;
  1272. update();
  1273. } else {
  1274. if (cursor.line < selection.selecting_line || (cursor.line == selection.selecting_line && cursor.column < selection.selecting_column)) {
  1275. if (selection.shiftclick_left) {
  1276. SWAP(selection.from_column, selection.to_column);
  1277. SWAP(selection.from_line, selection.to_line);
  1278. selection.shiftclick_left = !selection.shiftclick_left;
  1279. }
  1280. selection.from_column = cursor.column;
  1281. selection.from_line = cursor.line;
  1282. } else if (cursor.line > selection.selecting_line || (cursor.line == selection.selecting_line && cursor.column > selection.selecting_column)) {
  1283. if (!selection.shiftclick_left) {
  1284. SWAP(selection.from_column, selection.to_column);
  1285. SWAP(selection.from_line, selection.to_line);
  1286. selection.shiftclick_left = !selection.shiftclick_left;
  1287. }
  1288. selection.to_column = cursor.column;
  1289. selection.to_line = cursor.line;
  1290. } else {
  1291. selection.active = false;
  1292. }
  1293. update();
  1294. }
  1295. } else {
  1296. //if sel active and dblick last time < something
  1297. //else
  1298. selection.active = false;
  1299. selection.selecting_mode = Selection::MODE_POINTER;
  1300. selection.selecting_line = row;
  1301. selection.selecting_column = col;
  1302. }
  1303. if (!mb->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < 600 && cursor.line == prev_line) {
  1304. //tripleclick select line
  1305. select(cursor.line, 0, cursor.line, text[cursor.line].length());
  1306. selection.selecting_column = 0;
  1307. last_dblclk = 0;
  1308. } else if (mb->is_doubleclick() && text[cursor.line].length()) {
  1309. //doubleclick select world
  1310. String s = text[cursor.line];
  1311. int beg = CLAMP(cursor.column, 0, s.length());
  1312. int end = beg;
  1313. if (s[beg] > 32 || beg == s.length()) {
  1314. bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this
  1315. while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) {
  1316. beg--;
  1317. }
  1318. while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) {
  1319. end++;
  1320. }
  1321. if (end < s.length())
  1322. end += 1;
  1323. select(cursor.line, beg, cursor.line, end);
  1324. selection.selecting_column = beg;
  1325. }
  1326. last_dblclk = OS::get_singleton()->get_ticks_msec();
  1327. }
  1328. update();
  1329. }
  1330. if (mb->get_button_index() == BUTTON_RIGHT && context_menu_enabled) {
  1331. menu->set_position(get_global_transform().xform(get_local_mouse_pos()));
  1332. menu->set_size(Vector2(1, 1));
  1333. menu->popup();
  1334. grab_focus();
  1335. }
  1336. } else {
  1337. if (mb->get_button_index() == BUTTON_LEFT)
  1338. click_select_held->stop();
  1339. // notify to show soft keyboard
  1340. notification(NOTIFICATION_FOCUS_ENTER);
  1341. }
  1342. }
  1343. Ref<InputEventMouseMotion> mm = p_gui_input;
  1344. if (mm.is_valid()) {
  1345. if (select_identifiers_enabled) {
  1346. if (mm->get_command() && mm->get_button_mask() == 0) {
  1347. String new_word = get_word_at_pos(mm->get_position());
  1348. if (new_word != highlighted_word) {
  1349. highlighted_word = new_word;
  1350. update();
  1351. }
  1352. } else {
  1353. if (highlighted_word != String()) {
  1354. highlighted_word = String();
  1355. update();
  1356. }
  1357. }
  1358. }
  1359. if (mm->get_button_mask() & BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { //ignore if dragging
  1360. if (selection.selecting_mode != Selection::MODE_NONE) {
  1361. _reset_caret_blink_timer();
  1362. int row, col;
  1363. _get_mouse_pos(mm->get_position(), row, col);
  1364. select(selection.selecting_line, selection.selecting_column, row, col);
  1365. cursor_set_line(row);
  1366. cursor_set_column(col);
  1367. update();
  1368. click_select_held->start();
  1369. }
  1370. }
  1371. }
  1372. Ref<InputEventKey> k = p_gui_input;
  1373. if (k.is_valid()) {
  1374. k = k->duplicate(); //it will be modified later on
  1375. #ifdef OSX_ENABLED
  1376. if (k->get_scancode() == KEY_META) {
  1377. #else
  1378. if (k->get_scancode() == KEY_CONTROL) {
  1379. #endif
  1380. if (select_identifiers_enabled) {
  1381. if (k->is_pressed()) {
  1382. highlighted_word = get_word_at_pos(get_local_mouse_pos());
  1383. update();
  1384. } else {
  1385. highlighted_word = String();
  1386. update();
  1387. }
  1388. }
  1389. }
  1390. if (!k->is_pressed())
  1391. return;
  1392. if (completion_active) {
  1393. if (readonly)
  1394. return;
  1395. bool valid = true;
  1396. if (k->get_command() || k->get_metakey())
  1397. valid = false;
  1398. if (valid) {
  1399. if (!k->get_alt()) {
  1400. if (k->get_scancode() == KEY_UP) {
  1401. if (completion_index > 0) {
  1402. completion_index--;
  1403. completion_current = completion_options[completion_index];
  1404. update();
  1405. }
  1406. accept_event();
  1407. return;
  1408. }
  1409. if (k->get_scancode() == KEY_DOWN) {
  1410. if (completion_index < completion_options.size() - 1) {
  1411. completion_index++;
  1412. completion_current = completion_options[completion_index];
  1413. update();
  1414. }
  1415. accept_event();
  1416. return;
  1417. }
  1418. if (k->get_scancode() == KEY_PAGEUP) {
  1419. completion_index -= get_constant("completion_lines");
  1420. if (completion_index < 0)
  1421. completion_index = 0;
  1422. completion_current = completion_options[completion_index];
  1423. update();
  1424. accept_event();
  1425. return;
  1426. }
  1427. if (k->get_scancode() == KEY_PAGEDOWN) {
  1428. completion_index += get_constant("completion_lines");
  1429. if (completion_index >= completion_options.size())
  1430. completion_index = completion_options.size() - 1;
  1431. completion_current = completion_options[completion_index];
  1432. update();
  1433. accept_event();
  1434. return;
  1435. }
  1436. if (k->get_scancode() == KEY_HOME && completion_index > 0) {
  1437. completion_index = 0;
  1438. completion_current = completion_options[completion_index];
  1439. update();
  1440. accept_event();
  1441. return;
  1442. }
  1443. if (k->get_scancode() == KEY_END && completion_index < completion_options.size() - 1) {
  1444. completion_index = completion_options.size() - 1;
  1445. completion_current = completion_options[completion_index];
  1446. update();
  1447. accept_event();
  1448. return;
  1449. }
  1450. if (k->get_scancode() == KEY_DOWN) {
  1451. if (completion_index < completion_options.size() - 1) {
  1452. completion_index++;
  1453. completion_current = completion_options[completion_index];
  1454. update();
  1455. }
  1456. accept_event();
  1457. return;
  1458. }
  1459. if (k->get_scancode() == KEY_KP_ENTER || k->get_scancode() == KEY_ENTER || k->get_scancode() == KEY_TAB) {
  1460. _confirm_completion();
  1461. accept_event();
  1462. return;
  1463. }
  1464. if (k->get_scancode() == KEY_BACKSPACE) {
  1465. _reset_caret_blink_timer();
  1466. backspace_at_cursor();
  1467. _update_completion_candidates();
  1468. accept_event();
  1469. return;
  1470. }
  1471. if (k->get_scancode() == KEY_SHIFT) {
  1472. accept_event();
  1473. return;
  1474. }
  1475. }
  1476. if (k->get_unicode() > 32) {
  1477. _reset_caret_blink_timer();
  1478. const CharType chr[2] = { (CharType)k->get_unicode(), 0 };
  1479. if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
  1480. _consume_pair_symbol(chr[0]);
  1481. } else {
  1482. // remove the old character if in insert mode
  1483. if (insert_mode) {
  1484. begin_complex_operation();
  1485. // make sure we don't try and remove empty space
  1486. if (cursor.column < get_line(cursor.line).length()) {
  1487. _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
  1488. }
  1489. }
  1490. _insert_text_at_cursor(chr);
  1491. if (insert_mode) {
  1492. end_complex_operation();
  1493. }
  1494. }
  1495. _update_completion_candidates();
  1496. accept_event();
  1497. return;
  1498. }
  1499. }
  1500. _cancel_completion();
  1501. }
  1502. /* TEST CONTROL FIRST!! */
  1503. // some remaps for duplicate functions..
  1504. if (k->get_command() && !k->get_shift() && !k->get_alt() && !k->get_metakey() && k->get_scancode() == KEY_INSERT) {
  1505. k->set_scancode(KEY_C);
  1506. }
  1507. if (!k->get_command() && k->get_shift() && !k->get_alt() && !k->get_metakey() && k->get_scancode() == KEY_INSERT) {
  1508. k->set_scancode(KEY_V);
  1509. k->set_command(true);
  1510. k->set_shift(false);
  1511. }
  1512. if (!k->get_command()) {
  1513. _reset_caret_blink_timer();
  1514. }
  1515. // save here for insert mode, just in case it is cleared in the following section
  1516. bool had_selection = selection.active;
  1517. // stuff to do when selection is active..
  1518. if (selection.active) {
  1519. if (readonly)
  1520. return;
  1521. bool clear = false;
  1522. bool unselect = false;
  1523. bool dobreak = false;
  1524. switch (k->get_scancode()) {
  1525. case KEY_TAB: {
  1526. if (k->get_shift()) {
  1527. indent_selection_left();
  1528. } else {
  1529. indent_selection_right();
  1530. }
  1531. dobreak = true;
  1532. accept_event();
  1533. } break;
  1534. case KEY_X:
  1535. case KEY_C:
  1536. //special keys often used with control, wait...
  1537. clear = (!k->get_command() || k->get_shift() || k->get_alt());
  1538. break;
  1539. case KEY_DELETE:
  1540. if (!k->get_shift()) {
  1541. accept_event();
  1542. clear = true;
  1543. dobreak = true;
  1544. } else if (k->get_command() || k->get_alt()) {
  1545. dobreak = true;
  1546. }
  1547. break;
  1548. case KEY_BACKSPACE:
  1549. accept_event();
  1550. clear = true;
  1551. dobreak = true;
  1552. break;
  1553. case KEY_LEFT:
  1554. case KEY_RIGHT:
  1555. case KEY_UP:
  1556. case KEY_DOWN:
  1557. case KEY_PAGEUP:
  1558. case KEY_PAGEDOWN:
  1559. case KEY_HOME:
  1560. case KEY_END:
  1561. // ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys)
  1562. if (k->get_command() || k->get_shift() || k->get_alt())
  1563. break;
  1564. unselect = true;
  1565. break;
  1566. default:
  1567. if (k->get_unicode() >= 32 && !k->get_command() && !k->get_alt() && !k->get_metakey())
  1568. clear = true;
  1569. if (auto_brace_completion_enabled && _is_pair_left_symbol(k->get_unicode()))
  1570. clear = false;
  1571. }
  1572. if (unselect) {
  1573. selection.active = false;
  1574. selection.selecting_mode = Selection::MODE_NONE;
  1575. update();
  1576. }
  1577. if (clear) {
  1578. if (!dobreak) {
  1579. begin_complex_operation();
  1580. }
  1581. selection.active = false;
  1582. update();
  1583. _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
  1584. cursor_set_line(selection.from_line);
  1585. cursor_set_column(selection.from_column);
  1586. update();
  1587. }
  1588. if (dobreak)
  1589. return;
  1590. }
  1591. selection.selecting_text = false;
  1592. bool scancode_handled = true;
  1593. // special scancode test...
  1594. switch (k->get_scancode()) {
  1595. case KEY_KP_ENTER:
  1596. case KEY_ENTER: {
  1597. if (readonly)
  1598. break;
  1599. String ins = "\n";
  1600. //keep indentation
  1601. int space_count = 0;
  1602. for (int i = 0; i < text[cursor.line].length(); i++) {
  1603. if (text[cursor.line][i] == '\t') {
  1604. if (indent_using_spaces) {
  1605. ins += space_indent;
  1606. } else {
  1607. ins += "\t";
  1608. }
  1609. space_count = 0;
  1610. } else if (text[cursor.line][i] == ' ') {
  1611. space_count++;
  1612. if (space_count == indent_size) {
  1613. if (indent_using_spaces) {
  1614. ins += space_indent;
  1615. } else {
  1616. ins += "\t";
  1617. }
  1618. space_count = 0;
  1619. }
  1620. } else {
  1621. break;
  1622. }
  1623. }
  1624. if (auto_indent) {
  1625. // indent once again if previous line will end with ':'
  1626. // (i.e. colon precedes current cursor position)
  1627. if (cursor.column > 0 && text[cursor.line][cursor.column - 1] == ':') {
  1628. if (indent_using_spaces) {
  1629. ins += space_indent;
  1630. } else {
  1631. ins += "\t";
  1632. }
  1633. }
  1634. }
  1635. bool first_line = false;
  1636. if (k->get_command()) {
  1637. if (k->get_shift()) {
  1638. if (cursor.line > 0) {
  1639. cursor_set_line(cursor.line - 1);
  1640. cursor_set_column(text[cursor.line].length());
  1641. } else {
  1642. cursor_set_column(0);
  1643. first_line = true;
  1644. }
  1645. } else {
  1646. cursor_set_column(text[cursor.line].length());
  1647. }
  1648. }
  1649. _insert_text_at_cursor(ins);
  1650. _push_current_op();
  1651. if (first_line) {
  1652. cursor_set_line(0);
  1653. }
  1654. } break;
  1655. case KEY_ESCAPE: {
  1656. if (completion_hint != "") {
  1657. completion_hint = "";
  1658. update();
  1659. } else {
  1660. scancode_handled = false;
  1661. }
  1662. } break;
  1663. case KEY_TAB: {
  1664. if (k->get_command()) break; // avoid tab when command
  1665. if (readonly)
  1666. break;
  1667. if (selection.active) {
  1668. } else {
  1669. if (k->get_shift()) {
  1670. //simple unindent
  1671. int cc = cursor.column;
  1672. if (cc > 0 && cc <= text[cursor.line].length()) {
  1673. if (text[cursor.line][cursor.column - 1] == '\t') {
  1674. backspace_at_cursor();
  1675. } else {
  1676. if (cursor.column - indent_size >= 0) {
  1677. bool unindent = true;
  1678. for (int i = 1; i <= indent_size; i++) {
  1679. if (text[cursor.line][cursor.column - i] != ' ') {
  1680. unindent = false;
  1681. break;
  1682. }
  1683. }
  1684. if (unindent) {
  1685. _remove_text(cursor.line, cursor.column - indent_size, cursor.line, cursor.column);
  1686. cursor_set_column(cursor.column - indent_size);
  1687. }
  1688. }
  1689. }
  1690. }
  1691. } else {
  1692. //simple indent
  1693. if (indent_using_spaces) {
  1694. _insert_text_at_cursor(space_indent);
  1695. } else {
  1696. _insert_text_at_cursor("\t");
  1697. }
  1698. }
  1699. }
  1700. } break;
  1701. case KEY_BACKSPACE: {
  1702. if (readonly)
  1703. break;
  1704. #ifdef APPLE_STYLE_KEYS
  1705. if (k->get_alt() && cursor.column > 1) {
  1706. #else
  1707. if (k->get_alt()) {
  1708. scancode_handled = false;
  1709. break;
  1710. } else if (k->get_command() && cursor.column > 1) {
  1711. #endif
  1712. int line = cursor.line;
  1713. int column = cursor.column;
  1714. // check if we are removing a single whitespace, if so remove it and the next char type
  1715. // else we just remove the whitespace
  1716. bool only_whitespace = false;
  1717. if (_is_whitespace(text[line][column - 1]) && _is_whitespace(text[line][column - 2])) {
  1718. only_whitespace = true;
  1719. } else if (_is_whitespace(text[line][column - 1])) {
  1720. // remove the single whitespace
  1721. column--;
  1722. }
  1723. // check if its a text char
  1724. bool only_char = (_is_text_char(text[line][column - 1]) && !only_whitespace);
  1725. // if its not whitespace or char then symbol.
  1726. bool only_symbols = !(only_whitespace || only_char);
  1727. while (column > 0) {
  1728. bool is_whitespace = _is_whitespace(text[line][column - 1]);
  1729. bool is_text_char = _is_text_char(text[line][column - 1]);
  1730. if (only_whitespace && !is_whitespace) {
  1731. break;
  1732. } else if (only_char && !is_text_char) {
  1733. break;
  1734. } else if (only_symbols && (is_whitespace || is_text_char)) {
  1735. break;
  1736. }
  1737. column--;
  1738. }
  1739. _remove_text(line, column, cursor.line, cursor.column);
  1740. cursor_set_line(line);
  1741. cursor_set_column(column);
  1742. } else {
  1743. backspace_at_cursor();
  1744. }
  1745. } break;
  1746. case KEY_KP_4: {
  1747. if (k->get_unicode() != 0) {
  1748. scancode_handled = false;
  1749. break;
  1750. }
  1751. // numlock disabled. fallthrough to key_left
  1752. }
  1753. case KEY_LEFT: {
  1754. if (k->get_shift())
  1755. _pre_shift_selection();
  1756. #ifdef APPLE_STYLE_KEYS
  1757. else
  1758. #else
  1759. else if (!k->get_alt())
  1760. #endif
  1761. deselect();
  1762. #ifdef APPLE_STYLE_KEYS
  1763. if (k->get_command()) {
  1764. cursor_set_column(0);
  1765. } else if (k->get_alt()) {
  1766. #else
  1767. if (k->get_alt()) {
  1768. scancode_handled = false;
  1769. break;
  1770. } else if (k->get_command()) {
  1771. #endif
  1772. bool prev_char = false;
  1773. int cc = cursor.column;
  1774. while (cc > 0) {
  1775. bool ischar = _is_text_char(text[cursor.line][cc - 1]);
  1776. if (prev_char && !ischar)
  1777. break;
  1778. prev_char = ischar;
  1779. cc--;
  1780. }
  1781. cursor_set_column(cc);
  1782. } else if (cursor.column == 0) {
  1783. if (cursor.line > 0) {
  1784. cursor_set_line(cursor.line - 1);
  1785. cursor_set_column(text[cursor.line].length());
  1786. }
  1787. } else {
  1788. cursor_set_column(cursor_get_column() - 1);
  1789. }
  1790. if (k->get_shift())
  1791. _post_shift_selection();
  1792. } break;
  1793. case KEY_KP_6: {
  1794. if (k->get_unicode() != 0) {
  1795. scancode_handled = false;
  1796. break;
  1797. }
  1798. // numlock disabled. fallthrough to key_right
  1799. }
  1800. case KEY_RIGHT: {
  1801. if (k->get_shift())
  1802. _pre_shift_selection();
  1803. #ifdef APPLE_STYLE_KEYS
  1804. else
  1805. #else
  1806. else if (!k->get_alt())
  1807. #endif
  1808. deselect();
  1809. #ifdef APPLE_STYLE_KEYS
  1810. if (k->get_command()) {
  1811. cursor_set_column(text[cursor.line].length());
  1812. } else if (k->get_alt()) {
  1813. #else
  1814. if (k->get_alt()) {
  1815. scancode_handled = false;
  1816. break;
  1817. } else if (k->get_command()) {
  1818. #endif
  1819. bool prev_char = false;
  1820. int cc = cursor.column;
  1821. while (cc < text[cursor.line].length()) {
  1822. bool ischar = _is_text_char(text[cursor.line][cc]);
  1823. if (prev_char && !ischar)
  1824. break;
  1825. prev_char = ischar;
  1826. cc++;
  1827. }
  1828. cursor_set_column(cc);
  1829. } else if (cursor.column == text[cursor.line].length()) {
  1830. if (cursor.line < text.size() - 1) {
  1831. cursor_set_line(cursor.line + 1);
  1832. cursor_set_column(0);
  1833. }
  1834. } else {
  1835. cursor_set_column(cursor_get_column() + 1);
  1836. }
  1837. if (k->get_shift())
  1838. _post_shift_selection();
  1839. } break;
  1840. case KEY_KP_8: {
  1841. if (k->get_unicode() != 0) {
  1842. scancode_handled = false;
  1843. break;
  1844. }
  1845. // numlock disabled. fallthrough to key_up
  1846. }
  1847. case KEY_UP: {
  1848. if (k->get_shift())
  1849. _pre_shift_selection();
  1850. if (k->get_alt()) {
  1851. scancode_handled = false;
  1852. break;
  1853. }
  1854. #ifndef APPLE_STYLE_KEYS
  1855. if (k->get_command()) {
  1856. _scroll_lines_up();
  1857. break;
  1858. }
  1859. #else
  1860. if (k->get_command() && k->get_alt()) {
  1861. _scroll_lines_up();
  1862. break;
  1863. }
  1864. if (k->get_command())
  1865. cursor_set_line(0);
  1866. else
  1867. #endif
  1868. cursor_set_line(cursor_get_line() - 1);
  1869. if (k->get_shift())
  1870. _post_shift_selection();
  1871. _cancel_code_hint();
  1872. } break;
  1873. case KEY_KP_2: {
  1874. if (k->get_unicode() != 0) {
  1875. scancode_handled = false;
  1876. break;
  1877. }
  1878. // numlock disabled. fallthrough to key_down
  1879. }
  1880. case KEY_DOWN: {
  1881. if (k->get_shift())
  1882. _pre_shift_selection();
  1883. if (k->get_alt()) {
  1884. scancode_handled = false;
  1885. break;
  1886. }
  1887. #ifndef APPLE_STYLE_KEYS
  1888. if (k->get_command()) {
  1889. _scroll_lines_down();
  1890. break;
  1891. }
  1892. #else
  1893. if (k->get_command() && k->get_alt()) {
  1894. _scroll_lines_down();
  1895. break;
  1896. }
  1897. if (k->get_command())
  1898. cursor_set_line(text.size() - 1);
  1899. else
  1900. #endif
  1901. cursor_set_line(cursor_get_line() + 1);
  1902. if (k->get_shift())
  1903. _post_shift_selection();
  1904. _cancel_code_hint();
  1905. } break;
  1906. case KEY_DELETE: {
  1907. if (readonly)
  1908. break;
  1909. if (k->get_shift() && !k->get_command() && !k->get_alt()) {
  1910. cut();
  1911. break;
  1912. }
  1913. int curline_len = text[cursor.line].length();
  1914. if (cursor.line == text.size() - 1 && cursor.column == curline_len)
  1915. break; //nothing to do
  1916. int next_line = cursor.column < curline_len ? cursor.line : cursor.line + 1;
  1917. int next_column;
  1918. #ifdef APPLE_STYLE_KEYS
  1919. if (k->get_alt() && cursor.column < curline_len - 1) {
  1920. #else
  1921. if (k->get_alt()) {
  1922. scancode_handled = false;
  1923. break;
  1924. } else if (k->get_command() && cursor.column < curline_len - 1) {
  1925. #endif
  1926. int line = cursor.line;
  1927. int column = cursor.column;
  1928. // check if we are removing a single whitespace, if so remove it and the next char type
  1929. // else we just remove the whitespace
  1930. bool only_whitespace = false;
  1931. if (_is_whitespace(text[line][column]) && _is_whitespace(text[line][column + 1])) {
  1932. only_whitespace = true;
  1933. } else if (_is_whitespace(text[line][column])) {
  1934. // remove the single whitespace
  1935. column++;
  1936. }
  1937. // check if its a text char
  1938. bool only_char = (_is_text_char(text[line][column]) && !only_whitespace);
  1939. // if its not whitespace or char then symbol.
  1940. bool only_symbols = !(only_whitespace || only_char);
  1941. while (column < curline_len) {
  1942. bool is_whitespace = _is_whitespace(text[line][column]);
  1943. bool is_text_char = _is_text_char(text[line][column]);
  1944. if (only_whitespace && !is_whitespace) {
  1945. break;
  1946. } else if (only_char && !is_text_char) {
  1947. break;
  1948. } else if (only_symbols && (is_whitespace || is_text_char)) {
  1949. break;
  1950. }
  1951. column++;
  1952. }
  1953. next_line = line;
  1954. next_column = column;
  1955. } else {
  1956. next_column = cursor.column < curline_len ? (cursor.column + 1) : 0;
  1957. }
  1958. _remove_text(cursor.line, cursor.column, next_line, next_column);
  1959. update();
  1960. } break;
  1961. case KEY_KP_7: {
  1962. if (k->get_unicode() != 0) {
  1963. scancode_handled = false;
  1964. break;
  1965. }
  1966. // numlock disabled. fallthrough to key_home
  1967. }
  1968. #ifdef APPLE_STYLE_KEYS
  1969. case KEY_HOME: {
  1970. if (k->get_shift())
  1971. _pre_shift_selection();
  1972. cursor_set_line(0);
  1973. if (k->get_shift())
  1974. _post_shift_selection();
  1975. else if (k->get_command() || k->get_control())
  1976. deselect();
  1977. } break;
  1978. #else
  1979. case KEY_HOME: {
  1980. if (k->get_shift())
  1981. _pre_shift_selection();
  1982. if (k->get_command()) {
  1983. cursor_set_line(0);
  1984. cursor_set_column(0);
  1985. } else {
  1986. // compute whitespace symbols seq length
  1987. int current_line_whitespace_len = 0;
  1988. while (current_line_whitespace_len < text[cursor.line].length()) {
  1989. CharType c = text[cursor.line][current_line_whitespace_len];
  1990. if (c != '\t' && c != ' ')
  1991. break;
  1992. current_line_whitespace_len++;
  1993. }
  1994. if (cursor_get_column() == current_line_whitespace_len)
  1995. cursor_set_column(0);
  1996. else
  1997. cursor_set_column(current_line_whitespace_len);
  1998. }
  1999. if (k->get_shift())
  2000. _post_shift_selection();
  2001. else if (k->get_command() || k->get_control())
  2002. deselect();
  2003. _cancel_completion();
  2004. completion_hint = "";
  2005. } break;
  2006. #endif
  2007. case KEY_KP_1: {
  2008. if (k->get_unicode() != 0) {
  2009. scancode_handled = false;
  2010. break;
  2011. }
  2012. // numlock disabled. fallthrough to key_end
  2013. }
  2014. #ifdef APPLE_STYLE_KEYS
  2015. case KEY_END: {
  2016. if (k->get_shift())
  2017. _pre_shift_selection();
  2018. cursor_set_line(text.size() - 1);
  2019. if (k->get_shift())
  2020. _post_shift_selection();
  2021. else if (k->get_command() || k->get_control())
  2022. deselect();
  2023. } break;
  2024. #else
  2025. case KEY_END: {
  2026. if (k->get_shift())
  2027. _pre_shift_selection();
  2028. if (k->get_command())
  2029. cursor_set_line(text.size() - 1);
  2030. cursor_set_column(text[cursor.line].length());
  2031. if (k->get_shift())
  2032. _post_shift_selection();
  2033. else if (k->get_command() || k->get_control())
  2034. deselect();
  2035. _cancel_completion();
  2036. completion_hint = "";
  2037. } break;
  2038. #endif
  2039. case KEY_KP_9: {
  2040. if (k->get_unicode() != 0) {
  2041. scancode_handled = false;
  2042. break;
  2043. }
  2044. // numlock disabled. fallthrough to key_pageup
  2045. }
  2046. case KEY_PAGEUP: {
  2047. if (k->get_shift())
  2048. _pre_shift_selection();
  2049. cursor_set_line(cursor_get_line() - get_visible_rows());
  2050. if (k->get_shift())
  2051. _post_shift_selection();
  2052. _cancel_completion();
  2053. completion_hint = "";
  2054. } break;
  2055. case KEY_KP_3: {
  2056. if (k->get_unicode() != 0) {
  2057. scancode_handled = false;
  2058. break;
  2059. }
  2060. // numlock disabled. fallthrough to key_pageup
  2061. }
  2062. case KEY_PAGEDOWN: {
  2063. if (k->get_shift())
  2064. _pre_shift_selection();
  2065. cursor_set_line(cursor_get_line() + get_visible_rows());
  2066. if (k->get_shift())
  2067. _post_shift_selection();
  2068. _cancel_completion();
  2069. completion_hint = "";
  2070. } break;
  2071. case KEY_A: {
  2072. if (!k->get_command() || k->get_shift() || k->get_alt()) {
  2073. scancode_handled = false;
  2074. break;
  2075. }
  2076. select_all();
  2077. } break;
  2078. case KEY_X: {
  2079. if (readonly) {
  2080. break;
  2081. }
  2082. if (!k->get_command() || k->get_shift() || k->get_alt()) {
  2083. scancode_handled = false;
  2084. break;
  2085. }
  2086. cut();
  2087. } break;
  2088. case KEY_C: {
  2089. if (!k->get_command() || k->get_shift() || k->get_alt()) {
  2090. scancode_handled = false;
  2091. break;
  2092. }
  2093. copy();
  2094. } break;
  2095. case KEY_Z: {
  2096. if (!k->get_command()) {
  2097. scancode_handled = false;
  2098. break;
  2099. }
  2100. if (k->get_shift())
  2101. redo();
  2102. else
  2103. undo();
  2104. } break;
  2105. case KEY_V: {
  2106. if (readonly) {
  2107. break;
  2108. }
  2109. if (!k->get_command() || k->get_shift() || k->get_alt()) {
  2110. scancode_handled = false;
  2111. break;
  2112. }
  2113. paste();
  2114. } break;
  2115. case KEY_SPACE: {
  2116. #ifdef OSX_ENABLED
  2117. if (completion_enabled && k->get_metakey()) { //cmd-space is spotlight shortcut in OSX
  2118. #else
  2119. if (completion_enabled && k->get_command()) {
  2120. #endif
  2121. query_code_comple();
  2122. scancode_handled = true;
  2123. } else {
  2124. scancode_handled = false;
  2125. }
  2126. } break;
  2127. case KEY_U: {
  2128. if (!k->get_command() || k->get_shift()) {
  2129. scancode_handled = false;
  2130. break;
  2131. } else {
  2132. if (selection.active) {
  2133. int ini = selection.from_line;
  2134. int end = selection.to_line;
  2135. for (int i = ini; i <= end; i++) {
  2136. if (text[i][0] == '#')
  2137. _remove_text(i, 0, i, 1);
  2138. }
  2139. } else {
  2140. if (text[cursor.line][0] == '#')
  2141. _remove_text(cursor.line, 0, cursor.line, 1);
  2142. }
  2143. update();
  2144. }
  2145. } break;
  2146. default: {
  2147. scancode_handled = false;
  2148. } break;
  2149. }
  2150. if (scancode_handled)
  2151. accept_event();
  2152. /*
  2153. if (!scancode_handled && !k->get_command() && !k->get_alt()) {
  2154. if (k->get_unicode()>=32) {
  2155. if (readonly)
  2156. break;
  2157. accept_event();
  2158. } else {
  2159. break;
  2160. }
  2161. }
  2162. */
  2163. if (k->get_scancode() == KEY_INSERT) {
  2164. set_insert_mode(!insert_mode);
  2165. accept_event();
  2166. return;
  2167. }
  2168. if (!scancode_handled && !k->get_command()) { //for german kbds
  2169. if (k->get_unicode() >= 32) {
  2170. if (readonly)
  2171. return;
  2172. // remove the old character if in insert mode and no selection
  2173. if (insert_mode && !had_selection) {
  2174. begin_complex_operation();
  2175. // make sure we don't try and remove empty space
  2176. if (cursor.column < get_line(cursor.line).length()) {
  2177. _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
  2178. }
  2179. }
  2180. const CharType chr[2] = { (CharType)k->get_unicode(), 0 };
  2181. if (completion_hint != "" && k->get_unicode() == ')') {
  2182. completion_hint = "";
  2183. }
  2184. if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
  2185. _consume_pair_symbol(chr[0]);
  2186. } else {
  2187. _insert_text_at_cursor(chr);
  2188. }
  2189. if (insert_mode && !had_selection) {
  2190. end_complex_operation();
  2191. }
  2192. if (selection.active != had_selection) {
  2193. end_complex_operation();
  2194. }
  2195. accept_event();
  2196. } else {
  2197. }
  2198. }
  2199. return;
  2200. }
  2201. }
  2202. void TextEdit::_pre_shift_selection() {
  2203. if (!selection.active || selection.selecting_mode == Selection::MODE_NONE) {
  2204. selection.selecting_line = cursor.line;
  2205. selection.selecting_column = cursor.column;
  2206. selection.active = true;
  2207. }
  2208. selection.selecting_mode = Selection::MODE_SHIFT;
  2209. }
  2210. void TextEdit::_post_shift_selection() {
  2211. if (selection.active && selection.selecting_mode == Selection::MODE_SHIFT) {
  2212. select(selection.selecting_line, selection.selecting_column, cursor.line, cursor.column);
  2213. update();
  2214. }
  2215. selection.selecting_text = true;
  2216. }
  2217. void TextEdit::_scroll_lines_up() {
  2218. // adjust the vertical scroll
  2219. if (get_v_scroll() > 0) {
  2220. set_v_scroll(get_v_scroll() - 1);
  2221. }
  2222. // adjust the cursor
  2223. if (cursor_get_line() >= (get_visible_rows() + get_v_scroll()) && !selection.active) {
  2224. cursor_set_line((get_visible_rows() + get_v_scroll()) - 1, false);
  2225. }
  2226. }
  2227. void TextEdit::_scroll_lines_down() {
  2228. // calculate the maximum vertical scroll position
  2229. int max_v_scroll = get_line_count() - 1;
  2230. if (!scroll_past_end_of_file_enabled) {
  2231. max_v_scroll -= get_visible_rows() - 1;
  2232. }
  2233. // adjust the vertical scroll
  2234. if (get_v_scroll() < max_v_scroll) {
  2235. set_v_scroll(get_v_scroll() + 1);
  2236. }
  2237. // adjust the cursor
  2238. if ((cursor_get_line()) <= get_v_scroll() - 1 && !selection.active) {
  2239. cursor_set_line(get_v_scroll(), false);
  2240. }
  2241. }
  2242. /**** TEXT EDIT CORE API ****/
  2243. void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, int &r_end_line, int &r_end_column) {
  2244. //save for undo...
  2245. ERR_FAIL_INDEX(p_line, text.size());
  2246. ERR_FAIL_COND(p_char < 0);
  2247. /* STEP 1 add spaces if the char is greater than the end of the line */
  2248. while (p_char > text[p_line].length()) {
  2249. text.set(p_line, text[p_line] + String::chr(' '));
  2250. }
  2251. /* STEP 2 separate dest string in pre and post text */
  2252. String preinsert_text = text[p_line].substr(0, p_char);
  2253. String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
  2254. /* STEP 3 remove \r from source text and separate in substrings */
  2255. //buh bye \r and split
  2256. Vector<String> substrings = p_text.replace("\r", "").split("\n");
  2257. for (int i = 0; i < substrings.size(); i++) {
  2258. //insert the substrings
  2259. if (i == 0) {
  2260. text.set(p_line, preinsert_text + substrings[i]);
  2261. } else {
  2262. text.insert(p_line + i, substrings[i]);
  2263. }
  2264. if (i == substrings.size() - 1) {
  2265. text.set(p_line + i, text[p_line + i] + postinsert_text);
  2266. }
  2267. }
  2268. r_end_line = p_line + substrings.size() - 1;
  2269. r_end_column = text[r_end_line].length() - postinsert_text.length();
  2270. if (!text_changed_dirty && !setting_text) {
  2271. if (is_inside_tree())
  2272. MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
  2273. text_changed_dirty = true;
  2274. }
  2275. }
  2276. String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const {
  2277. ERR_FAIL_INDEX_V(p_from_line, text.size(), String());
  2278. ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, String());
  2279. ERR_FAIL_INDEX_V(p_to_line, text.size(), String());
  2280. ERR_FAIL_INDEX_V(p_to_column, text[p_to_line].length() + 1, String());
  2281. ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // from > to
  2282. ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // from > to
  2283. String ret;
  2284. for (int i = p_from_line; i <= p_to_line; i++) {
  2285. int begin = (i == p_from_line) ? p_from_column : 0;
  2286. int end = (i == p_to_line) ? p_to_column : text[i].length();
  2287. if (i > p_from_line)
  2288. ret += "\n";
  2289. ret += text[i].substr(begin, end - begin);
  2290. }
  2291. return ret;
  2292. }
  2293. void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
  2294. ERR_FAIL_INDEX(p_from_line, text.size());
  2295. ERR_FAIL_INDEX(p_from_column, text[p_from_line].length() + 1);
  2296. ERR_FAIL_INDEX(p_to_line, text.size());
  2297. ERR_FAIL_INDEX(p_to_column, text[p_to_line].length() + 1);
  2298. ERR_FAIL_COND(p_to_line < p_from_line); // from > to
  2299. ERR_FAIL_COND(p_to_line == p_from_line && p_to_column < p_from_column); // from > to
  2300. String pre_text = text[p_from_line].substr(0, p_from_column);
  2301. String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
  2302. for (int i = p_from_line; i < p_to_line; i++) {
  2303. text.remove(p_from_line + 1);
  2304. }
  2305. text.set(p_from_line, pre_text + post_text);
  2306. if (!text_changed_dirty && !setting_text) {
  2307. if (is_inside_tree())
  2308. MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
  2309. text_changed_dirty = true;
  2310. }
  2311. }
  2312. void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) {
  2313. if (!setting_text)
  2314. idle_detect->start();
  2315. if (undo_enabled) {
  2316. _clear_redo();
  2317. }
  2318. int retline, retchar;
  2319. _base_insert_text(p_line, p_char, p_text, retline, retchar);
  2320. if (r_end_line)
  2321. *r_end_line = retline;
  2322. if (r_end_char)
  2323. *r_end_char = retchar;
  2324. if (!undo_enabled)
  2325. return;
  2326. /* UNDO!! */
  2327. TextOperation op;
  2328. op.type = TextOperation::TYPE_INSERT;
  2329. op.from_line = p_line;
  2330. op.from_column = p_char;
  2331. op.to_line = retline;
  2332. op.to_column = retchar;
  2333. op.text = p_text;
  2334. op.version = ++version;
  2335. op.chain_forward = false;
  2336. op.chain_backward = false;
  2337. //see if it shold just be set as current op
  2338. if (current_op.type != op.type) {
  2339. op.prev_version = get_version();
  2340. _push_current_op();
  2341. current_op = op;
  2342. return; //set as current op, return
  2343. }
  2344. //see if it can be merged
  2345. if (current_op.to_line != p_line || current_op.to_column != p_char) {
  2346. op.prev_version = get_version();
  2347. _push_current_op();
  2348. current_op = op;
  2349. return; //set as current op, return
  2350. }
  2351. //merge current op
  2352. current_op.text += p_text;
  2353. current_op.to_column = retchar;
  2354. current_op.to_line = retline;
  2355. current_op.version = op.version;
  2356. }
  2357. void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
  2358. if (!setting_text)
  2359. idle_detect->start();
  2360. String text;
  2361. if (undo_enabled) {
  2362. _clear_redo();
  2363. text = _base_get_text(p_from_line, p_from_column, p_to_line, p_to_column);
  2364. }
  2365. _base_remove_text(p_from_line, p_from_column, p_to_line, p_to_column);
  2366. if (!undo_enabled)
  2367. return;
  2368. /* UNDO!! */
  2369. TextOperation op;
  2370. op.type = TextOperation::TYPE_REMOVE;
  2371. op.from_line = p_from_line;
  2372. op.from_column = p_from_column;
  2373. op.to_line = p_to_line;
  2374. op.to_column = p_to_column;
  2375. op.text = text;
  2376. op.version = ++version;
  2377. op.chain_forward = false;
  2378. op.chain_backward = false;
  2379. //see if it shold just be set as current op
  2380. if (current_op.type != op.type) {
  2381. op.prev_version = get_version();
  2382. _push_current_op();
  2383. current_op = op;
  2384. return; //set as current op, return
  2385. }
  2386. //see if it can be merged
  2387. if (current_op.from_line == p_to_line && current_op.from_column == p_to_column) {
  2388. //basckace or similar
  2389. current_op.text = text + current_op.text;
  2390. current_op.from_line = p_from_line;
  2391. current_op.from_column = p_from_column;
  2392. return; //update current op
  2393. }
  2394. if (current_op.from_line == p_from_line && current_op.from_column == p_from_column) {
  2395. //current_op.text=text+current_op.text;
  2396. //current_op.from_line=p_from_line;
  2397. //current_op.from_column=p_from_column;
  2398. //return; //update current op
  2399. }
  2400. op.prev_version = get_version();
  2401. _push_current_op();
  2402. current_op = op;
  2403. }
  2404. void TextEdit::_insert_text_at_cursor(const String &p_text) {
  2405. int new_column, new_line;
  2406. _insert_text(cursor.line, cursor.column, p_text, &new_line, &new_column);
  2407. cursor_set_line(new_line);
  2408. cursor_set_column(new_column);
  2409. update();
  2410. }
  2411. int TextEdit::get_char_count() {
  2412. int totalsize = 0;
  2413. for (int i = 0; i < text.size(); i++) {
  2414. if (i > 0)
  2415. totalsize++; // incliude \n
  2416. totalsize += text[i].length();
  2417. }
  2418. return totalsize; // omit last \n
  2419. }
  2420. Size2 TextEdit::get_minimum_size() const {
  2421. return cache.style_normal->get_minimum_size();
  2422. }
  2423. int TextEdit::get_visible_rows() const {
  2424. int total = cache.size.height;
  2425. total -= cache.style_normal->get_minimum_size().height;
  2426. total /= get_row_height();
  2427. return total;
  2428. }
  2429. void TextEdit::adjust_viewport_to_cursor() {
  2430. if (cursor.line_ofs > cursor.line)
  2431. cursor.line_ofs = cursor.line;
  2432. int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width;
  2433. if (v_scroll->is_visible_in_tree())
  2434. visible_width -= v_scroll->get_combined_minimum_size().width;
  2435. visible_width -= 20; // give it a little more space
  2436. //printf("rowofs %i, visrows %i, cursor.line %i\n",cursor.line_ofs,get_visible_rows(),cursor.line);
  2437. int visible_rows = get_visible_rows();
  2438. if (h_scroll->is_visible_in_tree())
  2439. visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height());
  2440. if (cursor.line >= (cursor.line_ofs + visible_rows))
  2441. cursor.line_ofs = cursor.line - visible_rows + 1;
  2442. if (cursor.line < cursor.line_ofs)
  2443. cursor.line_ofs = cursor.line;
  2444. int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
  2445. if (cursor_x > (cursor.x_ofs + visible_width))
  2446. cursor.x_ofs = cursor_x - visible_width + 1;
  2447. if (cursor_x < cursor.x_ofs)
  2448. cursor.x_ofs = cursor_x;
  2449. update();
  2450. /*
  2451. get_range()->set_max(text.size());
  2452. get_range()->set_page(get_visible_rows());
  2453. get_range()->set((int)cursor.line_ofs);
  2454. */
  2455. }
  2456. void TextEdit::center_viewport_to_cursor() {
  2457. if (cursor.line_ofs > cursor.line)
  2458. cursor.line_ofs = cursor.line;
  2459. int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width;
  2460. if (v_scroll->is_visible_in_tree())
  2461. visible_width -= v_scroll->get_combined_minimum_size().width;
  2462. visible_width -= 20; // give it a little more space
  2463. int visible_rows = get_visible_rows();
  2464. if (h_scroll->is_visible_in_tree())
  2465. visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height());
  2466. int max_ofs = text.size() - (scroll_past_end_of_file_enabled ? 1 : visible_rows);
  2467. cursor.line_ofs = CLAMP(cursor.line - (visible_rows / 2), 0, max_ofs);
  2468. int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
  2469. if (cursor_x > (cursor.x_ofs + visible_width))
  2470. cursor.x_ofs = cursor_x - visible_width + 1;
  2471. if (cursor_x < cursor.x_ofs)
  2472. cursor.x_ofs = cursor_x;
  2473. update();
  2474. }
  2475. void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) {
  2476. if (p_col < 0)
  2477. p_col = 0;
  2478. cursor.column = p_col;
  2479. if (cursor.column > get_line(cursor.line).length())
  2480. cursor.column = get_line(cursor.line).length();
  2481. cursor.last_fit_x = get_column_x_offset(cursor.column, get_line(cursor.line));
  2482. if (p_adjust_viewport)
  2483. adjust_viewport_to_cursor();
  2484. if (!cursor_changed_dirty) {
  2485. if (is_inside_tree())
  2486. MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit");
  2487. cursor_changed_dirty = true;
  2488. }
  2489. }
  2490. void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport) {
  2491. if (setting_row)
  2492. return;
  2493. setting_row = true;
  2494. if (p_row < 0)
  2495. p_row = 0;
  2496. if (p_row >= (int)text.size())
  2497. p_row = (int)text.size() - 1;
  2498. cursor.line = p_row;
  2499. cursor.column = get_char_pos_for(cursor.last_fit_x, get_line(cursor.line));
  2500. if (p_adjust_viewport)
  2501. adjust_viewport_to_cursor();
  2502. setting_row = false;
  2503. if (!cursor_changed_dirty) {
  2504. if (is_inside_tree())
  2505. MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit");
  2506. cursor_changed_dirty = true;
  2507. }
  2508. }
  2509. int TextEdit::cursor_get_column() const {
  2510. return cursor.column;
  2511. }
  2512. int TextEdit::cursor_get_line() const {
  2513. return cursor.line;
  2514. }
  2515. bool TextEdit::cursor_get_blink_enabled() const {
  2516. return caret_blink_enabled;
  2517. }
  2518. void TextEdit::cursor_set_blink_enabled(const bool p_enabled) {
  2519. caret_blink_enabled = p_enabled;
  2520. if (p_enabled) {
  2521. caret_blink_timer->start();
  2522. } else {
  2523. caret_blink_timer->stop();
  2524. }
  2525. draw_caret = true;
  2526. }
  2527. float TextEdit::cursor_get_blink_speed() const {
  2528. return caret_blink_timer->get_wait_time();
  2529. }
  2530. void TextEdit::cursor_set_blink_speed(const float p_speed) {
  2531. ERR_FAIL_COND(p_speed <= 0);
  2532. caret_blink_timer->set_wait_time(p_speed);
  2533. }
  2534. void TextEdit::cursor_set_block_mode(const bool p_enable) {
  2535. block_caret = p_enable;
  2536. update();
  2537. }
  2538. bool TextEdit::cursor_is_block_mode() const {
  2539. return block_caret;
  2540. }
  2541. void TextEdit::_scroll_moved(double p_to_val) {
  2542. if (updating_scrolls)
  2543. return;
  2544. if (h_scroll->is_visible_in_tree())
  2545. cursor.x_ofs = h_scroll->get_value();
  2546. if (v_scroll->is_visible_in_tree())
  2547. cursor.line_ofs = v_scroll->get_value();
  2548. update();
  2549. }
  2550. int TextEdit::get_row_height() const {
  2551. return cache.font->get_height() + cache.line_spacing;
  2552. }
  2553. int TextEdit::get_char_pos_for(int p_px, String p_str) const {
  2554. int px = 0;
  2555. int c = 0;
  2556. int tab_w = cache.font->get_char_size(' ').width * indent_size;
  2557. while (c < p_str.length()) {
  2558. int w = 0;
  2559. if (p_str[c] == '\t') {
  2560. int left = px % tab_w;
  2561. if (left == 0)
  2562. w = tab_w;
  2563. else
  2564. w = tab_w - px % tab_w; // is right...
  2565. } else {
  2566. w = cache.font->get_char_size(p_str[c], p_str[c + 1]).width;
  2567. }
  2568. if (p_px < (px + w / 2))
  2569. break;
  2570. px += w;
  2571. c++;
  2572. }
  2573. return c;
  2574. }
  2575. int TextEdit::get_column_x_offset(int p_char, String p_str) {
  2576. int px = 0;
  2577. int tab_w = cache.font->get_char_size(' ').width * indent_size;
  2578. for (int i = 0; i < p_char; i++) {
  2579. if (i >= p_str.length())
  2580. break;
  2581. if (p_str[i] == '\t') {
  2582. int left = px % tab_w;
  2583. if (left == 0)
  2584. px += tab_w;
  2585. else
  2586. px += tab_w - px % tab_w; // is right...
  2587. } else {
  2588. px += cache.font->get_char_size(p_str[i], p_str[i + 1]).width;
  2589. }
  2590. }
  2591. return px;
  2592. }
  2593. void TextEdit::insert_text_at_cursor(const String &p_text) {
  2594. if (selection.active) {
  2595. cursor_set_line(selection.from_line);
  2596. cursor_set_column(selection.from_column);
  2597. _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
  2598. selection.active = false;
  2599. selection.selecting_mode = Selection::MODE_NONE;
  2600. }
  2601. _insert_text_at_cursor(p_text);
  2602. update();
  2603. }
  2604. Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
  2605. if (highlighted_word != String())
  2606. return CURSOR_POINTING_HAND;
  2607. int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width;
  2608. if ((completion_active && completion_rect.has_point(p_pos)) || p_pos.x < gutter) {
  2609. return CURSOR_ARROW;
  2610. }
  2611. return CURSOR_IBEAM;
  2612. }
  2613. void TextEdit::set_text(String p_text) {
  2614. setting_text = true;
  2615. clear();
  2616. _insert_text_at_cursor(p_text);
  2617. clear_undo_history();
  2618. cursor.column = 0;
  2619. cursor.line = 0;
  2620. cursor.x_ofs = 0;
  2621. cursor.line_ofs = 0;
  2622. cursor.last_fit_x = 0;
  2623. cursor_set_line(0);
  2624. cursor_set_column(0);
  2625. update();
  2626. setting_text = false;
  2627. _text_changed_emit();
  2628. //get_range()->set(0);
  2629. };
  2630. String TextEdit::get_text() {
  2631. String longthing;
  2632. int len = text.size();
  2633. for (int i = 0; i < len; i++) {
  2634. longthing += text[i];
  2635. if (i != len - 1)
  2636. longthing += "\n";
  2637. }
  2638. return longthing;
  2639. };
  2640. String TextEdit::get_text_for_lookup_completion() {
  2641. int row, col;
  2642. _get_mouse_pos(get_local_mouse_pos(), row, col);
  2643. String longthing;
  2644. int len = text.size();
  2645. for (int i = 0; i < len; i++) {
  2646. if (i == row) {
  2647. longthing += text[i].substr(0, col);
  2648. longthing += String::chr(0xFFFF); //not unicode, represents the cursor
  2649. longthing += text[i].substr(col, text[i].size());
  2650. } else {
  2651. longthing += text[i];
  2652. }
  2653. if (i != len - 1)
  2654. longthing += "\n";
  2655. }
  2656. return longthing;
  2657. }
  2658. String TextEdit::get_text_for_completion() {
  2659. String longthing;
  2660. int len = text.size();
  2661. for (int i = 0; i < len; i++) {
  2662. if (i == cursor.line) {
  2663. longthing += text[i].substr(0, cursor.column);
  2664. longthing += String::chr(0xFFFF); //not unicode, represents the cursor
  2665. longthing += text[i].substr(cursor.column, text[i].size());
  2666. } else {
  2667. longthing += text[i];
  2668. }
  2669. if (i != len - 1)
  2670. longthing += "\n";
  2671. }
  2672. return longthing;
  2673. };
  2674. String TextEdit::get_line(int line) const {
  2675. if (line < 0 || line >= text.size())
  2676. return "";
  2677. return text[line];
  2678. };
  2679. void TextEdit::_clear() {
  2680. clear_undo_history();
  2681. text.clear();
  2682. cursor.column = 0;
  2683. cursor.line = 0;
  2684. cursor.x_ofs = 0;
  2685. cursor.line_ofs = 0;
  2686. cursor.last_fit_x = 0;
  2687. }
  2688. void TextEdit::clear() {
  2689. setting_text = true;
  2690. _clear();
  2691. setting_text = false;
  2692. };
  2693. void TextEdit::set_readonly(bool p_readonly) {
  2694. readonly = p_readonly;
  2695. }
  2696. void TextEdit::set_wrap(bool p_wrap) {
  2697. wrap = p_wrap;
  2698. }
  2699. void TextEdit::set_max_chars(int p_max_chars) {
  2700. max_chars = p_max_chars;
  2701. }
  2702. void TextEdit::_reset_caret_blink_timer() {
  2703. if (caret_blink_enabled) {
  2704. caret_blink_timer->stop();
  2705. caret_blink_timer->start();
  2706. draw_caret = true;
  2707. update();
  2708. }
  2709. }
  2710. void TextEdit::_toggle_draw_caret() {
  2711. draw_caret = !draw_caret;
  2712. if (is_visible_in_tree() && has_focus() && window_has_focus) {
  2713. update();
  2714. }
  2715. }
  2716. void TextEdit::_update_caches() {
  2717. cache.style_normal = get_stylebox("normal");
  2718. cache.style_focus = get_stylebox("focus");
  2719. cache.completion_background_color = get_color("completion_background_color");
  2720. cache.completion_selected_color = get_color("completion_selected_color");
  2721. cache.completion_existing_color = get_color("completion_existing_color");
  2722. cache.completion_font_color = get_color("completion_font_color");
  2723. cache.font = get_font("font");
  2724. cache.caret_color = get_color("caret_color");
  2725. cache.caret_background_color = get_color("caret_background_color");
  2726. cache.line_number_color = get_color("line_number_color");
  2727. cache.font_color = get_color("font_color");
  2728. cache.font_selected_color = get_color("font_selected_color");
  2729. cache.keyword_color = get_color("keyword_color");
  2730. cache.function_color = get_color("function_color");
  2731. cache.member_variable_color = get_color("member_variable_color");
  2732. cache.number_color = get_color("number_color");
  2733. cache.selection_color = get_color("selection_color");
  2734. cache.mark_color = get_color("mark_color");
  2735. cache.current_line_color = get_color("current_line_color");
  2736. cache.line_length_guideline_color = get_color("line_length_guideline_color");
  2737. cache.breakpoint_color = get_color("breakpoint_color");
  2738. cache.brace_mismatch_color = get_color("brace_mismatch_color");
  2739. cache.word_highlighted_color = get_color("word_highlighted_color");
  2740. cache.search_result_color = get_color("search_result_color");
  2741. cache.search_result_border_color = get_color("search_result_border_color");
  2742. cache.symbol_color = get_color("symbol_color");
  2743. cache.background_color = get_color("background_color");
  2744. cache.line_spacing = get_constant("line_spacing");
  2745. cache.row_height = cache.font->get_height() + cache.line_spacing;
  2746. cache.tab_icon = get_icon("tab");
  2747. text.set_font(cache.font);
  2748. }
  2749. void TextEdit::clear_colors() {
  2750. keywords.clear();
  2751. color_regions.clear();
  2752. text.clear_caches();
  2753. }
  2754. void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) {
  2755. keywords[p_keyword] = p_color;
  2756. update();
  2757. }
  2758. void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
  2759. color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
  2760. text.clear_caches();
  2761. update();
  2762. }
  2763. void TextEdit::set_syntax_coloring(bool p_enabled) {
  2764. syntax_coloring = p_enabled;
  2765. update();
  2766. }
  2767. bool TextEdit::is_syntax_coloring_enabled() const {
  2768. return syntax_coloring;
  2769. }
  2770. void TextEdit::set_auto_indent(bool p_auto_indent) {
  2771. auto_indent = p_auto_indent;
  2772. }
  2773. void TextEdit::cut() {
  2774. if (!selection.active) {
  2775. String clipboard = text[cursor.line];
  2776. OS::get_singleton()->set_clipboard(clipboard);
  2777. cursor_set_line(cursor.line);
  2778. cursor_set_column(0);
  2779. _remove_text(cursor.line, 0, cursor.line, text[cursor.line].length());
  2780. backspace_at_cursor();
  2781. update();
  2782. cursor_set_line(cursor.line + 1);
  2783. cut_copy_line = true;
  2784. } else {
  2785. String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
  2786. OS::get_singleton()->set_clipboard(clipboard);
  2787. cursor_set_line(selection.from_line);
  2788. cursor_set_column(selection.from_column);
  2789. _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
  2790. selection.active = false;
  2791. selection.selecting_mode = Selection::MODE_NONE;
  2792. update();
  2793. cut_copy_line = false;
  2794. }
  2795. }
  2796. void TextEdit::copy() {
  2797. if (!selection.active)
  2798. return;
  2799. if (!selection.active) {
  2800. String clipboard = _base_get_text(cursor.line, 0, cursor.line, text[cursor.line].length());
  2801. OS::get_singleton()->set_clipboard(clipboard);
  2802. cut_copy_line = true;
  2803. } else {
  2804. String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
  2805. OS::get_singleton()->set_clipboard(clipboard);
  2806. cut_copy_line = false;
  2807. }
  2808. }
  2809. void TextEdit::paste() {
  2810. String clipboard = OS::get_singleton()->get_clipboard();
  2811. if (selection.active) {
  2812. selection.active = false;
  2813. selection.selecting_mode = Selection::MODE_NONE;
  2814. _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
  2815. cursor_set_line(selection.from_line);
  2816. cursor_set_column(selection.from_column);
  2817. } else if (cut_copy_line) {
  2818. cursor_set_column(0);
  2819. String ins = "\n";
  2820. clipboard += ins;
  2821. }
  2822. _insert_text_at_cursor(clipboard);
  2823. update();
  2824. }
  2825. void TextEdit::select_all() {
  2826. if (text.size() == 1 && text[0].length() == 0)
  2827. return;
  2828. selection.active = true;
  2829. selection.from_line = 0;
  2830. selection.from_column = 0;
  2831. selection.selecting_line = 0;
  2832. selection.selecting_column = 0;
  2833. selection.to_line = text.size() - 1;
  2834. selection.to_column = text[selection.to_line].length();
  2835. selection.selecting_mode = Selection::MODE_SHIFT;
  2836. selection.shiftclick_left = true;
  2837. cursor_set_line(selection.to_line, false);
  2838. cursor_set_column(selection.to_column, false);
  2839. update();
  2840. }
  2841. void TextEdit::deselect() {
  2842. selection.active = false;
  2843. update();
  2844. }
  2845. void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
  2846. if (p_from_line >= text.size())
  2847. p_from_line = text.size() - 1;
  2848. if (p_from_column >= text[p_from_line].length())
  2849. p_from_column = text[p_from_line].length();
  2850. if (p_to_line >= text.size())
  2851. p_to_line = text.size() - 1;
  2852. if (p_to_column >= text[p_to_line].length())
  2853. p_to_column = text[p_to_line].length();
  2854. selection.from_line = p_from_line;
  2855. selection.from_column = p_from_column;
  2856. selection.to_line = p_to_line;
  2857. selection.to_column = p_to_column;
  2858. selection.active = true;
  2859. if (selection.from_line == selection.to_line) {
  2860. if (selection.from_column == selection.to_column) {
  2861. selection.active = false;
  2862. } else if (selection.from_column > selection.to_column) {
  2863. selection.shiftclick_left = false;
  2864. SWAP(selection.from_column, selection.to_column);
  2865. } else {
  2866. selection.shiftclick_left = true;
  2867. }
  2868. } else if (selection.from_line > selection.to_line) {
  2869. selection.shiftclick_left = false;
  2870. SWAP(selection.from_line, selection.to_line);
  2871. SWAP(selection.from_column, selection.to_column);
  2872. } else {
  2873. selection.shiftclick_left = true;
  2874. }
  2875. update();
  2876. }
  2877. bool TextEdit::is_selection_active() const {
  2878. return selection.active;
  2879. }
  2880. int TextEdit::get_selection_from_line() const {
  2881. ERR_FAIL_COND_V(!selection.active, -1);
  2882. return selection.from_line;
  2883. }
  2884. int TextEdit::get_selection_from_column() const {
  2885. ERR_FAIL_COND_V(!selection.active, -1);
  2886. return selection.from_column;
  2887. }
  2888. int TextEdit::get_selection_to_line() const {
  2889. ERR_FAIL_COND_V(!selection.active, -1);
  2890. return selection.to_line;
  2891. }
  2892. int TextEdit::get_selection_to_column() const {
  2893. ERR_FAIL_COND_V(!selection.active, -1);
  2894. return selection.to_column;
  2895. }
  2896. String TextEdit::get_selection_text() const {
  2897. if (!selection.active)
  2898. return "";
  2899. return _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
  2900. }
  2901. String TextEdit::get_word_under_cursor() const {
  2902. int prev_cc = cursor.column;
  2903. while (prev_cc > 0) {
  2904. bool is_char = _is_text_char(text[cursor.line][prev_cc - 1]);
  2905. if (!is_char)
  2906. break;
  2907. --prev_cc;
  2908. }
  2909. int next_cc = cursor.column;
  2910. while (next_cc < text[cursor.line].length()) {
  2911. bool is_char = _is_text_char(text[cursor.line][next_cc]);
  2912. if (!is_char)
  2913. break;
  2914. ++next_cc;
  2915. }
  2916. if (prev_cc == cursor.column || next_cc == cursor.column)
  2917. return "";
  2918. return text[cursor.line].substr(prev_cc, next_cc - prev_cc);
  2919. }
  2920. void TextEdit::set_search_text(const String &p_search_text) {
  2921. search_text = p_search_text;
  2922. }
  2923. void TextEdit::set_search_flags(uint32_t p_flags) {
  2924. search_flags = p_flags;
  2925. }
  2926. void TextEdit::set_current_search_result(int line, int col) {
  2927. search_result_line = line;
  2928. search_result_col = col;
  2929. update();
  2930. }
  2931. void TextEdit::set_highlight_all_occurrences(const bool p_enabled) {
  2932. highlight_all_occurrences = p_enabled;
  2933. update();
  2934. }
  2935. bool TextEdit::is_highlight_all_occurrences_enabled() const {
  2936. return highlight_all_occurrences;
  2937. }
  2938. int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) {
  2939. int col = -1;
  2940. if (p_key.length() > 0 && p_search.length() > 0) {
  2941. if (p_from_column < 0 || p_from_column > p_search.length()) {
  2942. p_from_column = 0;
  2943. }
  2944. while (col == -1 && p_from_column <= p_search.length()) {
  2945. if (p_search_flags & SEARCH_MATCH_CASE) {
  2946. col = p_search.find(p_key, p_from_column);
  2947. } else {
  2948. col = p_search.findn(p_key, p_from_column);
  2949. }
  2950. // whole words only
  2951. if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) {
  2952. p_from_column = col;
  2953. if (col > 0 && _is_text_char(p_search[col - 1])) {
  2954. col = -1;
  2955. } else if ((col + p_key.length()) < p_search.length() && _is_text_char(p_search[col + p_key.length()])) {
  2956. col = -1;
  2957. }
  2958. }
  2959. p_from_column += 1;
  2960. }
  2961. }
  2962. return col;
  2963. }
  2964. PoolVector<int> TextEdit::_search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const {
  2965. int col, line;
  2966. if (search(p_key, p_search_flags, p_from_line, p_from_column, col, line)) {
  2967. PoolVector<int> result;
  2968. result.resize(2);
  2969. result.set(0, line);
  2970. result.set(1, col);
  2971. return result;
  2972. } else {
  2973. return PoolVector<int>();
  2974. }
  2975. }
  2976. bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column, int &r_line, int &r_column) const {
  2977. if (p_key.length() == 0)
  2978. return false;
  2979. ERR_FAIL_INDEX_V(p_from_line, text.size(), false);
  2980. ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, false);
  2981. //search through the whole documment, but start by current line
  2982. int line = -1;
  2983. int pos = -1;
  2984. line = p_from_line;
  2985. for (int i = 0; i < text.size() + 1; i++) {
  2986. //backwards is broken...
  2987. //int idx=(p_search_flags&SEARCH_BACKWARDS)?(text.size()-i):i; //do backwards seearch
  2988. if (line < 0) {
  2989. line = text.size() - 1;
  2990. }
  2991. if (line == text.size()) {
  2992. line = 0;
  2993. }
  2994. String text_line = text[line];
  2995. int from_column = 0;
  2996. if (line == p_from_line) {
  2997. if (i == text.size()) {
  2998. //wrapped
  2999. if (p_search_flags & SEARCH_BACKWARDS) {
  3000. from_column = text_line.length();
  3001. } else {
  3002. from_column = 0;
  3003. }
  3004. } else {
  3005. from_column = p_from_column;
  3006. }
  3007. } else {
  3008. if (p_search_flags & SEARCH_BACKWARDS)
  3009. from_column = text_line.length() - 1;
  3010. else
  3011. from_column = 0;
  3012. }
  3013. pos = -1;
  3014. int pos_from = 0;
  3015. int last_pos = -1;
  3016. while ((last_pos = (p_search_flags & SEARCH_MATCH_CASE) ? text_line.find(p_key, pos_from) : text_line.findn(p_key, pos_from)) != -1) {
  3017. if (p_search_flags & SEARCH_BACKWARDS) {
  3018. if (last_pos > from_column)
  3019. break;
  3020. pos = last_pos;
  3021. } else {
  3022. if (last_pos >= from_column) {
  3023. pos = last_pos;
  3024. break;
  3025. }
  3026. }
  3027. pos_from = last_pos + p_key.length();
  3028. }
  3029. if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) {
  3030. //validate for whole words
  3031. if (pos > 0 && _is_text_char(text_line[pos - 1]))
  3032. pos = -1;
  3033. else if (_is_text_char(text_line[pos + p_key.length()]))
  3034. pos = -1;
  3035. }
  3036. if (pos != -1)
  3037. break;
  3038. if (p_search_flags & SEARCH_BACKWARDS)
  3039. line--;
  3040. else
  3041. line++;
  3042. }
  3043. if (pos == -1) {
  3044. r_line = -1;
  3045. r_column = -1;
  3046. return false;
  3047. }
  3048. r_line = line;
  3049. r_column = pos;
  3050. return true;
  3051. }
  3052. void TextEdit::_cursor_changed_emit() {
  3053. emit_signal("cursor_changed");
  3054. cursor_changed_dirty = false;
  3055. }
  3056. void TextEdit::_text_changed_emit() {
  3057. emit_signal("text_changed");
  3058. text_changed_dirty = false;
  3059. }
  3060. void TextEdit::set_line_as_marked(int p_line, bool p_marked) {
  3061. ERR_FAIL_INDEX(p_line, text.size());
  3062. text.set_marked(p_line, p_marked);
  3063. update();
  3064. }
  3065. bool TextEdit::is_line_set_as_breakpoint(int p_line) const {
  3066. ERR_FAIL_INDEX_V(p_line, text.size(), false);
  3067. return text.is_breakpoint(p_line);
  3068. }
  3069. void TextEdit::set_line_as_breakpoint(int p_line, bool p_breakpoint) {
  3070. ERR_FAIL_INDEX(p_line, text.size());
  3071. text.set_breakpoint(p_line, p_breakpoint);
  3072. update();
  3073. }
  3074. void TextEdit::get_breakpoints(List<int> *p_breakpoints) const {
  3075. for (int i = 0; i < text.size(); i++) {
  3076. if (text.is_breakpoint(i))
  3077. p_breakpoints->push_back(i);
  3078. }
  3079. }
  3080. int TextEdit::get_line_count() const {
  3081. return text.size();
  3082. }
  3083. void TextEdit::_do_text_op(const TextOperation &p_op, bool p_reverse) {
  3084. ERR_FAIL_COND(p_op.type == TextOperation::TYPE_NONE);
  3085. bool insert = p_op.type == TextOperation::TYPE_INSERT;
  3086. if (p_reverse)
  3087. insert = !insert;
  3088. if (insert) {
  3089. int check_line;
  3090. int check_column;
  3091. _base_insert_text(p_op.from_line, p_op.from_column, p_op.text, check_line, check_column);
  3092. ERR_FAIL_COND(check_line != p_op.to_line); // BUG
  3093. ERR_FAIL_COND(check_column != p_op.to_column); // BUG
  3094. } else {
  3095. _base_remove_text(p_op.from_line, p_op.from_column, p_op.to_line, p_op.to_column);
  3096. }
  3097. }
  3098. void TextEdit::_clear_redo() {
  3099. if (undo_stack_pos == NULL)
  3100. return; //nothing to clear
  3101. _push_current_op();
  3102. while (undo_stack_pos) {
  3103. List<TextOperation>::Element *elem = undo_stack_pos;
  3104. undo_stack_pos = undo_stack_pos->next();
  3105. undo_stack.erase(elem);
  3106. }
  3107. }
  3108. void TextEdit::undo() {
  3109. _push_current_op();
  3110. if (undo_stack_pos == NULL) {
  3111. if (!undo_stack.size())
  3112. return; //nothing to undo
  3113. undo_stack_pos = undo_stack.back();
  3114. } else if (undo_stack_pos == undo_stack.front())
  3115. return; // at the bottom of the undo stack
  3116. else
  3117. undo_stack_pos = undo_stack_pos->prev();
  3118. TextOperation op = undo_stack_pos->get();
  3119. _do_text_op(op, true);
  3120. current_op.version = op.prev_version;
  3121. if (undo_stack_pos->get().chain_backward) {
  3122. while (true) {
  3123. ERR_BREAK(!undo_stack_pos->prev());
  3124. undo_stack_pos = undo_stack_pos->prev();
  3125. op = undo_stack_pos->get();
  3126. _do_text_op(op, true);
  3127. current_op.version = op.prev_version;
  3128. if (undo_stack_pos->get().chain_forward) {
  3129. break;
  3130. }
  3131. }
  3132. }
  3133. if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) {
  3134. cursor_set_line(undo_stack_pos->get().to_line);
  3135. cursor_set_column(undo_stack_pos->get().to_column);
  3136. _cancel_code_hint();
  3137. } else {
  3138. cursor_set_line(undo_stack_pos->get().from_line);
  3139. cursor_set_column(undo_stack_pos->get().from_column);
  3140. }
  3141. update();
  3142. }
  3143. void TextEdit::redo() {
  3144. _push_current_op();
  3145. if (undo_stack_pos == NULL)
  3146. return; //nothing to do.
  3147. TextOperation op = undo_stack_pos->get();
  3148. _do_text_op(op, false);
  3149. current_op.version = op.version;
  3150. if (undo_stack_pos->get().chain_forward) {
  3151. while (true) {
  3152. ERR_BREAK(!undo_stack_pos->next());
  3153. undo_stack_pos = undo_stack_pos->next();
  3154. op = undo_stack_pos->get();
  3155. _do_text_op(op, false);
  3156. current_op.version = op.version;
  3157. if (undo_stack_pos->get().chain_backward)
  3158. break;
  3159. }
  3160. }
  3161. cursor_set_line(undo_stack_pos->get().to_line);
  3162. cursor_set_column(undo_stack_pos->get().to_column);
  3163. undo_stack_pos = undo_stack_pos->next();
  3164. update();
  3165. }
  3166. void TextEdit::clear_undo_history() {
  3167. saved_version = 0;
  3168. current_op.type = TextOperation::TYPE_NONE;
  3169. undo_stack_pos = NULL;
  3170. undo_stack.clear();
  3171. }
  3172. void TextEdit::begin_complex_operation() {
  3173. _push_current_op();
  3174. next_operation_is_complex = true;
  3175. }
  3176. void TextEdit::end_complex_operation() {
  3177. _push_current_op();
  3178. ERR_FAIL_COND(undo_stack.size() == 0);
  3179. if (undo_stack.back()->get().chain_forward) {
  3180. undo_stack.back()->get().chain_forward = false;
  3181. return;
  3182. }
  3183. undo_stack.back()->get().chain_backward = true;
  3184. }
  3185. void TextEdit::_push_current_op() {
  3186. if (current_op.type == TextOperation::TYPE_NONE)
  3187. return; // do nothing
  3188. if (next_operation_is_complex) {
  3189. current_op.chain_forward = true;
  3190. next_operation_is_complex = false;
  3191. }
  3192. undo_stack.push_back(current_op);
  3193. current_op.type = TextOperation::TYPE_NONE;
  3194. current_op.text = "";
  3195. current_op.chain_forward = false;
  3196. }
  3197. void TextEdit::set_indent_using_spaces(const bool p_use_spaces) {
  3198. indent_using_spaces = p_use_spaces;
  3199. }
  3200. bool TextEdit::is_indent_using_spaces() const {
  3201. return indent_using_spaces;
  3202. }
  3203. void TextEdit::set_indent_size(const int p_size) {
  3204. ERR_FAIL_COND(p_size <= 0);
  3205. indent_size = p_size;
  3206. text.set_indent_size(p_size);
  3207. space_indent = "";
  3208. for (int i = 0; i < p_size; i++) {
  3209. space_indent += " ";
  3210. }
  3211. update();
  3212. }
  3213. void TextEdit::set_draw_tabs(bool p_draw) {
  3214. draw_tabs = p_draw;
  3215. }
  3216. bool TextEdit::is_drawing_tabs() const {
  3217. return draw_tabs;
  3218. }
  3219. void TextEdit::set_insert_mode(bool p_enabled) {
  3220. insert_mode = p_enabled;
  3221. update();
  3222. }
  3223. bool TextEdit::is_insert_mode() const {
  3224. return insert_mode;
  3225. }
  3226. uint32_t TextEdit::get_version() const {
  3227. return current_op.version;
  3228. }
  3229. uint32_t TextEdit::get_saved_version() const {
  3230. return saved_version;
  3231. }
  3232. void TextEdit::tag_saved_version() {
  3233. saved_version = get_version();
  3234. }
  3235. int TextEdit::get_v_scroll() const {
  3236. return v_scroll->get_value();
  3237. }
  3238. void TextEdit::set_v_scroll(int p_scroll) {
  3239. v_scroll->set_value(p_scroll);
  3240. cursor.line_ofs = p_scroll;
  3241. }
  3242. int TextEdit::get_h_scroll() const {
  3243. return h_scroll->get_value();
  3244. }
  3245. void TextEdit::set_h_scroll(int p_scroll) {
  3246. h_scroll->set_value(p_scroll);
  3247. }
  3248. void TextEdit::set_completion(bool p_enabled, const Vector<String> &p_prefixes) {
  3249. completion_prefixes.clear();
  3250. completion_enabled = p_enabled;
  3251. for (int i = 0; i < p_prefixes.size(); i++)
  3252. completion_prefixes.insert(p_prefixes[i]);
  3253. }
  3254. void TextEdit::_confirm_completion() {
  3255. begin_complex_operation();
  3256. _remove_text(cursor.line, cursor.column - completion_base.length(), cursor.line, cursor.column);
  3257. cursor_set_column(cursor.column - completion_base.length(), false);
  3258. insert_text_at_cursor(completion_current);
  3259. if (completion_current.ends_with("(") && auto_brace_completion_enabled) {
  3260. insert_text_at_cursor(")");
  3261. cursor.column--;
  3262. }
  3263. end_complex_operation();
  3264. _cancel_completion();
  3265. }
  3266. void TextEdit::_cancel_code_hint() {
  3267. VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0);
  3268. raised_from_completion = false;
  3269. completion_hint = "";
  3270. update();
  3271. }
  3272. void TextEdit::_cancel_completion() {
  3273. VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0);
  3274. raised_from_completion = false;
  3275. if (!completion_active)
  3276. return;
  3277. completion_active = false;
  3278. update();
  3279. }
  3280. static bool _is_completable(CharType c) {
  3281. return !_is_symbol(c) || c == '"' || c == '\'';
  3282. }
  3283. void TextEdit::_update_completion_candidates() {
  3284. String l = text[cursor.line];
  3285. int cofs = CLAMP(cursor.column, 0, l.length());
  3286. String s;
  3287. //look for keywords first
  3288. bool inquote = false;
  3289. int first_quote = -1;
  3290. int c = cofs - 1;
  3291. while (c >= 0) {
  3292. if (l[c] == '"' || l[c] == '\'') {
  3293. inquote = !inquote;
  3294. if (first_quote == -1)
  3295. first_quote = c;
  3296. }
  3297. c--;
  3298. }
  3299. bool pre_keyword = false;
  3300. bool cancel = false;
  3301. //print_line("inquote: "+itos(inquote)+"first quote "+itos(first_quote)+" cofs-1 "+itos(cofs-1));
  3302. if (!inquote && first_quote == cofs - 1) {
  3303. //no completion here
  3304. //print_line("cancel!");
  3305. cancel = true;
  3306. } else if (inquote && first_quote != -1) {
  3307. s = l.substr(first_quote, cofs - first_quote);
  3308. //print_line("s: 1"+s);
  3309. } else if (cofs > 0 && l[cofs - 1] == ' ') {
  3310. int kofs = cofs - 1;
  3311. String kw;
  3312. while (kofs >= 0 && l[kofs] == ' ')
  3313. kofs--;
  3314. while (kofs >= 0 && l[kofs] > 32 && _is_completable(l[kofs])) {
  3315. kw = String::chr(l[kofs]) + kw;
  3316. kofs--;
  3317. }
  3318. pre_keyword = keywords.has(kw);
  3319. //print_line("KW "+kw+"? "+itos(pre_keyword));
  3320. } else {
  3321. while (cofs > 0 && l[cofs - 1] > 32 && _is_completable(l[cofs - 1])) {
  3322. s = String::chr(l[cofs - 1]) + s;
  3323. if (l[cofs - 1] == '\'' || l[cofs - 1] == '"' || l[cofs - 1] == '$')
  3324. break;
  3325. cofs--;
  3326. }
  3327. }
  3328. if (cursor.column > 0 && l[cursor.column - 1] == '(' && !pre_keyword && !completion_strings[0].begins_with("\"")) {
  3329. cancel = true;
  3330. }
  3331. update();
  3332. if (cancel || (!pre_keyword && s == "" && (cofs == 0 || !completion_prefixes.has(String::chr(l[cofs - 1]))))) {
  3333. //none to complete, cancel
  3334. _cancel_completion();
  3335. return;
  3336. }
  3337. completion_options.clear();
  3338. completion_index = 0;
  3339. completion_base = s;
  3340. Vector<float> sim_cache;
  3341. for (int i = 0; i < completion_strings.size(); i++) {
  3342. if (s == completion_strings[i]) {
  3343. // A perfect match, stop completion
  3344. _cancel_completion();
  3345. return;
  3346. }
  3347. if (s.is_subsequence_ofi(completion_strings[i])) {
  3348. // don't remove duplicates if no input is provided
  3349. if (s != "" && completion_options.find(completion_strings[i]) != -1) {
  3350. continue;
  3351. }
  3352. // Calculate the similarity to keep completions in good order
  3353. float similarity;
  3354. if (completion_strings[i].to_lower().begins_with(s.to_lower())) {
  3355. // Substrings are the best candidates
  3356. similarity = 1.1;
  3357. } else {
  3358. // Otherwise compute the similarity
  3359. similarity = s.to_lower().similarity(completion_strings[i].to_lower());
  3360. }
  3361. int comp_size = completion_options.size();
  3362. if (comp_size == 0) {
  3363. completion_options.push_back(completion_strings[i]);
  3364. sim_cache.push_back(similarity);
  3365. } else {
  3366. float comp_sim;
  3367. int pos = 0;
  3368. do {
  3369. comp_sim = sim_cache[pos++];
  3370. } while (pos < comp_size && similarity < comp_sim);
  3371. pos = similarity > comp_sim ? pos - 1 : pos; // Pos will be off by one
  3372. completion_options.insert(pos, completion_strings[i]);
  3373. sim_cache.insert(pos, similarity);
  3374. }
  3375. }
  3376. }
  3377. if (completion_options.size() == 0) {
  3378. //no options to complete, cancel
  3379. _cancel_completion();
  3380. return;
  3381. }
  3382. // The top of the list is the best match
  3383. completion_current = completion_options[0];
  3384. #if 0 // even there's only one option, user still get the chance to choose using it or not
  3385. if (completion_options.size()==1) {
  3386. //one option to complete, just complete it automagically
  3387. _confirm_completion();
  3388. //insert_text_at_cursor(completion_options[0].substr(s.length(),completion_options[0].length()-s.length()));
  3389. _cancel_completion();
  3390. return;
  3391. }
  3392. #endif
  3393. completion_enabled = true;
  3394. }
  3395. void TextEdit::query_code_comple() {
  3396. String l = text[cursor.line];
  3397. int ofs = CLAMP(cursor.column, 0, l.length());
  3398. bool inquote = false;
  3399. int c = ofs - 1;
  3400. while (c >= 0) {
  3401. if (l[c] == '"' || l[c] == '\'')
  3402. inquote = !inquote;
  3403. c--;
  3404. }
  3405. if (ofs > 0 && (inquote || _is_completable(l[ofs - 1]) || completion_prefixes.has(String::chr(l[ofs - 1]))))
  3406. emit_signal("request_completion");
  3407. }
  3408. void TextEdit::set_code_hint(const String &p_hint) {
  3409. VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1);
  3410. raised_from_completion = true;
  3411. completion_hint = p_hint;
  3412. completion_hint_offset = -0xFFFF;
  3413. update();
  3414. }
  3415. void TextEdit::code_complete(const Vector<String> &p_strings) {
  3416. VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1);
  3417. raised_from_completion = true;
  3418. completion_strings = p_strings;
  3419. completion_active = true;
  3420. completion_current = "";
  3421. completion_index = 0;
  3422. _update_completion_candidates();
  3423. //
  3424. }
  3425. String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
  3426. int row, col;
  3427. _get_mouse_pos(p_pos, row, col);
  3428. String s = text[row];
  3429. if (s.length() == 0)
  3430. return "";
  3431. int beg = CLAMP(col, 0, s.length());
  3432. int end = beg;
  3433. if (s[beg] > 32 || beg == s.length()) {
  3434. bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this
  3435. bool inside_quotes = false;
  3436. int qbegin, qend;
  3437. for (int i = 0; i < s.length(); i++) {
  3438. if (s[i] == '"') {
  3439. if (inside_quotes) {
  3440. qend = i;
  3441. inside_quotes = false;
  3442. if (col >= qbegin && col <= qend) {
  3443. return s.substr(qbegin, qend - qbegin);
  3444. }
  3445. } else {
  3446. qbegin = i + 1;
  3447. inside_quotes = true;
  3448. }
  3449. }
  3450. }
  3451. while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) {
  3452. beg--;
  3453. }
  3454. while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) {
  3455. end++;
  3456. }
  3457. if (end < s.length())
  3458. end += 1;
  3459. return s.substr(beg, end - beg);
  3460. }
  3461. return String();
  3462. }
  3463. String TextEdit::get_tooltip(const Point2 &p_pos) const {
  3464. if (!tooltip_obj)
  3465. return Control::get_tooltip(p_pos);
  3466. int row, col;
  3467. _get_mouse_pos(p_pos, row, col);
  3468. String s = text[row];
  3469. if (s.length() == 0)
  3470. return Control::get_tooltip(p_pos);
  3471. int beg = CLAMP(col, 0, s.length());
  3472. int end = beg;
  3473. if (s[beg] > 32 || beg == s.length()) {
  3474. bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this
  3475. while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) {
  3476. beg--;
  3477. }
  3478. while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) {
  3479. end++;
  3480. }
  3481. if (end < s.length())
  3482. end += 1;
  3483. String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud);
  3484. return tt;
  3485. }
  3486. return Control::get_tooltip(p_pos);
  3487. }
  3488. void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) {
  3489. tooltip_obj = p_obj;
  3490. tooltip_func = p_function;
  3491. tooltip_ud = p_udata;
  3492. }
  3493. void TextEdit::set_line(int line, String new_text) {
  3494. if (line < 0 || line > text.size())
  3495. return;
  3496. _remove_text(line, 0, line, text[line].length());
  3497. _insert_text(line, 0, new_text);
  3498. if (cursor.line == line) {
  3499. cursor.column = MIN(cursor.column, new_text.length());
  3500. }
  3501. }
  3502. void TextEdit::insert_at(const String &p_text, int at) {
  3503. cursor_set_column(0);
  3504. cursor_set_line(at);
  3505. _insert_text(at, 0, p_text + "\n");
  3506. }
  3507. void TextEdit::set_show_line_numbers(bool p_show) {
  3508. line_numbers = p_show;
  3509. update();
  3510. }
  3511. void TextEdit::set_line_numbers_zero_padded(bool p_zero_padded) {
  3512. line_numbers_zero_padded = p_zero_padded;
  3513. update();
  3514. }
  3515. bool TextEdit::is_show_line_numbers_enabled() const {
  3516. return line_numbers;
  3517. }
  3518. void TextEdit::set_show_line_length_guideline(bool p_show) {
  3519. line_length_guideline = p_show;
  3520. update();
  3521. }
  3522. void TextEdit::set_line_length_guideline_column(int p_column) {
  3523. line_length_guideline_col = p_column;
  3524. update();
  3525. }
  3526. void TextEdit::set_draw_breakpoint_gutter(bool p_draw) {
  3527. draw_breakpoint_gutter = p_draw;
  3528. update();
  3529. }
  3530. bool TextEdit::is_drawing_breakpoint_gutter() const {
  3531. return draw_breakpoint_gutter;
  3532. }
  3533. void TextEdit::set_breakpoint_gutter_width(int p_gutter_width) {
  3534. breakpoint_gutter_width = p_gutter_width;
  3535. update();
  3536. }
  3537. int TextEdit::get_breakpoint_gutter_width() const {
  3538. return cache.breakpoint_gutter_width;
  3539. }
  3540. bool TextEdit::is_text_field() const {
  3541. return true;
  3542. }
  3543. void TextEdit::menu_option(int p_option) {
  3544. switch (p_option) {
  3545. case MENU_CUT: {
  3546. if (!readonly) {
  3547. cut();
  3548. }
  3549. } break;
  3550. case MENU_COPY: {
  3551. copy();
  3552. } break;
  3553. case MENU_PASTE: {
  3554. if (!readonly) {
  3555. paste();
  3556. }
  3557. } break;
  3558. case MENU_CLEAR: {
  3559. if (!readonly) {
  3560. clear();
  3561. }
  3562. } break;
  3563. case MENU_SELECT_ALL: {
  3564. select_all();
  3565. } break;
  3566. case MENU_UNDO: {
  3567. undo();
  3568. } break;
  3569. };
  3570. }
  3571. void TextEdit::set_select_identifiers_on_hover(bool p_enable) {
  3572. select_identifiers_enabled = p_enable;
  3573. }
  3574. bool TextEdit::is_selecting_identifiers_on_hover_enabled() const {
  3575. return select_identifiers_enabled;
  3576. }
  3577. void TextEdit::set_context_menu_enabled(bool p_enable) {
  3578. context_menu_enabled = p_enable;
  3579. }
  3580. PopupMenu *TextEdit::get_menu() const {
  3581. return menu;
  3582. }
  3583. void TextEdit::_bind_methods() {
  3584. ClassDB::bind_method(D_METHOD("_gui_input"), &TextEdit::_gui_input);
  3585. ClassDB::bind_method(D_METHOD("_scroll_moved"), &TextEdit::_scroll_moved);
  3586. ClassDB::bind_method(D_METHOD("_cursor_changed_emit"), &TextEdit::_cursor_changed_emit);
  3587. ClassDB::bind_method(D_METHOD("_text_changed_emit"), &TextEdit::_text_changed_emit);
  3588. ClassDB::bind_method(D_METHOD("_push_current_op"), &TextEdit::_push_current_op);
  3589. ClassDB::bind_method(D_METHOD("_click_selection_held"), &TextEdit::_click_selection_held);
  3590. ClassDB::bind_method(D_METHOD("_toggle_draw_caret"), &TextEdit::_toggle_draw_caret);
  3591. BIND_CONSTANT(SEARCH_MATCH_CASE);
  3592. BIND_CONSTANT(SEARCH_WHOLE_WORDS);
  3593. BIND_CONSTANT(SEARCH_BACKWARDS);
  3594. /*
  3595. ClassDB::bind_method(D_METHOD("delete_char"),&TextEdit::delete_char);
  3596. ClassDB::bind_method(D_METHOD("delete_line"),&TextEdit::delete_line);
  3597. */
  3598. ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text);
  3599. ClassDB::bind_method(D_METHOD("insert_text_at_cursor", "text"), &TextEdit::insert_text_at_cursor);
  3600. ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count);
  3601. ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text);
  3602. ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line);
  3603. ClassDB::bind_method(D_METHOD("cursor_set_column", "column", "adjust_viewport"), &TextEdit::cursor_set_column, DEFVAL(true));
  3604. ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport"), &TextEdit::cursor_set_line, DEFVAL(true));
  3605. ClassDB::bind_method(D_METHOD("cursor_get_column"), &TextEdit::cursor_get_column);
  3606. ClassDB::bind_method(D_METHOD("cursor_get_line"), &TextEdit::cursor_get_line);
  3607. ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enable"), &TextEdit::cursor_set_blink_enabled);
  3608. ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &TextEdit::cursor_get_blink_enabled);
  3609. ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &TextEdit::cursor_set_blink_speed);
  3610. ClassDB::bind_method(D_METHOD("cursor_get_blink_speed"), &TextEdit::cursor_get_blink_speed);
  3611. ClassDB::bind_method(D_METHOD("cursor_set_block_mode", "enable"), &TextEdit::cursor_set_block_mode);
  3612. ClassDB::bind_method(D_METHOD("cursor_is_block_mode"), &TextEdit::cursor_is_block_mode);
  3613. ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly);
  3614. ClassDB::bind_method(D_METHOD("set_wrap", "enable"), &TextEdit::set_wrap);
  3615. ClassDB::bind_method(D_METHOD("set_max_chars", "amount"), &TextEdit::set_max_chars);
  3616. ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut);
  3617. ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy);
  3618. ClassDB::bind_method(D_METHOD("paste"), &TextEdit::paste);
  3619. ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all);
  3620. ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &TextEdit::select);
  3621. ClassDB::bind_method(D_METHOD("is_selection_active"), &TextEdit::is_selection_active);
  3622. ClassDB::bind_method(D_METHOD("get_selection_from_line"), &TextEdit::get_selection_from_line);
  3623. ClassDB::bind_method(D_METHOD("get_selection_from_column"), &TextEdit::get_selection_from_column);
  3624. ClassDB::bind_method(D_METHOD("get_selection_to_line"), &TextEdit::get_selection_to_line);
  3625. ClassDB::bind_method(D_METHOD("get_selection_to_column"), &TextEdit::get_selection_to_column);
  3626. ClassDB::bind_method(D_METHOD("get_selection_text"), &TextEdit::get_selection_text);
  3627. ClassDB::bind_method(D_METHOD("get_word_under_cursor"), &TextEdit::get_word_under_cursor);
  3628. ClassDB::bind_method(D_METHOD("search", "key", "flags", "from_line", "from_column"), &TextEdit::_search_bind);
  3629. ClassDB::bind_method(D_METHOD("undo"), &TextEdit::undo);
  3630. ClassDB::bind_method(D_METHOD("redo"), &TextEdit::redo);
  3631. ClassDB::bind_method(D_METHOD("clear_undo_history"), &TextEdit::clear_undo_history);
  3632. ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers);
  3633. ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled);
  3634. ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences);
  3635. ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled);
  3636. ClassDB::bind_method(D_METHOD("set_syntax_coloring", "enable"), &TextEdit::set_syntax_coloring);
  3637. ClassDB::bind_method(D_METHOD("is_syntax_coloring_enabled"), &TextEdit::is_syntax_coloring_enabled);
  3638. ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color);
  3639. ClassDB::bind_method(D_METHOD("add_color_region", "begin_key", "end_key", "color", "line_only"), &TextEdit::add_color_region, DEFVAL(false));
  3640. ClassDB::bind_method(D_METHOD("clear_colors"), &TextEdit::clear_colors);
  3641. ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
  3642. ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
  3643. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
  3644. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
  3645. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
  3646. ADD_GROUP("Caret", "caret_");
  3647. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode");
  3648. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
  3649. ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed");
  3650. ADD_SIGNAL(MethodInfo("cursor_changed"));
  3651. ADD_SIGNAL(MethodInfo("text_changed"));
  3652. ADD_SIGNAL(MethodInfo("request_completion"));
  3653. ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "row")));
  3654. ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::INT, "column")));
  3655. BIND_CONSTANT(MENU_CUT);
  3656. BIND_CONSTANT(MENU_COPY);
  3657. BIND_CONSTANT(MENU_PASTE);
  3658. BIND_CONSTANT(MENU_CLEAR);
  3659. BIND_CONSTANT(MENU_SELECT_ALL);
  3660. BIND_CONSTANT(MENU_UNDO);
  3661. BIND_CONSTANT(MENU_MAX);
  3662. GLOBAL_DEF("gui/timers/text_edit_idle_detect_sec", 3);
  3663. }
  3664. TextEdit::TextEdit() {
  3665. readonly = false;
  3666. setting_row = false;
  3667. draw_tabs = false;
  3668. draw_caret = true;
  3669. max_chars = 0;
  3670. clear();
  3671. wrap = false;
  3672. set_focus_mode(FOCUS_ALL);
  3673. _update_caches();
  3674. cache.size = Size2(1, 1);
  3675. cache.row_height = 1;
  3676. cache.line_spacing = 1;
  3677. cache.line_number_w = 1;
  3678. cache.breakpoint_gutter_width = 0;
  3679. breakpoint_gutter_width = 0;
  3680. indent_size = 4;
  3681. text.set_indent_size(indent_size);
  3682. text.clear();
  3683. //text.insert(1,"Mongolia..");
  3684. //text.insert(2,"PAIS GENEROSO!!");
  3685. text.set_color_regions(&color_regions);
  3686. h_scroll = memnew(HScrollBar);
  3687. v_scroll = memnew(VScrollBar);
  3688. add_child(h_scroll);
  3689. add_child(v_scroll);
  3690. updating_scrolls = false;
  3691. selection.active = false;
  3692. h_scroll->connect("value_changed", this, "_scroll_moved");
  3693. v_scroll->connect("value_changed", this, "_scroll_moved");
  3694. cursor_changed_dirty = false;
  3695. text_changed_dirty = false;
  3696. selection.selecting_mode = Selection::MODE_NONE;
  3697. selection.selecting_line = 0;
  3698. selection.selecting_column = 0;
  3699. selection.selecting_text = false;
  3700. selection.active = false;
  3701. syntax_coloring = false;
  3702. block_caret = false;
  3703. caret_blink_enabled = false;
  3704. caret_blink_timer = memnew(Timer);
  3705. add_child(caret_blink_timer);
  3706. caret_blink_timer->set_wait_time(0.65);
  3707. caret_blink_timer->connect("timeout", this, "_toggle_draw_caret");
  3708. cursor_set_blink_enabled(false);
  3709. idle_detect = memnew(Timer);
  3710. add_child(idle_detect);
  3711. idle_detect->set_one_shot(true);
  3712. idle_detect->set_wait_time(GLOBAL_GET("gui/timers/text_edit_idle_detect_sec"));
  3713. idle_detect->connect("timeout", this, "_push_current_op");
  3714. click_select_held = memnew(Timer);
  3715. add_child(click_select_held);
  3716. click_select_held->set_wait_time(0.05);
  3717. click_select_held->connect("timeout", this, "_click_selection_held");
  3718. #if 0
  3719. syntax_coloring=true;
  3720. keywords["void"]=Color(0.3,0.0,0.1);
  3721. keywords["int"]=Color(0.3,0.0,0.1);
  3722. keywords["function"]=Color(0.3,0.0,0.1);
  3723. keywords["class"]=Color(0.3,0.0,0.1);
  3724. keywords["extends"]=Color(0.3,0.0,0.1);
  3725. keywords["constructor"]=Color(0.3,0.0,0.1);
  3726. symbol_color=Color(0.1,0.0,0.3,1.0);
  3727. color_regions.push_back(ColorRegion("/*","*/",Color(0.4,0.6,0,4)));
  3728. color_regions.push_back(ColorRegion("//","",Color(0.6,0.6,0.4)));
  3729. color_regions.push_back(ColorRegion("\"","\"",Color(0.4,0.7,0.7)));
  3730. color_regions.push_back(ColorRegion("'","'",Color(0.4,0.8,0.8)));
  3731. color_regions.push_back(ColorRegion("#","",Color(0.2,1.0,0.2)));
  3732. #endif
  3733. current_op.type = TextOperation::TYPE_NONE;
  3734. undo_enabled = true;
  3735. undo_stack_pos = NULL;
  3736. setting_text = false;
  3737. last_dblclk = 0;
  3738. current_op.version = 0;
  3739. version = 0;
  3740. saved_version = 0;
  3741. completion_enabled = false;
  3742. completion_active = false;
  3743. completion_line_ofs = 0;
  3744. tooltip_obj = NULL;
  3745. line_numbers = false;
  3746. line_numbers_zero_padded = false;
  3747. line_length_guideline = false;
  3748. line_length_guideline_col = 80;
  3749. draw_breakpoint_gutter = false;
  3750. next_operation_is_complex = false;
  3751. scroll_past_end_of_file_enabled = false;
  3752. auto_brace_completion_enabled = false;
  3753. brace_matching_enabled = false;
  3754. highlight_all_occurrences = false;
  3755. indent_using_spaces = false;
  3756. space_indent = " ";
  3757. auto_indent = false;
  3758. insert_mode = false;
  3759. window_has_focus = true;
  3760. select_identifiers_enabled = false;
  3761. raised_from_completion = false;
  3762. context_menu_enabled = true;
  3763. menu = memnew(PopupMenu);
  3764. add_child(menu);
  3765. menu->add_item(TTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
  3766. menu->add_item(TTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
  3767. menu->add_item(TTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
  3768. menu->add_separator();
  3769. menu->add_item(TTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
  3770. menu->add_item(TTR("Clear"), MENU_CLEAR);
  3771. menu->add_separator();
  3772. menu->add_item(TTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
  3773. menu->connect("id_pressed", this, "menu_option");
  3774. }
  3775. TextEdit::~TextEdit() {
  3776. }