guiFormCtrl.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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. dglDrawRectFill(boundsRect, mProfile->mFillColor);
  191. //if (mProfile->mBorder)
  192. //renderBorder(boundsRect, mProfile);
  193. // If we don't have a child (other than the menu), put some text in the child area
  194. if(size() <= 1)
  195. {
  196. dglSetBitmapModulation(ColorI(0,0,0));
  197. renderText(boundsRect.point, boundsRect.extent, "[none]", mProfile);
  198. }
  199. S32 textWidth = 0;
  200. // Draw our little bar, too
  201. if(mProfile->mBitmapArrayRects.size() >= 5)
  202. {
  203. dglClearBitmapModulation();
  204. S32 barStart = 0;//(mHasMenu ? mThumbSize.x : 1 + mProfile->mBorderSize) + offset.x + textWidth;
  205. S32 barTop = mThumbSize.y/2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y /2;
  206. Point2I barOffset(barStart, barTop);
  207. // Draw the start of the bar...
  208. dglDrawBitmapStretchSR(mProfile->mTextureHandle,RectI(barOffset, mProfile->mBitmapArrayRects[2].extent), mProfile->mBitmapArrayRects[2] );
  209. // Now draw the middle...
  210. barOffset.x += mProfile->mBitmapArrayRects[2].extent.x;
  211. S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x;
  212. if(barMiddleSize>0)
  213. {
  214. // We have to do this inset to prevent nasty stretching artifacts
  215. RectI foo = mProfile->mBitmapArrayRects[3];
  216. foo.inset(1,0);
  217. dglDrawBitmapStretchSR(
  218. mProfile->mTextureHandle,
  219. RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
  220. foo
  221. );
  222. }
  223. // And the end
  224. barOffset.x += barMiddleSize;
  225. dglDrawBitmapStretchSR( mProfile->mTextureHandle, RectI(barOffset, mProfile->mBitmapArrayRects[4].extent),
  226. mProfile->mBitmapArrayRects[4]);
  227. dglSetBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
  228. renderText(Point2I(mThumbSize.x, 0) + offset, Point2I(mBounds.extent.x - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x, mThumbSize.y), (mUseSmallCaption ? mSmallCaption : mCaption), mProfile);
  229. }
  230. // Render the children
  231. renderChildControls(offset, mBounds, updateRect);
  232. }
  233. void GuiFormCtrl::onTouchDragged(const GuiEvent &event)
  234. {
  235. GuiControl *parent = getParent();
  236. GuiCanvas *root = getRoot();
  237. if (! root) return;
  238. Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
  239. Point2I newPosition = mBounds.point;
  240. Point2I newExtent = mBounds.extent;
  241. if (mMouseMovingWin && parent)
  242. {
  243. newPosition.x = getMax(0, getMin(parent->mBounds.extent.x - mBounds.extent.x, mOrigBounds.point.x + deltaMousePosition.x));
  244. newPosition.y = getMax(0, getMin(parent->mBounds.extent.y - mBounds.extent.y, mOrigBounds.point.y + deltaMousePosition.y));
  245. Point2I pos = parent->localToGlobalCoord(mBounds.point);
  246. root->addUpdateRegion(pos, mBounds.extent);
  247. resize(newPosition, newExtent);
  248. }
  249. }
  250. void GuiFormCtrl::onTouchMove(const GuiEvent &event)
  251. {
  252. Point2I localMove = globalToLocalCoord(event.mousePoint);
  253. // If we're clicking in the header then resize
  254. mMouseOver = (localMove.y < mThumbSize.y);
  255. if(isMouseLocked())
  256. mDepressed = mMouseOver;
  257. }
  258. void GuiFormCtrl::onTouchEnter(const GuiEvent &event)
  259. {
  260. setUpdate();
  261. if(isMouseLocked())
  262. {
  263. mDepressed = true;
  264. mMouseOver = true;
  265. }
  266. else
  267. {
  268. mMouseOver = true;
  269. }
  270. }
  271. void GuiFormCtrl::onTouchLeave(const GuiEvent &event)
  272. {
  273. setUpdate();
  274. if(isMouseLocked())
  275. mDepressed = false;
  276. mMouseOver = false;
  277. }
  278. void GuiFormCtrl::onTouchDown(const GuiEvent &event)
  279. {
  280. Point2I localClick = globalToLocalCoord(event.mousePoint);
  281. // If we're clicking in the header then resize
  282. if(localClick.y < mThumbSize.y)
  283. {
  284. mouseLock();
  285. mDepressed = true;
  286. mMouseMovingWin = mCanMove;
  287. //update
  288. setUpdate();
  289. }
  290. mOrigBounds = mBounds;
  291. mMouseDownPosition = event.mousePoint;
  292. ////if we clicked within the title bar
  293. //if (localPoint.y < mTitleHeight)
  294. //{
  295. // //if we clicked on the close button
  296. // if (mCanClose && mCloseButton.pointInRect(localPoint))
  297. // {
  298. // mPressClose = mCanClose;
  299. // }
  300. // else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint))
  301. // {
  302. // mPressMaximize = mCanMaximize;
  303. // }
  304. // else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint))
  305. // {
  306. // mPressMinimize = mCanMinimize;
  307. // }
  308. // //else we clicked within the title
  309. // else
  310. // {
  311. // mMouseMovingWin = mCanMove;
  312. // mMouseResizeWidth = false;
  313. // mMouseResizeHeight = false;
  314. // }
  315. //}
  316. //else
  317. //{
  318. // mMouseMovingWin = false;
  319. // //see if we clicked on the right edge
  320. // if (mResizeWidth && (localPoint.x > mBounds.extent.x - mResizeRightWidth))
  321. // {
  322. // mMouseResizeWidth = true;
  323. // }
  324. // //see if we clicked on the bottom edge (as well)
  325. // if (mResizeHeight && (localPoint.y > mBounds.extent.y - mResizeBottomHeight))
  326. // {
  327. // mMouseResizeHeight = true;
  328. // }
  329. //}
  330. if (mMouseMovingWin )//|| mMouseResizeWidth || mMouseResizeHeight ||
  331. //mPressClose || mPressMaximize || mPressMinimize)
  332. {
  333. mouseLock();
  334. }
  335. else
  336. {
  337. GuiControl *ctrl = findHitControl(localClick);
  338. if (ctrl && ctrl != this)
  339. ctrl->onTouchDown(event);
  340. }
  341. }
  342. void GuiFormCtrl::onTouchUp(const GuiEvent &event)
  343. {
  344. // Make sure we only get events we ought to be getting...
  345. if (! mActive)
  346. return;
  347. mouseUnlock();
  348. setUpdate();
  349. //mMouseMovingWin = false;
  350. //mMouseResizeWidth = false;
  351. //mMouseResizeHeight = false;
  352. //Point2I localClick = globalToLocalCoord(event.mousePoint);
  353. // If we're clicking in the header then resize
  354. //if(localClick.y < mThumbSize.y && mDepressed)
  355. // setCollapsed(!mCollapsed);
  356. }