guiFormCtrl.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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 "gui/editor/guiMenuBar.h"
  23. #include "gui/containers/guiFormCtrl.h"
  24. #include "gui/guiDefaultControlRender.h"
  25. IMPLEMENT_CONOBJECT(GuiFormCtrl);
  26. ConsoleMethod(GuiFormCtrl, getMenuID, S32, 2, 2, "Returns the ID of the Form Menu")
  27. {
  28. return object->getMenuBarID();
  29. }
  30. ConsoleMethod(GuiFormCtrl, setCaption, void, 3, 3, "setCaption(caption) - Sets the title of the Form Menu")
  31. {
  32. object->setCaption(argv[2]);
  33. }
  34. GuiFormCtrl::GuiFormCtrl()
  35. {
  36. mMinExtent.set(10, 10);
  37. mActive = true;
  38. mMouseOver = false;
  39. mDepressed = false;
  40. mCanMove = false;
  41. mCaption = StringTable->insert("[none]");
  42. mUseSmallCaption = false;
  43. mSmallCaption = StringTable->EmptyString;
  44. mContentLibrary = StringTable->EmptyString;
  45. mContent = StringTable->EmptyString;
  46. mCanSaveFieldDictionary = true;
  47. mHasMenu = false;
  48. mIsContainer = true;
  49. // The attached menu bar
  50. mMenuBar = NULL;
  51. }
  52. GuiFormCtrl::~GuiFormCtrl()
  53. {
  54. if(mMenuBar)
  55. {
  56. removeObject(mMenuBar);
  57. //mMenuBar->deleteObject();
  58. }
  59. }
  60. void GuiFormCtrl::initPersistFields()
  61. {
  62. addField("Caption", TypeString, Offset(mCaption, GuiFormCtrl));
  63. addField("ContentLibrary",TypeString, Offset(mContentLibrary, GuiFormCtrl));
  64. addField("Content", TypeString, Offset(mContent, GuiFormCtrl));
  65. addField("Movable", TypeBool, Offset(mCanMove, GuiFormCtrl));
  66. addField("HasMenu", TypeBool, Offset(mHasMenu, GuiFormCtrl));
  67. Parent::initPersistFields();
  68. }
  69. void GuiFormCtrl::setCaption(const char* caption)
  70. {
  71. if(caption)
  72. {
  73. mCaption = StringTable->insert(caption, true);
  74. }
  75. }
  76. bool GuiFormCtrl::onWake()
  77. {
  78. if ( !Parent::onWake() )
  79. return false;
  80. mFont = mProfile->mFont;
  81. AssertFatal(mFont, "GuiFormCtrl::onWake: invalid font in profile" );
  82. mProfile->constructBitmapArray();
  83. if(mProfile->mBitmapArrayRects.size())
  84. {
  85. mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y );
  86. mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent );
  87. if(mFont->getHeight() > (U32)mThumbSize.y)
  88. mThumbSize.y = mFont->getHeight();
  89. }
  90. else
  91. {
  92. mThumbSize.set(20, 20);
  93. }
  94. return true;
  95. }
  96. void GuiFormCtrl::addObject(SimObject *newObj )
  97. {
  98. if( ( mHasMenu && size() > 1) || (!mHasMenu && size() > 0 ) )
  99. {
  100. Con::warnf("GuiFormCtrl::addObject - Forms may only have one *direct* child - Placing on Parent!");
  101. GuiControl *parent = getParent();
  102. if( parent )
  103. parent->addObject( newObj );
  104. return;
  105. }
  106. GuiControl *newCtrl = dynamic_cast<GuiControl*>( newObj );
  107. GuiFormCtrl*formCtrl = dynamic_cast<GuiFormCtrl*>( newObj );
  108. if( newCtrl && formCtrl )
  109. newCtrl->setCanSave( true );
  110. else if ( newCtrl )
  111. newCtrl->setCanSave( false );
  112. Parent::addObject( newObj );
  113. }
  114. void GuiFormCtrl::onSleep()
  115. {
  116. Parent::onSleep();
  117. mFont = NULL;
  118. }
  119. bool GuiFormCtrl::onAdd()
  120. {
  121. if(!Parent::onAdd())
  122. return false;
  123. if( !mMenuBar && mHasMenu )
  124. {
  125. mMenuBar = new GuiMenuBar();
  126. AssertFatal(mMenuBar, "GuiFormCtrl::onWake: cannot create form menu" );
  127. if( mMenuBar )
  128. {
  129. mMenuBar->setField("profile","GuiFormMenuBarProfile");
  130. mMenuBar->setField("horizSizing", "right");
  131. mMenuBar->setField("vertSizing", "bottom");
  132. mMenuBar->setField("extent", "16 16");
  133. mMenuBar->setField("minExtent", "16 16");
  134. mMenuBar->setField("position", "0 0");
  135. mMenuBar->setField("class", "FormMenuBarClass"); // Give a generic class to the menu bar so that one set of functions may be used for all of them.
  136. mMenuBar->registerObject();
  137. mMenuBar->setProcessTicks(true); // Activate the processing of ticks to track if the mouse pointer has been hovering within the menu
  138. addObject(mMenuBar); // Add the menu bar to the form
  139. }
  140. }
  141. return true;
  142. }
  143. U32 GuiFormCtrl::getMenuBarID()
  144. {
  145. if(mMenuBar)
  146. {
  147. return mMenuBar->getId();
  148. }
  149. return 0;
  150. }
  151. void GuiFormCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
  152. {
  153. Parent::resize(newPosition, newExtent);
  154. if( !mAwake || !mProfile->mBitmapArrayRects.size() )
  155. return;
  156. // Should the caption be modified because the title bar is too small?
  157. S32 textWidth = mProfile->mFont->getStrWidth(mCaption);
  158. S32 newTextArea = mBounds.extent.x - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x;
  159. if(newTextArea < textWidth)
  160. {
  161. static char buf[256];
  162. mUseSmallCaption = true;
  163. mSmallCaption = StringTable->EmptyString;
  164. S32 strlen = dStrlen((const char*)mCaption);
  165. for(S32 i=strlen; i>=0; --i)
  166. {
  167. dStrcpy(buf, "");
  168. dStrncat(buf, (const char*)mCaption, i);
  169. dStrcat(buf, "...");
  170. textWidth = mProfile->mFont->getStrWidth(buf);
  171. if(textWidth < newTextArea)
  172. {
  173. mSmallCaption = StringTable->insert(buf, true);
  174. break;
  175. }
  176. }
  177. } else
  178. {
  179. mUseSmallCaption = false;
  180. }
  181. Con::executef(this, 1, "onResize");
  182. }
  183. void GuiFormCtrl::onRender(Point2I offset, const RectI &updateRect)
  184. {
  185. // Fill in the control's child area
  186. RectI boundsRect(offset, mBounds.extent);
  187. boundsRect.point.y += mThumbSize.y;
  188. boundsRect.extent.y -= mThumbSize.y;
  189. // draw the border of the form if specified
  190. if (mProfile->mOpaque)
  191. dglDrawRectFill(boundsRect, mProfile->mFillColor);
  192. if (mProfile->mBorder)
  193. renderBorder(boundsRect, mProfile);
  194. // If we don't have a child (other than the menu), put some text in the child area
  195. if(size() <= 1)
  196. {
  197. dglSetBitmapModulation(ColorI(0,0,0));
  198. renderJustifiedText(boundsRect.point, boundsRect.extent, "[none]");
  199. }
  200. S32 textWidth = 0;
  201. // Draw our little bar, too
  202. if(mProfile->mBitmapArrayRects.size() >= 5)
  203. {
  204. dglClearBitmapModulation();
  205. S32 barStart = (mHasMenu ? mThumbSize.x : 1 + mProfile->mBorderThickness) + offset.x + textWidth;
  206. S32 barTop = mThumbSize.y/2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y /2;
  207. Point2I barOffset(barStart, barTop);
  208. // Draw the start of the bar...
  209. dglDrawBitmapStretchSR(mProfile->mTextureHandle,RectI(barOffset, mProfile->mBitmapArrayRects[2].extent), mProfile->mBitmapArrayRects[2] );
  210. // Now draw the middle...
  211. barOffset.x += mProfile->mBitmapArrayRects[2].extent.x;
  212. S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x;
  213. if(barMiddleSize>0)
  214. {
  215. // We have to do this inset to prevent nasty stretching artifacts
  216. RectI foo = mProfile->mBitmapArrayRects[3];
  217. foo.inset(1,0);
  218. dglDrawBitmapStretchSR(
  219. mProfile->mTextureHandle,
  220. RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
  221. foo
  222. );
  223. }
  224. // And the end
  225. barOffset.x += barMiddleSize;
  226. dglDrawBitmapStretchSR( mProfile->mTextureHandle, RectI(barOffset, mProfile->mBitmapArrayRects[4].extent),
  227. mProfile->mBitmapArrayRects[4]);
  228. dglSetBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
  229. renderJustifiedText(Point2I(mThumbSize.x, 0) + offset, Point2I(mBounds.extent.x - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x, mThumbSize.y), (mUseSmallCaption ? mSmallCaption : mCaption) );
  230. }
  231. // Render the children
  232. renderChildControls(offset, updateRect);
  233. }
  234. void GuiFormCtrl::onMouseDragged(const GuiEvent &event)
  235. {
  236. GuiControl *parent = getParent();
  237. GuiCanvas *root = getRoot();
  238. if (! root) return;
  239. Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
  240. Point2I newPosition = mBounds.point;
  241. Point2I newExtent = mBounds.extent;
  242. if (mMouseMovingWin && parent)
  243. {
  244. newPosition.x = getMax(0, getMin(parent->mBounds.extent.x - mBounds.extent.x, mOrigBounds.point.x + deltaMousePosition.x));
  245. newPosition.y = getMax(0, getMin(parent->mBounds.extent.y - mBounds.extent.y, mOrigBounds.point.y + deltaMousePosition.y));
  246. Point2I pos = parent->localToGlobalCoord(mBounds.point);
  247. root->addUpdateRegion(pos, mBounds.extent);
  248. resize(newPosition, newExtent);
  249. }
  250. }
  251. void GuiFormCtrl::onMouseMove(const GuiEvent &event)
  252. {
  253. Point2I localMove = globalToLocalCoord(event.mousePoint);
  254. // If we're clicking in the header then resize
  255. mMouseOver = (localMove.y < mThumbSize.y);
  256. if(isMouseLocked())
  257. mDepressed = mMouseOver;
  258. }
  259. void GuiFormCtrl::onMouseEnter(const GuiEvent &event)
  260. {
  261. setUpdate();
  262. if(isMouseLocked())
  263. {
  264. mDepressed = true;
  265. mMouseOver = true;
  266. }
  267. else
  268. {
  269. mMouseOver = true;
  270. }
  271. }
  272. void GuiFormCtrl::onMouseLeave(const GuiEvent &event)
  273. {
  274. setUpdate();
  275. if(isMouseLocked())
  276. mDepressed = false;
  277. mMouseOver = false;
  278. }
  279. void GuiFormCtrl::onMouseDown(const GuiEvent &event)
  280. {
  281. Point2I localClick = globalToLocalCoord(event.mousePoint);
  282. // If we're clicking in the header then resize
  283. if(localClick.y < mThumbSize.y)
  284. {
  285. mouseLock();
  286. mDepressed = true;
  287. mMouseMovingWin = mCanMove;
  288. //update
  289. setUpdate();
  290. }
  291. mOrigBounds = mBounds;
  292. mMouseDownPosition = event.mousePoint;
  293. ////if we clicked within the title bar
  294. //if (localPoint.y < mTitleHeight)
  295. //{
  296. // //if we clicked on the close button
  297. // if (mCanClose && mCloseButton.pointInRect(localPoint))
  298. // {
  299. // mPressClose = mCanClose;
  300. // }
  301. // else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint))
  302. // {
  303. // mPressMaximize = mCanMaximize;
  304. // }
  305. // else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint))
  306. // {
  307. // mPressMinimize = mCanMinimize;
  308. // }
  309. // //else we clicked within the title
  310. // else
  311. // {
  312. // mMouseMovingWin = mCanMove;
  313. // mMouseResizeWidth = false;
  314. // mMouseResizeHeight = false;
  315. // }
  316. //}
  317. //else
  318. //{
  319. // mMouseMovingWin = false;
  320. // //see if we clicked on the right edge
  321. // if (mResizeWidth && (localPoint.x > mBounds.extent.x - mResizeRightWidth))
  322. // {
  323. // mMouseResizeWidth = true;
  324. // }
  325. // //see if we clicked on the bottom edge (as well)
  326. // if (mResizeHeight && (localPoint.y > mBounds.extent.y - mResizeBottomHeight))
  327. // {
  328. // mMouseResizeHeight = true;
  329. // }
  330. //}
  331. if (mMouseMovingWin )//|| mMouseResizeWidth || mMouseResizeHeight ||
  332. //mPressClose || mPressMaximize || mPressMinimize)
  333. {
  334. mouseLock();
  335. }
  336. else
  337. {
  338. GuiControl *ctrl = findHitControl(localClick);
  339. if (ctrl && ctrl != this)
  340. ctrl->onMouseDown(event);
  341. }
  342. }
  343. void GuiFormCtrl::onMouseUp(const GuiEvent &event)
  344. {
  345. // Make sure we only get events we ought to be getting...
  346. if (! mActive)
  347. return;
  348. mouseUnlock();
  349. setUpdate();
  350. //mMouseMovingWin = false;
  351. //mMouseResizeWidth = false;
  352. //mMouseResizeHeight = false;
  353. //Point2I localClick = globalToLocalCoord(event.mousePoint);
  354. // If we're clicking in the header then resize
  355. //if(localClick.y < mThumbSize.y && mDepressed)
  356. // setCollapsed(!mCollapsed);
  357. }