guiArrayCtrl.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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/console.h"
  23. #include "graphics/dgl.h"
  24. #include "platform/event.h"
  25. #include "gui/containers/guiScrollCtrl.h"
  26. #include "gui/guiArrayCtrl.h"
  27. IMPLEMENT_CONOBJECT(GuiArrayCtrl);
  28. // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
  29. GuiArrayCtrl::GuiArrayCtrl()
  30. {
  31. mActive = true;
  32. mCellSize.set(80, 30);
  33. mSize = Point2I(5, 30);
  34. mSelectedCell.set(-1, -1);
  35. mMouseOverCell.set(-1, -1);
  36. mHeaderDim.set(0, 0);
  37. mIsContainer = true;
  38. }
  39. bool GuiArrayCtrl::onWake()
  40. {
  41. if (! Parent::onWake())
  42. return false;
  43. //get the font
  44. mFont = mProfile->mFont;
  45. return true;
  46. }
  47. void GuiArrayCtrl::onSleep()
  48. {
  49. Parent::onSleep();
  50. mFont = NULL;
  51. }
  52. // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
  53. void GuiArrayCtrl::setSize(Point2I newSize)
  54. {
  55. mSize = newSize;
  56. Point2I newExtent(newSize.x * mCellSize.x + mHeaderDim.x, newSize.y * mCellSize.y + mHeaderDim.y);
  57. resize(mBounds.point, newExtent);
  58. }
  59. void GuiArrayCtrl::getScrollDimensions(S32 &cell_size, S32 &num_cells)
  60. {
  61. cell_size = mCellSize.y;
  62. num_cells = mSize.y;
  63. }
  64. // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
  65. bool GuiArrayCtrl::cellSelected(Point2I cell)
  66. {
  67. if (cell.x < 0 || cell.x >= mSize.x || cell.y < 0 || cell.y >= mSize.y)
  68. {
  69. mSelectedCell = Point2I(-1,-1);
  70. return false;
  71. }
  72. mSelectedCell = cell;
  73. scrollSelectionVisible();
  74. onCellSelected(cell);
  75. setUpdate();
  76. return true;
  77. }
  78. void GuiArrayCtrl::onCellSelected(Point2I cell)
  79. {
  80. Con::executef(this, 3, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y));
  81. //call the console function
  82. execConsoleCallback();
  83. }
  84. // Called when a cell is highlighted
  85. void GuiArrayCtrl::onCellHighlighted(Point2I cell)
  86. {
  87. // Do nothing
  88. }
  89. void GuiArrayCtrl::setSelectedCell(Point2I cell)
  90. {
  91. cellSelected(cell);
  92. }
  93. Point2I GuiArrayCtrl::getSelectedCell()
  94. {
  95. return mSelectedCell;
  96. }
  97. void GuiArrayCtrl::scrollSelectionVisible()
  98. {
  99. scrollCellVisible(mSelectedCell);
  100. }
  101. void GuiArrayCtrl::scrollCellVisible(Point2I cell)
  102. {
  103. //make sure we have a parent
  104. //make sure we have a valid cell selected
  105. GuiScrollCtrl *parent = dynamic_cast<GuiScrollCtrl*>(getParent());
  106. if(!parent || cell.x < 0 || cell.y < 0)
  107. return;
  108. RectI cellBounds(cell.x * mCellSize.x, cell.y * mCellSize.y, mCellSize.x, mCellSize.y);
  109. parent->scrollRectVisible(cellBounds);
  110. }
  111. // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
  112. void GuiArrayCtrl::onRenderColumnHeaders(Point2I offset, Point2I parentOffset, Point2I headerDim)
  113. {
  114. if (mProfile->mBorderDefault && *(mProfile->mBorderDefault->mBorder) > 0)
  115. {
  116. RectI cellR(offset.x + headerDim.x, parentOffset.y, mBounds.extent.x - headerDim.x, headerDim.y);
  117. dglDrawRectFill(cellR, mProfile->mBorderDefault->mBorderColor[0]);
  118. }
  119. }
  120. void GuiArrayCtrl::onRenderRowHeader(Point2I offset, Point2I parentOffset, Point2I headerDim, Point2I cell)
  121. {
  122. ColorI color;
  123. RectI cellR;
  124. if (cell.x % 2)
  125. color.set(255, 0, 0, 255);
  126. else
  127. color.set(0, 255, 0, 255);
  128. cellR.point.set(parentOffset.x, offset.y);
  129. cellR.extent.set(headerDim.x, mCellSize.y);
  130. dglDrawRectFill(cellR, color);
  131. }
  132. void GuiArrayCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
  133. {
  134. ColorI color(255 * (cell.x % 2), 255 * (cell.y % 2), 255 * ((cell.x + cell.y) % 2), 255);
  135. if (selected)
  136. {
  137. color.set(255, 0, 0, 255);
  138. }
  139. else if (mouseOver)
  140. {
  141. color.set(0, 0, 255, 255);
  142. }
  143. //draw the cell
  144. RectI cellR(offset.x, offset.y, mCellSize.x, mCellSize.y);
  145. dglDrawRectFill(cellR, color);
  146. }
  147. void GuiArrayCtrl::onRender(Point2I offset, const RectI &updateRect)
  148. {
  149. //make sure we have a parent
  150. GuiControl *parent = getParent();
  151. if (! parent)
  152. return;
  153. S32 i, j;
  154. RectI headerClip;
  155. RectI clipRect = dglGetClipRect();
  156. Point2I parentOffset = parent->localToGlobalCoord(Point2I(0, 0));
  157. //if we have column headings
  158. if (mHeaderDim.y > 0)
  159. {
  160. headerClip.point.x = parentOffset.x + mHeaderDim.x;
  161. headerClip.point.y = parentOffset.y;
  162. headerClip.extent.x = clipRect.extent.x;// - headerClip.point.x; // This seems to fix some strange problems with some Gui's, bug? -pw
  163. headerClip.extent.y = mHeaderDim.y;
  164. if (headerClip.intersect(clipRect))
  165. {
  166. dglSetClipRect(headerClip);
  167. //now render the header
  168. onRenderColumnHeaders(offset, parentOffset, mHeaderDim);
  169. clipRect.point.y = headerClip.point.y + headerClip.extent.y - 1;
  170. }
  171. offset.y += mHeaderDim.y;
  172. }
  173. //if we have row headings
  174. if (mHeaderDim.x > 0)
  175. {
  176. clipRect.point.x = getMax(clipRect.point.x, parentOffset.x + mHeaderDim.x);
  177. offset.x += mHeaderDim.x;
  178. }
  179. //save the original for clipping the row headers
  180. RectI origClipRect = clipRect;
  181. for (j = 0; j < mSize.y; j++)
  182. {
  183. //skip until we get to a visible row
  184. if ((j + 1) * mCellSize.y + offset.y < updateRect.point.y)
  185. continue;
  186. //break once we've reached the last visible row
  187. if(j * mCellSize.y + offset.y >= updateRect.point.y + updateRect.extent.y)
  188. break;
  189. //render the header
  190. if (mHeaderDim.x > 0)
  191. {
  192. headerClip.point.x = parentOffset.x;
  193. headerClip.extent.x = mHeaderDim.x;
  194. headerClip.point.y = offset.y + j * mCellSize.y;
  195. headerClip.extent.y = mCellSize.y;
  196. if (headerClip.intersect(origClipRect))
  197. {
  198. dglSetClipRect(headerClip);
  199. //render the row header
  200. onRenderRowHeader(Point2I(0, offset.y + j * mCellSize.y),
  201. Point2I(parentOffset.x, offset.y + j * mCellSize.y),
  202. mHeaderDim, Point2I(0, j));
  203. }
  204. }
  205. //render the cells for the row
  206. for (i = 0; i < mSize.x; i++)
  207. {
  208. //skip past columns off the left edge
  209. if ((i + 1) * mCellSize.x + offset.x < updateRect.point.x)
  210. continue;
  211. //break once past the last visible column
  212. if (i * mCellSize.x + offset.x >= updateRect.point.x + updateRect.extent.x)
  213. break;
  214. S32 cellx = offset.x + i * mCellSize.x;
  215. S32 celly = offset.y + j * mCellSize.y;
  216. RectI cellClip(cellx, celly, mCellSize.x, mCellSize.y);
  217. //make sure the cell is within the update region
  218. if (cellClip.intersect(clipRect))
  219. {
  220. //set the clip rect
  221. dglSetClipRect(cellClip);
  222. //render the cell
  223. onRenderCell(Point2I(cellx, celly), Point2I(i, j),
  224. i == mSelectedCell.x && j == mSelectedCell.y,
  225. i == mMouseOverCell.x && j == mMouseOverCell.y);
  226. }
  227. }
  228. }
  229. }
  230. void GuiArrayCtrl::onMouseDown(const GuiEvent &event)
  231. {
  232. if ( !mActive || !mAwake || !mVisible )
  233. return;
  234. //let the guiControl method take care of the rest
  235. Parent::onTouchDown(event);
  236. Point2I pt = globalToLocalCoord(event.mousePoint);
  237. pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
  238. Point2I cell(
  239. (pt.x < 0 ? -1 : pt.x / mCellSize.x),
  240. (pt.y < 0 ? -1 : pt.y / mCellSize.y)
  241. );
  242. if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
  243. {
  244. //store the previously selected cell
  245. Point2I prevSelected = mSelectedCell;
  246. //select the new cell
  247. cellSelected(Point2I(cell.x, cell.y));
  248. //if we double clicked on the *same* cell, evaluate the altConsole Command
  249. if ( ( event.mouseClickCount > 1 ) && ( prevSelected == mSelectedCell ) && mAltConsoleCommand[0] )
  250. Con::evaluate( mAltConsoleCommand, false );
  251. }
  252. }
  253. void GuiArrayCtrl::onMouseEnter(const GuiEvent &event)
  254. {
  255. Point2I pt = globalToLocalCoord(event.mousePoint);
  256. pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
  257. //get the cell
  258. Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
  259. if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
  260. {
  261. mMouseOverCell = cell;
  262. setUpdateRegion(Point2I(cell.x * mCellSize.x + mHeaderDim.x,
  263. cell.y * mCellSize.y + mHeaderDim.y), mCellSize );
  264. onCellHighlighted(mMouseOverCell);
  265. }
  266. }
  267. void GuiArrayCtrl::onMouseLeave(const GuiEvent & /*event*/)
  268. {
  269. setUpdateRegion(Point2I(mMouseOverCell.x * mCellSize.x + mHeaderDim.x,
  270. mMouseOverCell.y * mCellSize.y + mHeaderDim.y), mCellSize);
  271. mMouseOverCell.set(-1,-1);
  272. onCellHighlighted(mMouseOverCell);
  273. }
  274. void GuiArrayCtrl::onMouseDragged(const GuiEvent &event)
  275. {
  276. // for the array control, the behaviour of onMouseDragged is the same
  277. // as on mouse moved - basically just recalc the currend mouse over cell
  278. // and set the update regions if necessary
  279. GuiArrayCtrl::onMouseMove(event);
  280. }
  281. void GuiArrayCtrl::onMouseMove(const GuiEvent &event)
  282. {
  283. Point2I pt = globalToLocalCoord(event.mousePoint);
  284. pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
  285. Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
  286. if (cell.x != mMouseOverCell.x || cell.y != mMouseOverCell.y)
  287. {
  288. if (mMouseOverCell.x != -1)
  289. {
  290. setUpdateRegion(Point2I(mMouseOverCell.x * mCellSize.x + mHeaderDim.x,
  291. mMouseOverCell.y * mCellSize.y + mHeaderDim.y), mCellSize);
  292. }
  293. if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
  294. {
  295. setUpdateRegion(Point2I(cell.x * mCellSize.x + mHeaderDim.x,
  296. cell.y * mCellSize.y + mHeaderDim.y), mCellSize);
  297. mMouseOverCell = cell;
  298. }
  299. else
  300. mMouseOverCell.set(-1,-1);
  301. }
  302. onCellHighlighted(mMouseOverCell);
  303. }
  304. bool GuiArrayCtrl::onKeyDown(const GuiEvent &event)
  305. {
  306. //if this control is a dead end, kill the event
  307. if ((! mVisible) || (! mActive) || (! mAwake)) return true;
  308. //get the parent
  309. S32 pageSize = 1;
  310. GuiControl *parent = getParent();
  311. if (parent && mCellSize.y > 0)
  312. {
  313. pageSize = getMax(1, (parent->mBounds.extent.y / mCellSize.y) - 1);
  314. }
  315. Point2I delta(0,0);
  316. switch (event.keyCode)
  317. {
  318. case KEY_LEFT:
  319. delta.set(-1, 0);
  320. break;
  321. case KEY_RIGHT:
  322. delta.set(1, 0);
  323. break;
  324. case KEY_UP:
  325. delta.set(0, -1);
  326. break;
  327. case KEY_DOWN:
  328. delta.set(0, 1);
  329. break;
  330. case KEY_PAGE_UP:
  331. delta.set(0, -pageSize);
  332. break;
  333. case KEY_PAGE_DOWN:
  334. delta.set(0, pageSize);
  335. break;
  336. case KEY_HOME:
  337. cellSelected( Point2I( 0, 0 ) );
  338. return( true );
  339. case KEY_END:
  340. cellSelected( Point2I( 0, mSize.y - 1 ) );
  341. return( true );
  342. default:
  343. return Parent::onKeyDown(event);
  344. }
  345. if (mSize.x < 1 || mSize.y < 1)
  346. return true;
  347. //select the first cell if no previous cell was selected
  348. if (mSelectedCell.x == -1 || mSelectedCell.y == -1)
  349. {
  350. cellSelected(Point2I(0,0));
  351. return true;
  352. }
  353. //select the cell
  354. Point2I cell = mSelectedCell;
  355. cell.x = getMax(0, getMin(mSize.x - 1, cell.x + delta.x));
  356. cell.y = getMax(0, getMin(mSize.y - 1, cell.y + delta.y));
  357. cellSelected(cell);
  358. return true;
  359. }
  360. void GuiArrayCtrl::onRightMouseDown(const GuiEvent &event)
  361. {
  362. if ( !mActive || !mAwake || !mVisible )
  363. return;
  364. Parent::onRightMouseDown( event );
  365. Point2I pt = globalToLocalCoord( event.mousePoint );
  366. pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
  367. Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
  368. if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
  369. {
  370. char buf[32];
  371. dSprintf( buf, sizeof( buf ), "%d %d", event.mousePoint.x, event.mousePoint.y );
  372. // Pass it to the console:
  373. Con::executef(this, 4, "onRightMouseDown", Con::getIntArg(cell.x), Con::getIntArg(cell.y), buf);
  374. }
  375. }