guiFormCtrl.cpp
Engine/source/gui/containers/guiFormCtrl.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "console/engineAPI.h" 25#include "platform/platform.h" 26#include "gui/containers/guiFormCtrl.h" 27 28#include "gui/core/guiDefaultControlRender.h" 29#include "gfx/gfxDrawUtil.h" 30 31#ifdef TORQUE_TOOLS 32 33IMPLEMENT_CONOBJECT(GuiFormCtrl); 34 35ConsoleDocClass( GuiFormCtrl, 36 "@brief A generic form control.\n\n" 37 "Currently editor use only.\n\n " 38 "@internal" 39); 40 41IMPLEMENT_CALLBACK( GuiFormCtrl, onResize, void, (), (), 42 "Called when the control is resized." ); 43 44GuiFormCtrl::GuiFormCtrl() 45{ 46 setMinExtent(Point2I(200,100)); 47 mActive = true; 48 mMouseOver = false; 49 mDepressed = false; 50 mCanMove = false; 51 mCaption = "[none]"; 52 mUseSmallCaption = false; 53 54 mContentLibrary = StringTable->EmptyString(); 55 mContent = StringTable->EmptyString(); 56 57 mCanSaveFieldDictionary = true; 58 mIsContainer = true; 59 60 // The attached menu bar 61 mHasMenu = false; 62 mMenuBar = NULL; 63 mMouseMovingWin = false; 64} 65 66GuiFormCtrl::~GuiFormCtrl() 67{ 68 // If we still have a menu bar, delete it. 69 70 if( mMenuBar ) 71 mMenuBar->deleteObject(); 72} 73 74bool GuiFormCtrl::_setHasMenu( void *object, const char *index, const char *data ) 75{ 76 GuiFormCtrl* ctrl = reinterpret_cast< GuiFormCtrl* >( object ); 77 ctrl->setHasMenu( dAtob( data ) ); 78 return false; 79} 80 81void GuiFormCtrl::initPersistFields() 82{ 83 addField("caption", TypeRealString, Offset(mCaption, GuiFormCtrl)); 84 addField("contentLibrary",TypeString, Offset(mContentLibrary, GuiFormCtrl)); 85 addField("content", TypeString, Offset(mContent, GuiFormCtrl)); 86 addField("movable", TypeBool, Offset(mCanMove, GuiFormCtrl)); 87 88 addProtectedField( "hasMenu", TypeBool, Offset(mHasMenu, GuiFormCtrl), 89 &_setHasMenu, &defaultProtectedGetFn, 90 "" ); 91 92 Parent::initPersistFields(); 93} 94 95void GuiFormCtrl::setHasMenu( bool value ) 96{ 97 if( mHasMenu == value ) 98 return; 99 100 if( !value ) 101 { 102 mMenuBar->deleteObject(); 103 mMenuBar = NULL; 104 } 105 else 106 { 107 if( !mMenuBar ) 108 { 109 mMenuBar = new GuiMenuBar(); 110 mMenuBar->setField( "profile", "GuiFormMenuBarProfile" ); 111 mMenuBar->setField( "horizSizing", "right" ); 112 mMenuBar->setField( "vertSizing", "bottom" ); 113 mMenuBar->setField( "extent", "16 16" ); 114 mMenuBar->setField( "minExtent", "16 16" ); 115 mMenuBar->setField( "position", "0 0" ); 116 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. 117 118 mMenuBar->registerObject(); 119 mMenuBar->setProcessTicks(true); // Activate the processing of ticks to track if the mouse pointer has been hovering within the menu 120 } 121 122 addObject( mMenuBar ); // Add the menu bar to the form 123 } 124 125 mHasMenu = value; 126} 127 128bool GuiFormCtrl::onWake() 129{ 130 if ( !Parent::onWake() ) 131 return false; 132 133 mFont = mProfile->mFont; 134 AssertFatal(mFont, "GuiFormCtrl::onWake: invalid font in profile" ); 135 136 mProfile->constructBitmapArray(); 137 138 if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size()) 139 { 140 mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y ); 141 mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent ); 142 143 if(mFont->getHeight() > mThumbSize.y) 144 mThumbSize.y = mFont->getHeight(); 145 } 146 else 147 { 148 mThumbSize.set(20, 20); 149 } 150 151 return true; 152} 153 154 155void GuiFormCtrl::addObject(SimObject *newObj ) 156{ 157 if( ( mHasMenu && size() > 1) || (!mHasMenu && size() > 0 ) ) 158 { 159 Con::warnf("GuiFormCtrl::addObject - Forms may only have one *direct* child - Placing on Parent!"); 160 161 GuiControl* parent = getParent(); 162 if ( parent ) 163 parent->addObject( newObj ); 164 165 return; 166 } 167 168 GuiControl *newCtrl = dynamic_cast<GuiControl*>( newObj ); 169 GuiFormCtrl*formCtrl = dynamic_cast<GuiFormCtrl*>( newObj ); 170 171 if( newCtrl && formCtrl ) 172 newCtrl->setCanSave( true ); 173 else if ( newCtrl ) 174 newCtrl->setCanSave( false ); 175 176 Parent::addObject( newObj ); 177} 178 179void GuiFormCtrl::removeObject( SimObject* object ) 180{ 181 if( object == mMenuBar ) 182 { 183 mHasMenu = false; 184 mMenuBar = NULL; 185 } 186 187 Parent::removeObject( object ); 188} 189 190bool GuiFormCtrl::acceptsAsChild( SimObject* object ) const 191{ 192 return Parent::acceptsAsChild( object ) && 193 ( ( mHasMenu && size() == 1 ) || ( !mHasMenu && !size() ) ); // Only accept a single child. 194} 195 196void GuiFormCtrl::onSleep() 197{ 198 Parent::onSleep(); 199 mFont = NULL; 200} 201 202bool GuiFormCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) 203{ 204 if( !Parent::resize(newPosition, newExtent) ) 205 return false; 206 207 if( !mAwake || !mProfile->mBitmapArrayRects.size() ) 208 return false; 209 210 // Should the caption be modified because the title bar is too small? 211 S32 textWidth = mProfile->mFont->getStrWidth(mCaption); 212 S32 newTextArea = getWidth() - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x; 213 if(newTextArea < textWidth) 214 { 215 static char buf[256]; 216 217 mUseSmallCaption = true; 218 mSmallCaption = StringTable->EmptyString(); 219 220 S32 strlen = dStrlen((const char*)mCaption); 221 for(S32 i=strlen; i>=0; --i) 222 { 223 dStrcpy(buf, "", i); 224 dStrcat(buf, (const char*)mCaption, i); 225 dStrcat(buf, "...", i); 226 227 textWidth = mProfile->mFont->getStrWidth(buf); 228 229 if(textWidth < newTextArea) 230 { 231 mSmallCaption = StringTable->insert(buf, true); 232 break; 233 } 234 } 235 236 } else 237 { 238 mUseSmallCaption = false; 239 } 240 241 onResize_callback(); 242 243 return true; 244} 245 246void GuiFormCtrl::onRender(Point2I offset, const RectI &updateRect) 247{ 248 // Fill in the control's child area 249 RectI boundsRect(offset, getExtent()); 250 boundsRect.point.y += mThumbSize.y; 251 boundsRect.extent.y -= mThumbSize.y; 252 253 // draw the border of the form if specified 254 if (mProfile->mOpaque) 255 GFX->getDrawUtil()->drawRectFill(boundsRect, mProfile->mFillColor); 256 257 if (mProfile->mBorder) 258 renderBorder(boundsRect, mProfile); 259 260 // If we don't have a child, put some text in the child area 261 if( empty() ) 262 { 263 GFX->getDrawUtil()->setBitmapModulation(ColorI(0,0,0)); 264 renderJustifiedText(boundsRect.point, boundsRect.extent, "[none]"); 265 } 266 267 S32 textWidth = 0; 268 269 // Draw our little bar, too 270 if (mProfile->mBitmapArrayRects.size() >= 5) 271 { 272 GFX->getDrawUtil()->clearBitmapModulation(); 273 274 S32 barStart = offset.x + textWidth; 275 S32 barTop = mThumbSize.y / 2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y / 2; 276 277 Point2I barOffset(barStart, barTop); 278 279 // Draw the start of the bar... 280 GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject ,RectI(barOffset, mProfile->mBitmapArrayRects[2].extent), mProfile->mBitmapArrayRects[2] ); 281 282 // Now draw the middle... 283 barOffset.x += mProfile->mBitmapArrayRects[2].extent.x; 284 285 S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x + 1; 286 287 if (barMiddleSize > 0) 288 { 289 // We have to do this inset to prevent nasty stretching artifacts 290 RectI foo = mProfile->mBitmapArrayRects[3]; 291 foo.inset(1,0); 292 293 GFX->getDrawUtil()->drawBitmapStretchSR( 294 mProfile->mTextureObject, 295 RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)), 296 foo 297 ); 298 } 299 300 // And the end 301 barOffset.x += barMiddleSize; 302 303 GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(barOffset, mProfile->mBitmapArrayRects[4].extent), 304 mProfile->mBitmapArrayRects[4]); 305 306 GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor)); 307 renderJustifiedText(Point2I(mThumbSize.x, 0) + offset, Point2I(getWidth() - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x, mThumbSize.y), (mUseSmallCaption ? mSmallCaption : mCaption) ); 308 309 } 310 311 // Render the children 312 renderChildControls(offset, updateRect); 313} 314 315void GuiFormCtrl::onMouseMove(const GuiEvent &event) 316{ 317 Point2I localMove = globalToLocalCoord(event.mousePoint); 318 319 // If we're clicking in the header then resize 320 mMouseOver = (localMove.y < mThumbSize.y); 321 if(isMouseLocked()) 322 mDepressed = mMouseOver; 323 324} 325 326void GuiFormCtrl::onMouseEnter(const GuiEvent &event) 327{ 328 setUpdate(); 329 if(isMouseLocked()) 330 { 331 mDepressed = true; 332 mMouseOver = true; 333 } 334 else 335 { 336 mMouseOver = true; 337 } 338 339} 340 341void GuiFormCtrl::onMouseLeave(const GuiEvent &event) 342{ 343 setUpdate(); 344 if(isMouseLocked()) 345 mDepressed = false; 346 mMouseOver = false; 347} 348 349void GuiFormCtrl::onMouseDown(const GuiEvent &event) 350{ 351 Point2I localClick = globalToLocalCoord(event.mousePoint); 352 353 // If we're clicking in the header then resize 354 if(localClick.y < mThumbSize.y) 355 { 356 mouseLock(); 357 mDepressed = true; 358 mMouseMovingWin = mCanMove; 359 360 //update 361 setUpdate(); 362 } 363 364 mOrigBounds = getBounds(); 365 366 mMouseDownPosition = event.mousePoint; 367 368 if (mMouseMovingWin ) 369 { 370 mouseLock(); 371 } 372 else 373 { 374 GuiControl *ctrl = findHitControl(localClick); 375 if (ctrl && ctrl != this) 376 ctrl->onMouseDown(event); 377 } 378} 379 380void GuiFormCtrl::onMouseUp(const GuiEvent &event) 381{ 382 // Make sure we only get events we ought to be getting... 383 if (! mActive) 384 return; 385 386 mouseUnlock(); 387 setUpdate(); 388 389 // If we're clicking in the header then resize 390 //if(localClick.y < mThumbSize.y && mDepressed) 391 // setCollapsed(!mCollapsed); 392} 393 394DefineEngineMethod( GuiFormCtrl, getMenuID, S32, (),, 395 "Get the ID of this form's menu.\n\n" 396 "@return The ID of the form menu\n" ) 397{ 398 return object->getMenuBarID(); 399} 400 401U32 GuiFormCtrl::getMenuBarID() 402{ 403 return mMenuBar ? mMenuBar->getId() : 0; 404} 405 406DefineEngineMethod( GuiFormCtrl, setCaption, void, ( const char* caption ),, 407 "Sets the title of the form.\n\n" 408 "@param caption Form caption\n" ) 409{ 410 object->setCaption( caption ); 411} 412 413#endif 414