guiWindowCtrl.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "console/consoleTypes.h"
  23. #include "console/console.h"
  24. #include "graphics/dgl.h"
  25. #include "gui/guiCanvas.h"
  26. #include "gui/containers/guiWindowCtrl.h"
  27. #include "gui/guiDefaultControlRender.h"
  28. #include "guiWindowCtrl_ScriptBinding.h"
  29. IMPLEMENT_CONOBJECT(GuiWindowCtrl);
  30. GuiWindowCtrl::GuiWindowCtrl(void)
  31. {
  32. mResizeWidth = true;
  33. mResizeHeight = true;
  34. mCanMove = true;
  35. mCanClose = true;
  36. mCanMinimize = true;
  37. mCanMaximize = true;
  38. mTitleHeight = 20;
  39. mResizeRightWidth = 10;
  40. mResizeBottomHeight = 10;
  41. mIsContainer = true;
  42. mDepressed = false;
  43. curHitRegion = None;
  44. mActive = true;
  45. mMinimized = false;
  46. mMaximized = false;
  47. mMouseMovingWin = false;
  48. mMouseResizeWidth = false;
  49. mMouseResizeHeight = false;
  50. mBounds.extent.set(100, 200);
  51. mMinimizeIndex = -1;
  52. mTabIndex = -1;
  53. //other defaults
  54. mActive = true;
  55. mPressClose = false;
  56. mPressMaximize = false;
  57. mPressMinimize = false;
  58. mContentProfile = NULL;
  59. mCloseButtonProfile = NULL;
  60. mMinButtonProfile = NULL;
  61. mMaxButtonProfile = NULL;
  62. setField("contentProfile", "GuiWindowContentProfile");
  63. setField("closeButtonProfile", "GuiWindowCloseButtonProfile");
  64. setField("minButtonProfile", "GuiWindowMinButtonProfile");
  65. setField("maxButtonProfile", "GuiWindowMaxButtonProfile");
  66. setField("profile", "GuiWindowProfile");
  67. mLeftRightCursor = NULL;
  68. mUpDownCursor = NULL;
  69. mNWSECursor = NULL;
  70. }
  71. void GuiWindowCtrl::initPersistFields()
  72. {
  73. Parent::initPersistFields();
  74. addField("resizeWidth", TypeBool, Offset(mResizeWidth, GuiWindowCtrl));
  75. addField("resizeHeight", TypeBool, Offset(mResizeHeight, GuiWindowCtrl));
  76. addField("canMove", TypeBool, Offset(mCanMove, GuiWindowCtrl));
  77. addField("canClose", TypeBool, Offset(mCanClose, GuiWindowCtrl));
  78. addField("canMinimize", TypeBool, Offset(mCanMinimize, GuiWindowCtrl));
  79. addField("canMaximize", TypeBool, Offset(mCanMaximize, GuiWindowCtrl));
  80. addField("titleHeight", TypeS32, Offset(mTitleHeight, GuiWindowCtrl));
  81. addField("contentProfile", TypeGuiProfile, Offset(mContentProfile, GuiWindowCtrl));
  82. addField("closeButtonProfile", TypeGuiProfile, Offset(mCloseButtonProfile, GuiWindowCtrl));
  83. addField("minButtonProfile", TypeGuiProfile, Offset(mMinButtonProfile, GuiWindowCtrl));
  84. addField("maxButtonProfile", TypeGuiProfile, Offset(mMaxButtonProfile, GuiWindowCtrl));
  85. addField("leftRightCursor", TypeGuiCursor, Offset(mLeftRightCursor, GuiWindowCtrl));
  86. addField("upDownCursor", TypeGuiCursor, Offset(mUpDownCursor, GuiWindowCtrl));
  87. addField("nWSECursor", TypeGuiCursor, Offset(mNWSECursor, GuiWindowCtrl));
  88. }
  89. bool GuiWindowCtrl::isMinimized(S32 &index)
  90. {
  91. index = mMinimizeIndex;
  92. return mMinimized && mVisible;
  93. }
  94. bool GuiWindowCtrl::onWake()
  95. {
  96. if (!Parent::onWake())
  97. return false;
  98. if (mContentProfile != NULL)
  99. mContentProfile->incRefCount();
  100. if (mCloseButtonProfile != NULL)
  101. mCloseButtonProfile->incRefCount();
  102. if (mMinButtonProfile != NULL)
  103. mMinButtonProfile->incRefCount();
  104. if (mMaxButtonProfile != NULL)
  105. mMaxButtonProfile->incRefCount();
  106. //set the tab index
  107. mTabIndex = -1;
  108. GuiControl *parent = getParent();
  109. if (parent && mFirstResponder)
  110. {
  111. mTabIndex = 0;
  112. //count the number of windows preceeding this one
  113. iterator i;
  114. for (i = parent->begin(); i != parent->end(); i++)
  115. {
  116. GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
  117. if (ctrl)
  118. {
  119. if (ctrl == this) break;
  120. else if (ctrl->mFirstResponder) mTabIndex++;
  121. }
  122. }
  123. }
  124. return true;
  125. }
  126. void GuiWindowCtrl::onSleep()
  127. {
  128. Parent::onSleep();
  129. if (mContentProfile != NULL)
  130. mContentProfile->decRefCount();
  131. if (mCloseButtonProfile != NULL)
  132. mCloseButtonProfile->decRefCount();
  133. if (mMinButtonProfile != NULL)
  134. mMinButtonProfile->decRefCount();
  135. if (mMaxButtonProfile != NULL)
  136. mMaxButtonProfile->decRefCount();
  137. }
  138. GuiControl* GuiWindowCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
  139. {
  140. if (! mMinimized)
  141. return Parent::findHitControl(pt, initialLayer);
  142. else
  143. return this;
  144. }
  145. void GuiWindowCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
  146. {
  147. Parent::resize(newPosition, newExtent);
  148. }
  149. void GuiWindowCtrl::onTouchMove(const GuiEvent &event)
  150. {
  151. curHitRegion = findHitRegion(globalToLocalCoord(event.mousePoint));
  152. }
  153. void GuiWindowCtrl::onTouchLeave(const GuiEvent &event)
  154. {
  155. curHitRegion = None;
  156. }
  157. void GuiWindowCtrl::onTouchDown(const GuiEvent &event)
  158. {
  159. setUpdate();
  160. mOrigBounds = mBounds;
  161. mMouseDownPosition = event.mousePoint;
  162. Point2I localPoint = globalToLocalCoord(event.mousePoint);
  163. curHitRegion = findHitRegion(localPoint);
  164. mDepressed = true;
  165. //select this window - move it to the front, and set the first responder
  166. selectWindow();
  167. //if we clicked within the title bar
  168. if (localPoint.y < mTitleHeight)
  169. {
  170. //if we clicked on the close button
  171. if (curHitRegion == CloseButton)
  172. {
  173. mPressClose = true;
  174. }
  175. else if (curHitRegion == MaxButton)
  176. {
  177. mPressMaximize = true;
  178. }
  179. else if (curHitRegion == MinButton)
  180. {
  181. mPressMinimize = true;
  182. }
  183. else if (curHitRegion == TitleBar)
  184. {
  185. mMouseMovingWin = mCanMove;
  186. ResizeComplete();
  187. }
  188. }
  189. else
  190. {
  191. MoveComplete();
  192. //see if we clicked on the right edge
  193. if (mResizeWidth && (localPoint.x > mBounds.extent.x - mResizeRightWidth))
  194. {
  195. mMouseResizeWidth = true;
  196. }
  197. //see if we clicked on the bottom edge (as well)
  198. if (mResizeHeight && (localPoint.y > mBounds.extent.y - mResizeBottomHeight))
  199. {
  200. mMouseResizeHeight = true;
  201. }
  202. }
  203. if (mMouseMovingWin || mMouseResizeWidth || mMouseResizeHeight ||
  204. mPressClose || mPressMaximize || mPressMinimize)
  205. {
  206. mouseLock();
  207. }
  208. else
  209. {
  210. GuiControl *ctrl = findHitControl(localPoint);
  211. if (ctrl && ctrl != this)
  212. ctrl->onTouchDown(event);
  213. }
  214. }
  215. void GuiWindowCtrl::onTouchDragged(const GuiEvent &event)
  216. {
  217. GuiControl *parent = getParent();
  218. GuiCanvas *root = getRoot();
  219. if (! root) return;
  220. curHitRegion = findHitRegion(globalToLocalCoord(event.mousePoint));
  221. Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
  222. Point2I newPosition = mBounds.point;
  223. Point2I newExtent = mBounds.extent;
  224. bool update = false;
  225. if (mMouseMovingWin && parent)
  226. {
  227. newPosition.x = getMax(0, getMin(parent->mBounds.extent.x - mBounds.extent.x, mOrigBounds.point.x + deltaMousePosition.x));
  228. newPosition.y = getMax(0, getMin(parent->mBounds.extent.y - mBounds.extent.y, mOrigBounds.point.y + deltaMousePosition.y));
  229. update = true;
  230. }
  231. else if(mPressClose || mPressMaximize || mPressMinimize)
  232. {
  233. setUpdate();
  234. }
  235. else
  236. {
  237. Point2I minExtent = getMinExtent();
  238. if (mMouseResizeWidth && parent)
  239. {
  240. newExtent.x = getMax(0, getMax(minExtent.x, getMin(parent->getWidth(), mOrigBounds.extent.x + deltaMousePosition.x)));
  241. update = true;
  242. }
  243. if (mMouseResizeHeight && parent)
  244. {
  245. newExtent.y = getMax(0, getMax(minExtent.y, getMin(parent->getHeight(), mOrigBounds.extent.y + deltaMousePosition.y)));
  246. update = true;
  247. }
  248. }
  249. if (update)
  250. {
  251. Point2I pos = parent->localToGlobalCoord(getPosition());
  252. root->addUpdateRegion(pos, getExtent());
  253. resize(newPosition, newExtent);
  254. }
  255. }
  256. void GuiWindowCtrl::onTouchUp(const GuiEvent &event)
  257. {
  258. bool closing = mPressClose;
  259. bool maximizing = mPressMaximize;
  260. bool minimizing = mPressMinimize;
  261. mPressClose = false;
  262. mPressMaximize = false;
  263. mPressMinimize = false;
  264. mDepressed = false;
  265. mouseUnlock();
  266. setUpdate();
  267. MoveComplete();
  268. ResizeComplete();
  269. GuiControl *parent = getParent();
  270. if (! parent)
  271. return;
  272. //see if we take an action
  273. Point2I localPoint = globalToLocalCoord(event.mousePoint);
  274. if (closing && mCloseButton.pointInRect(localPoint))
  275. {
  276. if(isMethod("onClose"))
  277. {
  278. Con::executef(this, 1, "onClose");
  279. }
  280. }
  281. else if (maximizing && mMaximizeButton.pointInRect(localPoint))
  282. {
  283. if (mMaximized)
  284. {
  285. //resize to the previous position and extent, bounded by the parent
  286. resize(Point2I(getMax(0, getMin(parent->mBounds.extent.x - mStandardBounds.extent.x, mStandardBounds.point.x)),
  287. getMax(0, getMin(parent->mBounds.extent.y - mStandardBounds.extent.y, mStandardBounds.point.y))),
  288. mStandardBounds.extent);
  289. //set the flag
  290. mMaximized = false;
  291. if (isMethod("onRestore"))
  292. {
  293. Con::executef(this, 1, "onRestore");
  294. }
  295. }
  296. else
  297. {
  298. //only save the position if we're not minimized
  299. if (! mMinimized)
  300. {
  301. mStandardBounds = mBounds;
  302. }
  303. else
  304. {
  305. mMinimized = false;
  306. }
  307. //resize to fit the parent
  308. resize(Point2I(0, 0), parent->mBounds.extent);
  309. //set the flag
  310. mMaximized = true;
  311. if (isMethod("onMaximize"))
  312. {
  313. Con::executef(this, 1, "onMaximize");
  314. }
  315. }
  316. }
  317. else if (minimizing && mMinimizeButton.pointInRect(localPoint))
  318. {
  319. if (mMinimized)
  320. {
  321. //resize to the previous position and extent, bounded by the parent
  322. resize(Point2I(getMax(0, getMin(parent->mBounds.extent.x - mStandardBounds.extent.x, mStandardBounds.point.x)),
  323. getMax(0, getMin(parent->mBounds.extent.y - mStandardBounds.extent.y, mStandardBounds.point.y))),
  324. mStandardBounds.extent);
  325. //set the flag
  326. mMinimized = false;
  327. if (isMethod("onRestore"))
  328. {
  329. Con::executef(this, 1, "onRestore");
  330. }
  331. }
  332. else
  333. {
  334. if (parent->mBounds.extent.x < 100 || parent->mBounds.extent.y < mTitleHeight + 3)
  335. return;
  336. //only save the position if we're not maximized
  337. if (! mMaximized)
  338. {
  339. mStandardBounds = mBounds;
  340. }
  341. else
  342. {
  343. mMaximized = false;
  344. }
  345. //first find the lowest unused minimized index up to 32 minimized windows
  346. U32 indexMask = 0;
  347. iterator i;
  348. S32 count = 0;
  349. for (i = parent->begin(); i != parent->end() && count < 32; i++)
  350. {
  351. count++;
  352. S32 index;
  353. GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
  354. if (ctrl && ctrl->isMinimized(index))
  355. {
  356. indexMask |= (1 << index);
  357. }
  358. }
  359. //now find the first unused bit
  360. for (count = 0; count < 32; count++)
  361. {
  362. if (! (indexMask & (1 << count))) break;
  363. }
  364. //if we have more than 32 minimized windows, use the first position
  365. count = getMax(0, count);
  366. //this algorithm assumes all window have the same title height, and will minimize to 148 pix
  367. Point2I newExtent(148, mTitleHeight);
  368. //first, how many can fit across
  369. S32 numAcross = getMax(1, (parent->mBounds.extent.x / newExtent.x + 2));
  370. //find the new "mini position"
  371. Point2I newPosition;
  372. newPosition.x = (count % numAcross) * (newExtent.x + 2) + 2;
  373. newPosition.y = parent->mBounds.extent.y - (((count / numAcross) + 1) * (newExtent.y + 2)) - 2;
  374. //find the minimized position and extent
  375. resize(newPosition, newExtent);
  376. //set the index so other windows will not try to minimize to the same location
  377. mMinimizeIndex = count;
  378. //set the flag
  379. mMinimized = true;
  380. if (isMethod("onMinimize"))
  381. {
  382. Con::executef(this, 1, "onMinimize");
  383. }
  384. }
  385. }
  386. }
  387. GuiControl *GuiWindowCtrl::findNextTabable(GuiControl *curResponder, bool firstCall)
  388. {
  389. //set the global if this is the first call (directly from the canvas)
  390. if (firstCall)
  391. {
  392. GuiControl::smCurResponder = NULL;
  393. }
  394. //if the window does not already contain the first responder, return false
  395. //ie. Can't tab into or out of a window
  396. if (! ControlIsChild(curResponder))
  397. {
  398. return NULL;
  399. }
  400. //loop through, checking each child to see if it is the one that follows the firstResponder
  401. GuiControl *tabCtrl = NULL;
  402. iterator i;
  403. for (i = begin(); i != end(); i++)
  404. {
  405. GuiControl *ctrl = static_cast<GuiControl *>(*i);
  406. tabCtrl = ctrl->findNextTabable(curResponder, false);
  407. if (tabCtrl) break;
  408. }
  409. //to ensure the tab cycles within the current window...
  410. if (! tabCtrl)
  411. {
  412. tabCtrl = findFirstTabable();
  413. }
  414. mFirstResponder = tabCtrl;
  415. return tabCtrl;
  416. }
  417. GuiControl *GuiWindowCtrl::findPrevTabable(GuiControl *curResponder, bool firstCall)
  418. {
  419. if (firstCall)
  420. {
  421. GuiControl::smPrevResponder = NULL;
  422. }
  423. //if the window does not already contain the first responder, return false
  424. //ie. Can't tab into or out of a window
  425. if (! ControlIsChild(curResponder))
  426. {
  427. return NULL;
  428. }
  429. //loop through, checking each child to see if it is the one that follows the firstResponder
  430. GuiControl *tabCtrl = NULL;
  431. iterator i;
  432. for (i = begin(); i != end(); i++)
  433. {
  434. GuiControl *ctrl = static_cast<GuiControl *>(*i);
  435. tabCtrl = ctrl->findPrevTabable(curResponder, false);
  436. if (tabCtrl) break;
  437. }
  438. //to ensure the tab cycles within the current window...
  439. if (! tabCtrl)
  440. {
  441. tabCtrl = findLastTabable();
  442. }
  443. mFirstResponder = tabCtrl;
  444. return tabCtrl;
  445. }
  446. GuiWindowCtrl::Region GuiWindowCtrl::findHitRegion(const Point2I& pt)
  447. {
  448. if (mCanClose && mCloseButton.pointInRect(pt))
  449. {
  450. return CloseButton;
  451. }
  452. else if (mCanMaximize && mMaximizeButton.pointInRect(pt))
  453. {
  454. return MaxButton;
  455. }
  456. else if (mCanMinimize && mMinimizeButton.pointInRect(pt))
  457. {
  458. return MinButton;
  459. }
  460. else if (mTitleBar.pointInRect(pt))
  461. {
  462. return TitleBar;
  463. }
  464. return None;
  465. }
  466. bool GuiWindowCtrl::onKeyDown(const GuiEvent &event)
  467. {
  468. //if this control is a dead end, kill the event
  469. if ((! mVisible) || (! mActive) || (! mAwake)) return true;
  470. if ((event.keyCode == KEY_TAB) && (event.modifier & SI_CTRL))
  471. {
  472. //find the next sibling window, and select it
  473. GuiControl *parent = getParent();
  474. if (parent)
  475. {
  476. GuiWindowCtrl *firstWindow = NULL;
  477. iterator i;
  478. for (i = parent->begin(); i != parent->end(); i++)
  479. {
  480. GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
  481. if (ctrl && ctrl->getTabIndex() == mTabIndex + 1)
  482. {
  483. ctrl->selectWindow();
  484. return true;
  485. }
  486. else if (ctrl && ctrl->getTabIndex() == 0)
  487. {
  488. firstWindow = ctrl;
  489. }
  490. }
  491. //recycle from the beginning
  492. if (firstWindow != this)
  493. {
  494. firstWindow->selectWindow();
  495. return true;
  496. }
  497. }
  498. }
  499. return Parent::onKeyDown(event);
  500. }
  501. void GuiWindowCtrl::onFocus()
  502. {
  503. //bubble the focus up
  504. GuiControl *parent = getParent();
  505. if (parent)
  506. {
  507. parent->pushObjectToBack(this);
  508. parent->onFocus();
  509. }
  510. setFirstResponder(mFirstResponder);
  511. }
  512. void GuiWindowCtrl::selectWindow(void)
  513. {
  514. //first make sure this window is the front most of its siblings
  515. GuiControl *parent = getParent();
  516. if (parent)
  517. {
  518. parent->pushObjectToBack(this);
  519. }
  520. //also set the first responder to be the one within this window
  521. setFirstResponder(mFirstResponder);
  522. }
  523. void GuiWindowCtrl::ResizeComplete()
  524. {
  525. if ((mMouseResizeWidth || mMouseResizeHeight) && isMethod("onResize"))
  526. {
  527. Con::executef(this, 1, "onResize");
  528. }
  529. mMouseResizeWidth = false;
  530. mMouseResizeHeight = false;
  531. }
  532. void GuiWindowCtrl::MoveComplete()
  533. {
  534. if (mMouseMovingWin && isMethod("onMove"))
  535. {
  536. Con::executef(this, 1, "onMove");
  537. }
  538. mMouseMovingWin = false;
  539. }
  540. void GuiWindowCtrl::onRender(Point2I offset, const RectI &updateRect)
  541. {
  542. //Does this window have focus (does it or a child receive key events)?
  543. GuiCanvas *root = getRoot();
  544. GuiControl *firstResponder = root ? root->getFirstResponder() : NULL;
  545. bool hasFocus = (!firstResponder || ControlIsChild(firstResponder));
  546. GuiControlState currentState = NormalState;
  547. if (mMinimized)
  548. {
  549. currentState = DisabledState;
  550. }
  551. else if (hasFocus)
  552. {
  553. currentState = SelectedState;
  554. GuiControl *parent = getParent();
  555. if (parent)
  556. {
  557. parent->pushObjectToBack(this);
  558. }
  559. }
  560. else if (curHitRegion == TitleBar)
  561. {
  562. currentState = HighlightState;
  563. }
  564. //Render the title bar
  565. RectI ctrlRectTitle = applyMargins(offset, Point2I(mBounds.extent.x, mTitleHeight), currentState, mProfile);
  566. if (!ctrlRectTitle.isValidRect())
  567. {
  568. return;
  569. }
  570. mTitleBar.set(Point2I(ctrlRectTitle.point.x - offset.x, ctrlRectTitle.point.y - offset.y), ctrlRectTitle.extent);
  571. renderUniversalRect(ctrlRectTitle, mProfile, currentState);
  572. //Render Text and buttons
  573. dglSetBitmapModulation(mProfile->mFontColor);
  574. RectI fillRectTitle = applyBorders(ctrlRectTitle.point, ctrlRectTitle.extent, currentState, mProfile);
  575. RectI contentRectTitle = applyPadding(fillRectTitle.point, fillRectTitle.extent, currentState, mProfile);
  576. if (contentRectTitle.isValidRect())
  577. {
  578. RectI textRect = renderButtons(offset, contentRectTitle);
  579. renderText(textRect.point, textRect.extent, mText, mProfile);
  580. }
  581. //Render window contents
  582. if (!mMinimized)
  583. {
  584. currentState = currentState != SelectedState ? NormalState : SelectedState;
  585. RectI ctrlRectWindow = applyMargins(Point2I(offset.x, offset.y + mTitleHeight), Point2I(mBounds.extent.x, mBounds.extent.y - mTitleHeight), currentState, mContentProfile);
  586. if (!ctrlRectWindow.isValidRect())
  587. {
  588. return;
  589. }
  590. renderUniversalRect(ctrlRectWindow, mContentProfile, currentState);
  591. RectI fillRectWindow = applyBorders(ctrlRectWindow.point, ctrlRectWindow.extent, currentState, mContentProfile);
  592. RectI contentRectWindow = applyPadding(fillRectWindow.point, fillRectWindow.extent, currentState, mContentProfile);
  593. if (contentRectWindow.isValidRect())
  594. {
  595. //render the children
  596. renderChildControls(offset, contentRectWindow, updateRect);
  597. }
  598. }
  599. }
  600. RectI GuiWindowCtrl::renderButtons(const Point2I &offset, const RectI &contentRect)
  601. {
  602. S32 distanceFromEdge = 0;
  603. if (mCanClose)
  604. {
  605. GuiControlState state = getRegionCurrentState(Region::CloseButton);
  606. RectI content = renderButton(contentRect, distanceFromEdge, state, mCloseButtonProfile, Icon::Close);
  607. mCloseButton.set(Point2I(content.point.x - offset.x, content.point.y - offset.y), content.extent);
  608. distanceFromEdge += content.extent.x;
  609. GuiBorderProfile *leftProfile = mCloseButtonProfile->getLeftBorder();
  610. S32 leftSize = (leftProfile) ? leftProfile->getMargin(state) : 0;
  611. distanceFromEdge += leftSize;
  612. GuiBorderProfile *rightProfile = mCloseButtonProfile->getRightBorder();
  613. S32 rightSize = (rightProfile) ? rightProfile->getMargin(state) : 0;
  614. distanceFromEdge += rightSize;
  615. }
  616. if (mCanMaximize)
  617. {
  618. GuiControlState state = getRegionCurrentState(Region::MaxButton);
  619. RectI content = renderButton(contentRect, distanceFromEdge, state, mMaxButtonProfile, Icon::Max);
  620. mMaximizeButton.set(Point2I(content.point.x - offset.x, content.point.y - offset.y), content.extent);
  621. distanceFromEdge += content.extent.x;
  622. GuiBorderProfile *leftProfile = mMaxButtonProfile->getLeftBorder();
  623. S32 leftSize = (leftProfile) ? leftProfile->getMargin(state) : 0;
  624. distanceFromEdge += leftSize;
  625. GuiBorderProfile *rightProfile = mMaxButtonProfile->getRightBorder();
  626. S32 rightSize = (rightProfile) ? rightProfile->getMargin(state) : 0;
  627. distanceFromEdge += rightSize;
  628. }
  629. if (mCanMinimize)
  630. {
  631. GuiControlState state = getRegionCurrentState(Region::MinButton);
  632. RectI content = renderButton(contentRect, distanceFromEdge, state, mMinButtonProfile, Icon::Min);
  633. mMinimizeButton.set(Point2I(content.point.x - offset.x, content.point.y - offset.y), content.extent);
  634. distanceFromEdge += content.extent.x;
  635. GuiBorderProfile *leftProfile = mMinButtonProfile->getLeftBorder();
  636. S32 leftSize = (leftProfile) ? leftProfile->getMargin(state) : 0;
  637. distanceFromEdge += leftSize;
  638. GuiBorderProfile *rightProfile = mMinButtonProfile->getRightBorder();
  639. S32 rightSize = (rightProfile) ? rightProfile->getMargin(state) : 0;
  640. distanceFromEdge += rightSize;
  641. }
  642. if (mProfile->mAlignment != GuiControlProfile::AlignmentType::RightAlign)
  643. {
  644. return RectI(contentRect.point.x, contentRect.point.y, contentRect.extent.x - distanceFromEdge, contentRect.extent.y);
  645. }
  646. else
  647. {
  648. return RectI(contentRect.point.x + distanceFromEdge, contentRect.point.y, contentRect.extent.x - distanceFromEdge, contentRect.extent.y);
  649. }
  650. }
  651. RectI GuiWindowCtrl::renderButton(const RectI &contentRect, S32 distanceFromEdge, GuiControlState buttonState, GuiControlProfile *profile, Icon defaultIcon)
  652. {
  653. RectI buttonContent = applyMargins(contentRect.point, contentRect.extent, buttonState, profile);
  654. S32 horizMarginSize = contentRect.extent.x - buttonContent.extent.x;
  655. RectI finalButtonRect = RectI(contentRect.point, Point2I(buttonContent.extent.y + horizMarginSize, contentRect.extent.y));
  656. if (mProfile->mAlignment != GuiControlProfile::AlignmentType::RightAlign)
  657. {
  658. //get the right margin and add it to the distance from the edge
  659. GuiBorderProfile *rightProfile = profile->getRightBorder();
  660. S32 rightSize = (rightProfile) ? rightProfile->getMargin(buttonState) : 0;
  661. finalButtonRect.point.x = contentRect.point.x + (contentRect.extent.x - finalButtonRect.extent.x) - distanceFromEdge;
  662. distanceFromEdge += rightSize;
  663. }
  664. else
  665. {
  666. //get the left margin and add it to the disance from the edge
  667. GuiBorderProfile *leftProfile = profile->getLeftBorder();
  668. S32 leftSize = (leftProfile) ? leftProfile->getMargin(buttonState) : 0;
  669. finalButtonRect.point.x = contentRect.point.x + distanceFromEdge;
  670. distanceFromEdge += leftSize;
  671. }
  672. RectI finalButtonContent = applyMargins(finalButtonRect.point, finalButtonRect.extent, buttonState, profile);
  673. renderUniversalRect(finalButtonContent, profile, buttonState);
  674. //now draw an icon if default rendering was used.
  675. if ((profile->mImageAsset == NULL || !mCloseButtonProfile->mImageAsset->isAssetValid()) &&
  676. profile->mBitmapName == NULL)
  677. {
  678. RectI fillRect = applyBorders(finalButtonContent.point, finalButtonContent.extent, buttonState, profile);
  679. RectI contentRect = applyPadding(fillRect.point, fillRect.extent, buttonState, profile);
  680. //draw the icon
  681. if (defaultIcon == Icon::Close)
  682. {
  683. Point2I p1 = Point2I(contentRect.point.x + 1, contentRect.point.y);
  684. Point2I p2 = Point2I(contentRect.point.x, contentRect.point.y + 1);
  685. Point2I p3 = Point2I(contentRect.point.x + contentRect.extent.x - 1, contentRect.point.y + contentRect.extent.y);
  686. Point2I p4 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y + contentRect.extent.y - 1);
  687. dglDrawQuadFill(p1, p2, p3, p4, profile->getFontColor(buttonState));
  688. Point2I p5 = Point2I(contentRect.point.x, contentRect.point.y + contentRect.extent.y - 1);
  689. Point2I p6 = Point2I(contentRect.point.x + 1, contentRect.point.y + contentRect.extent.y);
  690. Point2I p7 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y + 1);
  691. Point2I p8 = Point2I(contentRect.point.x + contentRect.extent.x - 1, contentRect.point.y);
  692. dglDrawQuadFill(p5, p6, p7, p8, profile->getFontColor(buttonState));
  693. }
  694. else if (defaultIcon == Icon::Min)
  695. {
  696. S32 h = (contentRect.len_y() / 2) - 1;
  697. Point2I p1 = Point2I(contentRect.point.x, contentRect.point.y + h);
  698. Point2I p2 = Point2I(contentRect.point.x, contentRect.point.y + h + 2);
  699. Point2I p3 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y + h + 2);
  700. Point2I p4 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y + h);
  701. dglDrawQuadFill(p1, p2, p3, p4, profile->getFontColor(buttonState));
  702. }
  703. else if (defaultIcon == Icon::Max)
  704. {
  705. //left side
  706. S32 h = (contentRect.len_y() / 2) - 1;
  707. Point2I p1 = Point2I(contentRect.point.x, contentRect.point.y);
  708. Point2I p2 = Point2I(contentRect.point.x, contentRect.point.y + contentRect.extent.y);
  709. Point2I p3 = Point2I(contentRect.point.x + 2, contentRect.point.y + contentRect.extent.y);
  710. Point2I p4 = Point2I(contentRect.point.x + 2, contentRect.point.y);
  711. dglDrawQuadFill(p1, p2, p3, p4, profile->getFontColor(buttonState));
  712. //right side
  713. p1.set(contentRect.point.x + contentRect.extent.x - 2, contentRect.point.y);
  714. p2.set(contentRect.point.x + contentRect.extent.x - 2, contentRect.point.y + contentRect.extent.y);
  715. p3.set(contentRect.point.x + contentRect.extent.x, contentRect.point.y + contentRect.extent.y);
  716. p4.set(contentRect.point.x + contentRect.extent.x, contentRect.point.y);
  717. dglDrawQuadFill(p1, p2, p3, p4, profile->getFontColor(buttonState));
  718. //top
  719. p1.set(contentRect.point.x + 2, contentRect.point.y);
  720. p2.set(contentRect.point.x + 2, contentRect.point.y + 2);
  721. p3.set(contentRect.point.x + contentRect.extent.x - 2, contentRect.point.y + 2);
  722. p4.set(contentRect.point.x + contentRect.extent.x - 2, contentRect.point.y);
  723. dglDrawQuadFill(p1, p2, p3, p4, profile->getFontColor(buttonState));
  724. //bottom
  725. p1.set(contentRect.point.x + 2, contentRect.point.y + contentRect.extent.y - 2);
  726. p2.set(contentRect.point.x + 2, contentRect.point.y + contentRect.extent.y);
  727. p3.set(contentRect.point.x + contentRect.extent.x - 2, contentRect.point.y + contentRect.extent.y);
  728. p4.set(contentRect.point.x + contentRect.extent.x - 2, contentRect.point.y + contentRect.extent.y - 2);
  729. dglDrawQuadFill(p1, p2, p3, p4, profile->getFontColor(buttonState));
  730. }
  731. }
  732. return finalButtonContent;
  733. }
  734. GuiControlState GuiWindowCtrl::getRegionCurrentState(GuiWindowCtrl::Region region)
  735. {
  736. GuiControlState currentState = GuiControlState::NormalState;
  737. if (!mActive)
  738. {
  739. currentState = GuiControlState::DisabledState;
  740. }
  741. else if (curHitRegion == region && mDepressed)
  742. {
  743. currentState = GuiControlState::SelectedState;
  744. }
  745. else if (curHitRegion == region)
  746. {
  747. currentState = GuiControlState::HighlightState;
  748. }
  749. return currentState;
  750. }
  751. void GuiWindowCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
  752. {
  753. Point2I mousePos = lastGuiEvent.mousePoint;
  754. RectI winRect = mBounds;
  755. RectI rightRect = RectI( ( ( winRect.extent.x + winRect.point.x ) - mResizeRightWidth), winRect.point.y, mResizeRightWidth, winRect.extent.y );
  756. RectI bottomRect = RectI( winRect.point.x, ( ( winRect.point.y + winRect.extent.y ) - mResizeBottomHeight), winRect.extent.x , mResizeBottomHeight );
  757. bool resizeRight = rightRect.pointInRect( mousePos );
  758. bool resizeBottom = bottomRect.pointInRect( mousePos );
  759. if ( resizeRight && resizeBottom && mResizeHeight && mResizeWidth )
  760. {
  761. if (mNWSECursor == NULL)
  762. {
  763. SimObject *obj;
  764. obj = Sim::findObject("NWSECursor");
  765. mNWSECursor = dynamic_cast<GuiCursor*>(obj);
  766. }
  767. if(mNWSECursor != NULL)
  768. {
  769. cursor = mNWSECursor;
  770. }
  771. }
  772. else if ( resizeBottom && mResizeHeight )
  773. {
  774. if(mUpDownCursor == NULL)
  775. {
  776. SimObject *obj;
  777. obj = Sim::findObject("UpDownCursor");
  778. mUpDownCursor = dynamic_cast<GuiCursor*>(obj);
  779. }
  780. if (mUpDownCursor != NULL)
  781. {
  782. cursor = mUpDownCursor;
  783. }
  784. }
  785. else if ( resizeRight && mResizeWidth )
  786. {
  787. if (mLeftRightCursor == NULL)
  788. {
  789. SimObject *obj;
  790. obj = Sim::findObject("LeftRightCursor");
  791. mLeftRightCursor = dynamic_cast<GuiCursor*>(obj);
  792. }
  793. if (mLeftRightCursor != NULL)
  794. {
  795. cursor = mLeftRightCursor;
  796. }
  797. }
  798. }
  799. void GuiWindowCtrl::setControlContentProfile(GuiControlProfile* prof)
  800. {
  801. AssertFatal(prof, "GuiWindowCtrl::setControlContentProfile: invalid content profile");
  802. if (prof == mContentProfile)
  803. return;
  804. if (mAwake)
  805. mContentProfile->decRefCount();
  806. mContentProfile = prof;
  807. if (mAwake)
  808. mContentProfile->incRefCount();
  809. }
  810. void GuiWindowCtrl::setControlCloseButtonProfile(GuiControlProfile* prof)
  811. {
  812. AssertFatal(prof, "GuiWindowCtrl::setControlCloseButtonProfile: invalid close button profile");
  813. if (prof == mCloseButtonProfile)
  814. return;
  815. if (mAwake)
  816. mCloseButtonProfile->decRefCount();
  817. mCloseButtonProfile = prof;
  818. if (mAwake)
  819. mCloseButtonProfile->incRefCount();
  820. }
  821. void GuiWindowCtrl::setControlMinButtonProfile(GuiControlProfile* prof)
  822. {
  823. AssertFatal(prof, "GuiWindowCtrl::setControlMinButtonProfile: invalid minimize button profile");
  824. if (prof == mMinButtonProfile)
  825. return;
  826. if (mAwake)
  827. mMinButtonProfile->decRefCount();
  828. mMinButtonProfile = prof;
  829. if (mAwake)
  830. mMinButtonProfile->incRefCount();
  831. }
  832. void GuiWindowCtrl::setControlMaxButtonProfile(GuiControlProfile* prof)
  833. {
  834. AssertFatal(prof, "GuiWindowCtrl::setControlMaxButtonProfile: invalid maximize button profile");
  835. if (prof == mMaxButtonProfile)
  836. return;
  837. if (mAwake)
  838. mMaxButtonProfile->decRefCount();
  839. mMaxButtonProfile = prof;
  840. if (mAwake)
  841. mMaxButtonProfile->incRefCount();
  842. }
  843. void GuiWindowCtrl::setControlLeftRightCursor(GuiCursor* cursor)
  844. {
  845. AssertFatal(cursor, "GuiWindowCtrl::setControlLeftRightCursor: invalid cursor");
  846. if (cursor == mLeftRightCursor)
  847. return;
  848. mLeftRightCursor = cursor;
  849. }
  850. void GuiWindowCtrl::setControlUpDownCursor(GuiCursor* cursor)
  851. {
  852. AssertFatal(cursor, "GuiWindowCtrl::setControlUpDownCursor: invalid cursor");
  853. if (cursor == mUpDownCursor)
  854. return;
  855. mUpDownCursor = cursor;
  856. }
  857. void GuiWindowCtrl::setControlNWSECursor(GuiCursor* cursor)
  858. {
  859. AssertFatal(cursor, "GuiWindowCtrl::setControlNWSECursor: invalid cursor");
  860. if (cursor == mNWSECursor)
  861. return;
  862. mNWSECursor = cursor;
  863. }