Browse Source

Inspector work

Josh Engebretson 10 years ago
parent
commit
68e703fd98

+ 30 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/MainFrame.ts

@@ -12,6 +12,7 @@ class MainFrame extends ScriptWidget {
 
     projectframe: ProjectFrame;
     resourceframe: ResourceFrame;
+    inspectorframe: Editor.InspectorFrame;
 
     inspectorlayout: Atomic.UILayout;
 
@@ -24,21 +25,32 @@ class MainFrame extends ScriptWidget {
         this.load("AtomicEditor/editor/ui/mainframe.tb.txt");
 
         this.inspectorlayout = <Atomic.UILayout> this.getWidget("inspectorlayout");
+
+        this.inspectorframe = new Editor.InspectorFrame();
+        this.inspectorframe.load("AtomicEditor/editor/ui/inspectorframe.tb.txt");
+        this.inspectorlayout.addChild(this.inspectorframe);
+
         this.projectframe = new ProjectFrame(this);
         this.resourceframe = new ResourceFrame(this);
 
         this.showInspectorFrame(false);
 
+        this.subscribeToEvent(UIEvents.ResourceEditorChanged, (data) => this.handleResourceEditorChanged(data));
+
     }
 
     showInspectorFrame(show: boolean) {
 
         if (show) {
 
+            print("Showing Inspector");
             this.inspectorlayout.visibility = UI.VISIBILITY_VISIBLE;
+            this.inspectorframe.visibility = UI.VISIBILITY_VISIBLE;
 
         } else {
 
+            print("Hiding Inspector");
+            this.inspectorframe.visibility = UI.VISIBILITY_GONE;
             this.inspectorlayout.visibility = UI.VISIBILITY_GONE;
 
         }
@@ -89,6 +101,24 @@ class MainFrame extends ScriptWidget {
         }
     }
 
+    handleResourceEditorChanged(data) {
+
+      var editor = <Editor.ResourceEditor> data.editor;
+
+      if (editor) {
+
+        this.showInspectorFrame(editor.requiresInspector());
+
+      } else {
+
+        this.showInspectorFrame(false);
+
+      }
+
+
+    }
+
+
 }
 
 export = MainFrame;

+ 54 - 17
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ResourceFrame.ts

@@ -3,12 +3,22 @@ import UIEvents = require("./UIEvents");
 
 var UI = Atomic.UI;
 
+// the root content of editor widgets (rootContentWidget property) are extended with an editor field
+// so we can access the editor they belong to from the widget itself
+interface EditorRootContentWidget extends Atomic.UIWidget{
+  editor: Editor.ResourceEditor;
+}
+
 class ResourceFrame extends ScriptWidget {
 
     tabcontainer: Atomic.UITabContainer;
     resourceLayout: Atomic.UILayout;
     resourceViewContainer: Atomic.UILayout;
+    currentResourceEditor: Editor.ResourceEditor;
+
+    // editors have a rootCotentWidget which is what is a child of the tab container
 
+    // editors can be looked up by the full path of what they are editing
     editors: { [path: string]: Editor.ResourceEditor; } = {};
 
     show(value: boolean) {
@@ -35,8 +45,8 @@ class ResourceFrame extends ScriptWidget {
 
         if (ext == ".js") {
 
-            editor = new Editor.JSResourceEditor(path, this.tabcontainer);
-            
+           editor = new Editor.JSResourceEditor(path, this.tabcontainer);
+
         } else if (ext == ".scene") {
 
            editor = new Editor.SceneEditor3D(path, this.tabcontainer);
@@ -44,6 +54,10 @@ class ResourceFrame extends ScriptWidget {
         }
 
         if (editor) {
+
+            // add __editor which lets us lookup the editor via the rootContentWidget
+            // could this be formalized with an interface?
+            (<EditorRootContentWidget> editor.rootContentWidget).editor = editor;
             this.editors[path] = editor;
             this.tabcontainer.currentPage = this.tabcontainer.numPages - 1;
             editor.setFocus();
@@ -95,24 +109,11 @@ class ResourceFrame extends ScriptWidget {
         var navigate = <boolean> data.navigateToAvailableResource;
 
         // remove from lookup
-        this.editors[editor.fullPath] = undefined;
+        delete this.editors[editor.fullPath];
 
         var root = this.tabcontainer.contentRoot;
 
-        var found = false;
-
-        var i = 0;
-
-        for (var child = root.firstChild; child; child = child.next, i++) {
-            if (child == editor.rootContentWidget) {
-                found = true;
-                root.removeChild(child);
-                break;
-            }
-
-        }
-
-        assert(found);
+        root.removeChild(editor.rootContentWidget);
 
         this.tabcontainer.currentPage = -1;
 
@@ -130,6 +131,39 @@ class ResourceFrame extends ScriptWidget {
 
     }
 
+    handleResourceEditorChanged(data) {
+
+      var editor = <Editor.ResourceEditor> data.editor;
+      this.currentResourceEditor = editor;
+
+    }
+
+    handleWidgetEvent(data) {
+
+      // ok, first thing is to fix up this widget <-> editor mess
+
+      if (data.type == Atomic.UI.EVENT_TYPE_TAB_CHANGED && data.target == this.tabcontainer)
+      {
+        var w = <EditorRootContentWidget> this.tabcontainer.currentPageWidget;
+
+        if (w && w.editor) {
+
+          if (this.currentResourceEditor != w.editor) {
+
+            this.sendEvent(UIEvents.ResourceEditorChanged, { editor: w.editor});
+
+          }
+
+        }
+
+
+      }
+
+      // bubble
+      return false;
+
+    }
+
     constructor(parent: Atomic.UIWidget) {
 
         super();
@@ -146,6 +180,9 @@ class ResourceFrame extends ScriptWidget {
 
         this.subscribeToEvent(UIEvents.EditResource, (data) => this.handleEditResource(data));
         this.subscribeToEvent(UIEvents.CloseResourceEditor, (data) => this.handleCloseResourceEditor(data));
+        this.subscribeToEvent(UIEvents.ResourceEditorChanged, (data) => this.handleResourceEditorChanged(data));
+
+        this.subscribeToEvent("WidgetEvent", (data) => this.handleWidgetEvent(data));
 
     }
 

+ 2 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/UIEvents.ts

@@ -1,3 +1,5 @@
 
 export const MessageModalEvent = "MessageModalEvent";
 export const EditResource = "EditResource";
+export const CloseResourceEditor = "CloseResourceEditor";
+export const ResourceEditorChanged = "ResourceEditorChanged";

+ 10 - 0
Source/Atomic/UI/UITabContainer.cpp

@@ -28,6 +28,16 @@ UITabContainer::~UITabContainer()
 
 }
 
+UIWidget* UITabContainer::GetCurrentPageWidget()
+{
+    if (!widget_)
+        return 0;
+
+    TBWidget* w = ((TBTabContainer*)widget_)->GetCurrentPageWidget();
+
+    return GetSubsystem<UI>()->WrapWidget(w);
+}
+
 int UITabContainer::GetNumPages()
 {
     if (!widget_)

+ 2 - 0
Source/Atomic/UI/UITabContainer.h

@@ -18,6 +18,8 @@ public:
     int GetNumPages();
     void SetCurrentPage(int page);
 
+    UIWidget* GetCurrentPageWidget();
+
 protected:
 
     virtual bool OnEvent(const tb::TBWidgetEvent &ev);

+ 14 - 0
Source/Atomic/UI/UIWidget.cpp

@@ -455,6 +455,20 @@ bool UIWidget::OnEvent(const tb::TBWidgetEvent &ev)
 
         }
 
+    }
+    else if (ev.type == EVENT_TYPE_TAB_CHANGED)
+    {
+        if (!ev.target || ui->IsWidgetWrapped(ev.target))
+        {
+            VariantMap eventData;
+            ConvertEvent(this, ui->WrapWidget(ev.target), ev, eventData);
+            SendEvent(E_WIDGETEVENT, eventData);
+
+            if (eventData[WidgetEvent::P_HANDLED].GetBool())
+                return true;
+
+        }
+
     }
     else if (ev.type == EVENT_TYPE_CLICK)
     {

+ 14 - 14
Source/AtomicEditorWork/Editors/SceneEditor3D/Gizmo3D.h

@@ -81,26 +81,26 @@ public:
     }
 };
 
+enum EditMode
+{
+    EDIT_SELECT,
+    EDIT_MOVE,
+    EDIT_ROTATE,
+    EDIT_SCALE
+};
+
+enum AxisMode
+{
+    AXIS_WORLD = 0,
+    AXIS_LOCAL
+};
+
 class Gizmo3D: public Object
 {
     OBJECT(Gizmo3D);
 
 public:
 
-    enum EditMode
-    {
-        EDIT_SELECT,
-        EDIT_MOVE,
-        EDIT_ROTATE,
-        EDIT_SCALE
-    };
-
-    enum AxisMode
-    {
-        AXIS_WORLD = 0,
-        AXIS_LOCAL
-    };
-
     Gizmo3D(Context* context);
     virtual ~Gizmo3D();
 

+ 3 - 3
Source/AtomicEditorWork/Editors/SceneEditor3D/SceneEditor3D.cpp

@@ -123,17 +123,17 @@ bool SceneEditor3D::OnEvent(const TBWidgetEvent &ev)
         {
             if (ev.target->GetID() == TBIDC("3d_translate"))
             {
-                gizmo3D_->SetEditMode(Gizmo3D::EDIT_MOVE);
+                gizmo3D_->SetEditMode(EDIT_MOVE);
                 return false;
             }
             else if (ev.target->GetID() == TBIDC("3d_rotate"))
             {
-                gizmo3D_->SetEditMode(Gizmo3D::EDIT_ROTATE);
+                gizmo3D_->SetEditMode(EDIT_ROTATE);
                 return false;
             }
             else if (ev.target->GetID() == TBIDC("3d_scale"))
             {
-                gizmo3D_->SetEditMode(Gizmo3D::EDIT_SCALE);
+                gizmo3D_->SetEditMode(EDIT_SCALE);
                 return false;
             }
         }

+ 364 - 0
Source/AtomicEditorWork/Inspector/UIInspectorDataBinding.cpp

@@ -0,0 +1,364 @@
+
+#include <TurboBadger/tb_widgets_common.h>
+#include <TurboBadger/tb_menu_window.h>
+#include <TurboBadger/tb_inline_select.h>
+
+#include "AtomicEditor.h"
+
+#include <Atomic/Core/StringUtils.h>
+
+#include "UIInspectorDataBinding.h"
+
+namespace AtomicEditor
+{
+
+void InspectorDataBinding::SetObjectValueFromWidget(TBWidget *srcWidget)
+{
+    if (objectLocked_)
+        return;
+
+    objectLocked_ = true;
+
+    VariantType type = attrInfo_->type_;
+
+    if (type == VAR_BOOL)
+    {
+        TBCheckBox* box = (TBCheckBox *) widget_;
+        Variant v(box->GetValue() ? true : false);
+        object_->SetAttribute(attrInfo_->name_, v);
+    }
+    else if (type == VAR_INT)
+    {
+        TBEditField* field = (TBEditField*) widget_;
+        Variant v(ToInt(field->GetText().CStr()));
+        object_->SetAttribute(attrInfo_->name_, v);
+    }
+    else if (type == VAR_FLOAT)
+    {
+        TBEditField* field = (TBEditField*) widget_;
+        Variant v(ToFloat(field->GetText().CStr()));
+        object_->SetAttribute(attrInfo_->name_, v);
+    }
+    else if (type == VAR_STRING)
+    {
+        TBEditField* field = (TBEditField*) widget_;
+        Variant v(String(field->GetText().CStr()));
+        object_->SetAttribute(attrInfo_->name_, v);
+    }
+    else if (type == VAR_COLOR && srcWidget)
+    {
+        Color value = object_->GetAttribute(attrInfo_->name_).GetColor();
+
+        TBInlineSelect* select = NULL;
+        if (srcWidget->GetID() == TBID(unsigned(1)))
+        {
+            select = (TBInlineSelect*) srcWidget;
+            value.r_ = (float ) select->GetValueDouble();
+        }
+        else if (srcWidget->GetID() == TBID(unsigned(2)))
+        {
+            select = (TBInlineSelect*) srcWidget;
+            value.g_ = (float ) select->GetValueDouble();
+        }
+        else if (srcWidget->GetID() == TBID(unsigned(3)))
+        {
+            select = (TBInlineSelect*) srcWidget;
+            value.b_ = (float ) select->GetValueDouble();
+        }
+        else if (srcWidget->GetID() == TBID(unsigned(4)))
+        {
+            select = (TBInlineSelect*) srcWidget;
+            value.a_ = (float ) select->GetValueDouble();
+        }
+
+        object_->SetAttribute(attrInfo_->name_, value);
+    }
+    else if (type == VAR_VECTOR3 && srcWidget)
+    {
+        Vector3 value = object_->GetAttribute(attrInfo_->name_).GetVector3();
+
+        if (srcWidget->GetID() == TBID(unsigned(1)))
+            value.x_ = srcWidget->GetValueDouble();
+        else if (srcWidget->GetID() == TBID(unsigned(2)))
+            value.y_ = srcWidget->GetValueDouble();
+        else if (srcWidget->GetID() == TBID(unsigned(3)))
+            value.z_ = srcWidget->GetValueDouble();
+
+        object_->SetAttribute(attrInfo_->name_, value);
+    }
+    else if (type == VAR_QUATERNION && srcWidget)
+    {
+        Quaternion q = object_->GetAttribute(attrInfo_->name_).GetQuaternion();
+
+        Vector3 value = q.EulerAngles();
+
+        if (srcWidget->GetID() == TBID(unsigned(1)))
+            value.x_ = srcWidget->GetValueDouble();
+        else if (srcWidget->GetID() == TBID(unsigned(2)))
+            value.y_ = srcWidget->GetValueDouble();
+        else if (srcWidget->GetID() == TBID(unsigned(3)))
+            value.z_ = srcWidget->GetValueDouble();
+
+        q.FromEulerAngles(value.x_, value.y_, value.z_);
+
+        object_->SetAttribute(attrInfo_->name_, q);
+    }
+
+    // refresh widget
+    //SetWidgetValueFromObject();
+
+    objectLocked_ = false;
+
+}
+
+void InspectorDataBinding::SetWidgetValueFromObject()
+{
+    if (widgetLocked_)
+        return;
+
+    widgetLocked_ = true;
+
+    if (attrInfo_->type_ == VAR_BOOL)
+    {
+        bool value = object_->GetAttribute(attrInfo_->name_).GetBool();
+        widget_->SetValue(value ? 1 : 0);
+            }
+    else if (attrInfo_->type_ == VAR_VECTOR3)
+    {
+        Vector3 value = object_->GetAttribute(attrInfo_->name_).GetVector3();
+
+        TBInlineSelect* select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(1)));
+        if (select)
+            select->SetValueDouble(value.x_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(2)));
+        if (select)
+            select->SetValueDouble(value.y_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(3)));
+        if (select)
+            select->SetValueDouble(value.z_);
+    }
+    else if (attrInfo_->type_ == VAR_QUATERNION)
+    {
+        Vector3 value = object_->GetAttribute(attrInfo_->name_).GetQuaternion().EulerAngles();
+
+        TBInlineSelect* select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(1)));
+        if (select)
+            select->SetValueDouble(value.x_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(2)));
+        if (select)
+            select->SetValueDouble(value.y_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(3)));
+        if (select)
+            select->SetValueDouble(value.z_);
+    }
+    else if (attrInfo_->type_ == VAR_STRING)
+    {
+        String value = object_->GetAttribute(attrInfo_->name_).GetString();
+        widget_->SetText(value.CString());
+    }
+    else if (attrInfo_->type_ == VAR_FLOAT)
+    {
+        float value = object_->GetAttribute(attrInfo_->name_).GetFloat();
+        widget_->SetText(ToString("%f", value).CString());
+    }
+    else if (attrInfo_->type_ == VAR_INT)
+    {
+        int value = object_->GetAttribute(attrInfo_->name_).GetInt();
+
+        if (attrInfo_->enumNames_)
+        {
+            widget_->SetText(attrInfo_->enumNames_[value]);
+        }
+        else
+        {
+            widget_->SetText(ToString("%i", value).CString());
+        }
+
+    }
+    else if (attrInfo_->type_ == VAR_COLOR)
+    {
+        Color value = object_->GetAttribute(attrInfo_->name_).GetColor();
+
+        TBInlineSelect* select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(1)));
+        if (select)
+            select->SetValueDouble(value.r_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(2)));
+        if (select)
+            select->SetValueDouble(value.g_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(3)));
+        if (select)
+            select->SetValue(value.b_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(4)));
+        if (select)
+            select->SetValueDouble(value.a_);
+
+    }
+
+
+    widgetLocked_ = false;
+
+}
+
+bool InspectorDataBinding::OnEvent(const TBWidgetEvent &ev)
+{
+    if (ev.type == EVENT_TYPE_CLICK)
+    {
+        if (objectLocked_)
+            return false;
+
+        String id = attrInfo_->name_;
+        id += " enum popup";
+
+        if (ev.target->GetID() == TBIDC(id.CString()))
+        {
+            Variant value(int(ev.ref_id-1));
+            object_->SetAttribute(attrInfo_->name_, value);
+            SetWidgetValueFromObject();
+        }
+        else if (widget_ == ev.target && attrInfo_->enumNames_)
+        {
+            TBMenuWindow *menu = new TBMenuWindow(ev.target, TBIDC(id.CString()));
+            TBGenericStringItemSource* source = menu->GetList()->GetDefaultSource();
+            const char** enumPtr = attrInfo_->enumNames_;
+            unsigned v = 1;
+            while (*enumPtr)
+            {
+                source->AddItem(new TBGenericStringItem(*enumPtr, TBID(v++)));
+                enumPtr++;
+            }
+            menu->Show(source, TBPopupAlignment());
+
+        }
+        else if (widget_ == ev.target || widget_->IsAncestorOf(ev.target))
+        {
+            SetObjectValueFromWidget(ev.target);
+        }
+    }
+
+    if (ev.type == EVENT_TYPE_CHANGED)
+    {
+        if (objectLocked_)
+            return false;
+
+        if (widget_ == ev.target || widget_->IsAncestorOf(ev.target))
+        {
+            SetObjectValueFromWidget(ev.target);
+        }
+    }
+
+    return false;
+}
+
+InspectorDataBinding* InspectorDataBinding::Create(Serializable* object, const AttributeInfo* attrInfo)
+{
+
+    if (attrInfo->mode_ & AM_NOEDIT)
+        return NULL;
+
+    TBWidget* widget = NULL;
+    InspectorDataBinding* binding = NULL;
+
+    TBFontDescription fd;
+    fd.SetID(TBIDC("Vera"));
+    fd.SetSize(11);
+
+    if (attrInfo->type_ == VAR_BOOL)
+    {
+        TBCheckBox* box = new TBCheckBox();
+        box->SetSkinBg(TBIDC("TBGreyCheckBox"));
+        widget = box;
+    }
+    else if (attrInfo->type_ == VAR_STRING)
+    {
+        TBEditField* field = new TBEditField();
+        field->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+        field->SetSkinBg(TBIDC("TBAttrEditorField"));
+        field->SetFontDescription(fd);
+        LayoutParams lp;
+        lp.SetWidth(140);
+        field->SetLayoutParams(lp);
+
+        widget = field;
+    }
+    else if (attrInfo->type_ == VAR_FLOAT)
+    {
+        TBEditField* field = new TBEditField();
+        field->SetTextAlign(TB_TEXT_ALIGN_CENTER);
+        field->SetSkinBg(TBIDC("TBAttrEditorField"));
+        field->SetFontDescription(fd);
+        widget = field;
+    }
+    else if (attrInfo->type_ == VAR_COLOR)
+    {
+        TBLayout* layout = new TBLayout();
+        widget = layout;
+        layout->SetSpacing(0);
+
+        LayoutParams lp;
+        lp.SetWidth(70);
+
+        for (unsigned i = 0; i < 4; i++)
+        {
+            TBInlineSelect* select = new TBInlineSelect();
+            select->SetID(TBID(i + 1));
+            select->SetFontDescription(fd);
+            select->SetLimits(-10000000, 10000000);
+            select->SetLayoutParams(lp);
+            layout->AddChild(select);
+        }
+
+    }
+    else if (attrInfo->type_ == VAR_INT)
+    {
+        if (attrInfo->enumNames_)
+        {
+            TBButton* field = new TBButton();
+            field->SetFontDescription(fd);
+            widget = field;
+        }
+        else
+        {
+            TBEditField* field = new TBEditField();
+            field->SetTextAlign(TB_TEXT_ALIGN_CENTER);
+            field->SetFontDescription(fd);
+            field->SetSkinBg(TBIDC("TBAttrEditorField"));
+            widget = field;
+        }
+    }
+    else if (attrInfo->type_ == VAR_VECTOR3 || attrInfo->type_ == VAR_QUATERNION)
+    {
+        TBLayout* layout = new TBLayout();
+        widget = layout;
+        layout->SetSpacing(0);
+
+        LayoutParams lp;
+        lp.SetWidth(100);
+
+        for (unsigned i = 0; i < 3; i++)
+        {
+            TBInlineSelect* select = new TBInlineSelect();
+            select->SetID(TBID(i + 1));
+            select->SetFontDescription(fd);
+            select->SetSkinBg(TBIDC("InspectorTextAttrName"));
+            select->SetLimits(-10000000, 10000000);
+            LayoutParams editlp;
+            editlp.min_w = 60;
+            select->SetEditFieldLayoutParams(editlp);
+            select->SetLayoutParams(lp);
+            layout->AddChild(select);
+        }
+
+    }
+
+    if (widget)
+    {
+        binding = new InspectorDataBinding();
+        binding->object_ = object;
+        binding->widget_ = widget;
+        binding->attrInfo_ = attrInfo;
+    }
+
+    return binding;
+}
+
+}

+ 47 - 0
Source/AtomicEditorWork/Inspector/UIInspectorDataBinding.h

@@ -0,0 +1,47 @@
+
+#pragma once
+
+#include <TurboBadger/tb_widgets.h>
+#include <Atomic/Scene/Scene.h>
+
+namespace tb
+{
+    class TBWidget;
+    class TBMenuWindow;
+    class TBInlineSelect;
+}
+
+using namespace Atomic;
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+class InspectorDataBinding
+{
+
+public:
+
+    static InspectorDataBinding* Create(Serializable* object, const AttributeInfo* attrInfo);
+
+    void SetWidgetValueFromObject();
+    void SetObjectValueFromWidget(TBWidget* srcWidget = NULL);
+
+    TBWidget* GetWidget() { return widget_; }
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+private:
+
+    InspectorDataBinding() : widget_(0), object_(0), attrInfo_(0), objectLocked_(false), widgetLocked_(false) {}
+
+    TBWidget* widget_;
+    Serializable* object_;
+    const AttributeInfo* attrInfo_;
+
+    bool objectLocked_;
+    bool widgetLocked_;
+
+};
+
+}

+ 319 - 0
Source/AtomicEditorWork/Inspector/UIInspectorFrame.cpp

@@ -0,0 +1,319 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+
+#include <TurboBadger/tb_editfield.h>
+#include <Atomic/Core/Context.h>
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceEvents.h>
+
+#include <Atomic/Scene/Component.h>
+
+#include <Atomic/UI/UI.h>
+
+#include "AEEditor.h"
+#include "AEEvents.h"
+
+#include "UIInspectorDataBinding.h"
+#include "UIInspectorFrame.h"
+
+
+#include "UI/Modal/UIModalOps.h"
+
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+InspectorFrame::InspectorFrame(Context* context) :
+    UIWidget(context, true)
+{
+    SetGravity(WIDGET_GRAVITY_TOP_BOTTOM);
+
+    InitializeSources();
+
+    SubscribeToEvent(E_EDITORACTIVENODECHANGE, HANDLER(InspectorFrame, HandleEditorActiveNodeChange));
+
+}
+
+InspectorFrame::~InspectorFrame()
+{
+
+}
+
+void InspectorFrame::InitializeSources()
+{
+    /*
+    componentCreateSource.AddItem(new MenubarItem("Audio", &audioCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Geometry", &geometryCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Logic", &logicCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Navigation", &navigationCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Network", &networkCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Physics", &physicsCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Scene", &sceneCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Subsystem", &subsystemCreateSource));
+
+    audioCreateSource.AddItem(new MenubarItem("SoundListener", TBIDC("create component")));
+    audioCreateSource.AddItem(new MenubarItem("SoundSource",  TBIDC("create component")));
+    audioCreateSource.AddItem(new MenubarItem("SoundSource3D", TBIDC("create component")));
+
+    geometryCreateSource.AddItem(new MenubarItem("AnimatedModel", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("BillboardSet",  TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("CustomGeometry", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("ParticleEmitter", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("Skybox", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("StaticModel", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("StaticModelGroup", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("Terrain", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("Text3D", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("Water", TBIDC("create component")));
+
+    logicCreateSource.AddItem(new MenubarItem("AnimationController", TBIDC("create component")));
+    logicCreateSource.AddItem(new MenubarItem("Javascript Component", TBIDC("create component")));
+    logicCreateSource.AddItem(new MenubarItem("SplinePath", TBIDC("create component")));
+
+    navigationCreateSource.AddItem(new MenubarItem("Navigable", TBIDC("create component")));
+    navigationCreateSource.AddItem(new MenubarItem("NavigationMesh", TBIDC("create component")));
+    navigationCreateSource.AddItem(new MenubarItem("OffMeshConnection", TBIDC("create component")));
+
+    networkCreateSource.AddItem(new MenubarItem("Network Priority", TBIDC("create component")));
+
+    physicsCreateSource.AddItem(new MenubarItem("CollisionShape", TBIDC("create component")));
+    physicsCreateSource.AddItem(new MenubarItem("Constraint", TBIDC("create component")));
+    physicsCreateSource.AddItem(new MenubarItem("RigidBody", TBIDC("create component")));
+
+    sceneCreateSource.AddItem(new MenubarItem("Camera", TBIDC("create component")));
+    sceneCreateSource.AddItem(new MenubarItem("Light", TBIDC("create component")));
+    sceneCreateSource.AddItem(new MenubarItem("Zone", TBIDC("create component")));
+
+    subsystemCreateSource.AddItem(new MenubarItem("DebugRenderer", TBIDC("create component")));
+    subsystemCreateSource.AddItem(new MenubarItem("Octree", TBIDC("create component")));
+    subsystemCreateSource.AddItem(new MenubarItem("PhysicsWorld", TBIDC("create component")));
+    */
+
+}
+
+bool InspectorFrame::OnEvent(const TBWidgetEvent &ev)
+{
+    for (unsigned i = 0; i < dataBindings_.Size(); i++)
+        dataBindings_[i]->OnEvent(ev);
+
+    return false;
+}
+
+
+void InspectorFrame::InspectNode(Node* node)
+{
+    if (node_ == node)
+        return;
+
+    node_ = node;
+
+    TBLayout* inspectorContainer = GetInternalWidget()->GetWidgetByIDAndType<TBLayout>(TBIDC("inspectorcontainer"));
+    assert(inspectorContainer);
+
+
+    inspectorContainer->DeleteAllChildren();
+
+    for (unsigned i = 0; i < dataBindings_.Size(); i++)
+        delete dataBindings_[i];
+
+    dataBindings_.Clear();
+
+    if (!node_)
+    {
+        return;
+    }
+    else
+    {
+        TBFontDescription fd;
+        fd.SetID(TBIDC("Vera"));
+        fd.SetSize(11);
+
+        LayoutParams nlp;
+        nlp.SetWidth(304);
+        TBLayout* nodeLayout = new TBLayout(AXIS_Y);
+        nodeLayout->SetSpacing(4);
+
+        nodeLayout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+        nodeLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+        nodeLayout->SetLayoutParams(nlp);
+
+        TBContainer* nodeContainer = new TBContainer();
+        nodeContainer->SetGravity(WIDGET_GRAVITY_ALL);
+
+        nodeContainer->SetSkinBg("InspectorTopLayout");
+
+        TBLayout* attrsVerticalLayout = new TBLayout(AXIS_Y);
+        attrsVerticalLayout->SetGravity(WIDGET_GRAVITY_ALL);
+        attrsVerticalLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+        nodeContainer->AddChild(attrsVerticalLayout);
+
+        TBTextField* nodeLabel = new TBTextField();
+        nodeLabel->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+        nodeLabel->SetText("Node");
+        nodeLabel->SetSkinBg(TBIDC("InspectorTextLabel"));
+        attrsVerticalLayout->AddChild(nodeLabel);
+
+        const Vector<AttributeInfo>* attrs = node->GetAttributes();
+
+        for (unsigned i = 0; i < attrs->Size(); i++)
+        {
+            const AttributeInfo* attr = &attrs->At(i);
+
+            InspectorDataBinding*  binding = InspectorDataBinding::Create(node, attr);
+
+            if (binding)
+            {
+                dataBindings_.Push(binding);
+
+                TBLayout* attrLayout = new TBLayout();
+
+                if (attr->type_ == VAR_VECTOR3 || attr->type_ == VAR_COLOR || attr->type_ == VAR_QUATERNION)
+                {
+                    attrLayout->SetAxis(AXIS_Y);
+                    attrLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+                    attrLayout->SetSkinBg("InspectorVectorAttrLayout");
+                }
+
+                attrLayout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+
+                TBTextField* name = new TBTextField();
+                name->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+                name->SetSkinBg(TBIDC("InspectorTextAttrName"));
+
+                String bname = attr->name_;
+                if (bname == "Is Enabled")
+                    bname = "Enabled";
+
+                name->SetText(bname.CString());
+                name->SetFontDescription(fd);
+
+                attrLayout->AddChild(name);
+                TBWidget* bwidget = binding->GetWidget();
+                attrLayout->AddChild(bwidget);
+
+                attrsVerticalLayout->AddChild(attrLayout);
+            }
+
+        }
+
+        nodeLayout->AddChild(nodeContainer);
+
+        TBLayout* componentsLayout = new TBLayout();
+
+        TBTextField* componentsLabel = new TBTextField();
+        componentsLabel->SetText("Components");
+        componentsLabel->SetSkinBg(TBIDC("InspectorTextLabel"));
+        componentsLabel->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+        componentsLayout->AddChild(componentsLabel);
+
+        TBButton* create = new TBButton();
+        LayoutParams createLP;
+        createLP.SetHeight(20);
+        create->SetLayoutParams(createLP);
+        create->SetFontDescription(fd);
+        create->SetText("Create");
+        create->SetID(TBIDC("create button"));
+        componentsLayout->AddChild(create);
+
+        nodeLayout->AddChild(componentsLayout);
+
+        const Vector<SharedPtr<Component> > components = node->GetComponents();
+        for (unsigned i = 0; i < components.Size(); i++)
+        {
+            Component* c = components[i];
+
+            TBContainer* componentContainer = new TBContainer();
+
+            componentContainer->SetSkinBg("InspectorTopLayout");
+
+            componentContainer->SetGravity(WIDGET_GRAVITY_ALL);
+
+            TBLayout* attrsVerticalLayout = new TBLayout(AXIS_Y);
+            attrsVerticalLayout->SetGravity(WIDGET_GRAVITY_ALL);
+            attrsVerticalLayout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+            attrsVerticalLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+
+            TBTextField* cnameField = new TBTextField();
+            cnameField->SetText(c->GetTypeName().CString());
+            cnameField->SetSkinBg(TBIDC("InspectorTextLabel"));
+            cnameField->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+            cnameField->SetFontDescription(fd);
+            attrsVerticalLayout->AddChild(cnameField);
+
+            componentContainer->AddChild(attrsVerticalLayout);
+
+            const Vector<AttributeInfo>* attrs = c->GetAttributes();
+
+            if (attrs)
+                for (unsigned i = 0; i < attrs->Size(); i++)
+                {
+                    const AttributeInfo* attr = &attrs->At(i);
+
+                    InspectorDataBinding*  binding = InspectorDataBinding::Create(c, attr);
+
+                    if (binding)
+                    {
+                        dataBindings_.Push(binding);
+
+                        TBLayout* attrLayout = new TBLayout();
+
+                        attrLayout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+
+                        if (attr->type_ == VAR_VECTOR3 || attr->type_ == VAR_COLOR || attr->type_ == VAR_QUATERNION)
+                        {
+                            attrLayout->SetAxis(AXIS_Y);
+                            attrLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+                            attrLayout->SetSkinBg("InspectorVectorAttrLayout");
+                        }
+
+                        TBTextField* name = new TBTextField();
+                        String bname = attr->name_;
+                        if (bname == "Is Enabled")
+                            bname = "Enabled";
+
+                        name->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+                        name->SetText(bname.CString());
+                        name->SetFontDescription(fd);
+
+                        name->SetSkinBg(TBIDC("InspectorTextAttrName"));
+
+                        LayoutParams lp;
+                        lp.SetWidth(140);
+                        name->SetLayoutParams(lp);
+
+                        attrLayout->AddChild(name);
+
+                        TBWidget* bwidget = binding->GetWidget();
+
+                        attrLayout->AddChild(bwidget);
+
+                        attrsVerticalLayout->AddChild(attrLayout);
+                    }
+
+                }
+
+            nodeLayout->AddChild(componentContainer);
+
+        }
+
+        inspectorContainer->AddChild(nodeLayout);
+
+        for (unsigned i = 0; i < dataBindings_.Size(); i++)
+            dataBindings_[i]->SetWidgetValueFromObject();
+    }
+
+}
+
+void InspectorFrame::HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData)
+{
+    Node* node = (Node*) (eventData[EditorActiveNodeChange::P_NODE].GetPtr());
+
+    InspectNode(node);
+}
+
+}

+ 67 - 0
Source/AtomicEditorWork/Inspector/UIInspectorFrame.h

@@ -0,0 +1,67 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/UI/UIWidget.h>
+#include <Atomic/UI/UIMenubar.h>
+
+#include <Atomic/Scene/Scene.h>
+
+using namespace Atomic;
+
+namespace tb
+{
+    class TBLayout;
+    class TBMenuWindow;
+    class TBInlineSelect;
+}
+
+namespace AtomicEditor
+{
+
+class ListView;
+class ListViewItem;
+class InspectorDataBinding;
+
+class InspectorFrame : public UIWidget
+{
+
+    OBJECT(InspectorFrame);
+
+public:
+    /// Construct.
+    InspectorFrame(Context* context);
+    /// Destruct.
+    virtual ~InspectorFrame();
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+private:
+
+    void InitializeSources();
+
+    void InspectNode(Node* node);
+
+    void HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData);
+
+    Vector<InspectorDataBinding*> dataBindings_;
+
+    SharedPtr<Node> node_;
+
+    /*
+    UIMenuItemSource componentCreateSource;
+    UIMenuItemSource audioCreateSource;
+    UIMenuItemSource geometryCreateSource;
+    UIMenuItemSource logicCreateSource;
+    UIMenuItemSource navigationCreateSource;
+    UIMenuItemSource networkCreateSource;
+    UIMenuItemSource physicsCreateSource;
+    UIMenuItemSource sceneCreateSource;
+    UIMenuItemSource subsystemCreateSource;
+    */
+
+};
+
+}

+ 5 - 1
Source/AtomicJS/Javascript/JSAPI.cpp

@@ -191,7 +191,7 @@ void js_object_to_variantmap(duk_context* ctx, int objIdx, VariantMap &v)
 
         } else if (duk_get_heapptr(ctx, -1)) {
 
-            v[key] = js_to_class_instance<Object*>(ctx, -1, 0);
+            v[key] = js_to_class_instance<Object>(ctx, -1, 0);
 
         }
 
@@ -214,6 +214,10 @@ void js_push_variant(duk_context *ctx, const Variant& v)
 
     switch (type)
     {
+
+    case VAR_VOIDPTR:
+        duk_push_null(ctx);
+        break;
     case VAR_PTR:
 
         ref = v.GetPtr();

+ 4 - 2
Source/AtomicJS/Packages/Editor/Editor.json

@@ -1,5 +1,7 @@
 {
 	"name" : "Editor",
-	"sources" : ["Source/AtomicEditorWork/JSTest", "Source/AtomicEditorWork/Editors", "Source/AtomicEditorWork/Editors/SceneEditor3D"],
-	"classes" : ["MyJSClass", "ResourceEditor", "JSResourceEditor", "SceneEditor3D", "SceneView3D"]
+	"sources" : ["Source/AtomicEditorWork/JSTest", "Source/AtomicEditorWork/Editors", "Source/AtomicEditorWork/Editors/SceneEditor3D",
+		           "Source/AtomicEditorWork/Inspector"],
+	"classes" : ["MyJSClass", "ResourceEditor", "JSResourceEditor", "SceneEditor3D", "SceneView3D",
+								"InspectorFrame"]
 }