guiContainer.cpp
Engine/source/gui/containers/guiContainer.cpp
Public Functions
ImplementEnumType(GuiDockingType , "\n\n" "@ingroup GuiContainers" )
Detailed Description
Public Functions
ConsoleDocClass(GuiContainer )
IMPLEMENT_CONOBJECT(GuiContainer )
ImplementEnumType(GuiDockingType , "\n\n" "@ingroup GuiContainers" )
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 "platform/platform.h" 25#include "gui/containers/guiContainer.h" 26 27#include "gui/containers/guiPanel.h" 28#include "console/consoleTypes.h" 29#include "console/engineAPI.h" 30 31 32IMPLEMENT_CONOBJECT( GuiContainer ); 33 34ConsoleDocClass( GuiContainer, 35 "@brief Brief Desc.\n\n" 36 37 "@tsexample\n" 38 "// Comment:\n" 39 "%okButton = new ClassObject()\n" 40 "instantiation\n" 41 "@endtsexample\n\n" 42 43 "@ingroup GuiContainers" 44); 45 46ImplementEnumType( GuiDockingType, 47 "\n\n" 48 "@ingroup GuiContainers" ) 49 { Docking::dockNone, "None" }, 50 { Docking::dockClient, "Client" }, 51 { Docking::dockTop, "Top" }, 52 { Docking::dockBottom, "Bottom" }, 53 { Docking::dockLeft, "Left" }, 54 { Docking::dockRight, "Right" } 55EndImplementEnumType; 56 57 58//----------------------------------------------------------------------------- 59 60GuiContainer::GuiContainer() 61{ 62 mUpdateLayout = false; 63 mValidDockingMask = Docking::dockNone | Docking::dockBottom | 64 Docking::dockTop | Docking::dockClient | 65 Docking::dockLeft | Docking::dockRight; 66 mIsContainer = true; 67} 68 69//----------------------------------------------------------------------------- 70 71GuiContainer::~GuiContainer() 72{ 73} 74 75//----------------------------------------------------------------------------- 76 77void GuiContainer::initPersistFields() 78{ 79 Con::setIntVariable("$DOCKING_NONE", Docking::dockNone); 80 Con::setIntVariable("$DOCKING_CLIENT", Docking::dockClient); 81 Con::setIntVariable("$DOCKING_TOP", Docking::dockTop); 82 Con::setIntVariable("$DOCKING_BOTTOM", Docking::dockBottom); 83 Con::setIntVariable("$DOCKING_LEFT", Docking::dockLeft); 84 Con::setIntVariable("$DOCKING_RIGHT", Docking::dockRight); 85 86 addGroup( "Layout" ); 87 88 addProtectedField("docking", TYPEID< Docking::DockingType >(), Offset(mSizingOptions.mDocking, GuiContainer), &setDockingField, &defaultProtectedGetFn, "" ); 89 addField("margin", TypeRectSpacingI, Offset(mSizingOptions.mPadding, GuiContainer)); 90 addField("padding", TypeRectSpacingI, Offset(mSizingOptions.mInternalPadding, GuiContainer)); 91 addField("anchorTop", TypeBool, Offset(mSizingOptions.mAnchorTop, GuiContainer)); 92 addField("anchorBottom", TypeBool, Offset(mSizingOptions.mAnchorBottom, GuiContainer)); 93 addField("anchorLeft", TypeBool, Offset(mSizingOptions.mAnchorLeft, GuiContainer)); 94 addField("anchorRight", TypeBool, Offset(mSizingOptions.mAnchorRight, GuiContainer)); 95 96 endGroup( "Layout" ); 97 98 Parent::initPersistFields(); 99} 100 101//----------------------------------------------------------------------------- 102 103void GuiContainer::onChildAdded(GuiControl* control) 104{ 105 Parent::onChildAdded( control ); 106 setUpdateLayout(); 107} 108 109//----------------------------------------------------------------------------- 110 111void GuiContainer::onChildRemoved(GuiControl* control) 112{ 113 Parent::onChildRemoved( control ); 114 setUpdateLayout(); 115} 116 117//----------------------------------------------------------------------------- 118 119bool GuiContainer::reOrder(SimObject* obj, SimObject* target) 120{ 121 if ( !Parent::reOrder(obj, target) ) 122 return false; 123 124 setUpdateLayout(); 125 return true; 126} 127 128//----------------------------------------------------------------------------- 129 130bool GuiContainer::resize( const Point2I &newPosition, const Point2I &newExtent ) 131{ 132 if( !Parent::resize( newPosition, newExtent ) ) 133 return false; 134 135 RectI clientRect = getClientRect(); 136 layoutControls( clientRect ); 137 138 GuiControl *parent = getParent(); 139 S32 docking = getDocking(); 140 if( parent && docking != Docking::dockNone && docking != Docking::dockInvalid ) 141 setUpdateLayout( updateParent ); 142 143 return true; 144} 145 146//----------------------------------------------------------------------------- 147 148void GuiContainer::addObject(SimObject *obj) 149{ 150 Parent::addObject(obj); 151 152 setUpdateLayout(); 153} 154 155//----------------------------------------------------------------------------- 156 157void GuiContainer::removeObject(SimObject *obj) 158{ 159 Parent::removeObject(obj); 160 161 setUpdateLayout(); 162} 163 164//----------------------------------------------------------------------------- 165 166void GuiContainer::parentResized(const RectI &oldParentRect, const RectI &newParentRect) 167{ 168 //if(!mCanResize) 169 // return; 170 171 // If it's a control that specifies invalid docking, we'll just treat it as an old GuiControl 172 if( getDocking() & Docking::dockInvalid || getDocking() & Docking::dockNone) 173 return Parent::parentResized( oldParentRect, newParentRect ); 174 175 S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x; 176 S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y; 177 178 // Update Self 179 RectI oldThisRect = getBounds(); 180 anchorControl( this, Point2I( deltaX, deltaY ) ); 181 RectI newThisRect = getBounds(); 182 183 // Update Deltas to pass on to children 184 deltaX = newThisRect.extent.x - oldThisRect.extent.x; 185 deltaY = newThisRect.extent.y - oldThisRect.extent.y; 186 187 // Iterate over all children and update their anchors 188 iterator nI = begin(); 189 for( ; nI != end(); nI++ ) 190 { 191 // Sanity 192 GuiControl *control = dynamic_cast<GuiControl*>( (*nI) ); 193 if( control ) 194 control->parentResized( oldThisRect, newThisRect ); 195 } 196} 197 198//----------------------------------------------------------------------------- 199 200void GuiContainer::childResized(GuiControl *child) 201{ 202 Parent::childResized( child ); 203 setUpdateLayout(); 204} 205 206//----------------------------------------------------------------------------- 207 208bool GuiContainer::layoutControls( RectI &clientRect ) 209{ 210 // This variable is set to the first 'Client' docking 211 // control that is found. We defer client docking until 212 // after all other docks have been made since it will consume 213 // the remaining client area available. 214 GuiContainer *clientDocking = NULL; 215 216 // Iterate over all children and perform docking 217 iterator nI = begin(); 218 for( ; nI != end(); nI++ ) 219 { 220 // Layout Content with proper docking (Client Default) 221 GuiControl *control = static_cast<GuiControl*>(*nI); 222 223 // If we're invisible we don't get counted in docking 224 if( control == NULL || !control->isVisible() ) 225 continue; 226 227 S32 dockingMode = Docking::dockNone; 228 GuiContainer *container = dynamic_cast<GuiContainer*>(control); 229 if( container != NULL ) 230 dockingMode = container->getDocking(); 231 else 232 continue; 233 234 // See above note about clientDocking pointer 235 if( dockingMode & Docking::dockClient && clientDocking == NULL ) 236 clientDocking = container; 237 238 // Dock Appropriately 239 if( !(dockingMode & Docking::dockClient) ) 240 dockControl( container, dockingMode, clientRect ); 241 } 242 243 // Do client dock 244 if( clientDocking != NULL ) 245 dockControl( clientDocking, Docking::dockClient, clientRect ); 246 247 return true; 248} 249 250//----------------------------------------------------------------------------- 251 252bool GuiContainer::dockControl( GuiContainer *control, S32 dockingMode, RectI &clientRect ) 253{ 254 if( !control ) 255 return false; 256 257 // Make sure this class support docking of this type 258 if( !(dockingMode & getValidDockingMask())) 259 return false; 260 261 // If our client rect has run out of room, we can't dock any more 262 if( !clientRect.isValidRect() ) 263 return false; 264 265 // Dock Appropriately 266 RectI dockRect; 267 RectSpacingI rectShrinker; 268 ControlSizing sizingOptions = control->getSizingOptions(); 269 switch( dockingMode ) 270 { 271 case Docking::dockClient: 272 273 // Inset by padding 274 sizingOptions.mPadding.insetRect(clientRect); 275 276 // Dock to entirety of client rectangle 277 control->resize( clientRect.point, clientRect.extent ); 278 279 // Remove Client Rect, can only have one client dock 280 clientRect.set(0,0,0,0); 281 break; 282 case Docking::dockTop: 283 284 dockRect = clientRect; 285 dockRect.extent.y = getMin( control->getHeight() + sizingOptions.mPadding.top + sizingOptions.mPadding.bottom , clientRect.extent.y ); 286 287 // Subtract our rect 288 clientRect.point.y += dockRect.extent.y; 289 clientRect.extent.y -= dockRect.extent.y; 290 291 // Inset by padding 292 sizingOptions.mPadding.insetRect(dockRect); 293 294 // Resize 295 control->resize( dockRect.point, dockRect.extent ); 296 297 break; 298 case Docking::dockBottom: 299 300 dockRect = clientRect; 301 dockRect.extent.y = getMin( control->getHeight() + sizingOptions.mPadding.top + sizingOptions.mPadding.bottom, clientRect.extent.y ); 302 dockRect.point.y += clientRect.extent.y - dockRect.extent.y; 303 304 // Subtract our rect 305 clientRect.extent.y -= dockRect.extent.y; 306 307 // Inset by padding 308 sizingOptions.mPadding.insetRect(dockRect); 309 310 // Resize 311 control->resize( dockRect.point, dockRect.extent ); 312 313 break; 314 case Docking::dockLeft: 315 316 dockRect = clientRect; 317 dockRect.extent.x = getMin( control->getWidth() + sizingOptions.mPadding.left + sizingOptions.mPadding.right, clientRect.extent.x ); 318 319 // Subtract our rect 320 clientRect.point.x += dockRect.extent.x; 321 clientRect.extent.x -= dockRect.extent.x; 322 323 // Inset by padding 324 sizingOptions.mPadding.insetRect(dockRect); 325 326 // Resize 327 control->resize( dockRect.point, dockRect.extent ); 328 329 break; 330 case Docking::dockRight: 331 332 dockRect = clientRect; 333 dockRect.extent.x = getMin( control->getWidth() + sizingOptions.mPadding.left + sizingOptions.mPadding.right, clientRect.extent.x ); 334 dockRect.point.x += clientRect.extent.x - dockRect.extent.x; 335 336 // Subtract our rect 337 clientRect.extent.x -= dockRect.extent.x; 338 339 // Inset by padding 340 sizingOptions.mPadding.insetRect(dockRect); 341 342 // Resize 343 control->resize( dockRect.point, dockRect.extent ); 344 345 break; 346 case Docking::dockNone: 347 control->setUpdateLayout(); 348 break; 349 } 350 351 return true; 352} 353 354//----------------------------------------------------------------------------- 355 356bool GuiContainer::anchorControl( GuiControl *control, const Point2I &deltaParentExtent ) 357{ 358 GuiContainer *container = dynamic_cast<GuiContainer*>( control ); 359 if( !control || !container ) 360 return false; 361 362 // If we're docked, we don't anchor to anything 363 if( (container->getDocking() & Docking::dockAny) || !(container->getDocking() & Docking::dockInvalid) ) 364 return false; 365 366 if( deltaParentExtent.isZero() ) 367 return false; 368 369 RectI oldRect = control->getBounds(); 370 RectI newRect = control->getBounds(); 371 372 F32 deltaBottom = mSizingOptions.mAnchorBottom ? (F32)deltaParentExtent.y : 0.0f; 373 F32 deltaRight = mSizingOptions.mAnchorRight ? (F32)deltaParentExtent.x : 0.0f; 374 F32 deltaLeft = mSizingOptions.mAnchorLeft ? 0.0f : (F32)deltaParentExtent.x; 375 F32 deltaTop = mSizingOptions.mAnchorTop ? 0.0f : (F32)deltaParentExtent.y; 376 377 // Apply Delta's to newRect 378 newRect.point.x += (S32)deltaLeft; 379 newRect.extent.x += (S32)(deltaRight - deltaLeft); 380 newRect.point.y += (S32)deltaTop; 381 newRect.extent.y += (S32)(deltaBottom - deltaTop); 382 383 Point2I minExtent = control->getMinExtent(); 384 // Only resize if our minExtent is satisfied with it. 385 if( !( newRect.extent.x >= minExtent.x && newRect.extent.y >= minExtent.y ) ) 386 return false; 387 388 if( newRect.point == oldRect.point && newRect.extent == oldRect.extent ) 389 return false; 390 391 // Finally Size the control 392 control->resize( newRect.point, newRect.extent ); 393 394 // We made changes 395 return true; 396} 397 398//----------------------------------------------------------------------------- 399 400void GuiContainer::onPreRender() 401{ 402 if( mUpdateLayout == updateNone ) 403 return; 404 405 RectI clientRect = getClientRect(); 406 if( mUpdateLayout & updateSelf ) 407 layoutControls( clientRect ); 408 409 GuiContainer *parent = dynamic_cast<GuiContainer*>( getParent() ); 410 if( parent && ( mUpdateLayout & updateParent ) ) 411 parent->setUpdateLayout(); 412 413 // Always set AFTER layoutControls call to prevent recursive calling of layoutControls - JDD 414 mUpdateLayout = updateNone; 415 416 Parent::onPreRender(); 417} 418 419//----------------------------------------------------------------------------- 420 421const RectI GuiContainer::getClientRect() 422{ 423 RectI resRect = RectI( Point2I(0,0), getExtent() ); 424 425 // Inset by padding 426 mSizingOptions.mInternalPadding.insetRect( resRect ); 427 428 return resRect; 429} 430 431//----------------------------------------------------------------------------- 432 433void GuiContainer::setDocking( S32 docking ) 434{ 435 mSizingOptions.mDocking = docking; 436 setUpdateLayout( updateParent ); 437} 438