2
0

guiFrameCtrl.cpp 40 KB

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