guiFrameCtrl.cc 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  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 "graphics/dgl.h"
  24. #include "gui/guiCanvas.h"
  25. #include "gui/containers/guiFrameCtrl.h"
  26. //-----------------------------------------------------------------------------
  27. IMPLEMENT_CONOBJECT(GuiFrameSetCtrl);
  28. //-----------------------------------------------------------------------------
  29. static EnumTable::Enums borderStateEnums[] =
  30. {
  31. { GuiFrameSetCtrl::FRAME_STATE_ON, "alwaysOn" },
  32. { GuiFrameSetCtrl::FRAME_STATE_OFF, "alwaysOff" },
  33. { GuiFrameSetCtrl::FRAME_STATE_AUTO, "dynamic" }
  34. };
  35. static EnumTable gBorderStateTable(3, &borderStateEnums[0]);
  36. //-----------------------------------------------------------------------------
  37. void GuiFrameSetCtrl::initPersistFields()
  38. {
  39. Parent::initPersistFields();
  40. addField("columns", TypeS32Vector, Offset(mColumnOffsets, GuiFrameSetCtrl));
  41. addField("rows", TypeS32Vector, Offset(mRowOffsets, GuiFrameSetCtrl));
  42. addField("borderWidth", TypeS32, Offset(mFramesetDetails.mBorderWidth, GuiFrameSetCtrl));
  43. addField("borderColor", TypeColorI, Offset(mFramesetDetails.mBorderColor, GuiFrameSetCtrl));
  44. addField("borderEnable", TypeEnum, Offset(mFramesetDetails.mBorderEnable, GuiFrameSetCtrl), 1, &gBorderStateTable);
  45. addField("borderMovable", TypeEnum, Offset(mFramesetDetails.mBorderMovable, GuiFrameSetCtrl), 1, &gBorderStateTable);
  46. addField("autoBalance", TypeBool, Offset(mAutoBalance, GuiFrameSetCtrl));
  47. addField("fudgeFactor", TypeS32, Offset(mFudgeFactor, GuiFrameSetCtrl));
  48. }
  49. //-----------------------------------------------------------------------------
  50. ConsoleMethod( GuiFrameSetCtrl, frameBorder, void, 3, 4, "( index [ , enable = true ] ) Use the frameBorder method to change the frame's enable state.\n"
  51. "This function is not working as of this writing\n"
  52. "@param index Frame index to enable/disable/enable - Currently a boolean, but should actually be a string: alwaysOn, alwaysOff, dynamic.\n"
  53. "@return No return value.")
  54. {
  55. S32 index = dAtoi(argv[2]);
  56. if (argc == 3)
  57. object->frameBorderEnable(index);
  58. else
  59. object->frameBorderEnable(index, argv[3]);
  60. }
  61. ConsoleMethod( GuiFrameSetCtrl, frameMovable, void, 3, 4, "( index [ , enable = true ] ) Use the frameMovable method to change the frame's draggable state.\n"
  62. "This function is not working as of this writing\n"
  63. "@param index Frame index to enable/disable/enable - Currently a boolean, but should actually be a string: alwaysOn, alwaysOff, dynamic.\n"
  64. "@return No return value.")
  65. {
  66. S32 index = dAtoi(argv[2]);
  67. if (argc == 3)
  68. object->frameBorderMovable(index);
  69. else
  70. object->frameBorderMovable(index, argv[3]);
  71. }
  72. ConsoleMethod( GuiFrameSetCtrl, frameMinExtent, void, 5, 5, "(index, w, h ) Use the frameMinExtent method to set the minimum extent allowed for a frame.\n"
  73. "These minimum extents do not prevent a parent control from collapsing the frame control and its frames. These limits apply to dragging and resizing as is done with the frames' draggable borders\n"
  74. "@param index The frame number.\n"
  75. "@param w Minimum width in pixels.\n"
  76. "@param h Minimum height in pixels.\n"
  77. "@return No return value.")
  78. {
  79. Point2I extent(getMax(0, dAtoi(argv[3])), getMax(0, dAtoi(argv[4])));
  80. object->frameMinExtent(dAtoi(argv[2]), extent);
  81. }
  82. ConsoleMethod( GuiFrameSetCtrl, addColumn, void, 2, 2, "() Use the addColumn method to add another column to the control.\n"
  83. "The current contents of the GUIFrameCtrl may shift to fill the new column. New columns are added on the right of the control.\n"
  84. "@return No return value.\n"
  85. "@sa addRow, removeColumn, removeRow")
  86. {
  87. Vector<S32> * columns = object->columnOffsets();
  88. columns->push_back(0);
  89. object->balanceFrames();
  90. }
  91. ConsoleMethod( GuiFrameSetCtrl, addRow, void, 2, 2, "() Use the addRow method to add another row to the control.\n"
  92. "The current contents of the GUIFrameCtrl may shift to fill the new row. New rows are added on the bottom of the control.\n"
  93. "@return No return value.\n"
  94. "@sa addColumn, removeColumn, removeRow")
  95. {
  96. Vector<S32> * rows = object->rowOffsets();
  97. rows->push_back(0);
  98. object->balanceFrames();
  99. }
  100. ConsoleMethod( GuiFrameSetCtrl, removeColumn, void, 2, 2, "() Use the removeColumn method to remove a column from the right side of the control.\n"
  101. "Columns are removed right to left.\n"
  102. "@return No return value.\n"
  103. "@sa addColumn, addRow, removeRow")
  104. {
  105. Vector<S32> * columns = object->columnOffsets();
  106. if(columns->size() > 0)
  107. {
  108. columns->setSize(columns->size() - 1);
  109. object->balanceFrames();
  110. }
  111. else
  112. Con::errorf(ConsoleLogEntry::General, "No columns exist to remove");
  113. }
  114. ConsoleMethod( GuiFrameSetCtrl, removeRow, void, 2, 2, "() Use the removeRow method to remove the bottom row from the control.\n"
  115. "Rows are removed bottom to top.\n"
  116. "@return No return value.\n"
  117. "@sa addColumn, addRow, removeColumn")
  118. {
  119. Vector<S32> * rows = object->rowOffsets();
  120. if(rows->size() > 0)
  121. {
  122. rows->setSize(rows->size() - 1);
  123. object->balanceFrames();
  124. }
  125. else
  126. Con::errorf(ConsoleLogEntry::General, "No rows exist to remove");
  127. }
  128. ConsoleMethod( GuiFrameSetCtrl, getColumnCount, S32, 2, 2, "() Use the getColumnCount method to determine the number of columns in this control.\n"
  129. "@return Returns an integer value equal to the number of columns in this frame.\n"
  130. "@sa getRowCount")
  131. {
  132. return(object->columnOffsets()->size());
  133. }
  134. ConsoleMethod( GuiFrameSetCtrl, getRowCount, S32, 2, 2, "() Use the getRowCount method to determine the number of rows in this control.\n"
  135. "@return Returns an integer value equal to the number of rows in this frame.\n"
  136. "@sa getColumnCount")
  137. {
  138. return(object->rowOffsets()->size());
  139. }
  140. ConsoleMethod( GuiFrameSetCtrl, getColumnOffset, S32, 3, 3, "( column ) Use the getColumnOffset method to determine the current pixel location of the specified column.\n"
  141. "Column 0 is the first column on the left side of frame 0. Column 1 is on the right side of frame 0 and the left side of frame 1, etc.\n"
  142. "@param column An integer value specifying the column to examine.\n"
  143. "@return Returns the pixel offset for the specified column.\n"
  144. "@sa getRowOffset, setColumnOffset, setRowOffset")
  145. {
  146. S32 index = dAtoi(argv[2]);
  147. if(index < 0 || index > object->columnOffsets()->size())
  148. {
  149. Con::errorf(ConsoleLogEntry::General, "Column index out of range");
  150. return(0);
  151. }
  152. return((*object->columnOffsets())[index]);
  153. }
  154. ConsoleMethod( GuiFrameSetCtrl, getRowOffset, S32, 3, 3, "( row ) Use the getRowOffset method to determine the current pixel location of the specified row.\n"
  155. "Row 0 is the first row on the top of the first row of frames. Row 1 is below the first row of frames and above the second row of frames, etc. 1, etc.\n"
  156. "@param row An integer value specifying the row to examine.\n"
  157. "@return Returns the pixel offset for the specified row.\n"
  158. "@sa getColumnOffset, setColumnOffset, setRowOffset")
  159. {
  160. S32 index = dAtoi(argv[2]);
  161. if(index < 0 || index > object->rowOffsets()->size())
  162. {
  163. Con::errorf(ConsoleLogEntry::General, "Row index out of range");
  164. return(0);
  165. }
  166. return((*object->rowOffsets())[index]);
  167. }
  168. ConsoleMethod( GuiFrameSetCtrl, setColumnOffset, void, 4, 4, "( column , offset ) Use the setColumnOffset method to determine the current pixel location of the specified column.\n"
  169. "Column 0 is the first column on the left side of frame 0. Column 1 is on the right side of frame 0 and the left side of frame 1, etc. The left-most and right-most columns cannot be moved.\n"
  170. "@param column An integer value specifying the column to examine.\n"
  171. "@param offset An integer value specifying the new column offset in pixels.\n"
  172. "@return No return value.\n"
  173. "@sa getColumnOffset, getRowOffset, setRowOffset")
  174. {
  175. Vector<S32> & columns = *(object->columnOffsets());
  176. S32 index = dAtoi(argv[2]);
  177. if(index < 0 || index > columns.size())
  178. {
  179. Con::errorf(ConsoleLogEntry::General, "Column index out of range");
  180. return;
  181. }
  182. //
  183. S32 offset = dAtoi(argv[3]);
  184. // check the offset
  185. if(((index > 0) && (offset < columns[index-1])) ||
  186. ((index < (columns.size() - 1)) && (offset > columns[index+1])))
  187. {
  188. Con::errorf(ConsoleLogEntry::General, "Invalid column offset");
  189. return;
  190. }
  191. columns[index] = offset;
  192. object->updateSizes();
  193. }
  194. ConsoleMethod( GuiFrameSetCtrl, setRowOffset, void, 4, 4, "( row , offset ) Use the setRowOffset method to set the current pixel location of the specified row.\n"
  195. "Row 0 is the first row on the top of the first row of frames. Row 1 is below the first row of frames and above the second row of frames, etc. 1, etc. The bottom-most and top-most rows cannot be moved.\n"
  196. "@param row An integer value specifying the row to modify.\n"
  197. "@param offset An integer value specifying the new row offset in pixels.\n"
  198. "@return No return value.\n"
  199. "@sa getColumnOffset, getRowOffset, setColumnOffset")
  200. {
  201. Vector<S32> & rows = *(object->rowOffsets());
  202. S32 index = dAtoi(argv[2]);
  203. if(index < 0 || index > rows.size())
  204. {
  205. Con::errorf(ConsoleLogEntry::General, "Row index out of range");
  206. return;
  207. }
  208. //
  209. S32 offset = dAtoi(argv[3]);
  210. // check the offset
  211. if(((index > 0) && (offset < rows[index-1])) ||
  212. ((index < (rows.size() - 1)) && (offset > rows[index+1])))
  213. {
  214. Con::errorf(ConsoleLogEntry::General, "Invalid row offset");
  215. return;
  216. }
  217. rows[index] = offset;
  218. object->updateSizes();
  219. }
  220. //-----------------------------------------------------------------------------
  221. GuiFrameSetCtrl::GuiFrameSetCtrl()
  222. {
  223. VECTOR_SET_ASSOCIATION(mColumnOffsets);
  224. VECTOR_SET_ASSOCIATION(mRowOffsets);
  225. VECTOR_SET_ASSOCIATION(mFrameDetails);
  226. mAutoBalance = true;
  227. mIsContainer = true;
  228. init(1, 1, NULL, NULL);
  229. }
  230. //-----------------------------------------------------------------------------
  231. GuiFrameSetCtrl::GuiFrameSetCtrl(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[])
  232. {
  233. init(columns, rows, columnOffsets, rowOffsets);
  234. }
  235. //-----------------------------------------------------------------------------
  236. GuiFrameSetCtrl::~GuiFrameSetCtrl()
  237. {
  238. while (mFrameDetails.size() > 0)
  239. {
  240. delete mFrameDetails.last();
  241. mFrameDetails.pop_back();
  242. }
  243. }
  244. //-----------------------------------------------------------------------------
  245. void GuiFrameSetCtrl::addObject(SimObject *object)
  246. {
  247. AssertFatal(object != NULL, "GuiFrameSetCtrl::addObject: NULL object");
  248. // assign the object to a frame - give it default frame details
  249. Parent::addObject(object);
  250. GuiControl *gc = dynamic_cast<GuiControl *>(object);
  251. if (gc != NULL)
  252. {
  253. FrameDetail *detail = new FrameDetail;
  254. detail->mMinExtent = gc->mMinExtent;
  255. mFrameDetails.push_back(detail);
  256. }
  257. else
  258. mFrameDetails.push_back(NULL);
  259. // resize it to fit into the frame to which it is assigned (if no frame for it, don't bother resizing)
  260. if(isAwake())
  261. computeSizes();
  262. }
  263. //-----------------------------------------------------------------------------
  264. void GuiFrameSetCtrl::removeObject(SimObject *object)
  265. {
  266. if (object != NULL)
  267. {
  268. VectorPtr<SimObject *>::iterator soitr;
  269. VectorPtr<FrameDetail *>::iterator fditr = mFrameDetails.begin();
  270. for (soitr = begin(); soitr != end(); soitr++, fditr++)
  271. {
  272. if (*soitr == object)
  273. {
  274. delete *fditr;
  275. mFrameDetails.erase(fditr);
  276. break;
  277. }
  278. }
  279. }
  280. Parent::removeObject(object);
  281. }
  282. //-----------------------------------------------------------------------------
  283. void GuiFrameSetCtrl::resize(const Point2I &newPos, const Point2I &newExtent)
  284. {
  285. // rebalance before losing the old extent (if required)
  286. if (mAutoBalance == true)
  287. rebalance(newExtent);
  288. Parent::resize(newPos, newExtent);
  289. // compute new sizing info for the frames - takes care of resizing the children
  290. computeSizes( !mAutoBalance );
  291. }
  292. //-----------------------------------------------------------------------------
  293. void GuiFrameSetCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
  294. {
  295. Region curRegion = NONE;
  296. //*** Determine the region by mouse position.
  297. Point2I curMousePos = globalToLocalCoord(lastGuiEvent.mousePoint);
  298. curRegion = pointInAnyRegion(curMousePos);
  299. switch (curRegion)
  300. {
  301. case VERTICAL_DIVIDER:
  302. // change to left-right cursor
  303. if(GuiControl::smCursorChanged != CursorManager::curResizeVert)
  304. {
  305. //*** We've already changed the cursor, so set it back before we change it again.
  306. if(GuiControl::smCursorChanged != -1)
  307. Input::popCursor();
  308. //*** Now change the cursor shape
  309. Input::pushCursor(CursorManager::curResizeVert);
  310. GuiControl::smCursorChanged = CursorManager::curResizeVert;
  311. }
  312. break;
  313. case HORIZONTAL_DIVIDER:
  314. // change to up-down cursor
  315. if(GuiControl::smCursorChanged != CursorManager::curResizeHorz)
  316. {
  317. //*** We've already changed the cursor, so set it back before we change it again.
  318. if(GuiControl::smCursorChanged != -1)
  319. Input::popCursor();
  320. //*** Now change the cursor shape
  321. Input::pushCursor(CursorManager::curResizeHorz);
  322. GuiControl::smCursorChanged = CursorManager::curResizeHorz;
  323. }
  324. break;
  325. case DIVIDER_INTERSECTION:
  326. // change to move cursor
  327. if(GuiControl::smCursorChanged != CursorManager::curResizeAll)
  328. {
  329. //*** We've already changed the cursor, so set it back before we change it again.
  330. if(GuiControl::smCursorChanged != -1)
  331. Input::popCursor();
  332. //*** Now change the cursor shape
  333. Input::pushCursor(CursorManager::curResizeAll);
  334. GuiControl::smCursorChanged = CursorManager::curResizeAll;
  335. }
  336. break;
  337. case NONE:
  338. default:
  339. if(GuiControl::smCursorChanged != -1)
  340. {
  341. //*** We've already changed the cursor, so set it back before we change it again.
  342. Input::popCursor();
  343. GuiControl::smCursorChanged = -1;
  344. }
  345. break;
  346. }
  347. }
  348. //-----------------------------------------------------------------------------
  349. void GuiFrameSetCtrl::onMouseDown(const GuiEvent &event)
  350. {
  351. if (mFramesetDetails.mBorderEnable != FRAME_STATE_OFF && mFramesetDetails.mBorderMovable != FRAME_STATE_OFF)
  352. {
  353. // determine if a divider was hit
  354. Point2I curMousePos = globalToLocalCoord(event.mousePoint);
  355. findHitRegion(curMousePos); // sets mCurVerticalHit, mCurHorizontalHit, & mCurHitRegion
  356. if (mCurHitRegion != NONE)
  357. {
  358. mouseLock();
  359. setFirstResponder();
  360. setUpdate();
  361. }
  362. }
  363. }
  364. //-----------------------------------------------------------------------------
  365. void GuiFrameSetCtrl::onMouseUp(const GuiEvent &event)
  366. {
  367. if (mCurHitRegion != NONE)
  368. {
  369. mCurHitRegion = NONE;
  370. mCurVerticalHit = NO_HIT;
  371. mCurHorizontalHit = NO_HIT;
  372. mouseUnlock();
  373. setUpdate();
  374. }
  375. }
  376. //-----------------------------------------------------------------------------
  377. void GuiFrameSetCtrl::onMouseDragged(const GuiEvent &event)
  378. {
  379. if (mCurHitRegion != NONE)
  380. {
  381. // identify the frames involved in the resizing, checking if they are resizable
  382. S32 indexes[4];
  383. S32 activeFrames = findResizableFrames(indexes);
  384. if (activeFrames > 0)
  385. {
  386. S32 range[4];
  387. // determine the range of movement, limiting as specified by individual frames
  388. computeMovableRange(mCurHitRegion, mCurVerticalHit, mCurHorizontalHit, activeFrames, indexes, range);
  389. Point2I curMousePos = globalToLocalCoord(event.mousePoint);
  390. switch (mCurHitRegion)
  391. {
  392. case VERTICAL_DIVIDER:
  393. mColumnOffsets[mCurVerticalHit] = getMin(getMax(range[0], curMousePos.x - mLocOnDivider.x), range[1]);
  394. break;
  395. case HORIZONTAL_DIVIDER:
  396. mRowOffsets[mCurHorizontalHit] = getMin(getMax(range[0], curMousePos.y - mLocOnDivider.y), range[1]);
  397. break;
  398. case DIVIDER_INTERSECTION:
  399. mColumnOffsets[mCurVerticalHit] = getMin(getMax(range[0], curMousePos.x - mLocOnDivider.x), range[1]);
  400. mRowOffsets[mCurHorizontalHit] = getMin(getMax(range[2], curMousePos.y - mLocOnDivider.y), range[3]);
  401. break;
  402. default:
  403. return;
  404. }
  405. computeSizes();
  406. }
  407. }
  408. }
  409. //-----------------------------------------------------------------------------
  410. bool GuiFrameSetCtrl::onAdd()
  411. {
  412. if (Parent::onAdd() == false)
  413. return(false);
  414. return(true);
  415. }
  416. bool GuiFrameSetCtrl::onWake()
  417. {
  418. if (Parent::onWake() == false)
  419. return(false);
  420. computeSizes();
  421. return(true);
  422. }
  423. //-----------------------------------------------------------------------------
  424. void GuiFrameSetCtrl::onRender(Point2I offset, const RectI &updateRect )
  425. {
  426. RectI r(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y);
  427. // draw the border of the frameset if specified
  428. //if (mProfile->mOpaque)
  429. // dglDrawRectFill(r, mProfile->mFillColor);
  430. drawDividers(offset);
  431. //if (mProfile->mBorder)
  432. // dglDrawRect(r, mProfile->mBorderColor);
  433. // draw the frame contents
  434. renderChildControls(offset, mBounds, updateRect);
  435. }
  436. //-----------------------------------------------------------------------------
  437. bool GuiFrameSetCtrl::init(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[])
  438. {
  439. if (columns != 0 && rows != 0)
  440. {
  441. mColumnOffsets.clear();
  442. mRowOffsets.clear();
  443. U32 i;
  444. for (i = 0; i < columns; i++)
  445. {
  446. if (columnOffsets == NULL)
  447. mColumnOffsets.push_back(0);
  448. else
  449. {
  450. AssertFatal(columnOffsets != NULL, "GuiFrameSetCtrl::init: NULL column offsets");
  451. mColumnOffsets.push_back((U32)columnOffsets[i]);
  452. if (i > 0)
  453. {
  454. AssertFatal(mColumnOffsets[i - 1] < mColumnOffsets[i], "GuiFrameSetCtrl::init: column offsets must be monotonically increasing");
  455. mColumnOffsets.clear();
  456. return(false);
  457. }
  458. }
  459. }
  460. for (i = 0; i < rows; i++)
  461. {
  462. if (rowOffsets == NULL)
  463. mRowOffsets.push_back(0);
  464. else
  465. {
  466. AssertFatal(rowOffsets != NULL, "GuiFrameSetCtrl::init: NULL row offsets");
  467. mRowOffsets.push_back((U32)rowOffsets[i]);
  468. if (i > 0)
  469. {
  470. AssertFatal(mRowOffsets[i - 1] < mRowOffsets[i], "GuiFrameSetCtrl::init: row offsets must be monotonically increasing");
  471. mRowOffsets.clear();
  472. return(false);
  473. }
  474. }
  475. }
  476. }
  477. mFramesetDetails.mBorderWidth = DEFAULT_BORDER_WIDTH;
  478. mFramesetDetails.mBorderEnable = FRAME_STATE_AUTO;
  479. mFramesetDetails.mBorderMovable = FRAME_STATE_AUTO;
  480. mAutoBalance = false;
  481. mFudgeFactor = 0;
  482. mCurHitRegion = NONE;
  483. mCurVerticalHit = NO_HIT;
  484. mCurHorizontalHit = NO_HIT;
  485. return(true);
  486. }
  487. //-----------------------------------------------------------------------------
  488. // point is assumed to already be in local coordinates.
  489. GuiFrameSetCtrl::Region GuiFrameSetCtrl::findHitRegion(const Point2I &point)
  490. {
  491. Vector<S32>::iterator itr;
  492. S32 i = 1;
  493. // step through vertical dividers
  494. for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++, i++)
  495. {
  496. if (hitVerticalDivider(*itr, point) == true)
  497. {
  498. mCurVerticalHit = i;
  499. mLocOnDivider.x = point.x - (*itr);
  500. break;
  501. }
  502. }
  503. i = 1;
  504. // step through horizontal dividers
  505. for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++, i++)
  506. {
  507. if (hitHorizontalDivider(*itr, point) == true)
  508. {
  509. mCurHorizontalHit = i;
  510. mLocOnDivider.y = point.y - (*itr);
  511. break;
  512. }
  513. }
  514. // now set type of hit...
  515. if (mCurVerticalHit != NO_HIT)
  516. {
  517. if (mCurHorizontalHit != NO_HIT)
  518. return(mCurHitRegion = DIVIDER_INTERSECTION);
  519. else
  520. return(mCurHitRegion = VERTICAL_DIVIDER);
  521. }
  522. else if (mCurHorizontalHit != NO_HIT)
  523. return(mCurHitRegion = HORIZONTAL_DIVIDER);
  524. else
  525. return(mCurHitRegion = NONE);
  526. }
  527. GuiFrameSetCtrl::Region GuiFrameSetCtrl::pointInAnyRegion(const Point2I &point)
  528. {
  529. Vector<S32>::iterator itr;
  530. S32 i = 1;
  531. S32 curVertHit = NO_HIT, curHorzHit = NO_HIT;
  532. Region result = NONE;
  533. // step through vertical dividers
  534. for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++, i++)
  535. {
  536. if (hitVerticalDivider(*itr, point) == true)
  537. {
  538. curVertHit = i;
  539. break;
  540. }
  541. }
  542. i = 1;
  543. // step through horizontal dividers
  544. for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++, i++)
  545. {
  546. if (hitHorizontalDivider(*itr, point) == true)
  547. {
  548. curHorzHit = i;
  549. break;
  550. }
  551. }
  552. // now select the type of region in which the point lies
  553. if (curVertHit != NO_HIT)
  554. {
  555. if (curHorzHit != NO_HIT)
  556. result = DIVIDER_INTERSECTION;
  557. else
  558. result = VERTICAL_DIVIDER;
  559. }
  560. else if (curHorzHit != NO_HIT)
  561. result = HORIZONTAL_DIVIDER;
  562. return(result);
  563. }
  564. //-----------------------------------------------------------------------------
  565. // indexes must have at least 4 entries.
  566. // This *may* modify mCurVerticalHit, mCurHorizontalHit, and mCurHitRegion if it
  567. // determines that movement is disabled by frame content.
  568. // If it does make such a change, it also needs to do the reset performed by
  569. // onMouseUp if it sets mCurHitRegion to NONE.
  570. S32 GuiFrameSetCtrl::findResizableFrames(S32 indexes[])
  571. {
  572. AssertFatal(indexes != NULL, "GuiFrameSetCtrl::findResizableFrames: NULL indexes");
  573. // first, find the column and row indexes of the affected columns/rows
  574. S32 validIndexes = 0;
  575. switch (mCurHitRegion)
  576. {
  577. case VERTICAL_DIVIDER: // columns
  578. indexes[0] = mCurVerticalHit - 1;
  579. indexes[1] = mCurVerticalHit;
  580. validIndexes = 2;
  581. break;
  582. case HORIZONTAL_DIVIDER: // rows
  583. indexes[0] = mCurHorizontalHit - 1;
  584. indexes[1] = mCurHorizontalHit;
  585. validIndexes = 2;
  586. break;
  587. case DIVIDER_INTERSECTION: // columns & rows
  588. indexes[0] = mCurVerticalHit - 1; // columns
  589. indexes[1] = mCurVerticalHit;
  590. indexes[2] = mCurHorizontalHit - 1; // rows
  591. indexes[3] = mCurHorizontalHit;
  592. validIndexes = 4;
  593. break;
  594. default:
  595. break;
  596. }
  597. // now, make sure these indexes are for movable frames
  598. VectorPtr<SimObject *>::iterator soitr;
  599. VectorPtr<FrameDetail *>::iterator fditr = mFrameDetails.begin();
  600. GuiControl *gc;
  601. S32 column = 0;
  602. S32 row = 0;
  603. S32 columns = mColumnOffsets.size();
  604. S32 rows = mRowOffsets.size();
  605. for (soitr = begin(); soitr != end() && validIndexes > 0; soitr++, fditr++)
  606. {
  607. // don't continue if some of the frames are empty
  608. if (fditr == mFrameDetails.end())
  609. break;
  610. // otherwise, check the gui elements for move-restrictions
  611. gc = dynamic_cast<GuiControl *>(*soitr);
  612. if (gc != NULL)
  613. {
  614. if (column == columns)
  615. {
  616. column = 0;
  617. row++;
  618. }
  619. if (row == rows)
  620. break;
  621. switch (mCurHitRegion)
  622. {
  623. case VERTICAL_DIVIDER:
  624. if ((column == indexes[0] || column == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF)
  625. validIndexes = 0;
  626. break;
  627. case HORIZONTAL_DIVIDER:
  628. if ((row == indexes[0] || row == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF)
  629. validIndexes = 0;
  630. break;
  631. case DIVIDER_INTERSECTION:
  632. if ((column == indexes[0] || column == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF)
  633. {
  634. if ((row == indexes[2] || row == indexes[3]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF)
  635. validIndexes = 0;
  636. else
  637. {
  638. mCurHitRegion = HORIZONTAL_DIVIDER;
  639. mCurVerticalHit = NO_HIT;
  640. indexes[0] = indexes[2];
  641. indexes[1] = indexes[3];
  642. validIndexes = 2;
  643. }
  644. }
  645. else if ((row == indexes[2] || row == indexes[3]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF)
  646. {
  647. mCurHitRegion = VERTICAL_DIVIDER;
  648. mCurHorizontalHit = NO_HIT;
  649. validIndexes = 2;
  650. }
  651. break;
  652. default:
  653. return(0);
  654. }
  655. column++;
  656. }
  657. }
  658. if (validIndexes == 0)
  659. {
  660. mCurHitRegion = NONE;
  661. mCurVerticalHit = NO_HIT;
  662. mCurHorizontalHit = NO_HIT;
  663. mouseUnlock();
  664. setUpdate();
  665. }
  666. return(validIndexes);
  667. }
  668. //-----------------------------------------------------------------------------
  669. // This method locates the gui control and frame detail associated with a
  670. // particular frame index.
  671. bool GuiFrameSetCtrl::findFrameContents(S32 index, GuiControl **gc, FrameDetail **fd)
  672. {
  673. AssertFatal(gc != NULL, "GuiFrameSetCtrl::findFrameContents: NULL gui control pointer");
  674. AssertFatal(fd != NULL, "GuiFrameSetCtrl::findFrameContents: NULL frame detail pointer");
  675. AssertFatal(*gc == NULL, "GuiFrameSetCtrl::findFrameContents: contents of gui control must be NULL");
  676. AssertFatal(*fd == NULL, "GuiFrameSetCtrl::findFrameContents: contents of frame detail must be NULL");
  677. if (index >= 0 && index < size())
  678. {
  679. VectorPtr<SimObject *>::iterator soitr;
  680. VectorPtr<FrameDetail *>::iterator fditr = mFrameDetails.begin();
  681. for (soitr = begin(); soitr != end(); soitr++, fditr++, index--)
  682. {
  683. if (index == 0)
  684. {
  685. GuiControl *guiCtrl = dynamic_cast<GuiControl *>(*soitr);
  686. if (guiCtrl != NULL)
  687. {
  688. *gc = guiCtrl;
  689. *fd = *fditr;
  690. return(true);
  691. }
  692. else
  693. break;
  694. }
  695. }
  696. }
  697. return(false);
  698. }
  699. //-----------------------------------------------------------------------------
  700. void GuiFrameSetCtrl::computeSizes(bool balanceFrames)
  701. {
  702. S32 columns = mColumnOffsets.size();
  703. S32 rows = mRowOffsets.size();
  704. S32 vDividers = columns - 1;
  705. S32 hDividers = rows - 1;
  706. if ( !balanceFrames && mFrameDetails.size() == ( columns * rows ) )
  707. {
  708. // This will do some goofy things if you allow this control to resize smaller than
  709. // the additive minimum extents of its frames--so don't.
  710. S32 index, delta;
  711. if ( columns > 1 )
  712. {
  713. index = columns - 1;
  714. delta = mFrameDetails[index]->mMinExtent.x - ( mBounds.extent.x - mColumnOffsets[index] );
  715. while ( delta > 0 )
  716. {
  717. mColumnOffsets[index--] -= delta;
  718. if ( index >= 0 )
  719. delta = mFrameDetails[index]->mMinExtent.x - ( mColumnOffsets[index + 1] - mColumnOffsets[index] );
  720. else
  721. break;
  722. }
  723. }
  724. if ( rows > 1 )
  725. {
  726. index = rows - 1;
  727. delta = mFrameDetails[columns * index]->mMinExtent.y - ( mBounds.extent.y - mRowOffsets[index] );
  728. while ( delta > 0 )
  729. {
  730. mRowOffsets[index--] -= delta;
  731. if ( index >= 0 )
  732. delta = mFrameDetails[columns * index]->mMinExtent.y - ( mRowOffsets[index + 1] - mRowOffsets[index] );
  733. else
  734. break;
  735. }
  736. }
  737. }
  738. // first, update the divider placement if necessary
  739. if (balanceFrames == true && mColumnOffsets.size() > 0 && mRowOffsets.size() > 0)
  740. {
  741. Vector<S32>::iterator itr;
  742. F32 totWidth = F32(mBounds.extent.x - vDividers * mFramesetDetails.mBorderWidth);
  743. F32 totHeight = F32(mBounds.extent.y - hDividers * mFramesetDetails.mBorderWidth);
  744. F32 frameWidth = totWidth/(F32)columns;
  745. F32 frameHeight = totHeight/(F32)rows;
  746. F32 i = 0.;
  747. for (itr = mColumnOffsets.begin(); itr != mColumnOffsets.end(); itr++, i++)
  748. *itr = (S32)(i * (frameWidth + (F32)mFramesetDetails.mBorderWidth));
  749. i = 0.;
  750. for (itr = mRowOffsets.begin(); itr != mRowOffsets.end(); itr++, i++)
  751. *itr = (S32)(i * (frameHeight + (F32)mFramesetDetails.mBorderWidth));
  752. }
  753. // now, resize the contents of each frame (and move content w/o a frame beyond visible range)
  754. VectorPtr<SimObject *>::iterator soitr;
  755. GuiControl *gc;
  756. S32 column = 0;
  757. S32 row = 0;
  758. Point2I newPos;
  759. Point2I newExtent;
  760. // step through all the children
  761. for (soitr = begin(); soitr != end(); soitr++)
  762. {
  763. // column and row track the current frame being resized
  764. if (column == columns)
  765. {
  766. column = 0;
  767. row++;
  768. }
  769. // resize the contents if its a gui control...
  770. gc = dynamic_cast<GuiControl *>(*soitr);
  771. if (gc != NULL)
  772. {
  773. if (row >= rows)
  774. {
  775. // no more visible frames
  776. newPos = mBounds.extent;
  777. newExtent.set(DEFAULT_MIN_FRAME_EXTENT, DEFAULT_MIN_FRAME_EXTENT);
  778. gc->resize(newPos, newExtent);
  779. continue;
  780. }
  781. else
  782. {
  783. // determine x components of new position & extent
  784. newPos.x = mColumnOffsets[column];
  785. if (column == vDividers)
  786. newExtent.x = mBounds.extent.x - mColumnOffsets[column]; // last column
  787. else
  788. newExtent.x = mColumnOffsets[column + 1] - mColumnOffsets[column] - mFramesetDetails.mBorderWidth; // any other column
  789. // determine y components of new position & extent
  790. newPos.y = mRowOffsets[row];
  791. if (row == hDividers)
  792. newExtent.y = mBounds.extent.y - mRowOffsets[row]; // last row
  793. else
  794. newExtent.y = mRowOffsets[row + 1] - mRowOffsets[row] - mFramesetDetails.mBorderWidth; // any other row
  795. // apply the new position & extent
  796. gc->resize(newPos, newExtent);
  797. column++;
  798. }
  799. }
  800. }
  801. }
  802. //-----------------------------------------------------------------------------
  803. // this method looks at the previous offsets, and uses them to redistribute
  804. // the available height & width proportionally.
  805. void GuiFrameSetCtrl::rebalance(const Point2I &newExtent)
  806. {
  807. // look at old_width and old_height - current extent
  808. F32 widthScale = (F32)newExtent.x/(F32)mBounds.extent.x;
  809. F32 heightScale = (F32)newExtent.y/(F32)mBounds.extent.y;
  810. Vector<S32>::iterator itr;
  811. // look at old width offsets
  812. for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++)
  813. // multiply each by new_width/old_width
  814. *itr = S32(F32(*itr) * widthScale);
  815. // look at old height offsets
  816. for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++)
  817. // multiply each by new_height/new_width
  818. *itr = S32(F32(*itr) * heightScale);
  819. }
  820. //-----------------------------------------------------------------------------
  821. void GuiFrameSetCtrl::computeMovableRange(Region hitRegion, S32 vertHit, S32 horzHit, S32 numIndexes, const S32 indexes[], S32 ranges[])
  822. {
  823. S32 hardRanges[4];
  824. switch (numIndexes)
  825. {
  826. case 2:
  827. switch (hitRegion)
  828. {
  829. case VERTICAL_DIVIDER:
  830. ranges[0] = hardRanges[0] = (vertHit <= 1) ? mFramesetDetails.mBorderWidth : mColumnOffsets[vertHit - 1] + mFramesetDetails.mBorderWidth;
  831. ranges[1] = hardRanges[1] = (vertHit >= (mColumnOffsets.size() - 1)) ? mBounds.extent.x : mColumnOffsets[vertHit + 1] - mFramesetDetails.mBorderWidth;
  832. break;
  833. case HORIZONTAL_DIVIDER:
  834. ranges[0] = hardRanges[0] = (horzHit <= 1) ? mFramesetDetails.mBorderWidth : mRowOffsets[horzHit - 1] + mFramesetDetails.mBorderWidth;
  835. ranges[1] = hardRanges[1] = (horzHit >= (mRowOffsets.size() - 1)) ? mBounds.extent.y : mRowOffsets[horzHit + 1] - mFramesetDetails.mBorderWidth;
  836. break;
  837. default:
  838. return;
  839. }
  840. break;
  841. case 4:
  842. if (hitRegion == DIVIDER_INTERSECTION)
  843. {
  844. ranges[0] = hardRanges[0] = (vertHit <= 1) ? mFramesetDetails.mBorderWidth : mColumnOffsets[vertHit - 1] + mFramesetDetails.mBorderWidth;
  845. ranges[1] = hardRanges[1] = (vertHit >= (mColumnOffsets.size() - 1)) ? mBounds.extent.x : mColumnOffsets[vertHit + 1] - mFramesetDetails.mBorderWidth;
  846. ranges[2] = hardRanges[2] = (horzHit <= 1) ? mFramesetDetails.mBorderWidth : mRowOffsets[horzHit - 1] + mFramesetDetails.mBorderWidth;
  847. ranges[3] = hardRanges[3] = (horzHit >= (mRowOffsets.size() - 1)) ? mBounds.extent.y : mRowOffsets[horzHit + 1] - mFramesetDetails.mBorderWidth;
  848. }
  849. else
  850. return;
  851. break;
  852. default:
  853. return;
  854. }
  855. // now that we have the hard ranges, reduce ranges based on minimum frame extents
  856. VectorPtr<SimObject *>::iterator soitr;
  857. VectorPtr<FrameDetail *>::iterator fditr = mFrameDetails.begin();
  858. GuiControl *gc;
  859. S32 column = 0;
  860. S32 row = 0;
  861. S32 columns = mColumnOffsets.size();
  862. S32 rows = mRowOffsets.size();
  863. for (soitr = begin(); soitr != end(); soitr++, fditr++)
  864. {
  865. // only worry about visible frames
  866. if (column == columns)
  867. {
  868. column = 0;
  869. row++;
  870. }
  871. if (row == rows)
  872. return;
  873. gc = dynamic_cast<GuiControl *>(*soitr);
  874. if (gc != NULL)
  875. {
  876. // the gui control is in a visible frame, so look at its frame details
  877. if ((*fditr) != NULL)
  878. {
  879. switch (hitRegion)
  880. {
  881. case VERTICAL_DIVIDER:
  882. if (column == indexes[0])
  883. ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.x);
  884. if (column == indexes[1])
  885. ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.x);
  886. break;
  887. case HORIZONTAL_DIVIDER:
  888. if (row == indexes[0])
  889. ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.y);
  890. if (row == indexes[1])
  891. ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.y);
  892. break;
  893. case DIVIDER_INTERSECTION:
  894. if (column == indexes[0])
  895. ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.x);
  896. if (column == indexes[1])
  897. ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.x);
  898. if (row == indexes[2])
  899. ranges[2] = getMax(ranges[2], hardRanges[2] + (*fditr)->mMinExtent.y);
  900. if (row == indexes[3])
  901. ranges[3] = getMin(ranges[3], hardRanges[3] - (*fditr)->mMinExtent.y);
  902. break;
  903. default:
  904. return;
  905. }
  906. }
  907. column++;
  908. }
  909. }
  910. }
  911. //-----------------------------------------------------------------------------
  912. void GuiFrameSetCtrl::drawDividers(const Point2I &offset)
  913. {
  914. // draw the frame dividers, if they are enabled
  915. if (mFramesetDetails.mBorderEnable != FRAME_STATE_OFF)
  916. {
  917. RectI r;
  918. Vector<S32>::iterator itr;
  919. for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++)
  920. {
  921. r.point = Point2I(*itr - mFramesetDetails.mBorderWidth, mFudgeFactor) + offset;
  922. r.extent.set(mFramesetDetails.mBorderWidth, mBounds.extent.y - ( 2 * mFudgeFactor ) );
  923. dglDrawRectFill(r, mFramesetDetails.mBorderColor);
  924. }
  925. for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++)
  926. {
  927. r.point = Point2I(mFudgeFactor, *itr - mFramesetDetails.mBorderWidth) + offset;
  928. r.extent.set(mBounds.extent.x - ( 2 * mFudgeFactor ), mFramesetDetails.mBorderWidth);
  929. dglDrawRectFill(r, mFramesetDetails.mBorderColor);
  930. }
  931. }
  932. }
  933. //-----------------------------------------------------------------------------
  934. void GuiFrameSetCtrl::frameBorderEnable(S32 index, const char *state)
  935. {
  936. GuiControl *gc = NULL;
  937. FrameDetail *fd = NULL;
  938. if (findFrameContents(index, &gc, &fd) == true && fd != NULL)
  939. {
  940. if (state != NULL)
  941. {
  942. // find the value for the detail member
  943. for (S32 i = 0; i < gBorderStateTable.size; i++)
  944. {
  945. if (dStrcmp(state, gBorderStateTable.table[i].label) == 0)
  946. fd->mBorderEnable = gBorderStateTable.table[i].index;
  947. }
  948. }
  949. else
  950. // defaults to AUTO if NULL passed in state
  951. fd->mBorderEnable = FRAME_STATE_AUTO;
  952. }
  953. }
  954. //-----------------------------------------------------------------------------
  955. void GuiFrameSetCtrl::frameBorderMovable(S32 index, const char *state)
  956. {
  957. GuiControl *gc = NULL;
  958. FrameDetail *fd = NULL;
  959. if (findFrameContents(index, &gc, &fd) == true && fd != NULL)
  960. {
  961. if (state != NULL)
  962. {
  963. // find the value for the detail member
  964. for (S32 i = 0; i < gBorderStateTable.size; i++)
  965. {
  966. if (dStrcmp(state, gBorderStateTable.table[i].label) == 0)
  967. fd->mBorderMovable = gBorderStateTable.table[i].index;
  968. }
  969. }
  970. else
  971. // defaults to AUTO if NULL passed in state
  972. fd->mBorderMovable = FRAME_STATE_AUTO;
  973. }
  974. }
  975. //-----------------------------------------------------------------------------
  976. void GuiFrameSetCtrl::frameMinExtent(S32 index, const Point2I &extent)
  977. {
  978. GuiControl *gc = NULL;
  979. FrameDetail *fd = NULL;
  980. if (findFrameContents(index, &gc, &fd) == true && fd != NULL)
  981. fd->mMinExtent = extent;
  982. }