CGUIEditBox.cpp 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568
  1. // Copyright (C) 2002-2012 Nikolaus Gebhardt
  2. // This file is part of the "Irrlicht Engine".
  3. // For conditions of distribution and use, see copyright notice in irrlicht.h
  4. #include "CGUIEditBox.h"
  5. #ifdef _IRR_COMPILE_WITH_GUI_
  6. #include "IGUISkin.h"
  7. #include "IGUIEnvironment.h"
  8. #include "IGUIFont.h"
  9. #include "IVideoDriver.h"
  10. #include "rect.h"
  11. #include "os.h"
  12. #include "Keycodes.h"
  13. /*
  14. todo:
  15. optional scrollbars
  16. ctrl+left/right to select word
  17. double click/ctrl click: word select + drag to select whole words, triple click to select line
  18. optional? dragging selected text
  19. numerical
  20. */
  21. namespace irr
  22. {
  23. namespace gui
  24. {
  25. //! constructor
  26. CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border,
  27. IGUIEnvironment* environment, IGUIElement* parent, s32 id,
  28. const core::rect<s32>& rectangle)
  29. : IGUIEditBox(environment, parent, id, rectangle), MouseMarking(false),
  30. Border(border), Background(true), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0),
  31. OverrideColor(video::SColor(101,255,255,255)), OverrideFont(0), LastBreakFont(0),
  32. Operator(0), BlinkStartTime(0), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0),
  33. WordWrap(false), MultiLine(false), AutoScroll(true), PasswordBox(false),
  34. PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER),
  35. CurrentTextRect(0,0,1,1), FrameRect(rectangle)
  36. {
  37. #ifdef _DEBUG
  38. setDebugName("CGUIEditBox");
  39. #endif
  40. Text = text;
  41. if (Environment)
  42. Operator = Environment->getOSOperator();
  43. if (Operator)
  44. Operator->grab();
  45. // this element can be tabbed to
  46. setTabStop(true);
  47. setTabOrder(-1);
  48. calculateFrameRect();
  49. breakText();
  50. calculateScrollPos();
  51. }
  52. //! destructor
  53. CGUIEditBox::~CGUIEditBox()
  54. {
  55. if (OverrideFont)
  56. OverrideFont->drop();
  57. if (Operator)
  58. Operator->drop();
  59. }
  60. //! Sets another skin independent font.
  61. void CGUIEditBox::setOverrideFont(IGUIFont* font)
  62. {
  63. if (OverrideFont == font)
  64. return;
  65. if (OverrideFont)
  66. OverrideFont->drop();
  67. OverrideFont = font;
  68. if (OverrideFont)
  69. OverrideFont->grab();
  70. breakText();
  71. }
  72. //! Gets the override font (if any)
  73. IGUIFont * CGUIEditBox::getOverrideFont() const
  74. {
  75. return OverrideFont;
  76. }
  77. //! Get the font which is used right now for drawing
  78. IGUIFont* CGUIEditBox::getActiveFont() const
  79. {
  80. if ( OverrideFont )
  81. return OverrideFont;
  82. IGUISkin* skin = Environment->getSkin();
  83. if (skin)
  84. return skin->getFont();
  85. return 0;
  86. }
  87. //! Sets another color for the text.
  88. void CGUIEditBox::setOverrideColor(video::SColor color)
  89. {
  90. OverrideColor = color;
  91. OverrideColorEnabled = true;
  92. }
  93. video::SColor CGUIEditBox::getOverrideColor() const
  94. {
  95. return OverrideColor;
  96. }
  97. //! Turns the border on or off
  98. void CGUIEditBox::setDrawBorder(bool border)
  99. {
  100. Border = border;
  101. }
  102. //! Sets whether to draw the background
  103. void CGUIEditBox::setDrawBackground(bool draw)
  104. {
  105. Background = draw;
  106. }
  107. //! Sets if the text should use the overide color or the color in the gui skin.
  108. void CGUIEditBox::enableOverrideColor(bool enable)
  109. {
  110. OverrideColorEnabled = enable;
  111. }
  112. bool CGUIEditBox::isOverrideColorEnabled() const
  113. {
  114. _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
  115. return OverrideColorEnabled;
  116. }
  117. //! Enables or disables word wrap
  118. void CGUIEditBox::setWordWrap(bool enable)
  119. {
  120. WordWrap = enable;
  121. breakText();
  122. }
  123. void CGUIEditBox::updateAbsolutePosition()
  124. {
  125. core::rect<s32> oldAbsoluteRect(AbsoluteRect);
  126. IGUIElement::updateAbsolutePosition();
  127. if ( oldAbsoluteRect != AbsoluteRect )
  128. {
  129. calculateFrameRect();
  130. breakText();
  131. calculateScrollPos();
  132. }
  133. }
  134. //! Checks if word wrap is enabled
  135. bool CGUIEditBox::isWordWrapEnabled() const
  136. {
  137. _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
  138. return WordWrap;
  139. }
  140. //! Enables or disables newlines.
  141. void CGUIEditBox::setMultiLine(bool enable)
  142. {
  143. MultiLine = enable;
  144. }
  145. //! Checks if multi line editing is enabled
  146. bool CGUIEditBox::isMultiLineEnabled() const
  147. {
  148. _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
  149. return MultiLine;
  150. }
  151. void CGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
  152. {
  153. PasswordBox = passwordBox;
  154. if (PasswordBox)
  155. {
  156. PasswordChar = passwordChar;
  157. setMultiLine(false);
  158. setWordWrap(false);
  159. BrokenText.clear();
  160. }
  161. }
  162. bool CGUIEditBox::isPasswordBox() const
  163. {
  164. _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
  165. return PasswordBox;
  166. }
  167. //! Sets text justification
  168. void CGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
  169. {
  170. HAlign = horizontal;
  171. VAlign = vertical;
  172. }
  173. //! called if an event happened.
  174. bool CGUIEditBox::OnEvent(const SEvent& event)
  175. {
  176. if (isEnabled())
  177. {
  178. switch(event.EventType)
  179. {
  180. case EET_GUI_EVENT:
  181. if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
  182. {
  183. if (event.GUIEvent.Caller == this)
  184. {
  185. MouseMarking = false;
  186. setTextMarkers(0,0);
  187. }
  188. }
  189. break;
  190. case EET_KEY_INPUT_EVENT:
  191. if (processKey(event))
  192. return true;
  193. break;
  194. case EET_MOUSE_INPUT_EVENT:
  195. if (processMouse(event))
  196. return true;
  197. break;
  198. default:
  199. break;
  200. }
  201. }
  202. return IGUIElement::OnEvent(event);
  203. }
  204. bool CGUIEditBox::processKey(const SEvent& event)
  205. {
  206. if (!event.KeyInput.PressedDown)
  207. return false;
  208. bool textChanged = false;
  209. s32 newMarkBegin = MarkBegin;
  210. s32 newMarkEnd = MarkEnd;
  211. // control shortcut handling
  212. if (event.KeyInput.Control)
  213. {
  214. // german backlash '\' entered with control + '?'
  215. if ( event.KeyInput.Char == '\\' )
  216. {
  217. inputChar(event.KeyInput.Char);
  218. return true;
  219. }
  220. switch(event.KeyInput.Key)
  221. {
  222. case KEY_KEY_A:
  223. // select all
  224. newMarkBegin = 0;
  225. newMarkEnd = Text.size();
  226. break;
  227. case KEY_KEY_C:
  228. // copy to clipboard
  229. if (!PasswordBox && Operator && MarkBegin != MarkEnd)
  230. {
  231. const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
  232. const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
  233. #ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
  234. core::stringw s;
  235. #else
  236. core::stringc s;
  237. #endif
  238. s = Text.subString(realmbgn, realmend - realmbgn).c_str();
  239. Operator->copyToClipboard(s.c_str());
  240. }
  241. break;
  242. case KEY_KEY_X:
  243. // cut to the clipboard
  244. if (!PasswordBox && Operator && MarkBegin != MarkEnd)
  245. {
  246. const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
  247. const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
  248. // copy
  249. #ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
  250. core::stringw sc;
  251. #else
  252. core::stringc sc;
  253. #endif
  254. sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
  255. Operator->copyToClipboard(sc.c_str());
  256. if (isEnabled())
  257. {
  258. // delete
  259. core::stringw s;
  260. s = Text.subString(0, realmbgn);
  261. s.append( Text.subString(realmend, Text.size()-realmend) );
  262. Text = s;
  263. CursorPos = realmbgn;
  264. newMarkBegin = 0;
  265. newMarkEnd = 0;
  266. textChanged = true;
  267. }
  268. }
  269. break;
  270. case KEY_KEY_V:
  271. if ( !isEnabled() )
  272. break;
  273. // paste from the clipboard
  274. if (Operator)
  275. {
  276. const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
  277. const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
  278. // add new character
  279. #ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
  280. const wchar_t* p = Operator->getTextFromClipboard();
  281. #else
  282. const c8* p = Operator->getTextFromClipboard();
  283. #endif
  284. if (p)
  285. {
  286. if (MarkBegin == MarkEnd)
  287. {
  288. // insert text
  289. core::stringw s = Text.subString(0, CursorPos);
  290. s.append(p);
  291. s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
  292. if (!Max || s.size()<=Max) // thx to Fish FH for fix
  293. {
  294. Text = s;
  295. s = p;
  296. CursorPos += s.size();
  297. }
  298. }
  299. else
  300. {
  301. // replace text
  302. core::stringw s = Text.subString(0, realmbgn);
  303. s.append(p);
  304. s.append( Text.subString(realmend, Text.size()-realmend) );
  305. if (!Max || s.size()<=Max) // thx to Fish FH for fix
  306. {
  307. Text = s;
  308. s = p;
  309. CursorPos = realmbgn + s.size();
  310. }
  311. }
  312. }
  313. newMarkBegin = 0;
  314. newMarkEnd = 0;
  315. textChanged = true;
  316. }
  317. break;
  318. case KEY_HOME:
  319. // move/highlight to start of text
  320. if (event.KeyInput.Shift)
  321. {
  322. newMarkEnd = CursorPos;
  323. newMarkBegin = 0;
  324. CursorPos = 0;
  325. }
  326. else
  327. {
  328. CursorPos = 0;
  329. newMarkBegin = 0;
  330. newMarkEnd = 0;
  331. }
  332. break;
  333. case KEY_END:
  334. // move/highlight to end of text
  335. if (event.KeyInput.Shift)
  336. {
  337. newMarkBegin = CursorPos;
  338. newMarkEnd = Text.size();
  339. CursorPos = 0;
  340. }
  341. else
  342. {
  343. CursorPos = Text.size();
  344. newMarkBegin = 0;
  345. newMarkEnd = 0;
  346. }
  347. break;
  348. default:
  349. return false;
  350. }
  351. }
  352. // default keyboard handling
  353. else
  354. switch(event.KeyInput.Key)
  355. {
  356. case KEY_END:
  357. {
  358. s32 p = Text.size();
  359. if (WordWrap || MultiLine)
  360. {
  361. p = getLineFromPos(CursorPos);
  362. p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
  363. if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
  364. p-=1;
  365. }
  366. if (event.KeyInput.Shift)
  367. {
  368. if (MarkBegin == MarkEnd)
  369. newMarkBegin = CursorPos;
  370. newMarkEnd = p;
  371. }
  372. else
  373. {
  374. newMarkBegin = 0;
  375. newMarkEnd = 0;
  376. }
  377. CursorPos = p;
  378. BlinkStartTime = os::Timer::getTime();
  379. }
  380. break;
  381. case KEY_HOME:
  382. {
  383. s32 p = 0;
  384. if (WordWrap || MultiLine)
  385. {
  386. p = getLineFromPos(CursorPos);
  387. p = BrokenTextPositions[p];
  388. }
  389. if (event.KeyInput.Shift)
  390. {
  391. if (MarkBegin == MarkEnd)
  392. newMarkBegin = CursorPos;
  393. newMarkEnd = p;
  394. }
  395. else
  396. {
  397. newMarkBegin = 0;
  398. newMarkEnd = 0;
  399. }
  400. CursorPos = p;
  401. BlinkStartTime = os::Timer::getTime();
  402. }
  403. break;
  404. case KEY_RETURN:
  405. if (MultiLine)
  406. {
  407. inputChar(L'\n');
  408. }
  409. else
  410. {
  411. calculateScrollPos();
  412. sendGuiEvent( EGET_EDITBOX_ENTER );
  413. }
  414. return true;
  415. case KEY_LEFT:
  416. if (event.KeyInput.Shift)
  417. {
  418. if (CursorPos > 0)
  419. {
  420. if (MarkBegin == MarkEnd)
  421. newMarkBegin = CursorPos;
  422. newMarkEnd = CursorPos-1;
  423. }
  424. }
  425. else
  426. {
  427. newMarkBegin = 0;
  428. newMarkEnd = 0;
  429. }
  430. if (CursorPos > 0) CursorPos--;
  431. BlinkStartTime = os::Timer::getTime();
  432. break;
  433. case KEY_RIGHT:
  434. if (event.KeyInput.Shift)
  435. {
  436. if (Text.size() > (u32)CursorPos)
  437. {
  438. if (MarkBegin == MarkEnd)
  439. newMarkBegin = CursorPos;
  440. newMarkEnd = CursorPos+1;
  441. }
  442. }
  443. else
  444. {
  445. newMarkBegin = 0;
  446. newMarkEnd = 0;
  447. }
  448. if (Text.size() > (u32)CursorPos) CursorPos++;
  449. BlinkStartTime = os::Timer::getTime();
  450. break;
  451. case KEY_UP:
  452. if (MultiLine || (WordWrap && BrokenText.size() > 1) )
  453. {
  454. s32 lineNo = getLineFromPos(CursorPos);
  455. s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
  456. if (lineNo > 0)
  457. {
  458. s32 cp = CursorPos - BrokenTextPositions[lineNo];
  459. if ((s32)BrokenText[lineNo-1].size() < cp)
  460. CursorPos = BrokenTextPositions[lineNo-1] + core::max_((u32)1, BrokenText[lineNo-1].size())-1;
  461. else
  462. CursorPos = BrokenTextPositions[lineNo-1] + cp;
  463. }
  464. if (event.KeyInput.Shift)
  465. {
  466. newMarkBegin = mb;
  467. newMarkEnd = CursorPos;
  468. }
  469. else
  470. {
  471. newMarkBegin = 0;
  472. newMarkEnd = 0;
  473. }
  474. }
  475. else
  476. {
  477. return false;
  478. }
  479. break;
  480. case KEY_DOWN:
  481. if (MultiLine || (WordWrap && BrokenText.size() > 1) )
  482. {
  483. s32 lineNo = getLineFromPos(CursorPos);
  484. s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
  485. if (lineNo < (s32)BrokenText.size()-1)
  486. {
  487. s32 cp = CursorPos - BrokenTextPositions[lineNo];
  488. if ((s32)BrokenText[lineNo+1].size() < cp)
  489. CursorPos = BrokenTextPositions[lineNo+1] + core::max_((u32)1, BrokenText[lineNo+1].size())-1;
  490. else
  491. CursorPos = BrokenTextPositions[lineNo+1] + cp;
  492. }
  493. if (event.KeyInput.Shift)
  494. {
  495. newMarkBegin = mb;
  496. newMarkEnd = CursorPos;
  497. }
  498. else
  499. {
  500. newMarkBegin = 0;
  501. newMarkEnd = 0;
  502. }
  503. }
  504. else
  505. {
  506. return false;
  507. }
  508. break;
  509. case KEY_BACK:
  510. if ( !isEnabled() )
  511. break;
  512. if (Text.size())
  513. {
  514. core::stringw s;
  515. if (MarkBegin != MarkEnd)
  516. {
  517. // delete marked text
  518. const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
  519. const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
  520. s = Text.subString(0, realmbgn);
  521. s.append( Text.subString(realmend, Text.size()-realmend) );
  522. Text = s;
  523. CursorPos = realmbgn;
  524. }
  525. else
  526. {
  527. // delete text behind cursor
  528. if (CursorPos>0)
  529. s = Text.subString(0, CursorPos-1);
  530. else
  531. s = L"";
  532. s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
  533. Text = s;
  534. --CursorPos;
  535. }
  536. if (CursorPos < 0)
  537. CursorPos = 0;
  538. BlinkStartTime = os::Timer::getTime();
  539. newMarkBegin = 0;
  540. newMarkEnd = 0;
  541. textChanged = true;
  542. }
  543. break;
  544. case KEY_DELETE:
  545. if ( !isEnabled() )
  546. break;
  547. if (Text.size() != 0)
  548. {
  549. core::stringw s;
  550. if (MarkBegin != MarkEnd)
  551. {
  552. // delete marked text
  553. const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
  554. const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
  555. s = Text.subString(0, realmbgn);
  556. s.append( Text.subString(realmend, Text.size()-realmend) );
  557. Text = s;
  558. CursorPos = realmbgn;
  559. }
  560. else
  561. {
  562. // delete text before cursor
  563. s = Text.subString(0, CursorPos);
  564. s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
  565. Text = s;
  566. }
  567. if (CursorPos > (s32)Text.size())
  568. CursorPos = (s32)Text.size();
  569. BlinkStartTime = os::Timer::getTime();
  570. newMarkBegin = 0;
  571. newMarkEnd = 0;
  572. textChanged = true;
  573. }
  574. break;
  575. case KEY_ESCAPE:
  576. case KEY_TAB:
  577. case KEY_SHIFT:
  578. case KEY_F1:
  579. case KEY_F2:
  580. case KEY_F3:
  581. case KEY_F4:
  582. case KEY_F5:
  583. case KEY_F6:
  584. case KEY_F7:
  585. case KEY_F8:
  586. case KEY_F9:
  587. case KEY_F10:
  588. case KEY_F11:
  589. case KEY_F12:
  590. case KEY_F13:
  591. case KEY_F14:
  592. case KEY_F15:
  593. case KEY_F16:
  594. case KEY_F17:
  595. case KEY_F18:
  596. case KEY_F19:
  597. case KEY_F20:
  598. case KEY_F21:
  599. case KEY_F22:
  600. case KEY_F23:
  601. case KEY_F24:
  602. // ignore these keys
  603. return false;
  604. default:
  605. inputChar(event.KeyInput.Char);
  606. return true;
  607. }
  608. // Set new text markers
  609. setTextMarkers( newMarkBegin, newMarkEnd );
  610. // break the text if it has changed
  611. if (textChanged)
  612. {
  613. breakText();
  614. calculateScrollPos();
  615. sendGuiEvent(EGET_EDITBOX_CHANGED);
  616. }
  617. else
  618. {
  619. calculateScrollPos();
  620. }
  621. return true;
  622. }
  623. //! draws the element and its children
  624. void CGUIEditBox::draw()
  625. {
  626. if (!IsVisible)
  627. return;
  628. const bool focus = Environment->hasFocus(this);
  629. IGUISkin* skin = Environment->getSkin();
  630. if (!skin)
  631. return;
  632. EGUI_DEFAULT_COLOR bgCol = EGDC_GRAY_EDITABLE;
  633. if ( isEnabled() )
  634. bgCol = focus ? EGDC_FOCUSED_EDITABLE : EGDC_EDITABLE;
  635. if (!Border && Background)
  636. {
  637. skin->draw2DRectangle(this, skin->getColor(bgCol), AbsoluteRect, &AbsoluteClippingRect);
  638. }
  639. if (Border)
  640. {
  641. // draw the border
  642. skin->draw3DSunkenPane(this, skin->getColor(bgCol), false, Background, AbsoluteRect, &AbsoluteClippingRect);
  643. calculateFrameRect();
  644. }
  645. core::rect<s32> localClipRect = FrameRect;
  646. localClipRect.clipAgainst(AbsoluteClippingRect);
  647. // draw the text
  648. IGUIFont* font = getActiveFont();
  649. s32 cursorLine = 0;
  650. s32 charcursorpos = 0;
  651. if (font)
  652. {
  653. if (LastBreakFont != font)
  654. {
  655. breakText();
  656. }
  657. // calculate cursor pos
  658. core::stringw *txtLine = &Text;
  659. s32 startPos = 0;
  660. core::stringw s, s2;
  661. // get mark position
  662. const bool ml = (!PasswordBox && (WordWrap || MultiLine));
  663. const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
  664. const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
  665. const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
  666. const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
  667. const s32 lineCount = ml ? BrokenText.size() : 1;
  668. // Save the override color information.
  669. // Then, alter it if the edit box is disabled.
  670. const bool prevOver = OverrideColorEnabled;
  671. const video::SColor prevColor = OverrideColor;
  672. if (Text.size())
  673. {
  674. if (!isEnabled() && !OverrideColorEnabled)
  675. {
  676. OverrideColorEnabled = true;
  677. OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
  678. }
  679. for (s32 i=0; i < lineCount; ++i)
  680. {
  681. setTextRect(i);
  682. // clipping test - don't draw anything outside the visible area
  683. core::rect<s32> c = localClipRect;
  684. c.clipAgainst(CurrentTextRect);
  685. if (!c.isValid())
  686. continue;
  687. // get current line
  688. if (PasswordBox)
  689. {
  690. if (BrokenText.size() != 1)
  691. {
  692. BrokenText.clear();
  693. BrokenText.push_back(core::stringw());
  694. }
  695. if (BrokenText[0].size() != Text.size())
  696. {
  697. BrokenText[0] = Text;
  698. for (u32 q = 0; q < Text.size(); ++q)
  699. {
  700. BrokenText[0] [q] = PasswordChar;
  701. }
  702. }
  703. txtLine = &BrokenText[0];
  704. startPos = 0;
  705. }
  706. else
  707. {
  708. txtLine = ml ? &BrokenText[i] : &Text;
  709. startPos = ml ? BrokenTextPositions[i] : 0;
  710. }
  711. // draw normal text
  712. font->draw(txtLine->c_str(), CurrentTextRect,
  713. OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
  714. false, true, &localClipRect);
  715. // draw mark and marked text
  716. if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
  717. {
  718. s32 mbegin = 0, mend = 0;
  719. s32 lineStartPos = 0, lineEndPos = txtLine->size();
  720. if (i == hlineStart)
  721. {
  722. // highlight start is on this line
  723. s = txtLine->subString(0, realmbgn - startPos);
  724. mbegin = font->getDimension(s.c_str()).Width;
  725. // deal with kerning
  726. mbegin += font->getKerningWidth(
  727. &((*txtLine)[realmbgn - startPos]),
  728. realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);
  729. lineStartPos = realmbgn - startPos;
  730. }
  731. if (i == hlineStart + hlineCount - 1)
  732. {
  733. // highlight end is on this line
  734. s2 = txtLine->subString(0, realmend - startPos);
  735. mend = font->getDimension(s2.c_str()).Width;
  736. lineEndPos = (s32)s2.size();
  737. }
  738. else
  739. mend = font->getDimension(txtLine->c_str()).Width;
  740. CurrentTextRect.UpperLeftCorner.X += mbegin;
  741. CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
  742. // draw mark
  743. skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
  744. // draw marked text
  745. s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
  746. if (s.size())
  747. font->draw(s.c_str(), CurrentTextRect,
  748. OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
  749. false, true, &localClipRect);
  750. }
  751. }
  752. // Return the override color information to its previous settings.
  753. OverrideColorEnabled = prevOver;
  754. OverrideColor = prevColor;
  755. }
  756. // draw cursor
  757. if ( IsEnabled )
  758. {
  759. if (WordWrap || MultiLine)
  760. {
  761. cursorLine = getLineFromPos(CursorPos);
  762. txtLine = &BrokenText[cursorLine];
  763. startPos = BrokenTextPositions[cursorLine];
  764. }
  765. s = txtLine->subString(0,CursorPos-startPos);
  766. charcursorpos = font->getDimension(s.c_str()).Width +
  767. font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
  768. if (focus && (os::Timer::getTime() - BlinkStartTime) % 700 < 350)
  769. {
  770. setTextRect(cursorLine);
  771. CurrentTextRect.UpperLeftCorner.X += charcursorpos;
  772. font->draw(L"_", CurrentTextRect,
  773. OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
  774. false, true, &localClipRect);
  775. }
  776. }
  777. }
  778. // draw children
  779. IGUIElement::draw();
  780. }
  781. //! Sets the new caption of this element.
  782. void CGUIEditBox::setText(const wchar_t* text)
  783. {
  784. Text = text;
  785. if (u32(CursorPos) > Text.size())
  786. CursorPos = Text.size();
  787. HScrollPos = 0;
  788. breakText();
  789. }
  790. //! Enables or disables automatic scrolling with cursor position
  791. //! \param enable: If set to true, the text will move around with the cursor position
  792. void CGUIEditBox::setAutoScroll(bool enable)
  793. {
  794. AutoScroll = enable;
  795. }
  796. //! Checks to see if automatic scrolling is enabled
  797. //! \return true if automatic scrolling is enabled, false if not
  798. bool CGUIEditBox::isAutoScrollEnabled() const
  799. {
  800. _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
  801. return AutoScroll;
  802. }
  803. //! Gets the area of the text in the edit box
  804. //! \return Returns the size in pixels of the text
  805. core::dimension2du CGUIEditBox::getTextDimension()
  806. {
  807. core::rect<s32> ret;
  808. setTextRect(0);
  809. ret = CurrentTextRect;
  810. for (u32 i=1; i < BrokenText.size(); ++i)
  811. {
  812. setTextRect(i);
  813. ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
  814. ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
  815. }
  816. return core::dimension2du(ret.getSize());
  817. }
  818. //! Sets the maximum amount of characters which may be entered in the box.
  819. //! \param max: Maximum amount of characters. If 0, the character amount is
  820. //! infinity.
  821. void CGUIEditBox::setMax(u32 max)
  822. {
  823. Max = max;
  824. if (Text.size() > Max && Max != 0)
  825. Text = Text.subString(0, Max);
  826. }
  827. //! Returns maximum amount of characters, previously set by setMax();
  828. u32 CGUIEditBox::getMax() const
  829. {
  830. return Max;
  831. }
  832. bool CGUIEditBox::processMouse(const SEvent& event)
  833. {
  834. switch(event.MouseInput.Event)
  835. {
  836. case irr::EMIE_LMOUSE_LEFT_UP:
  837. if (Environment->hasFocus(this))
  838. {
  839. CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
  840. if (MouseMarking)
  841. {
  842. setTextMarkers( MarkBegin, CursorPos );
  843. }
  844. MouseMarking = false;
  845. calculateScrollPos();
  846. return true;
  847. }
  848. break;
  849. case irr::EMIE_MOUSE_MOVED:
  850. {
  851. if (MouseMarking)
  852. {
  853. CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
  854. setTextMarkers( MarkBegin, CursorPos );
  855. calculateScrollPos();
  856. return true;
  857. }
  858. }
  859. break;
  860. case EMIE_LMOUSE_PRESSED_DOWN:
  861. if (!Environment->hasFocus(this))
  862. {
  863. BlinkStartTime = os::Timer::getTime();
  864. MouseMarking = true;
  865. CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
  866. setTextMarkers(CursorPos, CursorPos );
  867. calculateScrollPos();
  868. return true;
  869. }
  870. else
  871. {
  872. if (!AbsoluteClippingRect.isPointInside(
  873. core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y)))
  874. {
  875. return false;
  876. }
  877. else
  878. {
  879. // move cursor
  880. CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
  881. s32 newMarkBegin = MarkBegin;
  882. if (!MouseMarking)
  883. newMarkBegin = CursorPos;
  884. MouseMarking = true;
  885. setTextMarkers( newMarkBegin, CursorPos);
  886. calculateScrollPos();
  887. return true;
  888. }
  889. }
  890. default:
  891. break;
  892. }
  893. return false;
  894. }
  895. s32 CGUIEditBox::getCursorPos(s32 x, s32 y)
  896. {
  897. IGUIFont* font = getActiveFont();
  898. const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
  899. core::stringw *txtLine=0;
  900. s32 startPos=0;
  901. x+=3;
  902. for (u32 i=0; i < lineCount; ++i)
  903. {
  904. setTextRect(i);
  905. if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
  906. y = CurrentTextRect.UpperLeftCorner.Y;
  907. if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y )
  908. y = CurrentTextRect.LowerRightCorner.Y;
  909. // is it inside this region?
  910. if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y)
  911. {
  912. // we've found the clicked line
  913. txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;
  914. startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;
  915. break;
  916. }
  917. }
  918. if (x < CurrentTextRect.UpperLeftCorner.X)
  919. x = CurrentTextRect.UpperLeftCorner.X;
  920. if ( !txtLine )
  921. return 0;
  922. s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X);
  923. // click was on or left of the line
  924. if (idx != -1)
  925. return idx + startPos;
  926. // click was off the right edge of the line, go to end.
  927. return txtLine->size() + startPos;
  928. }
  929. //! Breaks the single text line.
  930. void CGUIEditBox::breakText()
  931. {
  932. if ((!WordWrap && !MultiLine))
  933. return;
  934. BrokenText.clear(); // need to reallocate :/
  935. BrokenTextPositions.set_used(0);
  936. IGUIFont* font = getActiveFont();
  937. if (!font)
  938. return;
  939. LastBreakFont = font;
  940. core::stringw line;
  941. core::stringw word;
  942. core::stringw whitespace;
  943. s32 lastLineStart = 0;
  944. s32 size = Text.size();
  945. s32 length = 0;
  946. s32 elWidth = RelativeRect.getWidth() - 6;
  947. wchar_t c;
  948. for (s32 i=0; i<size; ++i)
  949. {
  950. c = Text[i];
  951. bool lineBreak = false;
  952. if (c == L'\r') // Mac or Windows breaks
  953. {
  954. lineBreak = true;
  955. c = 0;
  956. if (Text[i+1] == L'\n') // Windows breaks
  957. {
  958. // TODO: I (Michael) think that we shouldn't change the text given by the user for whatever reason.
  959. // Instead rework the cursor positioning to be able to handle this (but not in stable release
  960. // branch as users might already expect this behavior).
  961. Text.erase(i+1);
  962. --size;
  963. if ( CursorPos > i )
  964. --CursorPos;
  965. }
  966. }
  967. else if (c == L'\n') // Unix breaks
  968. {
  969. lineBreak = true;
  970. c = 0;
  971. }
  972. // don't break if we're not a multi-line edit box
  973. if (!MultiLine)
  974. lineBreak = false;
  975. if (c == L' ' || c == 0 || i == (size-1))
  976. {
  977. // here comes the next whitespace, look if
  978. // we can break the last word to the next line
  979. // We also break whitespace, otherwise cursor would vanish beside the right border.
  980. s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
  981. s32 worldlgth = font->getDimension(word.c_str()).Width;
  982. if (WordWrap && length + worldlgth + whitelgth > elWidth && line.size() > 0)
  983. {
  984. // break to next line
  985. length = worldlgth;
  986. BrokenText.push_back(line);
  987. BrokenTextPositions.push_back(lastLineStart);
  988. lastLineStart = i - (s32)word.size();
  989. line = word;
  990. }
  991. else
  992. {
  993. // add word to line
  994. line += whitespace;
  995. line += word;
  996. length += whitelgth + worldlgth;
  997. }
  998. word = L"";
  999. whitespace = L"";
  1000. if ( c )
  1001. whitespace += c;
  1002. // compute line break
  1003. if (lineBreak)
  1004. {
  1005. line += whitespace;
  1006. line += word;
  1007. BrokenText.push_back(line);
  1008. BrokenTextPositions.push_back(lastLineStart);
  1009. lastLineStart = i+1;
  1010. line = L"";
  1011. word = L"";
  1012. whitespace = L"";
  1013. length = 0;
  1014. }
  1015. }
  1016. else
  1017. {
  1018. // yippee this is a word..
  1019. word += c;
  1020. }
  1021. }
  1022. line += whitespace;
  1023. line += word;
  1024. BrokenText.push_back(line);
  1025. BrokenTextPositions.push_back(lastLineStart);
  1026. }
  1027. // TODO: that function does interpret VAlign according to line-index (indexed line is placed on top-center-bottom)
  1028. // but HAlign according to line-width (pixels) and not by row.
  1029. // Intuitively I suppose HAlign handling is better as VScrollPos should handle the line-scrolling.
  1030. // But please no one change this without also rewriting (and this time fucking testing!!!) autoscrolling (I noticed this when fixing the old autoscrolling).
  1031. void CGUIEditBox::setTextRect(s32 line)
  1032. {
  1033. if ( line < 0 )
  1034. return;
  1035. IGUIFont* font = getActiveFont();
  1036. if (!font)
  1037. return;
  1038. core::dimension2du d;
  1039. // get text dimension
  1040. const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
  1041. if (WordWrap || MultiLine)
  1042. {
  1043. d = font->getDimension(BrokenText[line].c_str());
  1044. }
  1045. else
  1046. {
  1047. d = font->getDimension(Text.c_str());
  1048. d.Height = AbsoluteRect.getHeight();
  1049. }
  1050. d.Height += font->getKerningHeight();
  1051. // justification
  1052. switch (HAlign)
  1053. {
  1054. case EGUIA_CENTER:
  1055. // align to h centre
  1056. CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);
  1057. CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);
  1058. break;
  1059. case EGUIA_LOWERRIGHT:
  1060. // align to right edge
  1061. CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;
  1062. CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();
  1063. break;
  1064. default:
  1065. // align to left edge
  1066. CurrentTextRect.UpperLeftCorner.X = 0;
  1067. CurrentTextRect.LowerRightCorner.X = d.Width;
  1068. }
  1069. switch (VAlign)
  1070. {
  1071. case EGUIA_CENTER:
  1072. // align to v centre
  1073. CurrentTextRect.UpperLeftCorner.Y =
  1074. (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
  1075. break;
  1076. case EGUIA_LOWERRIGHT:
  1077. // align to bottom edge
  1078. CurrentTextRect.UpperLeftCorner.Y =
  1079. FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
  1080. break;
  1081. default:
  1082. // align to top edge
  1083. CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
  1084. break;
  1085. }
  1086. CurrentTextRect.UpperLeftCorner.X -= HScrollPos;
  1087. CurrentTextRect.LowerRightCorner.X -= HScrollPos;
  1088. CurrentTextRect.UpperLeftCorner.Y -= VScrollPos;
  1089. CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
  1090. CurrentTextRect += FrameRect.UpperLeftCorner;
  1091. }
  1092. s32 CGUIEditBox::getLineFromPos(s32 pos)
  1093. {
  1094. if (!WordWrap && !MultiLine)
  1095. return 0;
  1096. s32 i=0;
  1097. while (i < (s32)BrokenTextPositions.size())
  1098. {
  1099. if (BrokenTextPositions[i] > pos)
  1100. return i-1;
  1101. ++i;
  1102. }
  1103. return (s32)BrokenTextPositions.size() - 1;
  1104. }
  1105. void CGUIEditBox::inputChar(wchar_t c)
  1106. {
  1107. if (!isEnabled())
  1108. return;
  1109. if (c != 0)
  1110. {
  1111. if (Text.size() < Max || Max == 0)
  1112. {
  1113. core::stringw s;
  1114. if (MarkBegin != MarkEnd)
  1115. {
  1116. // replace marked text
  1117. const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
  1118. const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
  1119. s = Text.subString(0, realmbgn);
  1120. s.append(c);
  1121. s.append( Text.subString(realmend, Text.size()-realmend) );
  1122. Text = s;
  1123. CursorPos = realmbgn+1;
  1124. }
  1125. else
  1126. {
  1127. // add new character
  1128. s = Text.subString(0, CursorPos);
  1129. s.append(c);
  1130. s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
  1131. Text = s;
  1132. ++CursorPos;
  1133. }
  1134. BlinkStartTime = os::Timer::getTime();
  1135. setTextMarkers(0, 0);
  1136. }
  1137. }
  1138. breakText();
  1139. calculateScrollPos();
  1140. sendGuiEvent(EGET_EDITBOX_CHANGED);
  1141. }
  1142. // calculate autoscroll
  1143. void CGUIEditBox::calculateScrollPos()
  1144. {
  1145. if (!AutoScroll)
  1146. return;
  1147. IGUISkin* skin = Environment->getSkin();
  1148. if (!skin)
  1149. return;
  1150. IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
  1151. if (!font)
  1152. return;
  1153. s32 cursLine = getLineFromPos(CursorPos);
  1154. if ( cursLine < 0 )
  1155. return;
  1156. setTextRect(cursLine);
  1157. const bool hasBrokenText = MultiLine || WordWrap;
  1158. // Check horizonal scrolling
  1159. // NOTE: Calculations different to vertical scrolling because setTextRect interprets VAlign relative to line but HAlign not relative to row
  1160. {
  1161. // get cursor position
  1162. IGUIFont* font = getActiveFont();
  1163. if (!font)
  1164. return;
  1165. // get cursor area
  1166. irr::u32 cursorWidth = font->getDimension(L"_").Width;
  1167. core::stringw *txtLine = hasBrokenText ? &BrokenText[cursLine] : &Text;
  1168. s32 cPos = hasBrokenText ? CursorPos - BrokenTextPositions[cursLine] : CursorPos; // column
  1169. s32 cStart = font->getDimension(txtLine->subString(0, cPos).c_str()).Width; // pixels from text-start
  1170. s32 cEnd = cStart + cursorWidth;
  1171. s32 txtWidth = font->getDimension(txtLine->c_str()).Width;
  1172. if ( txtWidth < FrameRect.getWidth() )
  1173. {
  1174. // TODO: Needs a clean left and right gap removal depending on HAlign, similar to vertical scrolling tests for top/bottom.
  1175. // This check just fixes the case where it was most noticable (text smaller than clipping area).
  1176. HScrollPos = 0;
  1177. setTextRect(cursLine);
  1178. }
  1179. if ( CurrentTextRect.UpperLeftCorner.X+cStart < FrameRect.UpperLeftCorner.X )
  1180. {
  1181. // cursor to the left of the clipping area
  1182. HScrollPos -= FrameRect.UpperLeftCorner.X-(CurrentTextRect.UpperLeftCorner.X+cStart);
  1183. setTextRect(cursLine);
  1184. // TODO: should show more characters to the left when we're scrolling left
  1185. // and the cursor reaches the border.
  1186. }
  1187. else if ( CurrentTextRect.UpperLeftCorner.X+cEnd > FrameRect.LowerRightCorner.X)
  1188. {
  1189. // cursor to the right of the clipping area
  1190. HScrollPos += (CurrentTextRect.UpperLeftCorner.X+cEnd)-FrameRect.LowerRightCorner.X;
  1191. setTextRect(cursLine);
  1192. }
  1193. }
  1194. // calculate vertical scrolling
  1195. if (hasBrokenText)
  1196. {
  1197. irr::u32 lineHeight = font->getDimension(L"A").Height + font->getKerningHeight();
  1198. // only up to 1 line fits?
  1199. if ( lineHeight >= (irr::u32)FrameRect.getHeight() )
  1200. {
  1201. VScrollPos = 0;
  1202. setTextRect(cursLine);
  1203. s32 unscrolledPos = CurrentTextRect.UpperLeftCorner.Y;
  1204. s32 pivot = FrameRect.UpperLeftCorner.Y;
  1205. switch (VAlign)
  1206. {
  1207. case EGUIA_CENTER:
  1208. pivot += FrameRect.getHeight()/2;
  1209. unscrolledPos += lineHeight/2;
  1210. break;
  1211. case EGUIA_LOWERRIGHT:
  1212. pivot += FrameRect.getHeight();
  1213. unscrolledPos += lineHeight;
  1214. break;
  1215. default:
  1216. break;
  1217. }
  1218. VScrollPos = unscrolledPos-pivot;
  1219. setTextRect(cursLine);
  1220. }
  1221. else
  1222. {
  1223. // First 2 checks are necessary when people delete lines
  1224. setTextRect(0);
  1225. if ( CurrentTextRect.UpperLeftCorner.Y > FrameRect.UpperLeftCorner.Y && VAlign != EGUIA_LOWERRIGHT)
  1226. {
  1227. // first line is leaving a gap on top
  1228. VScrollPos = 0;
  1229. }
  1230. else if (VAlign != EGUIA_UPPERLEFT)
  1231. {
  1232. u32 lastLine = BrokenTextPositions.empty() ? 0 : BrokenTextPositions.size()-1;
  1233. setTextRect(lastLine);
  1234. if ( CurrentTextRect.LowerRightCorner.Y < FrameRect.LowerRightCorner.Y)
  1235. {
  1236. // last line is leaving a gap on bottom
  1237. VScrollPos -= FrameRect.LowerRightCorner.Y-CurrentTextRect.LowerRightCorner.Y;
  1238. }
  1239. }
  1240. setTextRect(cursLine);
  1241. if ( CurrentTextRect.UpperLeftCorner.Y < FrameRect.UpperLeftCorner.Y )
  1242. {
  1243. // text above valid area
  1244. VScrollPos -= FrameRect.UpperLeftCorner.Y-CurrentTextRect.UpperLeftCorner.Y;
  1245. setTextRect(cursLine);
  1246. }
  1247. else if ( CurrentTextRect.LowerRightCorner.Y > FrameRect.LowerRightCorner.Y)
  1248. {
  1249. // text below valid area
  1250. VScrollPos += CurrentTextRect.LowerRightCorner.Y-FrameRect.LowerRightCorner.Y;
  1251. setTextRect(cursLine);
  1252. }
  1253. }
  1254. }
  1255. }
  1256. void CGUIEditBox::calculateFrameRect()
  1257. {
  1258. FrameRect = AbsoluteRect;
  1259. IGUISkin *skin = 0;
  1260. if (Environment)
  1261. skin = Environment->getSkin();
  1262. if (Border && skin)
  1263. {
  1264. FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
  1265. FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
  1266. FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
  1267. FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
  1268. }
  1269. }
  1270. //! set text markers
  1271. void CGUIEditBox::setTextMarkers(s32 begin, s32 end)
  1272. {
  1273. if ( begin != MarkBegin || end != MarkEnd )
  1274. {
  1275. MarkBegin = begin;
  1276. MarkEnd = end;
  1277. sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
  1278. }
  1279. }
  1280. //! send some gui event to parent
  1281. void CGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
  1282. {
  1283. if ( Parent )
  1284. {
  1285. SEvent e;
  1286. e.EventType = EET_GUI_EVENT;
  1287. e.GUIEvent.Caller = this;
  1288. e.GUIEvent.Element = 0;
  1289. e.GUIEvent.EventType = type;
  1290. Parent->OnEvent(e);
  1291. }
  1292. }
  1293. //! Writes attributes of the element.
  1294. void CGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
  1295. {
  1296. // IGUIEditBox::serializeAttributes(out,options);
  1297. out->addBool ("Border", Border);
  1298. out->addBool ("Background", Background);
  1299. out->addBool ("OverrideColorEnabled", OverrideColorEnabled );
  1300. out->addColor ("OverrideColor", OverrideColor);
  1301. // out->addFont("OverrideFont", OverrideFont);
  1302. out->addInt ("MaxChars", Max);
  1303. out->addBool ("WordWrap", WordWrap);
  1304. out->addBool ("MultiLine", MultiLine);
  1305. out->addBool ("AutoScroll", AutoScroll);
  1306. out->addBool ("PasswordBox", PasswordBox);
  1307. core::stringw ch = L" ";
  1308. ch[0] = PasswordChar;
  1309. out->addString("PasswordChar", ch.c_str());
  1310. out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
  1311. out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
  1312. IGUIEditBox::serializeAttributes(out,options);
  1313. }
  1314. //! Reads attributes of the element
  1315. void CGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
  1316. {
  1317. IGUIEditBox::deserializeAttributes(in,options);
  1318. setDrawBorder( in->getAttributeAsBool("Border") );
  1319. setDrawBackground( in->getAttributeAsBool("Background") );
  1320. setOverrideColor(in->getAttributeAsColor("OverrideColor"));
  1321. enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
  1322. setMax(in->getAttributeAsInt("MaxChars"));
  1323. setWordWrap(in->getAttributeAsBool("WordWrap"));
  1324. setMultiLine(in->getAttributeAsBool("MultiLine"));
  1325. setAutoScroll(in->getAttributeAsBool("AutoScroll"));
  1326. core::stringw ch = in->getAttributeAsStringW("PasswordChar");
  1327. if (!ch.size())
  1328. setPasswordBox(in->getAttributeAsBool("PasswordBox"));
  1329. else
  1330. setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
  1331. setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
  1332. (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
  1333. // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
  1334. }
  1335. } // end namespace gui
  1336. } // end namespace irr
  1337. #endif // _IRR_COMPILE_WITH_GUI_