guiPaneCtrl.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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 "console/engineAPI.h"
  23. #include "platform/platform.h"
  24. #include "gui/containers/guiPaneCtrl.h"
  25. #include "gfx/gfxDrawUtil.h"
  26. IMPLEMENT_CONOBJECT(GuiPaneControl);
  27. ConsoleDocClass( GuiPaneControl,
  28. "@brief A collapsable pane control.\n\n"
  29. "This class wraps a single child control and displays a header with caption "
  30. "above it. If you click the header it will collapse or expand (if <i>collapsable</i> "
  31. "is enabled). The control resizes itself based on its collapsed/expanded size.<br>"
  32. "In the GUI editor, if you just want the header you can make <i>collapsable</i> "
  33. "false. The caption field lets you set the caption; it expects a bitmap (from "
  34. "the GuiControlProfile) that contains two images - the first is displayed when "
  35. "the control is expanded and the second is displayed when it is collapsed. The "
  36. "header is sized based on the first image.\n\n"
  37. "@tsexample\n"
  38. "new GuiPaneControl()\n"
  39. "{\n"
  40. " caption = \"Example Pane\";\n"
  41. " collapsable = \"1\";\n"
  42. " barBehindText = \"1\";\n"
  43. " //Properties not specific to this control have been omitted from this example.\n"
  44. "};\n"
  45. "@endtsexample\n\n"
  46. "@ingroup GuiContainers"
  47. );
  48. //-----------------------------------------------------------------------------
  49. GuiPaneControl::GuiPaneControl()
  50. {
  51. setMinExtent(Point2I(16,16));
  52. mActive = true;
  53. mCollapsable = true;
  54. mCollapsed = false;
  55. mBarBehindText = true;
  56. mMouseOver = false;
  57. mDepressed = false;
  58. mCaption = "A Pane";
  59. mCaptionID = StringTable->EmptyString();
  60. mIsContainer = true;
  61. mOriginalExtents.set(10,10);
  62. }
  63. //-----------------------------------------------------------------------------
  64. void GuiPaneControl::initPersistFields()
  65. {
  66. addGroup( "Pane" );
  67. addField("caption", TypeRealString, Offset(mCaption, GuiPaneControl),
  68. "Text label to display as the pane header." );
  69. addField("captionID", TypeString, Offset(mCaptionID, GuiPaneControl),
  70. "String table text ID to use as caption string (overrides 'caption')." );
  71. addField("collapsable", TypeBool, Offset(mCollapsable, GuiPaneControl),
  72. "Whether the pane can be collapsed by clicking its header." );
  73. addField("barBehindText", TypeBool, Offset(mBarBehindText, GuiPaneControl),
  74. "Whether to draw the bitmapped pane bar behind the header text, too." );
  75. endGroup( "Pane" );
  76. Parent::initPersistFields();
  77. }
  78. //-----------------------------------------------------------------------------
  79. bool GuiPaneControl::onWake()
  80. {
  81. if ( !Parent::onWake() )
  82. return false;
  83. if( !mProfile->mFont )
  84. {
  85. Con::errorf( "GuiPaneControl::onWake - profile has no valid font" );
  86. return false;
  87. }
  88. if(mCaptionID && *mCaptionID != 0)
  89. setCaptionID(mCaptionID);
  90. mProfile->constructBitmapArray();
  91. if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size())
  92. {
  93. mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y );
  94. mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent );
  95. if( mProfile->mFont->getHeight() > mThumbSize.y )
  96. mThumbSize.y = mProfile->mFont->getHeight();
  97. }
  98. else
  99. {
  100. mThumbSize.set(20, 20);
  101. }
  102. return true;
  103. }
  104. //-----------------------------------------------------------------------------
  105. void GuiPaneControl::setCaptionID(const char *id)
  106. {
  107. S32 n = Con::getIntVariable(id, -1);
  108. if(n != -1)
  109. {
  110. mCaptionID = StringTable->insert(id);
  111. setCaptionID(n);
  112. }
  113. }
  114. //-----------------------------------------------------------------------------
  115. void GuiPaneControl::setCaptionID(S32 id)
  116. {
  117. mCaption = getGUIString(id);
  118. }
  119. //-----------------------------------------------------------------------------
  120. bool GuiPaneControl::resize(const Point2I &newPosition, const Point2I &newExtent)
  121. {
  122. // CodeReview WTF is going on here that we need to bypass parent sanity?
  123. // Investigate this [7/1/2007 justind]
  124. if( !Parent::resize( newPosition, newExtent ) )
  125. return false;
  126. mOriginalExtents.x = getWidth();
  127. /*
  128. GuiControl *parent = getParent();
  129. if (parent)
  130. parent->childResized(this);
  131. setUpdate();
  132. */
  133. // Resize the child control if we're not collapsed
  134. if(size() && !mCollapsed)
  135. {
  136. GuiControl *gc = dynamic_cast<GuiControl*>(operator[](0));
  137. if(gc)
  138. {
  139. Point2I offset(0, mThumbSize.y);
  140. gc->resize(offset, newExtent - offset);
  141. }
  142. }
  143. // For now.
  144. return true;
  145. }
  146. //-----------------------------------------------------------------------------
  147. void GuiPaneControl::onRender(Point2I offset, const RectI &updateRect)
  148. {
  149. // Render our awesome little doogong
  150. if(mProfile->mBitmapArrayRects.size() >= 2 && mCollapsable)
  151. {
  152. S32 idx = mCollapsed ? 0 : 1;
  153. GFX->getDrawUtil()->clearBitmapModulation();
  154. GFX->getDrawUtil()->drawBitmapStretchSR(
  155. mProfile->getBitmapResource(),
  156. RectI(offset, mProfile->mBitmapArrayRects[idx].extent),
  157. mProfile->mBitmapArrayRects[idx]
  158. );
  159. }
  160. S32 textWidth = 0;
  161. if(!mBarBehindText)
  162. {
  163. GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
  164. textWidth = GFX->getDrawUtil()->drawText(
  165. mProfile->mFont,
  166. Point2I(mThumbSize.x, 0) + offset,
  167. mCaption,
  168. mProfile->mFontColors
  169. );
  170. }
  171. // Draw our little bar, too
  172. if(mProfile->mBitmapArrayRects.size() >= 5)
  173. {
  174. GFX->getDrawUtil()->clearBitmapModulation();
  175. S32 barStart = mThumbSize.x + offset.x + textWidth;
  176. S32 barTop = mThumbSize.y/2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y /2;
  177. Point2I barOffset(barStart, barTop);
  178. // Draw the start of the bar...
  179. GFX->getDrawUtil()->drawBitmapStretchSR(
  180. mProfile->getBitmapResource(),
  181. RectI(barOffset, mProfile->mBitmapArrayRects[2].extent),
  182. mProfile->mBitmapArrayRects[2]
  183. );
  184. // Now draw the middle...
  185. barOffset.x += mProfile->mBitmapArrayRects[2].extent.x;
  186. S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x;
  187. if(barMiddleSize>0)
  188. {
  189. // We have to do this inset to prevent nasty stretching artifacts
  190. RectI foo = mProfile->mBitmapArrayRects[3];
  191. foo.inset(1,0);
  192. GFX->getDrawUtil()->drawBitmapStretchSR(
  193. mProfile->getBitmapResource(),
  194. RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
  195. foo
  196. );
  197. }
  198. // And the end
  199. barOffset.x += barMiddleSize;
  200. GFX->getDrawUtil()->drawBitmapStretchSR(
  201. mProfile->getBitmapResource(),
  202. RectI(barOffset, mProfile->mBitmapArrayRects[4].extent),
  203. mProfile->mBitmapArrayRects[4]
  204. );
  205. }
  206. if(mBarBehindText)
  207. {
  208. GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
  209. GFX->getDrawUtil()->drawText(
  210. mProfile->mFont,
  211. Point2I(mThumbSize.x, 0) + offset,
  212. mCaption,
  213. mProfile->mFontColors
  214. );
  215. }
  216. // Draw child controls if appropriate
  217. if(!mCollapsed)
  218. renderChildControls(offset, updateRect);
  219. }
  220. //-----------------------------------------------------------------------------
  221. void GuiPaneControl::setCollapsed(bool isCollapsed)
  222. {
  223. // Get the child
  224. if(size() == 0 || !mCollapsable) return;
  225. GuiControl *gc = dynamic_cast<GuiControl*>(operator[](0));
  226. if(mCollapsed && !isCollapsed)
  227. {
  228. resize(getPosition(), mOriginalExtents);
  229. mCollapsed = false;
  230. if(gc)
  231. gc->setVisible(true);
  232. }
  233. else if(!mCollapsed && isCollapsed)
  234. {
  235. mCollapsed = true;
  236. mOriginalExtents = getExtent();
  237. resize(getPosition(), Point2I(getExtent().x, mThumbSize.y));
  238. if(gc)
  239. gc->setVisible(false);
  240. }
  241. }
  242. //-----------------------------------------------------------------------------
  243. void GuiPaneControl::onMouseMove(const GuiEvent &event)
  244. {
  245. Point2I localMove = globalToLocalCoord(event.mousePoint);
  246. // If we're clicking in the header then resize
  247. mMouseOver = (localMove.y < mThumbSize.y);
  248. if(isMouseLocked())
  249. mDepressed = mMouseOver;
  250. }
  251. //-----------------------------------------------------------------------------
  252. void GuiPaneControl::onMouseEnter(const GuiEvent &event)
  253. {
  254. setUpdate();
  255. if(isMouseLocked())
  256. {
  257. mDepressed = true;
  258. mMouseOver = true;
  259. }
  260. else
  261. {
  262. mMouseOver = true;
  263. }
  264. }
  265. //-----------------------------------------------------------------------------
  266. void GuiPaneControl::onMouseLeave(const GuiEvent &event)
  267. {
  268. setUpdate();
  269. if(isMouseLocked())
  270. mDepressed = false;
  271. mMouseOver = false;
  272. }
  273. //-----------------------------------------------------------------------------
  274. void GuiPaneControl::onMouseDown(const GuiEvent &event)
  275. {
  276. if(!mCollapsable)
  277. return;
  278. Point2I localClick = globalToLocalCoord(event.mousePoint);
  279. // If we're clicking in the header then resize
  280. if(localClick.y < mThumbSize.y)
  281. {
  282. mouseLock();
  283. mDepressed = true;
  284. //update
  285. setUpdate();
  286. }
  287. }
  288. //-----------------------------------------------------------------------------
  289. void GuiPaneControl::onMouseUp(const GuiEvent &event)
  290. {
  291. // Make sure we only get events we ought to be getting...
  292. if (! mActive)
  293. return;
  294. if(!mCollapsable)
  295. return;
  296. mouseUnlock();
  297. setUpdate();
  298. Point2I localClick = globalToLocalCoord(event.mousePoint);
  299. // If we're clicking in the header then resize
  300. if(localClick.y < mThumbSize.y && mDepressed)
  301. setCollapsed(!mCollapsed);
  302. }
  303. //=============================================================================
  304. // Console Methods.
  305. //=============================================================================
  306. DefineEngineMethod( GuiPaneControl, setCollapsed, void, ( bool collapse ),,
  307. "Collapse or un-collapse the control.\n\n"
  308. "@param collapse True to collapse the control, false to un-collapse it\n" )
  309. {
  310. object->setCollapsed( collapse );
  311. }