Bläddra i källkod

Updates the VariableInspector, VariableGroup and VariableField objects to actually be useful. In addition to the original functionality of being able to have a var name passed in and search for all vars with that in it, it also lets you build out a completely custom Inspector.

Unlike the regular Inspector, which requires a specific object or objects, from which the fields are pulled from, this lets you manually create fields, which can tie into any given object and their fields, global vars, and also not only supports the engine types for fields, but also triggers a callback to script if a field type is not found allowing fully custom fields to be handled as needed.
Areloch 8 år sedan
förälder
incheckning
79081c6474

+ 1 - 0
Engine/source/console/consoleObject.h

@@ -475,6 +475,7 @@ public:
       FIELD_HideInInspectors     = BIT( 0 ),    ///< Do not show the field in inspectors.
       FIELD_ComponentInspectors = BIT(1),       ///< Custom fields used by components. They are likely to be non-standard size/configuration, so 
                                                 ///< They are handled specially
+      FIELD_CustomInspectors = BIT(2),          ///< Display as a button in inspectors.
    };
 
    struct Field

+ 7 - 1
Engine/source/gui/editor/guiInspector.h

@@ -82,7 +82,13 @@ public:
    virtual void clearInspectObjects();
 
    /// Get the currently inspected object
-   SimObject* getInspectObject( U32 index = 0 ) { return mTargets[ index ]; }
+   SimObject* getInspectObject(U32 index = 0)
+   {
+      if (!mTargets.empty())
+         return mTargets[index];
+      else
+         return nullptr;
+   }
    
    /// Return the number of objects being inspected by this GuiInspector.
    U32 getNumInspectObjects() const { return mTargets.size(); }

+ 43 - 15
Engine/source/gui/editor/guiInspectorTypes.cpp

@@ -400,7 +400,7 @@ ConsoleDocClass( GuiInspectorTypeCheckBox,
 
 GuiControl* GuiInspectorTypeCheckBox::constructEditControl()
 {
-   if ( mField->flag.test(AbstractClassRep::FieldFlags::FIELD_ComponentInspectors) )
+   if (mField && mField->flag.test(AbstractClassRep::FIELD_CustomInspectors))
    {
       // This checkbox (bool field) is meant to be treated as a button.
       GuiControl* retCtrl = new GuiButtonCtrl();
@@ -425,27 +425,55 @@ GuiControl* GuiInspectorTypeCheckBox::constructEditControl()
       button->setField("Command", szBuffer );
 
       return retCtrl;
-   } else {
-   GuiControl* retCtrl = new GuiCheckBoxCtrl();
+   }
+   else if (mField && mField->flag.test(AbstractClassRep::FieldFlags::FIELD_ComponentInspectors))
+   {
+      // This checkbox (bool field) is meant to be treated as a button.
+      GuiControl* retCtrl = new GuiButtonCtrl();
 
-   GuiCheckBoxCtrl *check = dynamic_cast<GuiCheckBoxCtrl*>(retCtrl);
+      // If we couldn't construct the control, bail!
+      if (retCtrl == NULL)
+         return retCtrl;
 
-   // Let's make it look pretty.
-   retCtrl->setDataField( StringTable->insert("profile"), NULL, "InspectorTypeCheckboxProfile" );
-   retCtrl->setField( "text", "" );
+      GuiButtonCtrl *button = dynamic_cast<GuiButtonCtrl*>(retCtrl);
 
-   check->setIndent( 4 );
+      // Let's make it look pretty.
+      retCtrl->setDataField(StringTable->insert("profile"), NULL, "InspectorTypeButtonProfile");
+      retCtrl->setField("text", "Click Here");
 
-   retCtrl->setScriptValue( getData() );
+      retCtrl->setScriptValue(getData());
 
-   _registerEditControl( retCtrl );
+      _registerEditControl(retCtrl);
 
-   // Configure it to update our value when the popup is closed
-   char szBuffer[512];
-   dSprintf( szBuffer, 512, "%d.apply(%d.getValue());",getId(),check->getId() );
-   check->setField("Command", szBuffer );
+      // Configure it to update our value when the popup is closed
+      char szBuffer[512];
+      dSprintf(szBuffer, 512, "%d.apply(%d.getValue());", getId(), button->getId());
+      button->setField("Command", szBuffer);
 
-   return retCtrl;
+      return retCtrl;
+   }
+   else 
+   {
+      GuiControl* retCtrl = new GuiCheckBoxCtrl();
+
+      GuiCheckBoxCtrl *check = dynamic_cast<GuiCheckBoxCtrl*>(retCtrl);
+
+      // Let's make it look pretty.
+      retCtrl->setDataField(StringTable->insert("profile"), NULL, "InspectorTypeCheckboxProfile");
+      retCtrl->setField("text", "");
+
+      check->setIndent(4);
+
+      retCtrl->setScriptValue(getData());
+
+      _registerEditControl(retCtrl);
+
+      // Configure it to update our value when the popup is closed
+      char szBuffer[512];
+      dSprintf(szBuffer, 512, "%d.apply(%d.getValue());", getId(), check->getId());
+      check->setField("Command", szBuffer);
+
+      return retCtrl;
    }
 }
 

+ 88 - 7
Engine/source/gui/editor/inspector/field.cpp

@@ -49,7 +49,8 @@ GuiInspectorField::GuiInspectorField( GuiInspector* inspector,
    mInspector( inspector ),
    mField( field ), 
    mFieldArrayIndex( NULL ), 
-   mEdit( NULL )
+   mEdit( NULL ),
+   mTargetObject(NULL)
 {
    if( field != NULL )
       mCaption    = field->pFieldname;
@@ -72,7 +73,11 @@ GuiInspectorField::GuiInspectorField()
    mEdit( NULL ),
    mCaption( StringTable->EmptyString() ),
    mFieldArrayIndex( NULL ),
-   mHighlighted( false )
+   mHighlighted( false ),
+   mTargetObject(NULL),
+   mVariableName(StringTable->EmptyString()),
+   mCallbackName(StringTable->EmptyString()),
+   mSpecialEditField(false)
 {
    setCanSave( false );
 }
@@ -118,6 +123,7 @@ bool GuiInspectorField::onAdd()
    // Force our editField to set it's value
    updateValue();
 
+   Con::evaluatef("%d.edit = %d;", this->getId(), mEdit->getId());
    return true;
 }
 
@@ -244,6 +250,24 @@ void GuiInspectorField::onRightMouseUp( const GuiEvent &event )
 
 void GuiInspectorField::setData( const char* data, bool callbacks )
 {
+   if (mSpecialEditField)
+   {
+      if (mTargetObject != nullptr && mVariableName != StringTable->EmptyString())
+      {
+         mTargetObject->setDataField(mVariableName, NULL, data);
+
+         if (mCallbackName != StringTable->EmptyString())
+            Con::executef(mInspector, mCallbackName, mVariableName, data, mTargetObject);
+      }
+      else if (mVariableName != StringTable->EmptyString())
+      {
+         Con::setVariable(mVariableName, data);
+
+         if (mCallbackName != StringTable->EmptyString())
+            Con::executef(mInspector, mCallbackName, mVariableName, data);
+      }
+   }
+
    if( mField == NULL )
       return;
 
@@ -257,7 +281,19 @@ void GuiInspectorField::setData( const char* data, bool callbacks )
             
       for( U32 i = 0; i < numTargets; ++ i )
       {
-         SimObject* target = mInspector->getInspectObject( i );
+         //For now, for simplicity's sake, you can only edit the components in a simple edit
+         SimObject* target = NULL;
+         if (numTargets == 1)
+         {
+            target = mTargetObject;
+
+            if (!target)
+               target = mInspector->getInspectObject(i);
+         }
+         else
+         {
+            target = mInspector->getInspectObject(i);
+         }
          
          String oldValue = target->getDataField( mField->pFieldname, mFieldArrayIndex);
          
@@ -347,10 +383,31 @@ void GuiInspectorField::setData( const char* data, bool callbacks )
 
 const char* GuiInspectorField::getData( U32 inspectObjectIndex )
 {
-   if( mField == NULL )
-      return "";
+   if (!mSpecialEditField)
+   {
+      if (mField == NULL)
+         return "";
+
+      if (mTargetObject)
+         return mTargetObject->getDataField(mField->pFieldname, mFieldArrayIndex);
 
-   return mInspector->getInspectObject( inspectObjectIndex )->getDataField( mField->pFieldname, mFieldArrayIndex );
+      return mInspector->getInspectObject(inspectObjectIndex)->getDataField(mField->pFieldname, mFieldArrayIndex);
+   }
+   else
+   {
+      if (mTargetObject != nullptr && mVariableName != StringTable->EmptyString())
+      {
+         return mTargetObject->getDataField(mVariableName, NULL);
+      }
+      else if (mVariableName != StringTable->EmptyString())
+      {
+         return Con::getVariable(mVariableName);
+      }
+      else
+      {
+         return "";
+      }
+   }
 }
 
 //-----------------------------------------------------------------------------
@@ -483,6 +540,17 @@ void GuiInspectorField::setValue( StringTableEntry newValue )
 
 //-----------------------------------------------------------------------------
 
+void GuiInspectorField::setEditControl(GuiControl* editCtrl) 
+{ 
+   if (mEdit)
+      mEdit->deleteObject();
+
+   mEdit = editCtrl; 
+   addObject(mEdit);
+}
+
+//-----------------------------------------------------------------------------
+
 bool GuiInspectorField::updateRects()
 {
    S32 dividerPos, dividerMargin;
@@ -587,7 +655,10 @@ void GuiInspectorField::_executeSelectedCallback()
 void GuiInspectorField::_registerEditControl( GuiControl *ctrl )
 {
    char szName[512];
-   dSprintf( szName, 512, "IE_%s_%d_%s_Field", ctrl->getClassName(), mInspector->getInspectObject()->getId(), mCaption);
+   if(mInspector->getInspectObject() != nullptr)
+      dSprintf( szName, 512, "IE_%s_%d_%s_Field", ctrl->getClassName(), mInspector->getInspectObject()->getId(), mCaption);
+   else
+      dSprintf(szName, 512, "IE_%s_%s_Field", ctrl->getClassName(), mCaption);
 
    // Register the object
    ctrl->registerObject( szName );
@@ -663,3 +734,13 @@ DefineConsoleMethod( GuiInspectorField, reset, void, (), , "() - Reset to defaul
 {
    object->resetData();
 }
+
+DefineConsoleMethod(GuiInspectorField, setCaption, void, (String newCaption),, "() - Reset to default value.")
+{
+   object->setCaption(StringTable->insert(newCaption.c_str()));
+}
+
+DefineConsoleMethod(GuiInspectorField, setEditControl, void, (GuiControl* editCtrl), (nullAsType<GuiControl*>()), "() - Reset to default value.")
+{
+   object->setEditControl(editCtrl);
+}

+ 20 - 0
Engine/source/gui/editor/inspector/field.h

@@ -84,6 +84,16 @@ class GuiInspectorField : public GuiControl
       ///
       bool mHighlighted;
 
+      //An override that lets us bypass inspector-dependent logic for setting/getting variables/fields
+      bool mSpecialEditField;
+      //An override to make sure this field is associated to an object that isn't expressly
+      //the one the inspector is inspecting. Such as an entity's component.
+      SimObject* mTargetObject;
+      //Special edit field, variable name - the variable or field name targeted
+      StringTableEntry mVariableName;
+      //Special edit field, callback name - if defined, we'll do a callback to the function listed here when editing the field
+      StringTableEntry mCallbackName;
+
       virtual void _registerEditControl( GuiControl *ctrl );
       virtual void _executeSelectedCallback();
       
@@ -116,6 +126,10 @@ class GuiInspectorField : public GuiControl
       /// this is exposed in case someone wants to override the normal caption.
       virtual void setCaption( StringTableEntry caption ) { mCaption = caption; }
 
+      void setEditControl(GuiControl* editCtrl);
+
+      virtual void setDocs(String docs) { mFieldDocs = docs; }
+
       /// Returns pointer to this InspectorField's edit ctrl.
       virtual GuiControl* getEditCtrl() { return mEdit; }
 
@@ -187,6 +201,12 @@ class GuiInspectorField : public GuiControl
       virtual void onMouseDown( const GuiEvent &event );
       virtual void onRightMouseUp( const GuiEvent &event );
 
+      void setTargetObject(SimObject* obj) { mTargetObject = obj; }
+      SimObject* getTargetObject() { return mTargetObject; }
+      void setSpecialEditField(bool isSpecialEditField) { mSpecialEditField = isSpecialEditField; }
+      void setSpecialEditVariableName(String varName) { mVariableName = StringTable->insert(varName); }
+      void setSpecialEditCallbackName(String callName) { mCallbackName = StringTable->insert(callName); }
+
       DECLARE_CONOBJECT( GuiInspectorField );
       DECLARE_CATEGORY( "Gui Editor" );
 };

+ 18 - 3
Engine/source/gui/editor/inspector/variableField.cpp

@@ -93,10 +93,25 @@ bool GuiInspectorVariableField::onAdd()
 
 void GuiInspectorVariableField::setData( const char* data, bool callbacks )
 {   
-   if ( !mCaption || mCaption[0] == 0 )
-      return;
+   if (mOwnerObject == nullptr && mVariableName == StringTable->EmptyString())
+   {
+      if (!mCaption || mCaption[0] == 0)
+         return;
 
-   Con::setVariable( mCaption, data );   
+      Con::setVariable(mCaption, data);
+   }
+   else
+   {
+      if (mOwnerObject != nullptr)
+      {
+         mOwnerObject->setDataField(mVariableName, NULL, data);
+      }
+      else
+      {
+         //probably a global var if we have no object attached, so just let it set
+         Con::setVariable(mVariableName, data);
+      }
+   }
 
    // Force our edit to update
    updateValue();

+ 2 - 1
Engine/source/gui/editor/inspector/variableField.h

@@ -56,7 +56,8 @@ public:
    virtual void updateData() {};
 
 protected:
-
+   StringTableEntry mVariableName;
+   SimObject* mOwnerObject;
 };
 
 #endif // _GUI_INSPECTOR_VARIABLEFIELD_H_

+ 168 - 19
Engine/source/gui/editor/inspector/variableGroup.cpp

@@ -51,7 +51,7 @@ GuiInspectorVariableGroup::~GuiInspectorVariableGroup()
 
 GuiInspectorField* GuiInspectorVariableGroup::constructField( S32 fieldType )
 {
-   return NULL;
+   return Parent::constructField(fieldType);
 }
 
 bool GuiInspectorVariableGroup::inspectGroup()
@@ -59,39 +59,133 @@ bool GuiInspectorVariableGroup::inspectGroup()
    // to prevent crazy resizing, we'll just freeze our stack for a sec..
    mStack->freeze(true);
 
-   clearFields();
+   bool bNewItems = false;
 
-   Vector<String> names;
+   if (!mSearchString.equal(""))
+   {
+      Vector<String> names;
 
-   gEvalState.globalVars.exportVariables( mSearchString, &names, NULL );
+      gEvalState.globalVars.exportVariables(mSearchString, &names, NULL);
 
-   bool bNewItems = false;
+      for (U32 i = 0; i < names.size(); i++)
+      {
+         const String &varName = names[i];
+
+         // If the field already exists, just update it
+         GuiInspectorVariableField *field = dynamic_cast<GuiInspectorVariableField*>(findField(varName));
+         if (field != NULL)
+         {
+            field->updateValue();
+            continue;
+         }
+
+         bNewItems = true;
+
+         field = new GuiInspectorVariableField();
+         field->init(mParent, this);
+         field->setInspectorField(NULL, StringTable->insert(varName));
+
+         if (field->registerObject())
+         {
+            mChildren.push_back(field);
+            mStack->addObject(field);
+         }
+         else
+            delete field;
+      }
+   }
+
+   for (U32 i = 0; i < mFields.size(); i++)
+   {
+      bNewItems = true;
 
-   for ( U32 i = 0; i < names.size(); i++ )
-   {      
-      const String &varName = names[i];
+      GuiInspectorField *fieldGui = findField(mFields[i]->mFieldName);
+      if (fieldGui != NULL)
+      {
+         fieldGui->updateValue();
+         continue;
+      }
 
-      // If the field already exists, just update it
-      GuiInspectorVariableField *field = dynamic_cast<GuiInspectorVariableField*>( findField( varName ) );
-      if ( field != NULL )
+      //first and foremost, nab the field type and check if it's a custom field or not.
+      //If it's not a custom field, proceed below, if it is, hand it off to script to be handled by the component
+      if (mFields[i]->mFieldType == -1)
       {
-         field->updateValue();
+         if (isMethod("onConstructField"))
+         {
+            //ensure our stack variable is bound if we need it
+            Con::evaluatef("%d.stack = %d;", this->getId(), mStack->getId());
+
+            Con::executef(this, "onConstructField", mFields[i]->mFieldName,
+               mFields[i]->mFieldLabel, mFields[i]->mFieldTypeName, mFields[i]->mFieldDescription,
+               mFields[i]->mDefaultValue, mFields[i]->mDataValues, mFields[i]->mOwnerObject);
+         }
          continue;
       }
 
       bNewItems = true;
 
-      field = new GuiInspectorVariableField();
-      field->init( mParent, this );            
-      field->setInspectorField( NULL, StringTable->insert( varName ) );
+      fieldGui = constructField(mFields[i]->mFieldType);
+      if (fieldGui == NULL)
+         fieldGui = new GuiInspectorField();
 
-      if ( field->registerObject() )
+      fieldGui->init(mParent, this);
+
+      fieldGui->setSpecialEditField(true);
+
+      if (mFields[i]->mOwnerObject)
+      {
+         fieldGui->setTargetObject(mFields[i]->mOwnerObject);
+      }
+      else
       {
-         mChildren.push_back( field );
-         mStack->addObject( field );
+         //check if we're binding to a global var first, if we have no owner
+         if (mFields[i]->mFieldName[0] != '$')
+         {
+            fieldGui->setTargetObject(mParent);
+         }
+      }
+
+      fieldGui->setSpecialEditVariableName(mFields[i]->mFieldName);
+      fieldGui->setSpecialEditCallbackName(mFields[i]->mSetCallbackName);
+
+      fieldGui->setInspectorField(NULL, mFields[i]->mFieldLabel);
+      fieldGui->setDocs(mFields[i]->mFieldDescription);
+
+      /*if (mFields[i]->mSetCallbackName != StringTable->EmptyString())
+      {
+         fieldGui->on.notify()
+      }*/
+         
+      if (fieldGui->registerObject())
+      {
+#ifdef DEBUG_SPEW
+         Platform::outputDebugString("[GuiInspectorVariableGroup] Adding field '%s'",
+            field->pFieldname);
+#endif
+
+         if (mFields[i]->mOwnerObject)
+         {
+            String val = mFields[i]->mOwnerObject->getDataField(mFields[i]->mFieldName, NULL);
+
+            if(val.isEmpty())
+               fieldGui->setValue(mFields[i]->mDefaultValue);
+            else
+               fieldGui->setValue(val);
+         }
+         else
+         {
+            fieldGui->setValue(mFields[i]->mDefaultValue);
+         }
+
+         fieldGui->setActive(mFields[i]->mEnabled);
+
+         mChildren.push_back(fieldGui);
+         mStack->addObject(fieldGui);
       }
       else
-         delete field;         
+      {
+         SAFE_DELETE(fieldGui);
+      }
    }
    
    mStack->freeze(false);
@@ -107,3 +201,58 @@ bool GuiInspectorVariableGroup::inspectGroup()
 
    return true;
 }
+
+void GuiInspectorVariableGroup::clearFields()
+{
+   mFields.clear();
+}
+
+void GuiInspectorVariableGroup::addField(VariableField* field)
+{
+   bool found = false;
+
+   for (U32 i = 0; i < mFields.size(); i++)
+   {
+      if (mFields[i]->mFieldName == field->mFieldName)
+      {
+         found = true;
+         break;
+      }
+   }
+
+   if(!found)
+      mFields.push_back(field);
+}
+
+void GuiInspectorVariableGroup::addInspectorField(GuiInspectorField* field)
+{
+   mStack->addObject(field);
+   mChildren.push_back(field);
+   mStack->updatePanes();
+}
+
+GuiInspectorField* GuiInspectorVariableGroup::createInspectorField()
+{
+   GuiInspectorField* newField = new GuiInspectorField();
+
+   newField->init(mParent, this);
+
+   newField->setSpecialEditField(true);
+
+   if (newField->registerObject())
+   {
+      return newField;
+   }
+
+   return NULL;
+}
+
+DefineConsoleMethod(GuiInspectorVariableGroup, createInspectorField, GuiInspectorField*, (),, "createInspectorField()")
+{
+   return object->createInspectorField();
+}
+
+DefineConsoleMethod(GuiInspectorVariableGroup, addInspectorField, void, (GuiInspectorField* field), (nullAsType<GuiInspectorField*>()), "addInspectorField( GuiInspectorFieldObject )")
+{
+   object->addInspectorField(field);
+}

+ 29 - 0
Engine/source/gui/editor/inspector/variableGroup.h

@@ -31,6 +31,28 @@
 class GuiInspector;
 class GuiInspectorField;
 
+struct VariableField
+{
+   StringTableEntry mFieldName;
+   StringTableEntry mFieldLabel;
+   StringTableEntry mFieldDescription;
+
+   StringTableEntry mFieldTypeName;
+   S32 mFieldType;
+
+   SimObject* mOwnerObject;
+
+   StringTableEntry mDefaultValue;
+   String mDataValues;
+
+   String mGroup;
+
+   StringTableEntry mSetCallbackName;
+
+   bool mHidden;
+   bool mEnabled;
+};
+
 class GuiInspectorVariableGroup : public GuiInspectorGroup
 {
 public:
@@ -49,7 +71,14 @@ public:
 
    virtual bool inspectGroup();
 
+   void clearFields();
+   void addField(VariableField* field);
+
+   void addInspectorField(GuiInspectorField* field);
+   GuiInspectorField* createInspectorField();
+
 protected:
+   Vector<VariableField*> mFields;
 };
 
 #endif // _GUI_INSPECTOR_VARIABLEGROUP_H_

+ 197 - 2
Engine/source/gui/editor/inspector/variableInspector.cpp

@@ -21,7 +21,6 @@
 //-----------------------------------------------------------------------------
 
 #include "gui/editor/inspector/variableInspector.h"
-#include "gui/editor/inspector/variableGroup.h"
 #include "console/engineAPI.h"
 
 GuiVariableInspector::GuiVariableInspector()
@@ -56,7 +55,203 @@ void GuiVariableInspector::loadVars( String searchStr )
    mGroups.push_back( group );
    addObject( group );
  
-   //group->inspectGroup();
+   group->inspectGroup();
+}
+
+void GuiVariableInspector::update()
+{
+   clearGroups();
+
+   for (U32 i = 0; i < mFields.size(); i++)
+   {
+      //first, get the var's group name. if the group exists, we'll add to it's list
+      GuiInspectorVariableGroup *group = nullptr;
+
+      for (U32 g = 0; g < mGroups.size(); g++)
+      {
+         if (mGroups[g]->getCaption().equal(mFields[i].mGroup))
+         {
+            group = static_cast<GuiInspectorVariableGroup*>(mGroups[g]);
+            break;
+         }
+      }
+
+      if (group == nullptr)
+      {
+         group = new GuiInspectorVariableGroup();
+
+         group->setHeaderHidden(false);
+         group->setCanCollapse(true);
+         group->mParent = this;
+         group->setCaption(mFields[i].mGroup);
+
+         group->registerObject();
+         mGroups.push_back(group);
+         addObject(group);
+      }
+      
+      group->addField(&mFields[i]);
+   }
+
+   //And now, cue our update for the groups themselves
+   for (U32 g = 0; g < mGroups.size(); g++)
+   {
+      mGroups[g]->inspectGroup();
+   }
+}
+
+void GuiVariableInspector::startGroup(const char* name)
+{
+   if (!mCurrentGroup.isEmpty())
+      return;
+
+   mCurrentGroup = name;
+}
+
+void GuiVariableInspector::endGroup()
+{
+   mCurrentGroup = "";
+}
+
+void GuiVariableInspector::addField(const char* name, const char* label, const char* typeName, const char* description, 
+   const char* defaultValue, const char* dataValues, SimObject* ownerObj)
+{
+   VariableField newField;
+   newField.mFieldName = StringTable->insert(name);
+   newField.mFieldLabel = StringTable->insert(label);
+   newField.mFieldTypeName = StringTable->insert(typeName);
+   newField.mFieldDescription = StringTable->insert(description);
+   newField.mDefaultValue = StringTable->insert(defaultValue);
+   newField.mDataValues = String(dataValues);
+   newField.mGroup = mCurrentGroup;
+   newField.mSetCallbackName = StringTable->EmptyString();
+   newField.mEnabled = true;
+
+   newField.mOwnerObject = ownerObj;
+
+   //establish the field on the ownerObject(if we have one)
+   //This way, we can let the field hook into the object's field and modify it when changed
+   if (newField.mOwnerObject != nullptr)
+   {
+      if (!newField.mOwnerObject->isField(newField.mFieldName))
+      {
+         newField.mOwnerObject->setDataField(newField.mFieldName, NULL, newField.mDefaultValue);
+      }
+   }
+
+   //
+   //find the field type
+   S32 fieldTypeMask = -1;
+
+   if (newField.mFieldTypeName == StringTable->insert("int"))
+      fieldTypeMask = TypeS32;
+   else if (newField.mFieldTypeName == StringTable->insert("float"))
+      fieldTypeMask = TypeF32;
+   else if (newField.mFieldTypeName == StringTable->insert("vector"))
+      fieldTypeMask = TypePoint3F;
+   //else if (fieldType == StringTable->insert("material"))
+   //   fieldTypeMask = TypeMaterialName;
+   else if (newField.mFieldTypeName == StringTable->insert("image"))
+      fieldTypeMask = TypeImageFilename;
+   else if (newField.mFieldTypeName == StringTable->insert("shape"))
+      fieldTypeMask = TypeShapeFilename;
+   else if (newField.mFieldTypeName == StringTable->insert("bool"))
+      fieldTypeMask = TypeBool;
+   else if (newField.mFieldTypeName == StringTable->insert("object"))
+      fieldTypeMask = TypeSimObjectPtr;
+   else if (newField.mFieldTypeName == StringTable->insert("string"))
+      fieldTypeMask = TypeString;
+   else if (newField.mFieldTypeName == StringTable->insert("colorI"))
+      fieldTypeMask = TypeColorI;
+   else if (newField.mFieldTypeName == StringTable->insert("colorF"))
+      fieldTypeMask = TypeColorF;
+   else if (newField.mFieldTypeName == StringTable->insert("ease"))
+      fieldTypeMask = TypeEaseF;
+   else
+      fieldTypeMask = -1;
+
+   newField.mFieldType = fieldTypeMask;
+   //
+
+   mFields.push_back(newField);
+
+   update();
+}
+
+void GuiVariableInspector::addCallbackField(const char* name, const char* label, const char* typeName, const char* description,
+   const char* defaultValue, const char* dataValues, const char* callbackName, SimObject* ownerObj)
+{
+   addField(name, label, typeName, description, defaultValue, dataValues, ownerObj);
+
+   //Add the callback name
+   mFields.last().mSetCallbackName = StringTable->insert(callbackName);
+
+   update();
+}
+
+void GuiVariableInspector::clearFields()
+{
+   mFields.clear();
+   update();
+}
+
+void GuiVariableInspector::setFieldEnabled(const char* name, bool enabled)
+{
+   String fieldName = name;
+   for (U32 i = 0; i < mFields.size(); i++)
+   {
+      if (fieldName.equal(mFields[i].mFieldName, String::NoCase))
+      {
+         mFields[i].mEnabled = enabled;
+         update();
+         return;
+      }
+   }
+}
+
+DefineConsoleMethod(GuiVariableInspector, startGroup, void, (const char* name),, "startGroup( groupName )")
+{
+   object->startGroup(name);
+}
+
+DefineConsoleMethod(GuiVariableInspector, endGroup, void, (),, "endGroup()")
+{
+   object->endGroup();
+}
+
+DefineConsoleMethod(GuiVariableInspector, addField, void, (const char* name, const char* label, const char* typeName, 
+   const char* description, const char* defaultValue, const char* dataValues, SimObject* ownerObj),
+   ("","","","","", "", nullAsType<SimObject*>()), "addField( fieldName/varName, fieldLabel, fieldTypeName, description, defaultValue, defaultValues, ownerObject )")
+{
+   if (name == "" || typeName == "")
+      return;
+
+   object->addField(name, label, typeName, description, defaultValue, dataValues, ownerObj);
+}
+
+DefineConsoleMethod(GuiVariableInspector, addCallbackField, void, (const char* name, const char* label, const char* typeName,
+   const char* description, const char* defaultValue, const char* dataValues, const char* callbackName, SimObject* ownerObj),
+   ("", "", "", "", "", "", nullAsType<SimObject*>()), "addField( fieldName/varName, fieldLabel, fieldTypeName, description, defaultValue, defaultValues, callbackName, ownerObject )")
+{
+   if (name == "" || typeName == "")
+      return;
+
+   object->addCallbackField(name, label, typeName, description, defaultValue, dataValues, callbackName, ownerObj);
+}
+
+DefineConsoleMethod(GuiVariableInspector, update, void, (), , "update()")
+{
+   object->update();
+}
+
+DefineConsoleMethod(GuiVariableInspector, clearFields, void, (), , "clearFields()")
+{
+   object->clearFields();
+}
+
+DefineConsoleMethod(GuiVariableInspector, setFieldEnabled, void, (const char* fieldName, bool isEnabled), (true), "setFieldEnabled( fieldName, isEnabled )")
+{
+   object->setFieldEnabled(fieldName, isEnabled);
 }
 
 DefineConsoleMethod( GuiVariableInspector, loadVars, void, ( const char * searchString ), , "loadVars( searchString )" )

+ 18 - 0
Engine/source/gui/editor/inspector/variableInspector.h

@@ -26,6 +26,9 @@
 #ifndef _GUI_INSPECTOR_H_
 #include "gui/editor/guiInspector.h"
 #endif
+#ifndef _GUI_INSPECTOR_VARIABLEGROUP_H_
+#include "gui/editor/inspector/variableGroup.h"
+#endif
 
 
 class GuiVariableInspector : public GuiInspector
@@ -44,8 +47,23 @@ public:
 
    virtual void loadVars( String searchString );
 
+   void update();
+
+   void startGroup(const char* name);
+   void endGroup();
+
+   void addField(const char* name, const char* label, const char* typeName, const char* description, 
+      const char* defaultValue, const char* dataValues, SimObject* ownerObj);
+   void addCallbackField(const char* name, const char* label, const char* typeName, const char* description,
+      const char* defaultValue, const char* dataValues, const char* callbackName, SimObject* ownerObj);
+   void setFieldEnabled(const char* name, bool enabled);
+   void clearFields();
 
 protected:
+   
+   Vector<VariableField> mFields;
+
+   String mCurrentGroup;
 
 };