group.cpp
Engine/source/gui/editor/inspector/group.cpp
Public Functions
ConsoleDocClass(GuiInspectorGroup , "@brief The <a href="/coding/class/classguiinspectorgroup/">GuiInspectorGroup</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> helper <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that the inspector " "makes use of which houses <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> collapsible pane type <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> separating " "inspected objects fields into <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">groups.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
Detailed Description
Public Functions
ConsoleDocClass(GuiInspectorGroup , "@brief The <a href="/coding/class/classguiinspectorgroup/">GuiInspectorGroup</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> helper <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that the inspector " "makes use of which houses <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> collapsible pane type <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> separating " "inspected objects fields into <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">groups.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
IMPLEMENT_CONOBJECT(GuiInspectorGroup )
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 "gui/editor/guiInspector.h" 25#include "gui/editor/inspector/group.h" 26#include "gui/editor/inspector/dynamicField.h" 27#include "gui/editor/inspector/datablockField.h" 28#include "gui/buttons/guiIconButtonCtrl.h" 29 30 31IMPLEMENT_CONOBJECT(GuiInspectorGroup); 32 33ConsoleDocClass( GuiInspectorGroup, 34 "@brief The GuiInspectorGroup control is a helper control that the inspector " 35 "makes use of which houses a collapsible pane type control for separating " 36 "inspected objects fields into groups.\n\n" 37 "Editor use only.\n\n" 38 "@internal" 39); 40 41//#define DEBUG_SPEW 42 43 44//----------------------------------------------------------------------------- 45 46GuiInspectorGroup::GuiInspectorGroup() 47 : mParent( NULL ), 48 mStack(NULL) 49{ 50 setBounds(0,0,200,20); 51 52 mChildren.clear(); 53 54 setCanSave( false ); 55 56 // Make sure we receive our ticks. 57 setProcessTicks(); 58 mMargin.set(0,0,5,0); 59} 60 61//----------------------------------------------------------------------------- 62 63GuiInspectorGroup::GuiInspectorGroup( const String& groupName, 64 SimObjectPtr<GuiInspector> parent ) 65 : mParent( parent ), 66 mStack(NULL) 67{ 68 69 setBounds(0,0,200,20); 70 71 mCaption = groupName; 72 setCanSave( false ); 73 74 mChildren.clear(); 75 mMargin.set(0,0,4,0); 76} 77 78//----------------------------------------------------------------------------- 79 80GuiInspectorGroup::~GuiInspectorGroup() 81{ 82} 83 84//----------------------------------------------------------------------------- 85 86bool GuiInspectorGroup::onAdd() 87{ 88 setDataField( StringTable->insert("profile"), NULL, "GuiInspectorGroupProfile" ); 89 90 if( !Parent::onAdd() ) 91 return false; 92 93 // Create our inner controls. Allow subclasses to provide other content. 94 if(!createContent()) 95 return false; 96 97 inspectGroup(); 98 99 return true; 100} 101 102//----------------------------------------------------------------------------- 103 104bool GuiInspectorGroup::createContent() 105{ 106 // Create our field stack control 107 mStack = new GuiStackControl(); 108 109 // Prefer GuiTransperantProfile for the stack. 110 mStack->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorStackProfile" ); 111 mStack->setInternalName(StringTable->insert("stack")); 112 if( !mStack->registerObject() ) 113 { 114 SAFE_DELETE( mStack ); 115 return false; 116 } 117 118 addObject( mStack ); 119 mStack->setField( "padding", "0" ); 120 return true; 121} 122 123//----------------------------------------------------------------------------- 124 125void GuiInspectorGroup::animateToContents() 126{ 127 calculateHeights(); 128 if(size() > 0) 129 animateTo( mExpanded.extent.y ); 130 else 131 animateTo( mHeader.extent.y ); 132} 133 134//----------------------------------------------------------------------------- 135 136GuiInspectorField* GuiInspectorGroup::constructField( S32 fieldType ) 137{ 138 // See if we can construct a field of this type 139 ConsoleBaseType *cbt = ConsoleBaseType::getType(fieldType); 140 if( !cbt ) 141 return NULL; 142 143 // Alright, is it a datablock? 144 if(cbt->isDatablock()) 145 { 146 // Default to GameBaseData 147 StringTableEntry typeClassName = cbt->getTypeClassName(); 148 149 if( mParent->getNumInspectObjects() == 1 && !dStricmp(typeClassName, "GameBaseData") ) 150 { 151 // Try and setup the classname based on the object type 152 char className[256]; 153 dSprintf(className,256,"%sData", mParent->getInspectObject( 0 )->getClassName()); 154 // Walk the ACR list and find a matching class if any. 155 AbstractClassRep *walk = AbstractClassRep::getClassList(); 156 while(walk) 157 { 158 if(!dStricmp(walk->getClassName(), className)) 159 break; 160 161 walk = walk->getNextClass(); 162 } 163 164 // We found a valid class 165 if (walk) 166 typeClassName = walk->getClassName(); 167 168 } 169 170 171 GuiInspectorDatablockField *dbFieldClass = new GuiInspectorDatablockField( typeClassName ); 172 173 // return our new datablock field with correct datablock type enumeration info 174 return dbFieldClass; 175} 176 177 // Nope, not a datablock. So maybe it has a valid inspector field override we can use? 178 if(!cbt->getInspectorFieldType()) 179 // Nothing, so bail. 180 return NULL; 181 182 // Otherwise try to make it! 183 ConsoleObject *co = create(cbt->getInspectorFieldType()); 184 GuiInspectorField *gif = dynamic_cast<GuiInspectorField*>(co); 185 186 if(!gif) 187 { 188 // Wasn't appropriate type, bail. 189 delete co; 190 return NULL; 191 } 192 193 return gif; 194} 195 196//----------------------------------------------------------------------------- 197 198GuiInspectorField *GuiInspectorGroup::findField( const char *fieldName ) 199{ 200 // If we don't have any field children we can't very well find one then can we? 201 if( mChildren.empty() ) 202 return NULL; 203 204 Vector<GuiInspectorField*>::iterator i = mChildren.begin(); 205 206 for( ; i != mChildren.end(); i++ ) 207 { 208 if( (*i)->getFieldName() != NULL && dStricmp( (*i)->getFieldName(), fieldName ) == 0 ) 209 return (*i); 210 } 211 212 return NULL; 213} 214 215//----------------------------------------------------------------------------- 216 217void GuiInspectorGroup::clearFields() 218{ 219 // Deallocates all field related controls. 220 mStack->clear(); 221 222 // Then just cleanup our vectors which also point to children 223 // that we keep for our own convenience. 224 mArrayCtrls.clear(); 225 mChildren.clear(); 226} 227 228//----------------------------------------------------------------------------- 229 230bool GuiInspectorGroup::inspectGroup() 231{ 232 // We can't inspect a group without a target! 233 if( !mParent || !mParent->getNumInspectObjects() ) 234 return false; 235 236 // to prevent crazy resizing, we'll just freeze our stack for a sec.. 237 mStack->freeze(true); 238 239 bool bNoGroup = false; 240 241 // Un-grouped fields are all sorted into the 'general' group 242 if ( dStricmp( mCaption, "General" ) == 0 ) 243 bNoGroup = true; 244 245 // Just delete all fields and recreate them (like the dynamicGroup) 246 // because that makes creating controls for array fields a lot easier 247 clearFields(); 248 249 bool bNewItems = false; 250 bool bMakingArray = false; 251 GuiStackControl *pArrayStack = NULL; 252 GuiRolloutCtrl *pArrayRollout = NULL; 253 bool bGrabItems = false; 254 255 AbstractClassRep* commonAncestorClass = findCommonAncestorClass(); 256 AbstractClassRep::FieldList& fieldList = commonAncestorClass->mFieldList; 257 for( AbstractClassRep::FieldList::iterator itr = fieldList.begin(); 258 itr != fieldList.end(); ++ itr ) 259 { 260 AbstractClassRep::Field* field = &( *itr ); 261 if( field->type == AbstractClassRep::StartGroupFieldType ) 262 { 263 // If we're dealing with general fields, always set grabItems to true (to skip them) 264 if( bNoGroup == true ) 265 bGrabItems = true; 266 else if( dStricmp( field->pGroupname, mCaption ) == 0 ) 267 bGrabItems = true; 268 continue; 269 } 270 else if ( field->type == AbstractClassRep::EndGroupFieldType ) 271 { 272 // If we're dealing with general fields, always set grabItems to false (to grab them) 273 if( bNoGroup == true ) 274 bGrabItems = false; 275 else if( dStricmp( field->pGroupname, mCaption ) == 0 ) 276 bGrabItems = false; 277 continue; 278 } 279 280 // Skip field if it has the HideInInspectors flag set. 281 282 if( field->flag.test( AbstractClassRep::FIELD_HideInInspectors ) ) 283 continue; 284 285 if( ( bGrabItems == true || ( bNoGroup == true && bGrabItems == false ) ) && itr->type != AbstractClassRep::DeprecatedFieldType ) 286 { 287 if( bNoGroup == true && bGrabItems == true ) 288 continue; 289 290 if ( field->type == AbstractClassRep::StartArrayFieldType ) 291 { 292 #ifdef DEBUG_SPEW 293 Platform::outputDebugString( "[GuiInspectorGroup] Beginning array '%s'", 294 field->pFieldname ); 295 #endif 296 297 // Starting an array... 298 // Create a rollout for the Array, give it the array's name. 299 GuiRolloutCtrl *arrayRollout = new GuiRolloutCtrl(); 300 GuiControlProfile *arrayRolloutProfile = dynamic_cast<GuiControlProfile*>( Sim::findObject( "GuiInspectorRolloutProfile0" ) ); 301 302 arrayRollout->setControlProfile(arrayRolloutProfile); 303 //arrayRollout->mCaption = StringTable->insert( String::ToString( "%s (%i)", field->pGroupname, field->elementCount ) ); 304 arrayRollout->setCaption( field->pGroupname ); 305 //arrayRollout->setMargin( 14, 0, 0, 0 ); 306 arrayRollout->registerObject(); 307 308 GuiStackControl *arrayStack = new GuiStackControl(); 309 arrayStack->registerObject(); 310 arrayStack->freeze(true); 311 arrayRollout->addObject(arrayStack); 312 313 // Allocate a rollout for each element-count in the array 314 // Give it the element count name. 315 for ( U32 i = 0; i < field->elementCount; i++ ) 316 { 317 GuiRolloutCtrl *elementRollout = new GuiRolloutCtrl(); 318 GuiControlProfile *elementRolloutProfile = dynamic_cast<GuiControlProfile*>( Sim::findObject( "GuiInspectorRolloutProfile0" ) ); 319 320 char buf[256]; 321 dSprintf( buf, 256, " [%i]", i ); 322 323 elementRollout->setControlProfile(elementRolloutProfile); 324 elementRollout->setCaption(buf); 325 //elementRollout->setMargin( 14, 0, 0, 0 ); 326 elementRollout->registerObject(); 327 328 GuiStackControl *elementStack = new GuiStackControl(); 329 elementStack->registerObject(); 330 elementRollout->addObject(elementStack); 331 elementRollout->instantCollapse(); 332 333 arrayStack->addObject( elementRollout ); 334 } 335 336 pArrayRollout = arrayRollout; 337 pArrayStack = arrayStack; 338 arrayStack->freeze(false); 339 pArrayRollout->instantCollapse(); 340 mStack->addObject(arrayRollout); 341 342 bMakingArray = true; 343 continue; 344 } 345 else if ( field->type == AbstractClassRep::EndArrayFieldType ) 346 { 347 #ifdef DEBUG_SPEW 348 Platform::outputDebugString( "[GuiInspectorGroup] Ending array '%s'", 349 field->pFieldname ); 350 #endif 351 352 bMakingArray = false; 353 continue; 354 } 355 356 if ( bMakingArray ) 357 { 358 // Add a GuiInspectorField for this field, 359 // for every element in the array... 360 for ( U32 i = 0; i < pArrayStack->size(); i++ ) 361 { 362 FrameTemp<char> intToStr( 64 ); 363 dSprintf( intToStr, 64, "%d", i ); 364 365 // The array stack should have a rollout for each element 366 // as children... 367 GuiRolloutCtrl *pRollout = dynamic_cast<GuiRolloutCtrl*>(pArrayStack->at(i)); 368 // And the each of those rollouts should have a stack for 369 // fields... 370 GuiStackControl *pStack = dynamic_cast<GuiStackControl*>(pRollout->at(0)); 371 372 // And we add a new GuiInspectorField to each of those stacks... 373 GuiInspectorField *fieldGui = constructField( field->type ); 374 if ( fieldGui == NULL ) 375 fieldGui = new GuiInspectorField(); 376 377 fieldGui->init( mParent, this ); 378 StringTableEntry caption = field->pFieldname; 379 fieldGui->setInspectorField( field, caption, intToStr ); 380 381 if( fieldGui->registerObject() ) 382 { 383 #ifdef DEBUG_SPEW 384 Platform::outputDebugString( "[GuiInspectorGroup] Adding array element '%s[%i]'", 385 field->pFieldname, i ); 386 #endif 387 388 mChildren.push_back( fieldGui ); 389 pStack->addObject( fieldGui ); 390 } 391 else 392 delete fieldGui; 393 } 394 395 continue; 396 } 397 398 // This is weird, but it should work for now. - JDD 399 // We are going to check to see if this item is an array 400 // if so, we're going to construct a field for each array element 401 if( field->elementCount > 1 ) 402 { 403 // Make a rollout control for this array 404 // 405 GuiRolloutCtrl *rollout = new GuiRolloutCtrl(); 406 rollout->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorRolloutProfile0" ); 407 rollout->setCaption(String::ToString( "%s (%i)", field->pFieldname, field->elementCount)); 408 rollout->setMargin( 14, 0, 0, 0 ); 409 rollout->registerObject(); 410 mArrayCtrls.push_back(rollout); 411 412 // Put a stack control within the rollout 413 // 414 GuiStackControl *stack = new GuiStackControl(); 415 stack->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorStackProfile" ); 416 stack->registerObject(); 417 stack->freeze(true); 418 rollout->addObject(stack); 419 420 mStack->addObject(rollout); 421 422 // Create each field and add it to the stack. 423 // 424 for (S32 nI = 0; nI < field->elementCount; nI++) 425 { 426 FrameTemp<char> intToStr( 64 ); 427 dSprintf( intToStr, 64, "%d", nI ); 428 429 // Construct proper ValueName[nI] format which is "ValueName0" for index 0, etc. 430 431 String fieldName = String::ToString( "%s%d", field->pFieldname, nI ); 432 433 // If the field already exists, just update it 434 GuiInspectorField *fieldGui = findField( fieldName ); 435 if( fieldGui != NULL ) 436 { 437 fieldGui->updateValue(); 438 continue; 439 } 440 441 bNewItems = true; 442 443 fieldGui = constructField( field->type ); 444 if ( fieldGui == NULL ) 445 fieldGui = new GuiInspectorField(); 446 447 fieldGui->init( mParent, this ); 448 StringTableEntry caption = StringTable->insert( String::ToString(" [%i]",nI) ); 449 fieldGui->setInspectorField( field, caption, intToStr ); 450 451 if ( fieldGui->registerObject() ) 452 { 453 mChildren.push_back( fieldGui ); 454 stack->addObject( fieldGui ); 455 } 456 else 457 delete fieldGui; 458 } 459 460 stack->freeze(false); 461 stack->updatePanes(); 462 rollout->instantCollapse(); 463 } 464 else 465 { 466 // If the field already exists, just update it 467 GuiInspectorField *fieldGui = findField( field->pFieldname ); 468 if ( fieldGui != NULL ) 469 { 470 fieldGui->updateValue(); 471 continue; 472 } 473 474 bNewItems = true; 475 476 fieldGui = constructField( field->type ); 477 if ( fieldGui == NULL ) 478 fieldGui = new GuiInspectorField(); 479 480 fieldGui->init( mParent, this ); 481 fieldGui->setInspectorField( field ); 482 483 if( fieldGui->registerObject() ) 484 { 485 #ifdef DEBUG_SPEW 486 Platform::outputDebugString( "[GuiInspectorGroup] Adding field '%s'", 487 field->pFieldname ); 488 #endif 489 490 mChildren.push_back( fieldGui ); 491 mStack->addObject( fieldGui ); 492 } 493 else 494 { 495 SAFE_DELETE( fieldGui ); 496 } 497 } 498 } 499 } 500 mStack->freeze(false); 501 mStack->updatePanes(); 502 503 // If we've no new items, there's no need to resize anything! 504 if( bNewItems == false && !mChildren.empty() ) 505 return true; 506 507 sizeToContents(); 508 509 setUpdate(); 510 511 return true; 512} 513 514//----------------------------------------------------------------------------- 515 516bool GuiInspectorGroup::updateFieldValue( StringTableEntry fieldName, StringTableEntry arrayIdx ) 517{ 518 // Check if we contain a field of this name, 519 // if so update its value and return true. 520 Vector<GuiInspectorField*>::iterator iter = mChildren.begin(); 521 522 if( arrayIdx == StringTable->EmptyString() ) 523 arrayIdx = NULL; 524 525 for( ; iter != mChildren.end(); iter++ ) 526 { 527 GuiInspectorField *field = (*iter); 528 if ( field->mField && 529 field->mField->pFieldname == fieldName && 530 field->mFieldArrayIndex == arrayIdx ) 531 { 532 field->updateValue(); 533 return true; 534 } 535 } 536 537 return false; 538} 539 540//----------------------------------------------------------------------------- 541 542void GuiInspectorGroup::updateAllFields() 543{ 544 Vector<GuiInspectorField*>::iterator iter = mChildren.begin(); 545 for( ; iter != mChildren.end(); iter++ ) 546 (*iter)->updateValue(); 547} 548 549//----------------------------------------------------------------------------- 550 551AbstractClassRep* GuiInspectorGroup::findCommonAncestorClass() 552{ 553 AbstractClassRep* classRep = getInspector()->getInspectObject( 0 )->getClassRep(); 554 const U32 numInspectObjects = getInspector()->getNumInspectObjects(); 555 556 for( U32 i = 1; i < numInspectObjects; ++ i ) 557 { 558 SimObject* object = getInspector()->getInspectObject( i ); 559 while( !object->getClassRep()->isClass( classRep ) ) 560 { 561 classRep = classRep->getParentClass(); 562 AssertFatal( classRep, "GuiInspectorGroup::findcommonAncestorClass - Walked above ConsoleObject!" ); 563 } 564 } 565 566 return classRep; 567} 568