guiFrameCtrl.cpp 40 KB

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