Browse Source

Multi-edit WIP

Josh Engebretson 10 years ago
parent
commit
32ab67cf62

BIN
Resources/EditorData/AtomicEditor/resources/default_skin/checkbox_mark_nonuniform.png


+ 12 - 0
Resources/EditorData/AtomicEditor/resources/default_skin/skin.tb.txt

@@ -400,6 +400,18 @@ elements
 		expand 7
 		type Image
 
+	TBGreyCheckBoxNonUniform
+		clone TBCheckBox
+		children
+			element TBGreyCheckBoxNonUniform.selected
+				state selected
+
+	TBGreyCheckBoxNonUniform.selected
+		bitmap checkbox_mark_nonuniform.png
+		expand 7
+		type Image
+
+
 	TBRadioButton
 		bitmap radio.png
 		cut 19

+ 512 - 0
Script/AtomicEditor/ui/frames/inspector/AttributeInfoEdit.ts

@@ -0,0 +1,512 @@
+
+import SerializableEditType = require("./SerializableEditType");
+
+class AttributeInfoEdit extends Atomic.UILayout {
+
+    attrInfo: Atomic.AttributeInfo;
+    editType: SerializableEditType;
+
+    editWidget: Atomic.UIWidget;
+
+    constructor() {
+
+        super();
+
+    }
+
+    initialize(editType: SerializableEditType, attrInfo: Atomic.AttributeInfo) {
+
+        this.editType = editType;
+        this.attrInfo = attrInfo;
+
+        this.createLayout();
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): boolean {
+
+        return false;
+
+    }
+
+    createLayout() {
+
+        this.createEditWidget();
+
+        this.editWidget.subscribeToEvent(this.editWidget, "WidgetEvent", (data) => this.handleWidgetEvent(data));
+
+        var attr = this.attrInfo;
+        var attrNameLP = AttributeInfoEdit.attrNameLP;
+
+        this.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
+
+        var name = new Atomic.UITextField();
+        name.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
+        name.skinBg = "InspectorTextAttrName";
+        name.layoutParams = attrNameLP;
+
+        if (attr.type == Atomic.VAR_VECTOR3 || attr.type == Atomic.VAR_COLOR ||
+            attr.type == Atomic.VAR_QUATERNION) {
+            this.axis = Atomic.UI_AXIS_Y;
+            this.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
+            this.skinBg = "InspectorVectorAttrLayout";
+        }
+
+        var bname = attr.name;
+
+        if (bname == "Is Enabled")
+            bname = "Enabled";
+
+        name.text = bname;
+        name.fontDescription = AttributeInfoEdit.fontDesc;
+
+        this.addChild(name);
+
+        this.addChild(this.editWidget);
+
+    }
+
+    createEditWidget() {
+
+    }
+
+    refresh() {
+
+
+    }
+
+    static createAttrEdit(editType: SerializableEditType, attrInfo: Atomic.AttributeInfo): AttributeInfoEdit {
+
+        var type: typeof AttributeInfoEdit;
+        var customTypes = AttributeInfoEdit.customAttrEditTypes[editType.typeName];
+        if (customTypes) {
+
+            type = customTypes[attrInfo.name];
+
+        }
+
+        if (!type) {
+
+            type = AttributeInfoEdit.standardAttrEditTypes[attrInfo.type];
+
+        }
+
+        if (!type)
+            return null;
+
+        var attrEdit = new type();
+        attrEdit.initialize(editType, attrInfo);
+
+        return attrEdit;
+
+    }
+
+    // atttribute name layout param
+    static attrNameLP: Atomic.UILayoutParams;
+    static fontDesc: Atomic.UIFontDescription;
+
+    static standardAttrEditTypes: { [variantType: number /*Atomic.VariantType*/]: typeof AttributeInfoEdit } = {};
+
+    static customAttrEditTypes: { [typeName: string]: { [name: string]: typeof AttributeInfoEdit } } = {};
+
+    private static Ctor = (() => {
+
+        var attrNameLP = AttributeInfoEdit.attrNameLP = new Atomic.UILayoutParams();
+        attrNameLP.width = 100;
+
+        var fd = AttributeInfoEdit.fontDesc = new Atomic.UIFontDescription();
+        fd.id = "Vera";
+        fd.size = 11;
+
+    })();
+
+}
+
+class BoolAttributeEdit extends AttributeInfoEdit {
+
+    createEditWidget() {
+
+        var box = new Atomic.UICheckBox();
+        this.editWidget = box;
+    }
+
+    refresh() {
+
+        var uniform = this.editType.getUniformValue(this.attrInfo);
+
+        if (uniform) {
+            var object = this.editType.getFirstObject();
+            this.editWidget.skinBg = "TBGreyCheckBox";
+            if (object) {
+                var value = object.getAttribute(this.attrInfo.name);
+                this.editWidget.value = (value ? 1 : 0);
+            }
+
+        } else {
+
+            this.editWidget.skinBg = "TBGreyCheckBoxNonUniform";
+            this.editWidget.value = 1;
+
+        }
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): boolean {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CHANGED) {
+
+            this.editType.onAttributeInfoEdited(this.attrInfo, this.editWidget.value ? true : false);
+            this.refresh();
+
+            return true;
+        }
+
+        return false;
+
+    }
+
+}
+
+class StringAttributeEdit extends AttributeInfoEdit {
+
+    createEditWidget() {
+
+        var field = new Atomic.UIEditField();
+        field.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
+        field.skinBg = "TBAttrEditorField";;
+        field.fontDescription = AttributeInfoEdit.fontDesc;
+        var lp = new Atomic.UILayoutParams();
+        lp.width = 140;
+        field.layoutParams = lp;
+
+        field.subscribeToEvent(field, "UIWidgetEditComplete", (ev) => this.handleUIWidgetEditCompleteEvent(ev));
+
+        this.editWidget = field;
+    }
+
+    refresh() {
+
+        var uniform = this.editType.getUniformValue(this.attrInfo);
+
+        if (uniform) {
+            var object = this.editType.getFirstObject();
+            if (object) {
+                var value = object.getAttribute(this.attrInfo.name);
+                this.editWidget.text = value;
+            }
+
+        } else {
+
+            this.editWidget.text = "--";
+
+        }
+
+    }
+
+    handleUIWidgetEditCompleteEvent(ev) {
+
+        this.editType.onAttributeInfoEdited(this.attrInfo, this.editWidget.text);
+        this.refresh();
+
+    }
+
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): boolean {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CHANGED) {
+
+            return true;
+        }
+
+        return false;
+
+    }
+
+}
+
+class IntAttributeEdit extends AttributeInfoEdit {
+
+    enumSource: Atomic.UISelectItemSource;
+
+    createEditWidget() {
+
+        var attrInfo = this.attrInfo;
+
+        if (attrInfo.enumNames.length) {
+
+            var enumSource = this.enumSource = new Atomic.UISelectItemSource();
+
+            for (var i in attrInfo.enumNames) {
+
+                enumSource.addItem(new Atomic.UISelectItem(attrInfo.enumNames[i], (Number(i) + 1).toString()));
+
+            }
+
+            var button = new Atomic.UIButton();
+            button.fontDescription = AttributeInfoEdit.fontDesc;
+            button.text = "Enum Value!";
+            var lp = new Atomic.UILayoutParams();
+            lp.width = 140;
+            button.layoutParams = lp;
+
+            this.editWidget = button;
+
+        } else {
+
+
+            var field = new Atomic.UIEditField();
+            field.textAlign = Atomic.UI_TEXT_ALIGN_CENTER;
+            field.skinBg = "TBAttrEditorField";;
+            field.fontDescription = AttributeInfoEdit.fontDesc;
+            var lp = new Atomic.UILayoutParams();
+            lp.width = 140;
+            field.layoutParams = lp;
+
+            field.subscribeToEvent(field, "UIWidgetEditComplete", (ev) => this.handleUIWidgetEditCompleteEvent(ev));
+
+            this.editWidget = field;
+        }
+    }
+
+    refresh() {
+
+        var uniform = this.editType.getUniformValue(this.attrInfo);
+
+        if (uniform) {
+            var object = this.editType.getFirstObject();
+            if (object) {
+                var value = object.getAttribute(this.attrInfo.name);
+
+                var widget = this.editWidget;
+                var attrInfo = this.attrInfo;
+
+                if (attrInfo.enumNames.length) {
+                    widget.text = attrInfo.enumNames[value];
+                }
+                else {
+                    widget.text = value.toString();
+                }
+            }
+
+        } else {
+
+            this.editWidget.text = "--";
+
+        }
+
+    }
+
+    handleUIWidgetEditCompleteEvent(ev) {
+
+        // non-enum
+        this.editType.onAttributeInfoEdited(this.attrInfo, Number(this.editWidget.text));
+        this.refresh();
+
+    }
+
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): boolean {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CHANGED) {
+
+            return true;
+        }
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CLICK) {
+
+            var id = this.attrInfo.name + " enum popup";
+
+            if (ev.target.id == id) {
+
+                this.editType.onAttributeInfoEdited(this.attrInfo, Number(ev.refid) - 1);
+                this.refresh();
+
+            }
+
+            else if (this.editWidget == ev.target && this.attrInfo.enumNames.length) {
+
+
+                if (this.enumSource) {
+                    var menu = new Atomic.UIMenuWindow(ev.target, id);
+                    menu.show(this.enumSource);
+                }
+
+                return true;
+
+            }
+
+        }
+
+        return false;
+
+    }
+
+}
+
+class FloatAttributeEdit extends AttributeInfoEdit {
+
+    createEditWidget() {
+
+        var attrInfo = this.attrInfo;
+
+        var field = new Atomic.UIEditField();
+        field.textAlign = Atomic.UI_TEXT_ALIGN_CENTER;
+        field.skinBg = "TBAttrEditorField";;
+        field.fontDescription = AttributeInfoEdit.fontDesc;
+        var lp = new Atomic.UILayoutParams();
+        lp.width = 140;
+        field.layoutParams = lp;
+
+        field.subscribeToEvent(field, "UIWidgetEditComplete", (ev) => this.handleUIWidgetEditCompleteEvent(ev));
+
+        this.editWidget = field;
+
+    }
+
+    refresh() {
+
+        var uniform = this.editType.getUniformValue(this.attrInfo);
+
+        if (uniform) {
+            var object = this.editType.getFirstObject();
+            if (object) {
+
+                var value = object.getAttribute(this.attrInfo.name);
+
+                var widget = this.editWidget;
+                var attrInfo = this.attrInfo;
+
+                var value = object.getAttribute(attrInfo.name);
+                widget.text = parseFloat(value.toFixed(5)).toString();
+
+            }
+
+        } else {
+
+            this.editWidget.text = "--";
+
+        }
+
+    }
+
+    handleUIWidgetEditCompleteEvent(ev) {
+
+        this.editType.onAttributeInfoEdited(this.attrInfo, Number(this.editWidget.text));
+        this.refresh();
+
+    }
+
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): boolean {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CHANGED) {
+
+            return true;
+        }
+
+        return false;
+
+    }
+
+}
+
+class Vector3AttributeEdit extends AttributeInfoEdit {
+
+    selects: Atomic.UIInlineSelect[] = [];
+
+    createEditWidget() {
+
+        var attrInfo = this.attrInfo;
+
+        var layout = new Atomic.UILayout();
+        layout.spacing = 0;
+
+        var lp = new Atomic.UILayoutParams();
+        lp.width = 100;
+
+        for (var i = 0; i < 3; i++) {
+
+            var select = new Atomic.UIInlineSelect();
+            this.selects.push(select);
+
+            select.id = String(i + 1);
+            select.fontDescription = AttributeInfoEdit.fontDesc;
+            select.skinBg = "InspectorVectorAttrName";
+            select.setLimits(-10000000, 10000000);
+            var editlp = new Atomic.UILayoutParams();
+            editlp.minWidth = 60;
+            select.editFieldLayoutParams = editlp;
+            select.layoutParams = lp;
+            layout.addChild(select);
+
+            select.subscribeToEvent(select, "WidgetEvent", (ev) => this.handleWidgetEvent(ev));
+            select.subscribeToEvent(select, "UIWidgetEditComplete", (ev) => this.handleUIWidgetEditCompleteEvent(ev));
+        }
+
+        this.editWidget = layout;
+
+    }
+
+    refresh() {
+
+        for (var i in this.selects) {
+
+            var select = this.selects[i];
+            var edit = select.getWidget("edit");
+
+            var uniform = this.editType.getUniformValue(this.attrInfo, i);
+
+            if (uniform) {
+
+                var object = this.editType.getFirstObject();
+
+                if (object) {
+
+                    var value = object.getAttribute(this.attrInfo.name);
+                    select.value = parseFloat(value[i].toFixed(5));
+
+                }
+
+            } else {
+
+                edit.text = "--";
+
+            }
+
+        }
+
+
+    }
+
+    handleUIWidgetEditCompleteEvent(ev: Atomic.UIWidgetEditCompleteEvent) {
+
+        var index = Number(ev.widget.id) - 1;
+        this.editType.onAttributeInfoEdited(this.attrInfo, ev.widget.value, index);
+        this.refresh();
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): boolean {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CHANGED) {
+
+            var index = Number(ev.target.id) - 1;
+            this.editType.onAttributeInfoEdited(this.attrInfo, ev.target.value, index, false);
+
+            return true;
+        }
+
+        return false;
+
+    }
+
+}
+
+AttributeInfoEdit.standardAttrEditTypes[Atomic.VAR_BOOL] = BoolAttributeEdit;
+AttributeInfoEdit.standardAttrEditTypes[Atomic.VAR_INT] = IntAttributeEdit;
+AttributeInfoEdit.standardAttrEditTypes[Atomic.VAR_FLOAT] = FloatAttributeEdit;
+AttributeInfoEdit.standardAttrEditTypes[Atomic.VAR_STRING] = StringAttributeEdit;
+
+AttributeInfoEdit.standardAttrEditTypes[Atomic.VAR_VECTOR3] = Vector3AttributeEdit;
+
+export = AttributeInfoEdit;

+ 25 - 36
Script/AtomicEditor/ui/frames/inspector/InspectorFrame.ts

@@ -13,16 +13,21 @@ import DataBinding = require("./DataBinding");
 
 import MaterialInspector = require("./MaterialInspector");
 import ModelInspector = require("./ModelInspector");
-import NodeInspector = require("./NodeInspector");
 import AssemblyInspector = require("./AssemblyInspector");
 import PrefabInspector = require("./PrefabInspector");
 
+import SelectionInspector = require("./SelectionInspector");
+// make sure these are hooked in
+import "./SelectionEditTypes";
+
+
 class InspectorFrame extends ScriptWidget {
 
-    nodeInspector: NodeInspector;
     scene: Atomic.Scene = null;
     sceneEditor: Editor.SceneEditor3D;
 
+    selectionInspector: SelectionInspector;
+
     constructor() {
 
         super();
@@ -62,14 +67,28 @@ class InspectorFrame extends ScriptWidget {
 
     }
 
+
+
     handleSceneNodeSelected(ev: Editor.SceneNodeSelectedEvent) {
 
         var selection = this.sceneEditor.selection;
 
-        if (selection.selectedNodeCount == 1) {
-            this.inspectNode(selection.getSelectedNode(0));
-        } else {
-            this.closeNodeInspector();
+        if (this.selectionInspector) {
+
+            if (ev.selected) {
+                this.selectionInspector.addNode(ev.node);
+            } else {
+                this.selectionInspector.removeNode(ev.node);
+            }
+        } else if (ev.selected) {
+
+          var container = this.getWidget("inspectorcontainer");
+          container.deleteAllChildren();
+
+          var inspector = this.selectionInspector = new SelectionInspector(this.sceneEditor);
+          inspector.addNode(ev.node);
+          container.addChild(inspector);
+
         }
 
         return;
@@ -78,7 +97,6 @@ class InspectorFrame extends ScriptWidget {
 
     handleProjectUnloaded(data) {
 
-        this.closeNodeInspector();
         var container = this.getWidget("inspectorcontainer");
         container.deleteAllChildren();
     }
@@ -99,17 +117,6 @@ class InspectorFrame extends ScriptWidget {
 
     }
 
-    closeNodeInspector() {
-
-        if (this.nodeInspector) {
-            this.nodeInspector.saveState();
-            var container = this.getWidget("inspectorcontainer");
-            container.deleteAllChildren();
-            this.nodeInspector = null;
-        }
-
-    }
-
 
     inspectAsset(asset: ToolCore.Asset) {
 
@@ -160,24 +167,6 @@ class InspectorFrame extends ScriptWidget {
 
     }
 
-    inspectNode(node: Atomic.Node) {
-
-        if (!node) return;
-
-        this.closeNodeInspector();
-
-        var container = this.getWidget("inspectorcontainer");
-        container.deleteAllChildren();
-
-        var inspector = new NodeInspector();
-        container.addChild(inspector);
-
-        inspector.inspect(node);
-
-        this.nodeInspector = inspector;
-
-    }
-
 }
 
 export = InspectorFrame;

+ 4 - 0
Script/AtomicEditor/ui/frames/inspector/NodeInspector.ts

@@ -135,6 +135,9 @@ class NodeInspector extends ScriptWidget {
 
         this.node = node;
 
+        return;
+
+        /*
         this.subscribeToEvent(node, "SceneEditStateChange", (data) => this.handleSceneEditStateChangeEvent(data));
 
         this.isPrefab = this.detectPrefab(node);
@@ -332,6 +335,7 @@ class NodeInspector extends ScriptWidget {
         }
 
         this.loadState();
+        */
 
     }
 

+ 30 - 0
Script/AtomicEditor/ui/frames/inspector/SelectionEditTypes.ts

@@ -0,0 +1,30 @@
+
+
+import SerializableEditType = require("./SerializableEditType");
+import SelectionInspector = require("./SelectionInspector");
+
+class JSComponentEditType extends SerializableEditType {
+
+    compareTypes(otherType: SerializableEditType): boolean {
+
+        if (this.typeName != otherType.typeName) {
+            return false;
+        }
+
+        var jsc1 = <Atomic.JSComponent>(otherType.objects[0]);
+        var jsc2 = <Atomic.JSComponent>(this.objects[0]);
+
+        return jsc1.componentFile == jsc2.componentFile;
+
+    }
+
+    private static Ctor = (() => {
+
+        SelectionInspector.registerEditType("JSComponent", JSComponentEditType);
+
+    })();
+
+
+}
+
+export = JSComponentEditType;

+ 336 - 0
Script/AtomicEditor/ui/frames/inspector/SelectionInspector.ts

@@ -0,0 +1,336 @@
+
+import ScriptWidget = require("ui/ScriptWidget");
+import EditorEvents = require("editor/EditorEvents");
+import SerializableEditType = require("./SerializableEditType");
+import SelectionSection = require("./SelectionSection");
+
+class NodeSection extends SelectionSection {
+
+    constructor(editType: SerializableEditType) {
+
+        super(editType);
+
+    }
+
+}
+
+class ComponentSection extends SelectionSection {
+
+    constructor(editType: SerializableEditType) {
+
+        super(editType);
+
+    }
+
+}
+
+// Node Inspector + Component Inspectors
+
+class SelectionInspector extends ScriptWidget {
+
+    constructor(sceneEditor: Editor.SceneEditor3D) {
+
+        super();
+
+        this.sceneEditor = sceneEditor;
+
+        var mainLayout = this.mainLayout = new Atomic.UILayout();
+        mainLayout.spacing = 4;
+
+        var lp = new Atomic.UILayoutParams();
+        lp.width = 304;
+
+        mainLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
+        mainLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
+        mainLayout.layoutParams = lp;
+        mainLayout.axis = Atomic.UI_AXIS_Y;
+
+        this.addChild(mainLayout);
+
+        this.subscribeToEvent(sceneEditor.scene, "SceneEditStateChangesBegin", (data) => this.handleSceneEditStateChangesBeginEvent());
+        this.subscribeToEvent("SceneEditStateChange", (data) => this.handleSceneEditStateChangeEvent(data));
+        this.subscribeToEvent(sceneEditor.scene, "SceneEditStateChangesEnd", (data) => this.handleSceneEditStateChangesEndEvent());
+
+    }
+
+    pruneSections() {
+
+        var remove: SelectionSection[] = [];
+
+        for (var i in this.sections) {
+
+            var section = this.sections[i];
+
+            var editType = section.editType;
+
+            if (editType.typeName == "Node") {
+                continue;
+            }
+
+            if (!editType.nodes.length) {
+
+                remove.push(section);
+
+            }
+
+        }
+
+        if (remove.length) {
+
+            for (var i in remove) {
+
+                var section = remove[i];
+                this.removeSection(section);
+
+            }
+
+            this.suppressSections();
+        }
+
+    }
+
+    suppressSections() {
+
+        for (var i in this.sections) {
+
+            var section = this.sections[i];
+            var editType = section.editType;
+
+            if (editType.typeName == "Node") {
+                continue;
+            }
+
+            var suppressed = false;
+
+            for (var j in this.nodes) {
+                if (editType.nodes.indexOf(this.nodes[j]) == -1) {
+                    suppressed = true;
+                    break;
+                }
+            }
+
+            section.suppress(suppressed);
+
+        }
+
+    }
+
+    refresh() {
+
+        Atomic.ui.blockChangedEvents = true;
+
+        this.pruneSections();
+        this.suppressSections();
+
+        for (var i in this.sections) {
+
+            this.sections[i].refresh();
+
+        }
+
+        Atomic.ui.blockChangedEvents = false;
+
+    }
+
+    addSection(editType: SerializableEditType) {
+
+        var section: SelectionSection;
+
+        if (editType.typeName == "Node") {
+
+            section = new NodeSection(editType);
+
+        } else {
+
+            section = new ComponentSection(editType);
+
+        }
+
+        this.mainLayout.addChild(section);
+        this.sections.push(section);
+
+    }
+
+    removeSection(section: SelectionSection) {
+
+        var index = this.sections.indexOf(section);
+        this.sections.splice(index, 1);
+        this.mainLayout.removeChild(section);
+
+    }
+
+    removeSerializable(serial: Atomic.Serializable) {
+
+        for (var i in this.sections) {
+
+            var section = this.sections[i];
+
+            var e = section.editType;
+
+            var index = e.objects.indexOf(serial);
+
+            if (index != -1) {
+
+                e.objects.splice(index, 1);
+
+            }
+
+            if (serial.typeName == "Node") {
+
+                index = e.nodes.indexOf(<Atomic.Node>serial);
+
+                if (index != -1) {
+
+                    e.nodes.splice(index, 1);
+
+                }
+            }
+
+        }
+
+    }
+
+    addSerializable(serial: Atomic.Serializable): SerializableEditType {
+
+        var editType = this.getEditType(serial);
+
+        // does it already exist?
+        for (var i in this.sections) {
+
+            var section = this.sections[i];
+
+            var e = section.editType;
+
+            if (e.compareTypes(editType)) {
+                e.addSerializable(serial);
+                return e;
+            }
+
+        }
+
+        this.addSection(editType);
+
+        return editType;
+
+    }
+
+    getEditType(serial: Atomic.Serializable): SerializableEditType {
+
+        var typeName = serial.typeName;
+
+        if (SelectionInspector._editTypes[typeName]) {
+            return new SelectionInspector._editTypes[typeName](serial);
+        }
+
+        return new SerializableEditType(serial);
+
+    }
+
+    addNode(node: Atomic.Node) {
+
+        var index = this.nodes.indexOf(node);
+
+        if (index == -1) {
+            this.nodes.push(node);
+            this.addSerializable(node);
+            var components = node.getComponents();
+            for (var i in components) {
+                var editType = this.addSerializable(components[i]);
+                editType.addNode(node);
+            }
+            this.refresh();
+        }
+    }
+
+    removeNode(node: Atomic.Node) {
+
+        var index = this.nodes.indexOf(node);
+
+        if (index != -1) {
+
+            this.nodes.splice(index, 1);
+            this.removeSerializable(node);
+            var components = node.getComponents();
+            for (var i in components) {
+                this.removeSerializable(components[i]);
+            }
+
+            this.refresh();
+        }
+
+    }
+
+    handleSceneEditStateChangesBeginEvent() {
+
+        this.stateChangesInProgress = true;
+
+    }
+
+
+    handleSceneEditStateChangeEvent(ev: Editor.SceneEditStateChangeEvent) {
+
+        if (!this.stateChangesInProgress)
+          return;
+
+        if (this.stateChanges.indexOf(ev.serializable) == -1) {
+            this.stateChanges.push(ev.serializable);
+        }
+
+    }
+
+    handleSceneEditStateChangesEndEvent() {
+
+        Atomic.ui.blockChangedEvents = true;
+
+        var sections: SelectionSection[] = [];
+
+        for (var i in this.stateChanges) {
+
+            var serial = this.stateChanges[i];
+
+            for (var j in this.sections) {
+
+                var section = this.sections[j];
+
+                if (sections.indexOf(section) != -1)
+                    continue;
+
+                if (section.editType.objects.indexOf(serial) != -1) {
+
+                    sections.push(section);
+                    section.refresh();
+                }
+
+            }
+
+        }
+
+        Atomic.ui.blockChangedEvents = false;
+        this.stateChanges = [];
+        this.stateChangesInProgress = false;
+
+    }
+
+
+    mainLayout: Atomic.UILayout;
+
+    sceneEditor: Editor.SceneEditor3D;
+    nodes: Atomic.Node[] = [];
+    sections: SelectionSection[] = [];
+
+
+    stateChangesInProgress:boolean =  false;
+    stateChanges: Atomic.Serializable[] = [];
+
+    // ------------------------------------
+
+    static registerEditType(typeName: string, type: typeof SerializableEditType) {
+
+        SelectionInspector._editTypes[typeName] = type;
+
+    }
+
+    private static _editTypes: { [typeName: string]: typeof SerializableEditType } = {};
+}
+
+export = SelectionInspector;

+ 92 - 0
Script/AtomicEditor/ui/frames/inspector/SelectionSection.ts

@@ -0,0 +1,92 @@
+
+import SerializableEditType = require("./SerializableEditType");
+import AttributeInfoEdit = require("./AttributeInfoEdit");
+
+abstract class SelectionSection extends Atomic.UISection {
+
+    editType: SerializableEditType;
+    attrLayout: Atomic.UILayout;
+    suppressed: boolean = false;
+
+    attrEdits: { [name: string]: AttributeInfoEdit } = {};
+
+    constructor(editType: SerializableEditType) {
+
+        super();
+
+        this.editType = editType;
+
+        this.text = editType.typeName;
+        this.value = 1;
+
+        this.createUI();
+
+    }
+
+    refresh() {
+
+        for (var name in this.attrEdits) {
+
+          this.attrEdits[name].refresh();
+
+        }
+
+    }
+
+    suppress(value: boolean) {
+
+        if (this.suppressed == value) {
+            return;
+        }
+
+        this.suppressed = value;
+        if (value) {
+            this.visibility = Atomic.UI_WIDGET_VISIBILITY_GONE;
+        } else {
+            this.visibility = Atomic.UI_WIDGET_VISIBILITY_VISIBLE;
+        }
+
+    }
+
+    createUI() {
+
+        var attrLayout = this.attrLayout = new Atomic.UILayout(Atomic.UI_AXIS_Y);
+        attrLayout.spacing = 3;
+        attrLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
+        attrLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_AVAILABLE;
+
+        this.contentRoot.addChild(attrLayout);
+
+        for (var i in this.editType.attrInfos) {
+
+            var attr = this.editType.attrInfos[i];
+
+            if (attr.mode & Atomic.AM_NOEDIT)
+                continue;
+
+            var attrEdit = AttributeInfoEdit.createAttrEdit(this.editType, attr);
+
+            if (!attrEdit)
+                continue;
+
+            this.attrEdits[attr.name] = attrEdit;
+
+            attrLayout.addChild(attrEdit);
+
+        }
+
+    }
+
+    static fontDesc: Atomic.UIFontDescription;
+
+    private static Ctor = (() => {
+
+        var fd = SelectionSection.fontDesc = new Atomic.UIFontDescription();
+        fd.id = "Vera";
+        fd.size = 11;
+
+    })();
+
+}
+
+export = SelectionSection;

+ 119 - 0
Script/AtomicEditor/ui/frames/inspector/SerializableEditType.ts

@@ -0,0 +1,119 @@
+
+class SerializableEditType {
+
+    constructor(serial: Atomic.Serializable) {
+        this.typeName = serial.typeName;
+        this.attrInfos = serial.getAttributes();
+        this.addSerializable(serial);
+    }
+
+    addSerializable(serial: Atomic.Serializable) {
+
+        this.objects.push(serial);
+
+    }
+
+    getUniformValue(attrInfo: Atomic.AttributeInfo, index: number = -1): boolean {
+
+        if (this.objects.length <= 1)
+            return true;
+
+        var value: any;
+
+        for (var i in this.objects) {
+
+            var object = this.objects[i];
+
+            if (i == 0) {
+
+                value = object.getAttribute(attrInfo.name);
+                if (index >= 0)
+                    value = value[index];
+
+            } else {
+
+                var value2 = object.getAttribute(attrInfo.name);
+                if (index >= 0)
+                    value2 = value2[index];
+
+                if (value != value2)
+                    return false;
+
+            }
+
+        }
+
+        return true;
+
+
+    }
+
+    onAttributeInfoEdited(attrInfo: Atomic.AttributeInfo, value: any, index: number = -1, genEdit: boolean = true) {
+
+        for (var i in this.objects) {
+
+            var object = this.objects[i];
+
+            if (index >= 0) {
+
+                var idxValue = object.getAttribute(attrInfo.name);
+                idxValue[index] = value;
+                object.setAttribute(attrInfo.name, idxValue);
+
+            } else {
+
+                object.setAttribute(attrInfo.name, value);
+
+            }
+
+        }
+
+        if (!genEdit)
+            return;
+
+        var node: Atomic.Node = null;
+        if (this.nodes.length) {
+            node = this.nodes[0];
+        } else if (this.objects.length && this.objects[0].typeName == "Node") {
+            node = <Atomic.Node>this.objects[0];
+        }
+
+        if (node)
+            node.scene.sendEvent("SceneEditEnd");
+
+    }
+
+    compareTypes(otherType: SerializableEditType): boolean {
+
+        return this.typeName == otherType.typeName;
+
+    }
+
+    addNode(node: Atomic.Node) {
+
+        if (this.nodes.indexOf(node) == -1) {
+            this.nodes.push(node);
+        }
+
+    }
+
+    getFirstObject(): Atomic.Serializable {
+
+        if (this.objects.length) {
+            return this.objects[0];
+        }
+
+        return null;
+
+    }
+
+    typeName: string;
+    typeWidget: Atomic.UIWidget;
+    attrInfos: Atomic.AttributeInfo[];
+
+    nodes: Atomic.Node[] = [];
+    objects: Atomic.Serializable[] = [];
+
+}
+
+export = SerializableEditType;

+ 11 - 1
Script/TypeScript/AtomicWork.d.ts

@@ -80,7 +80,7 @@ declare module Atomic {
 
         refid: string;
         selected: boolean;
-        
+
     }
 
     export interface NodeAddedEvent {
@@ -128,6 +128,10 @@ declare module Atomic {
         focused: boolean;
     }
 
+    export interface UIWidgetEditCompleteEvent {
+        widget: UIWidget;
+    }
+
     export interface UIWidgetDeletedEvent {
 
         widget: UIWidget;
@@ -285,6 +289,12 @@ declare module Editor {
 
   }
 
+  export interface SceneEditStateChangeEvent {
+
+    serializable:Atomic.Serializable;
+
+  }
+
   export interface GizmoEditModeChangedEvent {
     mode:EditMode;
   }

+ 18 - 1
Source/Atomic/UI/UI.cpp

@@ -112,7 +112,7 @@ UI::UI(Context* context) :
     skinLoaded_(false),
     consoleVisible_(false),
     exitRequested_(false),
-    changedEventsBlocked_(false)
+    changedEventsBlocked_(0)
 {
 
     SubscribeToEvent(E_EXITREQUESTED, HANDLER(UI, HandleExitRequested));
@@ -154,6 +154,23 @@ void UI::Shutdown()
 
 }
 
+void UI::SetBlockChangedEvents(bool blocked)
+{
+    if (blocked)
+        changedEventsBlocked_++;
+    else
+    {
+        changedEventsBlocked_--;
+
+        if (changedEventsBlocked_ < 0)
+        {
+            LOGERROR("UI::BlockChangedEvents - mismatched block calls, setting to 0");
+            changedEventsBlocked_ = 0;
+        }
+    }
+
+}
+
 void UI::Initialize(const String& languageFile)
 {
     Graphics* graphics = GetSubsystem<Graphics>();

+ 5 - 3
Source/Atomic/UI/UI.h

@@ -105,8 +105,9 @@ public:
 
     UIWidget* GetWidgetAt(int x, int y, bool include_children);
 
-    bool GetBlockChangedEvents() const { return changedEventsBlocked_; }
-    void SetBlockChangedEvents(bool blocked) { changedEventsBlocked_ = blocked; }
+    bool GetBlockChangedEvents() const { return changedEventsBlocked_ > 0; }
+
+    void SetBlockChangedEvents(bool blocked = true);
 
 private:
 
@@ -141,7 +142,8 @@ private:
     HashMap<tb::TBWidget*, SharedPtr<UIWidget> > widgetWrap_;
     HashMap<unsigned, String> tbidToString_;
 
-    bool changedEventsBlocked_;
+    int changedEventsBlocked_;
+
     bool inputDisabled_;
     bool keyboardDisabled_;
     bool initialized_;

+ 3 - 1
Source/Atomic/UI/UIEditField.cpp

@@ -198,7 +198,9 @@ bool UIEditField::OnEvent(const tb::TBWidgetEvent &ev)
 {
     if (ev.type == EVENT_TYPE_CUSTOM && ev.ref_id == TBIDC("edit_complete"))
     {
-        SendEvent(E_UIWIDGETEDITCOMPLETE);
+        VariantMap eventData;
+        eventData[UIWidgetEditComplete::P_WIDGET] = this;
+        SendEvent(E_UIWIDGETEDITCOMPLETE, eventData);
         return true;
     }
 

+ 1 - 0
Source/Atomic/UI/UIEvents.h

@@ -120,6 +120,7 @@ EVENT(E_UIWIDGETFOCUSESCAPED, UIWidgetFocusEscaped)
 
 EVENT(E_UIWIDGETEDITCOMPLETE, UIWidgetEditComplete)
 {
+    PARAM(P_WIDGET, Widget);             // UIWidget pointer
 }
 
 EVENT(E_UIUNHANDLEDSHORTCUT, UIUnhandledShortcut)

+ 4 - 1
Source/Atomic/UI/UIInlineSelect.cpp

@@ -72,7 +72,10 @@ bool UIInlineSelect::OnEvent(const tb::TBWidgetEvent &ev)
 {
     if (ev.type == EVENT_TYPE_CUSTOM && ev.ref_id == TBIDC("edit_complete"))
     {
-        SendEvent(E_UIWIDGETEDITCOMPLETE);
+        VariantMap eventData;
+        eventData[UIWidgetEditComplete::P_WIDGET] = this;
+        SendEvent(E_UIWIDGETEDITCOMPLETE, eventData);
+
         return true;
     }
     return UIWidget::OnEvent(ev);

+ 8 - 0
Source/AtomicEditor/Editors/SceneEditor3D/SceneEditOp.cpp

@@ -156,6 +156,8 @@ void SelectionEditOp::RegisterEdit()
 bool SelectionEditOp::Undo()
 {
 
+    scene_->SendEvent(E_SCENEEDITSTATECHANGESBEGIN);
+
     for (unsigned i = 0; i < editNodes_.Size(); i++)
     {
         EditNode* enode = editNodes_[i];
@@ -237,11 +239,15 @@ bool SelectionEditOp::Undo()
 
     }
 
+    scene_->SendEvent(E_SCENEEDITSTATECHANGESEND);
+
     return true;
 }
 
 bool SelectionEditOp::Redo()
 {
+    scene_->SendEvent(E_SCENEEDITSTATECHANGESBEGIN);
+
     for (unsigned i = 0; i < editNodes_.Size(); i++)
     {
         EditNode* enode = editNodes_[i];
@@ -328,6 +334,8 @@ bool SelectionEditOp::Redo()
 
     }
 
+    scene_->SendEvent(E_SCENEEDITSTATECHANGESEND);
+
     return true;
 }
 

+ 11 - 0
Source/AtomicEditor/Editors/SceneEditor3D/SceneEditor3DEvents.h

@@ -48,11 +48,22 @@ EVENT(E_SCENEEDITEND, SceneEditEnd)
     PARAM(P_SCENE, Scene);             // Scene
 }
 
+EVENT(E_SCENEEDITSTATECHANGESBEGIN, SceneEditStateChangesBegin)
+{
+
+}
+
 EVENT(E_SCENEEDITSTATECHANGE, SceneEditStateChange)
 {
     PARAM(P_SERIALIZABLE, Serializable);     // Serializable
 }
 
+EVENT(E_SCENEEDITSTATECHANGESEND, SceneEditStateChangesEnd)
+{
+
+}
+
+
 /// A child node has been added to a parent node.
 EVENT(E_SCENEEDITNODEADDED, SceneEditNodeAdded)
 {

+ 16 - 13
Source/ThirdParty/TurboBadger/tb_inline_select.cpp

@@ -39,7 +39,8 @@ TBInlineSelect::TBInlineSelect()
 	m_buttons[1].SetID(TBIDC("inc"));
 	m_buttons[0].SetAutoRepeat(true);
 	m_buttons[1].SetAutoRepeat(true);
-	m_editfield.SetTextAlign(TB_TEXT_ALIGN_CENTER);
+    m_editfield.SetID(TBIDC("edit"));
+    m_editfield.SetTextAlign(TB_TEXT_ALIGN_CENTER);
 	m_editfield.SetEditType(EDIT_TYPE_NUMBER);
 	m_editfield.SetText("0");
 
@@ -119,11 +120,23 @@ bool TBInlineSelect::OnEvent(const TBWidgetEvent &ev)
 	else if (ev.type == EVENT_TYPE_CLICK && ev.target->GetID() == TBIDC("dec"))
 	{
         SetValueDouble(GetValueDouble() - 1);
+        if (!ev.target->IsCaptured()) {
+
+            InvokeModifiedEvent();
+
+        }
 		return true;
 	}
 	else if (ev.type == EVENT_TYPE_CLICK && ev.target->GetID() == TBIDC("inc"))
 	{
         SetValueDouble(GetValueDouble() + 1);
+
+        if (!ev.target->IsCaptured()) {
+
+            InvokeModifiedEvent();
+
+        }
+
 		return true;
 	}
 	else if (ev.type == EVENT_TYPE_CHANGED && ev.target == &m_editfield)
@@ -132,19 +145,9 @@ bool TBInlineSelect::OnEvent(const TBWidgetEvent &ev)
 		m_editfield.GetText(text);
         SetValueInternal((double) atof(text), false);
 	}
-
-    // catch mouse up/mouse down on buttons for modifications
-    if (ev.target == &m_buttons[0] || ev.target == &m_buttons[1])
+    else if (ev.type == EVENT_TYPE_CHANGED && ev.target == this)
     {
-        if (ev.type == EVENT_TYPE_POINTER_DOWN)
-        {
-            m_modified = true;
-        }
-        else if (ev.type == EVENT_TYPE_POINTER_UP)
-        {
-            if (m_modified)
-                InvokeModifiedEvent();
-        }
+        return TBWidget::OnEvent(ev);
     }
 
 	return false;