PolyUITextInput.cpp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306
  1. /*
  2. Copyright (C) 2012 by Ivan Safrin
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. THE SOFTWARE.
  18. */
  19. #include "PolyUITextInput.h"
  20. #include "PolyConfig.h"
  21. #include "PolyInputEvent.h"
  22. #include "PolyLabel.h"
  23. #include "PolyCoreServices.h"
  24. #include "PolyEventHandler.h"
  25. using namespace Polycode;
  26. UITextInput::UITextInput(bool multiLine, Number width, Number height) : UIElement() {
  27. this->multiLine = multiLine;
  28. processInputEvents = true;
  29. isNumberOnly = false;
  30. useStrongHinting = false;
  31. draggingSelection = false;
  32. hasSelection = false;
  33. doSelectToCaret = false;
  34. caretPosition = 0;
  35. caretImagePosition = 0;
  36. settingText = false;
  37. currentLine = NULL;
  38. needFullRedraw = false;
  39. lineOffset = -1;
  40. numLines = 0;
  41. this->positionMode = ScreenEntity::POSITION_TOPLEFT;
  42. Config *conf = CoreServices::getInstance()->getConfig();
  43. if(multiLine)
  44. fontName = conf->getStringValue("Polycode", "uiTextInputFontNameMultiLine");
  45. else
  46. fontName = conf->getStringValue("Polycode", "uiTextInputFontName");
  47. if(multiLine)
  48. fontSize = conf->getNumericValue("Polycode", "uiTextInputFontSizeMultiline");
  49. else
  50. fontSize = conf->getNumericValue("Polycode", "uiTextInputFontSize");
  51. Number rectHeight = height;
  52. if(!multiLine) {
  53. rectHeight = fontSize+12;
  54. }
  55. linesContainer = new ScreenEntity();
  56. linesContainer->processInputEvents = true;
  57. lineSpacing = conf->getNumericValue("Polycode", "textEditLineSpacing");
  58. Number st = conf->getNumericValue("Polycode", "textBgSkinT");
  59. Number sr = conf->getNumericValue("Polycode", "textBgSkinR");
  60. Number sb = conf->getNumericValue("Polycode", "textBgSkinB");
  61. Number sl = conf->getNumericValue("Polycode", "textBgSkinL");
  62. padding = conf->getNumericValue("Polycode", "textBgSkinPadding");
  63. inputRect = new UIBox(conf->getStringValue("Polycode", "textBgSkin"),
  64. st,sr,sb,sl,
  65. width+(padding*2), height+(padding*2));
  66. addChild(inputRect);
  67. inputRect->addEventListener(this, InputEvent::EVENT_MOUSEDOWN);
  68. inputRect->addEventListener(this, InputEvent::EVENT_MOUSEUP);
  69. inputRect->addEventListener(this, InputEvent::EVENT_DOUBLECLICK);
  70. inputRect->addEventListener(this, InputEvent::EVENT_MOUSEMOVE);
  71. inputRect->addEventListener(this, InputEvent::EVENT_MOUSEOVER);
  72. inputRect->addEventListener(this, InputEvent::EVENT_MOUSEOUT);
  73. inputRect->processInputEvents = true;
  74. inputRect->setPositionMode(ScreenEntity::POSITION_TOPLEFT);
  75. selectorRectTop = new ScreenShape(ScreenShape::SHAPE_RECT, 1,1);
  76. selectorRectTop->setPositionMode(ScreenEntity::POSITION_TOPLEFT);
  77. selectorRectTop->setColor(181.0f/255.0f, 213.0f/255.0f, 255.0f/255.0f, 1);
  78. selectorRectTop->visible = false;
  79. linesContainer->addChild(selectorRectTop);
  80. selectorRectMiddle = new ScreenShape(ScreenShape::SHAPE_RECT, 1,1);
  81. selectorRectMiddle->setPositionMode(ScreenEntity::POSITION_TOPLEFT);
  82. selectorRectMiddle->setColor(181.0f/255.0f, 213.0f/255.0f, 255.0f/255.0f, 1);
  83. selectorRectMiddle->visible = false;
  84. linesContainer->addChild(selectorRectMiddle);
  85. selectorRectBottom = new ScreenShape(ScreenShape::SHAPE_RECT, 1,1);
  86. selectorRectBottom->setPositionMode(ScreenEntity::POSITION_TOPLEFT);
  87. selectorRectBottom->setColor(181.0f/255.0f, 213.0f/255.0f, 255.0f/255.0f, 1);
  88. selectorRectBottom->visible = false;
  89. linesContainer->addChild(selectorRectBottom);
  90. blinkerRect = new ScreenShape(ScreenShape::SHAPE_RECT, 1, fontSize+2,0,0);
  91. blinkerRect->setPositionMode(ScreenEntity::POSITION_TOPLEFT);
  92. blinkerRect->setColor(0,0,0,1);
  93. linesContainer->addChild(blinkerRect);
  94. blinkerRect->visible = false;
  95. blinkerRect->setPosition(0,3);
  96. blinkTimer = new Timer(true, 500);
  97. blinkTimer->addEventListener(this, Timer::EVENT_TRIGGER);
  98. focusable = true;
  99. this->width = width;
  100. this->height = rectHeight;
  101. setHitbox(width, rectHeight);
  102. updateCaretPosition();
  103. scrollContainer = NULL;
  104. if(multiLine) {
  105. scrollContainer = new UIScrollContainer(linesContainer, false, true, 200, 200);
  106. addChild(scrollContainer);
  107. } else {
  108. addChild(linesContainer);
  109. enableScissor = true;
  110. }
  111. undoStateIndex = 0;
  112. maxRedoIndex = 0;
  113. syntaxHighliter = NULL;
  114. textColor = Color(0.0,0.0,0.0,1.0);
  115. insertLine(true);
  116. }
  117. void UITextInput::setNumberOnly(bool val) {
  118. isNumberOnly = val;
  119. }
  120. void UITextInput::clearSelection() {
  121. hasSelection = false;
  122. selectorRectTop->visible = false;
  123. selectorRectMiddle->visible = false;
  124. selectorRectBottom->visible = false;
  125. }
  126. void UITextInput::setSelection(int lineStart, int lineEnd, int colStart, int colEnd) {
  127. if(lineStart == lineEnd && colStart == colEnd) {
  128. clearSelection();
  129. return;
  130. }
  131. if(lineStart == lineOffset) {
  132. selectionLine = lineEnd;
  133. } else {
  134. selectionLine = lineStart;
  135. }
  136. if(colStart == caretPosition) {
  137. selectionCaretPosition = colEnd;
  138. } else {
  139. selectionCaretPosition = colStart;
  140. }
  141. // printf("SET lineStart:%d lineEnd:%d colStart:%d colEnd:%d\n", lineStart, lineEnd, colStart, colEnd);
  142. if(lineStart > lineEnd) {
  143. int tmp = lineStart;
  144. lineStart = lineEnd;
  145. lineEnd = tmp;
  146. tmp = colStart;
  147. colStart = colEnd;
  148. colEnd = tmp;
  149. }
  150. if(colStart > colEnd && lineStart == lineEnd) {
  151. int tmp = colStart;
  152. colStart = colEnd;
  153. colEnd = tmp;
  154. }
  155. clearSelection();
  156. if(lineStart > lines.size()-1)
  157. return;
  158. ScreenLabel *topLine = lines[lineStart];
  159. if(colStart+1 > topLine->getText().length()) {
  160. colStart = topLine->getText().length();
  161. }
  162. Number fColEnd = colEnd;
  163. if(colEnd > topLine->getText().length() || lineStart != lineEnd)
  164. fColEnd = topLine->getText().length();
  165. Number topSize, topHeight, topX;
  166. selectorRectTop->visible = true;
  167. topSize = topLine->getLabel()->getTextWidthForString(topLine->getText().substr(colStart,fColEnd-colStart)) ;
  168. topHeight = lineHeight+lineSpacing;
  169. if(colStart >= 0) {
  170. topX = topLine->getLabel()->getTextWidthForString(topLine->getText().substr(0,colStart)) + 2;
  171. } else {
  172. topX = 0;
  173. }
  174. selectorRectTop->setScale(topSize, topHeight);
  175. selectorRectTop->setPosition(topX + padding + (topSize/2.0), padding + (lineStart * (lineHeight+lineSpacing)) + (topHeight/2.0));
  176. if(lineEnd > lineStart && lineEnd < lines.size()) {
  177. ScreenLabel *bottomLine = lines[lineEnd];
  178. selectorRectBottom->visible = true;
  179. Number bottomSize = bottomLine->getLabel()->getTextWidthForString(bottomLine->getText().substr(0,colEnd)) ;
  180. if(bottomSize < 0)
  181. bottomSize = this->width-padding;
  182. Number bottomHeight = lineHeight+lineSpacing;
  183. selectorRectBottom->setScale(bottomSize, bottomHeight);
  184. selectorRectBottom->setPosition(padding + (bottomSize/2.0), padding + (lineEnd * (lineHeight+lineSpacing)) + (bottomHeight/2.0));
  185. if(lineEnd != lineStart+1) {
  186. // need filler
  187. selectorRectMiddle->visible = true;
  188. Number midSize = this->width-padding;
  189. Number midHeight = 0;
  190. for(int i=lineStart+1; i < lineEnd;i++) {
  191. midHeight += lineHeight+lineSpacing;
  192. }
  193. selectorRectMiddle->setScale(midSize, midHeight);
  194. selectorRectMiddle->setPosition(padding + (midSize/2.0), padding + ((lineStart+1) * (lineHeight+lineSpacing)) + (midHeight/2.0));
  195. }
  196. }
  197. hasSelection = true;
  198. selectionTop = lineStart;
  199. selectionBottom = lineEnd;
  200. selectionL = colStart;
  201. selectionR = colEnd;
  202. }
  203. void UITextInput::deleteSelection() {
  204. if(selectionTop == selectionBottom) {
  205. String ctext = lines[selectionTop]->getText();
  206. String newText = ctext.substr(0, selectionL);
  207. int rside = selectionR;
  208. if(rside > ctext.length()-1)
  209. rside = ctext.length() - 1;
  210. newText += ctext.substr(rside,ctext.length() - selectionR);
  211. lines[selectionTop]->setText(newText);
  212. } else {
  213. String ctext = lines[selectionTop]->getText();
  214. String newText = ctext.substr(0, selectionL);
  215. lines[selectionTop]->setText(newText);
  216. ScreenLabel *bottomLine = lines[selectionBottom];
  217. // if whole lines to remove, do it
  218. vector<ScreenLabel*> linesToRemove;
  219. if(selectionBottom > selectionTop + 1) {
  220. for(int i=selectionTop+1; i < selectionBottom; i++) {
  221. linesToRemove.push_back(lines[i]);
  222. }
  223. for(int i=0; i < linesToRemove.size(); i++) {
  224. removeLine(linesToRemove[i]);
  225. }
  226. }
  227. ctext = bottomLine->getText();
  228. int rside = selectionR;
  229. if(rside > ctext.length()-1)
  230. rside = ctext.length() - 1;
  231. newText = ctext.substr(rside,ctext.length() - selectionR);
  232. lineOffset = selectionTop;
  233. selectLineFromOffset();
  234. caretPosition = currentLine->getText().length();
  235. updateCaretPosition();
  236. currentLine->setText(currentLine->getText() + newText);
  237. removeLine(bottomLine);
  238. }
  239. clearSelection();
  240. caretPosition = selectionL;
  241. updateCaretPosition();
  242. changedText();
  243. }
  244. void UITextInput::changedText() {
  245. if(settingText)
  246. return;
  247. if(syntaxHighliter && multiLine) {
  248. unsigned int startLine = (-linesContainer->getPosition().y) / (lineHeight+lineSpacing);
  249. unsigned int endLine = startLine + ((int)((height / (lineHeight+lineSpacing)))) + 1;
  250. if(endLine > lines.size())
  251. endLine = lines.size();
  252. if(needFullRedraw) {
  253. startLine = 0;
  254. endLine = lines.size();
  255. needFullRedraw = false;
  256. }
  257. String totalText = L"";
  258. for(int i=startLine; i < endLine; i++) {
  259. totalText += lines[i]->getText();
  260. if(i < lines.size()-1)
  261. totalText += L"\n";
  262. }
  263. std::vector<SyntaxHighlightToken> tokens = syntaxHighliter->parseText(totalText);
  264. for(int i=startLine; i < endLine; i++) {
  265. lines[i]->getLabel()->clearColors();
  266. }
  267. int lineIndex = startLine;
  268. int rangeStart = 0;
  269. int rangeEnd = 0;
  270. for(int i=0; i < tokens.size(); i++) {
  271. if(tokens[i].text == "\n") {
  272. lineIndex++;
  273. rangeStart = 0;
  274. rangeEnd = 0;
  275. } else {
  276. if(lineIndex < lines.size()) {
  277. int textLength = tokens[i].text.length();
  278. if(tokens[i].text.length() > 1) {
  279. rangeEnd = rangeStart + textLength-1;
  280. lines[lineIndex]->getLabel()->setColorForRange(tokens[i].color, rangeStart, rangeEnd);
  281. rangeStart = rangeStart + textLength;
  282. } else {
  283. rangeEnd = rangeStart;
  284. lines[lineIndex]->getLabel()->setColorForRange(tokens[i].color, rangeStart, rangeEnd);
  285. rangeStart++;
  286. }
  287. }
  288. }
  289. }
  290. for(int i=startLine; i < endLine; i++) {
  291. lines[i]->setText(lines[i]->getText());
  292. lines[i]->setColor(1.0, 1.0, 1.0, 1.0);
  293. }
  294. }
  295. dispatchEvent(new UIEvent(), UIEvent::CHANGE_EVENT);
  296. }
  297. void UITextInput::setSyntaxHighlighter(UITextInputSyntaxHighlighter *syntaxHighliter) {
  298. this->syntaxHighliter = syntaxHighliter;
  299. }
  300. void UITextInput::Resize(Number width, Number height) {
  301. inputRect->resizeBox(width, height);
  302. this->width = width;
  303. this->height = height;
  304. matrixDirty = true;
  305. setHitbox(width,height);
  306. if(multiLine) {
  307. inputRect->setHitbox(width - scrollContainer->getVScrollWidth(), height);
  308. }
  309. if(scrollContainer) {
  310. scrollContainer->Resize(width, height);
  311. }
  312. }
  313. int UITextInput::insertLine(bool after) {
  314. numLines++;
  315. int aaMode = Label::ANTIALIAS_FULL;
  316. if(useStrongHinting) {
  317. aaMode = Label::ANTIALIAS_STRONG;
  318. }
  319. ScreenLabel *newLine = new ScreenLabel(L"", fontSize, fontName, aaMode);
  320. newLine->color = textColor;
  321. lineHeight = newLine->getHeight();
  322. linesContainer->addChild(newLine);
  323. if(after) {
  324. if(currentLine) {
  325. String ctext = currentLine->getText();
  326. String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition);
  327. ctext = ctext.substr(0,caretPosition);
  328. currentLine->setText(ctext);
  329. newLine->setText(text2);
  330. caretPosition=0;
  331. }
  332. currentLine = newLine;
  333. vector<ScreenLabel*>::iterator it;
  334. it = lines.begin();
  335. for(int i=0; i < lineOffset+1; i++) {
  336. it++;
  337. }
  338. lineOffset = lineOffset + 1;
  339. lines.insert(it,newLine);
  340. restructLines();
  341. } else {
  342. // do we even need that? I don't think so.
  343. }
  344. changedText();
  345. return 1;
  346. }
  347. void UITextInput::restructLines() {
  348. for(int i=0; i < lines.size(); i++) {
  349. lines[i]->setPosition(padding,padding + (i*(lineHeight+lineSpacing)),0.0f);
  350. }
  351. if(scrollContainer) {
  352. scrollContainer->setContentSize(width, (((lines.size()) * ((lineHeight+lineSpacing)))) + padding);
  353. }
  354. if(multiLine) {
  355. inputRect->setHitbox(width - scrollContainer->getVScrollWidth(), height);
  356. }
  357. }
  358. void UITextInput::setText(String text) {
  359. if(!multiLine) {
  360. currentLine->setText(text);
  361. caretPosition = text.length();
  362. clearSelection();
  363. updateCaretPosition();
  364. } else {
  365. needFullRedraw = true;
  366. selectAll();
  367. insertText(text);
  368. clearSelection();
  369. }
  370. // this->text = text;
  371. // currentLine->setText(text);
  372. }
  373. void UITextInput::onLoseFocus() {
  374. blinkerRect->visible = false;
  375. clearSelection();
  376. }
  377. String UITextInput::getText() {
  378. if(!multiLine) {
  379. return currentLine->getText();
  380. } else {
  381. String totalText = L"";
  382. for(int i=0; i < lines.size(); i++) {
  383. totalText += lines[i]->getText();
  384. if(i < lines.size()-1)
  385. totalText += L"\n";
  386. }
  387. return totalText;
  388. }
  389. }
  390. void UITextInput::updateCaretPosition() {
  391. caretImagePosition = padding;
  392. if(caretPosition == 0) {
  393. caretImagePosition = padding;
  394. } else if(caretPosition > currentLine->getText().length()) {
  395. caretPosition = currentLine->getText().length();
  396. String caretSubString = currentLine->getText().substr(0,caretPosition);
  397. caretImagePosition = currentLine->getLabel()->getTextWidthForString(caretSubString);
  398. caretImagePosition = caretImagePosition + padding;
  399. } else {
  400. String caretSubString = currentLine->getText().substr(0,caretPosition);
  401. caretImagePosition = currentLine->getLabel()->getTextWidthForString(caretSubString);
  402. caretImagePosition = caretImagePosition + padding;
  403. }
  404. blinkerRect->visible = true;
  405. blinkTimer->Reset();
  406. if(doSelectToCaret) {
  407. doSelectToCaret = false;
  408. }
  409. if(multiLine && currentLine) {
  410. if(linesContainer->getPosition().y + currentLine->getPosition2D().y < 0.0) {
  411. scrollContainer->scrollVertical(-(lineHeight+lineSpacing+padding)/(scrollContainer->getContentSize().y));
  412. } else if(linesContainer->getPosition().y + currentLine->getPosition2D().y > scrollContainer->getHeight()) {
  413. scrollContainer->scrollVertical((lineHeight+lineSpacing+padding)/(scrollContainer->getContentSize().y));
  414. }
  415. }
  416. }
  417. void UITextInput::selectLineFromOffset() {
  418. for(int i=0; i < lines.size(); i++) {
  419. if(i == lineOffset) {
  420. currentLine = lines[i];
  421. return;
  422. }
  423. }
  424. }
  425. void UITextInput::dragSelectionTo(Number x, Number y) {
  426. x -= padding * 2.0;
  427. y -= padding;
  428. int lineOffset = y / (lineHeight+lineSpacing);
  429. if(lineOffset > lines.size()-1)
  430. lineOffset = lines.size()-1;
  431. ScreenLabel *selectToLine = lines[lineOffset];
  432. int len = selectToLine->getText().length();
  433. Number slen;
  434. int caretPosition = selectToLine->getLabel()->getTextWidthForString(selectToLine->getText().substr(0,len));
  435. for(int i=0; i < len; i++) {
  436. slen = selectToLine->getLabel()->getTextWidthForString(selectToLine->getText().substr(0,i));
  437. if(slen > x) {
  438. caretPosition = i;
  439. break;
  440. }
  441. }
  442. if(x > slen)
  443. caretPosition = len;
  444. // if(multiLine)
  445. // caretPosition++;
  446. if(caretPosition < 0)
  447. caretPosition = 0;
  448. setSelection(this->lineOffset, lineOffset, this->caretPosition, caretPosition);
  449. }
  450. int UITextInput::caretSkipWordBack(int caretLine, int caretPosition) {
  451. for(int i=caretPosition; i > 0; i--) {
  452. String bit = lines[caretLine]->getText().substr(i,1);
  453. char chr = ((char*)bit.c_str())[0];
  454. if(!isNumberOrCharacter(chr) && i < caretPosition-1) {
  455. return i+1;
  456. }
  457. }
  458. return 0;
  459. }
  460. int UITextInput::caretSkipWordForward(int caretLine, int caretPosition) {
  461. int len = lines[caretLine]->getText().length();
  462. for(int i=caretPosition; i < len; i++) {
  463. String bit = lines[caretLine]->getText().substr(i,1);
  464. char chr = ((char*)bit.c_str())[0];
  465. if(!isNumberOrCharacter(chr) && i > caretPosition) {
  466. return i;
  467. }
  468. }
  469. return lines[caretLine]->getText().length();
  470. }
  471. void UITextInput::selectWordAtCaret() {
  472. caretPosition = caretSkipWordBack(this->lineOffset,caretPosition);
  473. clearSelection();
  474. updateCaretPosition();
  475. setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretSkipWordForward(this->lineOffset, caretPosition));
  476. updateCaretPosition();
  477. }
  478. void UITextInput::replaceAll(String what, String withWhat) {
  479. for(int i=0; i < lines.size(); i++) {
  480. lines[i]->setText(lines[i]->getText().replace(what, withWhat));
  481. }
  482. needFullRedraw = true;
  483. changedText();
  484. }
  485. void UITextInput::findString(String stringToFind, bool replace, String replaceString) {
  486. clearSelection();
  487. findMatches.clear();
  488. for(int i=0; i < lines.size(); i++) {
  489. String lineText = lines[i]->getText();
  490. int offset = 0;
  491. int retVal = -1;
  492. do {
  493. retVal = lineText.find(stringToFind, offset);
  494. if(retVal != -1) {
  495. FindMatch match;
  496. match.lineNumber = i;
  497. match.caretStart = retVal;
  498. match.caretEnd = retVal + stringToFind.length();
  499. findMatches.push_back(match);
  500. offset = retVal + stringToFind.length();
  501. }
  502. } while(retVal != -1);
  503. }
  504. if(findMatches.size() > 0) {
  505. if(replace) {
  506. FindMatch match = findMatches[findIndex];
  507. String oldText = lines[match.lineNumber]->getText();
  508. String newText = oldText.substr(0,match.caretStart) + replaceString + oldText.substr(match.caretEnd);
  509. lines[match.lineNumber]->setText(newText);
  510. findMatches[findIndex].caretEnd = findMatches[findIndex].caretStart + replaceString.length();
  511. changedText();
  512. }
  513. findIndex = 0;
  514. findCurrent();
  515. }
  516. }
  517. void UITextInput::findNext() {
  518. if(findMatches.size() == 0)
  519. return;
  520. findIndex++;
  521. if(findIndex == findMatches.size()) {
  522. findIndex = 0;
  523. }
  524. findCurrent();
  525. }
  526. void UITextInput::findPrevious() {
  527. if(findMatches.size() == 0)
  528. return;
  529. findIndex--;
  530. if(findIndex < 0) {
  531. findIndex = findMatches.size()-1;
  532. }
  533. findCurrent();
  534. }
  535. void UITextInput::findCurrent() {
  536. if(findMatches.size() == 0)
  537. return;
  538. FindMatch match = findMatches[findIndex];
  539. currentLine = lines[match.lineNumber];
  540. caretPosition = match.caretStart;
  541. lineOffset = match.lineNumber;
  542. updateCaretPosition();
  543. showLine(findMatches[findIndex].lineNumber, false);
  544. setSelection(match.lineNumber, match.lineNumber, match.caretStart, match.caretEnd);
  545. }
  546. void UITextInput::setCaretToMouse(Number x, Number y) {
  547. clearSelection();
  548. x -= (padding);
  549. y -= padding;
  550. //if(lines.size() > 1) {
  551. lineOffset = y / (lineHeight+lineSpacing);
  552. if(lineOffset > lines.size()-1)
  553. lineOffset = lines.size()-1;
  554. selectLineFromOffset();
  555. //}
  556. int len = currentLine->getText().length();
  557. Number slen;
  558. int newCaretPosition = -1;
  559. for(int i=1; i < len; i++) {
  560. slen = currentLine->getLabel()->getTextWidthForString(currentLine->getText().substr(0,i));
  561. Number slen_prev = currentLine->getLabel()->getTextWidthForString(currentLine->getText().substr(0,i-1));
  562. if(x >= slen_prev && x <= slen) {
  563. if(x < slen_prev + ((slen - slen_prev) /2.0)) {
  564. newCaretPosition = i-1;
  565. break;
  566. } else {
  567. newCaretPosition = i;
  568. break;
  569. }
  570. }
  571. }
  572. if(newCaretPosition == -1)
  573. newCaretPosition = 0;
  574. if(x > slen)
  575. newCaretPosition = len;
  576. caretPosition = newCaretPosition;
  577. updateCaretPosition();
  578. }
  579. void UITextInput::removeLine(ScreenLabel *line) {
  580. for(int i=0;i<lines.size();i++) {
  581. if(lines[i] == line) {
  582. lines.erase(lines.begin()+i);
  583. }
  584. }
  585. linesContainer->removeChild(line);
  586. linesToDelete.push_back(line);
  587. restructLines();
  588. changedText();
  589. }
  590. void UITextInput::selectAll() {
  591. setSelection(0, lines.size()-1, 0, lines[lines.size()-1]->getText().length());
  592. }
  593. void UITextInput::insertText(String text) {
  594. vector<String> strings = text.split("\n");
  595. settingText = true;
  596. if(hasSelection)
  597. deleteSelection();
  598. if(strings.size() > 1) {
  599. String ctext = currentLine->getText();
  600. String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition);
  601. ctext = ctext.substr(0,caretPosition);
  602. ctext += strings[0];
  603. currentLine->setText(ctext);
  604. caretPosition = ctext.length();
  605. for(int i=1; i < strings.size()-1; i++) {
  606. insertLine(true);
  607. ctext = strings[i];
  608. currentLine->setText(ctext);
  609. caretPosition = ctext.length();
  610. }
  611. insertLine(true);
  612. ctext = strings[strings.size()-1] + text2;
  613. caretPosition = ctext.length();
  614. currentLine->setText(ctext);
  615. } else {
  616. String ctext = currentLine->getText();
  617. String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition);
  618. ctext = ctext.substr(0,caretPosition);
  619. ctext += text + text2;
  620. caretPosition += text.length();
  621. currentLine->setText(ctext);
  622. }
  623. settingText = false;
  624. changedText();
  625. updateCaretPosition();
  626. restructLines();
  627. }
  628. String UITextInput::getLineText(unsigned int index) {
  629. if(index < lines.size()) {
  630. return lines[index]->getText();
  631. } else {
  632. return "";
  633. }
  634. }
  635. String UITextInput::getSelectionText() {
  636. if(!hasSelection)
  637. return L"";
  638. String totalText = L"";
  639. if(selectionTop == selectionBottom) {
  640. totalText = lines[selectionTop]->getText().substr(selectionL, selectionR-selectionL);
  641. return totalText;
  642. } else {
  643. totalText += lines[selectionTop]->getText().substr(selectionL, lines[selectionTop]->getText().length()-selectionL);
  644. totalText += L"\n";
  645. }
  646. if(selectionBottom > selectionTop+1) {
  647. for(int i=selectionTop+1; i <= selectionBottom; i++) {
  648. totalText += lines[i]->getText();
  649. totalText += L"\n";
  650. }
  651. }
  652. totalText += lines[selectionBottom]->getText().substr(0, selectionL);
  653. return totalText;
  654. }
  655. void UITextInput::setSelectionColor(Color color) {
  656. selectorRectTop->color = color;
  657. selectorRectMiddle->color = color;
  658. selectorRectBottom->color = color;
  659. }
  660. void UITextInput::setCursorColor(Color color) {
  661. blinkerRect->color = color;
  662. }
  663. void UITextInput::setBackgroundColor(Color color) {
  664. inputRect->color = color;
  665. }
  666. void UITextInput::setTextColor(Color color) {
  667. textColor = color;
  668. for(int i=0; i < lines.size(); i++) {
  669. lines[i]->color = textColor;
  670. }
  671. }
  672. UIScrollContainer *UITextInput::getScrollContainer() {
  673. return scrollContainer;
  674. }
  675. void UITextInput::saveUndoState() {
  676. UITextInputUndoState newState;
  677. newState.content = getText();
  678. newState.caretPosition = caretPosition;
  679. newState.lineOffset = lineOffset;
  680. newState.hasSelection = hasSelection;
  681. if(hasSelection) {
  682. newState.selectionLine = selectionLine;
  683. newState.selectionCaretPosition = selectionCaretPosition;
  684. }
  685. undoStates[undoStateIndex] = newState;
  686. // if we hit undo state capacity, shift the whole stack
  687. if(undoStateIndex == MAX_TEXTINPUT_UNDO_STATES-1) {
  688. for(int i=0; i < MAX_TEXTINPUT_UNDO_STATES-1; i++) {
  689. undoStates[i] = undoStates[i+1];
  690. }
  691. } else {
  692. undoStateIndex++;
  693. }
  694. maxRedoIndex = undoStateIndex;
  695. }
  696. void UITextInput::setUndoState(UITextInputUndoState state) {
  697. clearSelection();
  698. setText(state.content);
  699. currentLine = lines[state.lineOffset];
  700. caretPosition = state.caretPosition;
  701. lineOffset = state.lineOffset;
  702. updateCaretPosition();
  703. if(state.hasSelection) {
  704. setSelection(lineOffset, state.selectionLine, caretPosition, state.selectionCaretPosition);
  705. }
  706. }
  707. void UITextInput::Undo() {
  708. if(undoStateIndex > 0) {
  709. undoStateIndex--;
  710. setUndoState(undoStates[undoStateIndex]);
  711. }
  712. }
  713. void UITextInput::Redo() {
  714. if(undoStateIndex < MAX_TEXTINPUT_UNDO_STATES-1 && undoStateIndex < maxRedoIndex) {
  715. undoStateIndex++;
  716. setUndoState(undoStates[undoStateIndex]);
  717. }
  718. }
  719. void UITextInput::Cut() {
  720. saveUndoState();
  721. Copy();
  722. if(hasSelection) {
  723. deleteSelection();
  724. }
  725. }
  726. void UITextInput::Copy() {
  727. if(hasSelection) {
  728. CoreServices::getInstance()->getCore()->copyStringToClipboard(getSelectionText());
  729. }
  730. }
  731. void UITextInput::Paste() {
  732. saveUndoState();
  733. insertText(CoreServices::getInstance()->getCore()->getClipboardString());
  734. }
  735. void UITextInput::showLine(unsigned int lineNumber, bool top) {
  736. if(top) {
  737. scrollContainer->setScrollValue(0.0, ((((lineNumber) * ((lineHeight+lineSpacing)))) + padding)/(scrollContainer->getContentSize().y-scrollContainer->getHeight()));
  738. } else {
  739. scrollContainer->setScrollValue(0.0, (((((lineNumber) * ((lineHeight+lineSpacing)))) + padding-(scrollContainer->getHeight()/2.0))/(scrollContainer->getContentSize().y-scrollContainer->getHeight())));
  740. }
  741. }
  742. bool UITextInput::isNumberOrCharacter(wchar_t charCode) {
  743. if(charCode > 47 && charCode < 58)
  744. return true;
  745. if(charCode > 64 && charCode < 91)
  746. return true;
  747. if(charCode > 96 && charCode < 123)
  748. return true;
  749. return false;
  750. }
  751. void UITextInput::onKeyDown(PolyKEY key, wchar_t charCode) {
  752. if(!hasFocus)
  753. return;
  754. // Logger::log("UCHAR: %d\n", charCode);
  755. CoreInput *input = CoreServices::getInstance()->getCore()->getInput();
  756. if(key == KEY_a && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) {
  757. selectAll();
  758. return;
  759. }
  760. if(key == KEY_c && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) {
  761. Copy();
  762. return;
  763. }
  764. if(key == KEY_x && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) {
  765. Cut();
  766. return;
  767. }
  768. if(key == KEY_z && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) {
  769. Undo();
  770. return;
  771. }
  772. if(key == KEY_z && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER)) && (input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT))) {
  773. Redo();
  774. return;
  775. }
  776. if(key == KEY_y && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) {
  777. Redo();
  778. return;
  779. }
  780. if(key == KEY_v && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) {
  781. Paste();
  782. return;
  783. }
  784. if(key == KEY_LEFT) {
  785. if(input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER)) {
  786. if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) {
  787. if(hasSelection) {
  788. setSelection(this->lineOffset, selectionLine, this->caretPosition, 0);
  789. } else {
  790. setSelection(this->lineOffset, this->lineOffset, this->caretPosition, 0);
  791. }
  792. } else {
  793. caretPosition = 0;
  794. clearSelection();
  795. updateCaretPosition();
  796. }
  797. } else if (input->getKeyState(KEY_LALT) || input->getKeyState(KEY_RALT)) {
  798. if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) {
  799. if(hasSelection) {
  800. setSelection(this->lineOffset, selectionLine, this->caretPosition, caretSkipWordBack(selectionLine, selectionCaretPosition));
  801. } else {
  802. setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretSkipWordBack(this->lineOffset, caretPosition));
  803. }
  804. } else {
  805. caretPosition = caretSkipWordBack(this->lineOffset,caretPosition);
  806. clearSelection();
  807. updateCaretPosition();
  808. }
  809. } else {
  810. if(caretPosition > 0) {
  811. if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) {
  812. if(hasSelection) {
  813. if(selectionCaretPosition > 0)
  814. setSelection(this->lineOffset, selectionLine, this->caretPosition, selectionCaretPosition-1);
  815. } else {
  816. setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretPosition-1);
  817. }
  818. } else {
  819. caretPosition--;
  820. clearSelection();
  821. updateCaretPosition();
  822. }
  823. }
  824. }
  825. return;
  826. }
  827. if(key == KEY_RIGHT) {
  828. if(caretPosition < currentLine->getText().length()) {
  829. if(input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER)) {
  830. if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) {
  831. if(hasSelection) {
  832. setSelection(this->lineOffset, selectionLine, this->caretPosition, lines[selectionLine]->getText().length());
  833. } else {
  834. setSelection(this->lineOffset, this->lineOffset, this->caretPosition, currentLine->getText().length());
  835. }
  836. } else {
  837. caretPosition = currentLine->getText().length();
  838. clearSelection();
  839. }
  840. } else if (input->getKeyState(KEY_LALT) || input->getKeyState(KEY_RALT)) {
  841. if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) {
  842. if(hasSelection) {
  843. setSelection(this->lineOffset, selectionLine, this->caretPosition, caretSkipWordForward(selectionLine, selectionCaretPosition));
  844. } else {
  845. setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretSkipWordForward(this->lineOffset, caretPosition));
  846. }
  847. } else {
  848. caretPosition = caretSkipWordForward(this->lineOffset,caretPosition);
  849. clearSelection();
  850. }
  851. } else {
  852. if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) {
  853. if(hasSelection) {
  854. setSelection(this->lineOffset, selectionLine, this->caretPosition, selectionCaretPosition+1);
  855. } else {
  856. setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretPosition+1);
  857. }
  858. } else {
  859. caretPosition++;
  860. clearSelection();
  861. }
  862. }
  863. updateCaretPosition();
  864. }
  865. return;
  866. }
  867. if(key == KEY_PAGEUP) {
  868. if(multiLine) {
  869. scrollContainer->scrollVertical(-(scrollContainer->getHeight())/(scrollContainer->getContentSize().y));
  870. }
  871. return;
  872. }
  873. if(key == KEY_PAGEDOWN) {
  874. if(multiLine) {
  875. scrollContainer->scrollVertical((scrollContainer->getHeight())/(scrollContainer->getContentSize().y));
  876. }
  877. return;
  878. }
  879. if(key == KEY_UP) {
  880. if(multiLine) {
  881. if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) {
  882. if(hasSelection) {
  883. if(selectionLine > 0)
  884. setSelection(this->lineOffset, selectionLine-1, this->caretPosition, selectionCaretPosition);
  885. } else {
  886. if(this->lineOffset > 0)
  887. setSelection(this->lineOffset, this->lineOffset-1, this->caretPosition, caretPosition);
  888. }
  889. } else {
  890. clearSelection();
  891. if(lineOffset > 0) {
  892. lineOffset--;
  893. selectLineFromOffset();
  894. updateCaretPosition();
  895. }
  896. }
  897. }
  898. blinkerRect->visible = true;
  899. return;
  900. }
  901. if(key == KEY_DOWN) {
  902. if(multiLine) {
  903. if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) {
  904. if(hasSelection) {
  905. if(selectionLine < lines.size()-1)
  906. setSelection(this->lineOffset, selectionLine+1, this->caretPosition, selectionCaretPosition);
  907. } else {
  908. if(this->lineOffset < lines.size()-1)
  909. setSelection(this->lineOffset, this->lineOffset+1, this->caretPosition, caretPosition);
  910. }
  911. } else {
  912. clearSelection();
  913. if(lineOffset < lines.size()-1) {
  914. lineOffset++;
  915. selectLineFromOffset();
  916. updateCaretPosition();
  917. }
  918. }
  919. }
  920. blinkerRect->visible = true;
  921. return;
  922. }
  923. if(key == KEY_ESCAPE) {
  924. if(!multiLine) {
  925. dispatchEvent(new Event(), Event::CANCEL_EVENT);
  926. }
  927. }
  928. if(key == KEY_RETURN) {
  929. if(multiLine) {
  930. saveUndoState();
  931. if(hasSelection) {
  932. deleteSelection();
  933. }
  934. insertLine(true);
  935. updateCaretPosition();
  936. } else {
  937. dispatchEvent(new Event(), Event::COMPLETE_EVENT);
  938. }
  939. return;
  940. }
  941. String ctext = currentLine->getText();
  942. if((charCode > 31 && charCode < 127) || charCode > 127) {
  943. if(!isNumberOnly || (isNumberOnly && ((charCode > 47 && charCode < 58) || (charCode == '.' || charCode == '-')))) {
  944. if(!isNumberOrCharacter(charCode)) {
  945. saveUndoState();
  946. }
  947. if(hasSelection)
  948. deleteSelection();
  949. ctext = currentLine->getText();
  950. String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition);
  951. ctext = ctext.substr(0,caretPosition);
  952. ctext += charCode + text2;
  953. caretPosition++;
  954. }
  955. }
  956. if(key == KEY_TAB && multiLine) {
  957. saveUndoState();
  958. if(hasSelection)
  959. deleteSelection();
  960. ctext = currentLine->getText();
  961. String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition);
  962. ctext = ctext.substr(0,caretPosition);
  963. ctext += (wchar_t)'\t' + text2;
  964. caretPosition++;
  965. }
  966. if(key == KEY_BACKSPACE) {
  967. if(hasSelection) {
  968. saveUndoState();
  969. deleteSelection();
  970. return;
  971. } else {
  972. ctext = currentLine->getText();
  973. if(caretPosition > 0) {
  974. saveUndoState();
  975. if(ctext.length() > 0) {
  976. String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition);
  977. ctext = ctext.substr(0,caretPosition-1);
  978. ctext += text2;
  979. caretPosition--;
  980. }
  981. } else {
  982. if(lineOffset > 0) {
  983. saveUndoState();
  984. ScreenLabel *lineToRemove = currentLine;
  985. lineOffset--;
  986. selectLineFromOffset();
  987. caretPosition = currentLine->getText().length();
  988. updateCaretPosition();
  989. currentLine->setText(currentLine->getText() + ctext);
  990. removeLine(lineToRemove);
  991. return;
  992. }
  993. }
  994. }
  995. }
  996. currentLine->setText(ctext);
  997. changedText();
  998. updateCaretPosition();
  999. }
  1000. void UITextInput::Update() {
  1001. if(!multiLine) {
  1002. Vector2 pos = getScreenPosition();
  1003. scissorBox.setRect(pos.x,pos.y, width, height);
  1004. }
  1005. if(hasSelection) {
  1006. blinkerRect->visible = false;
  1007. }
  1008. blinkerRect->setPosition(caretImagePosition + 1,currentLine->getPosition2D().y+1);
  1009. if(hasFocus) {
  1010. // inputRect->setStrokeColor(1.0f, 1.0f, 1.0f, 0.25f);
  1011. } else {
  1012. blinkerRect->visible = false;
  1013. // inputRect->setStrokeColor(1.0f, 1.0f, 1.0f, 0.1f);
  1014. }
  1015. for(int i=0; i < linesToDelete.size(); i++) {
  1016. delete linesToDelete[i];
  1017. }
  1018. linesToDelete.clear();
  1019. }
  1020. UITextInput::~UITextInput() {
  1021. }
  1022. void UITextInput::handleEvent(Event *event) {
  1023. if(event->getDispatcher() == inputRect) {
  1024. switch(event->getEventCode()) {
  1025. case InputEvent::EVENT_MOUSEDOWN:
  1026. if(parentEntity) {
  1027. ((ScreenEntity*)parentEntity)->focusChild(this);
  1028. } else {
  1029. hasFocus = true;
  1030. }
  1031. setCaretToMouse(((InputEvent*)event)->mousePosition.x, ((InputEvent*)event)->mousePosition.y - linesContainer->getPosition().y);
  1032. draggingSelection = true;
  1033. break;
  1034. case InputEvent::EVENT_MOUSEUP:
  1035. draggingSelection = false;
  1036. break;
  1037. case InputEvent::EVENT_DOUBLECLICK:
  1038. selectWordAtCaret();
  1039. break;
  1040. case InputEvent::EVENT_MOUSEMOVE:
  1041. CoreServices::getInstance()->getCore()->setCursor(CURSOR_TEXT);
  1042. if(draggingSelection) {
  1043. dragSelectionTo(((InputEvent*)event)->mousePosition.x, ((InputEvent*)event)->mousePosition.y - linesContainer->getPosition().y);
  1044. }
  1045. break;
  1046. case InputEvent::EVENT_MOUSEOVER:
  1047. CoreServices::getInstance()->getCore()->setCursor(CURSOR_TEXT);
  1048. break;
  1049. case InputEvent::EVENT_MOUSEOUT:
  1050. CoreServices::getInstance()->getCore()->setCursor(CURSOR_ARROW);
  1051. break;
  1052. }
  1053. }
  1054. if(event->getDispatcher() == blinkTimer) {
  1055. if(hasSelection || draggingSelection) {
  1056. blinkerRect->visible = false;
  1057. } else {
  1058. if(hasFocus)
  1059. blinkerRect->visible = !blinkerRect->visible;
  1060. else
  1061. blinkerRect->visible = false;
  1062. }
  1063. }
  1064. }