guiPaneCtrl.cpp 11 KB

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