guiFormCtrl.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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/guiFormCtrl.h"
  25. #include "gui/core/guiDefaultControlRender.h"
  26. #include "gfx/gfxDrawUtil.h"
  27. #ifdef TORQUE_TOOLS
  28. IMPLEMENT_CONOBJECT(GuiFormCtrl);
  29. ConsoleDocClass( GuiFormCtrl,
  30. "@brief A generic form control.\n\n"
  31. "Currently editor use only.\n\n "
  32. "@internal"
  33. );
  34. IMPLEMENT_CALLBACK( GuiFormCtrl, onResize, void, (), (),
  35. "Called when the control is resized." );
  36. GuiFormCtrl::GuiFormCtrl()
  37. {
  38. setMinExtent(Point2I(200,100));
  39. mActive = true;
  40. mMouseOver = false;
  41. mDepressed = false;
  42. mCanMove = false;
  43. mCaption = "[none]";
  44. mUseSmallCaption = false;
  45. mContentLibrary = StringTable->EmptyString();
  46. mContent = StringTable->EmptyString();
  47. mCanSaveFieldDictionary = true;
  48. mIsContainer = true;
  49. // The attached menu bar
  50. mHasMenu = false;
  51. mMenuBar = NULL;
  52. mMouseMovingWin = false;
  53. }
  54. GuiFormCtrl::~GuiFormCtrl()
  55. {
  56. // If we still have a menu bar, delete it.
  57. if( mMenuBar )
  58. mMenuBar->deleteObject();
  59. }
  60. bool GuiFormCtrl::_setHasMenu( void *object, const char *index, const char *data )
  61. {
  62. GuiFormCtrl* ctrl = reinterpret_cast< GuiFormCtrl* >( object );
  63. ctrl->setHasMenu( dAtob( data ) );
  64. return false;
  65. }
  66. void GuiFormCtrl::initPersistFields()
  67. {
  68. docsURL;
  69. addField("caption", TypeRealString, Offset(mCaption, GuiFormCtrl));
  70. addField("contentLibrary",TypeString, Offset(mContentLibrary, GuiFormCtrl));
  71. addField("content", TypeString, Offset(mContent, GuiFormCtrl));
  72. addField("movable", TypeBool, Offset(mCanMove, GuiFormCtrl));
  73. addProtectedField( "hasMenu", TypeBool, Offset(mHasMenu, GuiFormCtrl),
  74. &_setHasMenu, &defaultProtectedGetFn,
  75. "" );
  76. Parent::initPersistFields();
  77. }
  78. void GuiFormCtrl::setHasMenu( bool value )
  79. {
  80. if( mHasMenu == value )
  81. return;
  82. if( !value )
  83. {
  84. mMenuBar->deleteObject();
  85. mMenuBar = NULL;
  86. }
  87. else
  88. {
  89. if( !mMenuBar )
  90. {
  91. mMenuBar = new GuiMenuBar();
  92. mMenuBar->setField( "profile", "GuiFormMenuBarProfile" );
  93. mMenuBar->setField( "horizSizing", "right" );
  94. mMenuBar->setField( "vertSizing", "bottom" );
  95. mMenuBar->setField( "extent", "16 16" );
  96. mMenuBar->setField( "minExtent", "16 16" );
  97. mMenuBar->setField( "position", "0 0" );
  98. 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.
  99. mMenuBar->registerObject();
  100. mMenuBar->setProcessTicks(true); // Activate the processing of ticks to track if the mouse pointer has been hovering within the menu
  101. }
  102. addObject( mMenuBar ); // Add the menu bar to the form
  103. }
  104. mHasMenu = value;
  105. }
  106. bool GuiFormCtrl::onWake()
  107. {
  108. if ( !Parent::onWake() )
  109. return false;
  110. mFont = mProfile->mFont;
  111. AssertFatal(mFont, "GuiFormCtrl::onWake: invalid font in profile" );
  112. mProfile->constructBitmapArray();
  113. if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size())
  114. {
  115. mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y );
  116. mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent );
  117. if(mFont->getHeight() > mThumbSize.y)
  118. mThumbSize.y = mFont->getHeight();
  119. }
  120. else
  121. {
  122. mThumbSize.set(20, 20);
  123. }
  124. return true;
  125. }
  126. void GuiFormCtrl::addObject(SimObject *newObj )
  127. {
  128. if( ( mHasMenu && size() > 1) || (!mHasMenu && size() > 0 ) )
  129. {
  130. Con::warnf("GuiFormCtrl::addObject - Forms may only have one *direct* child - Placing on Parent!");
  131. GuiControl* parent = getParent();
  132. if ( parent )
  133. parent->addObject( newObj );
  134. return;
  135. }
  136. GuiControl *newCtrl = dynamic_cast<GuiControl*>( newObj );
  137. GuiFormCtrl*formCtrl = dynamic_cast<GuiFormCtrl*>( newObj );
  138. if( newCtrl && formCtrl )
  139. newCtrl->setCanSave( true );
  140. else if ( newCtrl )
  141. newCtrl->setCanSave( false );
  142. Parent::addObject( newObj );
  143. }
  144. void GuiFormCtrl::removeObject( SimObject* object )
  145. {
  146. if( object == mMenuBar )
  147. {
  148. mHasMenu = false;
  149. mMenuBar = NULL;
  150. }
  151. Parent::removeObject( object );
  152. }
  153. bool GuiFormCtrl::acceptsAsChild( SimObject* object ) const
  154. {
  155. return Parent::acceptsAsChild( object ) &&
  156. ( ( mHasMenu && size() == 1 ) || ( !mHasMenu && !size() ) ); // Only accept a single child.
  157. }
  158. void GuiFormCtrl::onSleep()
  159. {
  160. Parent::onSleep();
  161. mFont = NULL;
  162. }
  163. bool GuiFormCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
  164. {
  165. if( !Parent::resize(newPosition, newExtent) )
  166. return false;
  167. if( !mAwake || !mProfile->mBitmapArrayRects.size() )
  168. return false;
  169. // Should the caption be modified because the title bar is too small?
  170. S32 textWidth = mProfile->mFont->getStrWidth(mCaption);
  171. S32 newTextArea = getWidth() - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x;
  172. if(newTextArea < textWidth)
  173. {
  174. static char buf[256];
  175. mUseSmallCaption = true;
  176. mSmallCaption = StringTable->EmptyString();
  177. S32 strlen = dStrlen((const char*)mCaption);
  178. for(S32 i=strlen; i>=0; --i)
  179. {
  180. dStrcpy(buf, "", i);
  181. dStrcat(buf, (const char*)mCaption, i);
  182. dStrcat(buf, "...", i);
  183. textWidth = mProfile->mFont->getStrWidth(buf);
  184. if(textWidth < newTextArea)
  185. {
  186. mSmallCaption = StringTable->insert(buf, true);
  187. break;
  188. }
  189. }
  190. } else
  191. {
  192. mUseSmallCaption = false;
  193. }
  194. onResize_callback();
  195. return true;
  196. }
  197. void GuiFormCtrl::onRender(Point2I offset, const RectI &updateRect)
  198. {
  199. // Fill in the control's child area
  200. RectI boundsRect(offset, getExtent());
  201. boundsRect.point.y += mThumbSize.y;
  202. boundsRect.extent.y -= mThumbSize.y;
  203. // draw the border of the form if specified
  204. if (mProfile->mOpaque)
  205. GFX->getDrawUtil()->drawRectFill(boundsRect, mProfile->mFillColor);
  206. if (mProfile->mBorder)
  207. renderBorder(boundsRect, mProfile);
  208. // If we don't have a child, put some text in the child area
  209. if( empty() )
  210. {
  211. GFX->getDrawUtil()->setBitmapModulation(ColorI(0,0,0));
  212. renderJustifiedText(boundsRect.point, boundsRect.extent, "[none]");
  213. }
  214. S32 textWidth = 0;
  215. // Draw our little bar, too
  216. if (mProfile->mBitmapArrayRects.size() >= 5)
  217. {
  218. GFX->getDrawUtil()->clearBitmapModulation();
  219. S32 barStart = offset.x + textWidth;
  220. S32 barTop = mThumbSize.y / 2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y / 2;
  221. Point2I barOffset(barStart, barTop);
  222. // Draw the start of the bar...
  223. GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->getBitmapResource(),RectI(barOffset, mProfile->mBitmapArrayRects[2].extent), mProfile->mBitmapArrayRects[2] );
  224. // Now draw the middle...
  225. barOffset.x += mProfile->mBitmapArrayRects[2].extent.x;
  226. S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x + 1;
  227. if (barMiddleSize > 0)
  228. {
  229. // We have to do this inset to prevent nasty stretching artifacts
  230. RectI foo = mProfile->mBitmapArrayRects[3];
  231. foo.inset(1,0);
  232. GFX->getDrawUtil()->drawBitmapStretchSR(
  233. mProfile->getBitmapResource(),
  234. RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
  235. foo
  236. );
  237. }
  238. // And the end
  239. barOffset.x += barMiddleSize;
  240. GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->getBitmapResource(), RectI(barOffset, mProfile->mBitmapArrayRects[4].extent),
  241. mProfile->mBitmapArrayRects[4]);
  242. GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
  243. renderJustifiedText(Point2I(mThumbSize.x, 0) + offset, Point2I(getWidth() - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x, mThumbSize.y), (mUseSmallCaption ? mSmallCaption : mCaption) );
  244. }
  245. // Render the children
  246. renderChildControls(offset, updateRect);
  247. }
  248. void GuiFormCtrl::onMouseMove(const GuiEvent &event)
  249. {
  250. Point2I localMove = globalToLocalCoord(event.mousePoint);
  251. // If we're clicking in the header then resize
  252. mMouseOver = (localMove.y < mThumbSize.y);
  253. if(isMouseLocked())
  254. mDepressed = mMouseOver;
  255. }
  256. void GuiFormCtrl::onMouseEnter(const GuiEvent &event)
  257. {
  258. setUpdate();
  259. if(isMouseLocked())
  260. {
  261. mDepressed = true;
  262. mMouseOver = true;
  263. }
  264. else
  265. {
  266. mMouseOver = true;
  267. }
  268. }
  269. void GuiFormCtrl::onMouseLeave(const GuiEvent &event)
  270. {
  271. setUpdate();
  272. if(isMouseLocked())
  273. mDepressed = false;
  274. mMouseOver = false;
  275. }
  276. void GuiFormCtrl::onMouseDown(const GuiEvent &event)
  277. {
  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. mMouseMovingWin = mCanMove;
  285. //update
  286. setUpdate();
  287. }
  288. mOrigBounds = getBounds();
  289. mMouseDownPosition = event.mousePoint;
  290. if (mMouseMovingWin )
  291. {
  292. mouseLock();
  293. }
  294. else
  295. {
  296. GuiControl *ctrl = findHitControl(localClick);
  297. if (ctrl && ctrl != this)
  298. ctrl->onMouseDown(event);
  299. }
  300. }
  301. void GuiFormCtrl::onMouseUp(const GuiEvent &event)
  302. {
  303. // Make sure we only get events we ought to be getting...
  304. if (! mActive)
  305. return;
  306. mouseUnlock();
  307. setUpdate();
  308. // If we're clicking in the header then resize
  309. //if(localClick.y < mThumbSize.y && mDepressed)
  310. // setCollapsed(!mCollapsed);
  311. }
  312. DefineEngineMethod( GuiFormCtrl, getMenuID, S32, (),,
  313. "Get the ID of this form's menu.\n\n"
  314. "@return The ID of the form menu\n" )
  315. {
  316. return object->getMenuBarID();
  317. }
  318. U32 GuiFormCtrl::getMenuBarID()
  319. {
  320. return mMenuBar ? mMenuBar->getId() : 0;
  321. }
  322. DefineEngineMethod( GuiFormCtrl, setCaption, void, ( const char* caption ),,
  323. "Sets the title of the form.\n\n"
  324. "@param caption Form caption\n" )
  325. {
  326. object->setCaption( caption );
  327. }
  328. #endif