Josh Engebretson 10 years ago
parent
commit
77eb56c8f4

+ 11 - 7
Resources/EditorData/AtomicEditor/templates/template_component.js

@@ -1,13 +1,17 @@
 
-// Atomic Component
+"atomic component";
 
-var game = Atomic.game;
-var node = self.node;
+var component = function (self) {
 
-function start() {
 
-}
+  self.start = function() {
+
+  }
+
+  self.update = function(timeStep) {
 
-function update(timeStep) {
+  }
+
+}
 
-}
+module.exports = component;

+ 17 - 0
Script/AtomicEditor/editor/EditorEvents.ts

@@ -9,6 +9,16 @@ export interface ModalErrorEvent {
 
 }
 
+export const ActiveSceneChange = "EditorActiveSceneChange";
+export const ActiveNodeChange = "EditorActiveNodeChange";
+export const SceneClosed = "EditorSceneClosed";
+export interface SceneClosedEvent {
+
+  scene: Atomic.Scene;
+
+}
+
+
 export const ContentFolderChanged = "ContentFolderChanged";
 export interface ContentFolderChangedEvent {
 
@@ -33,6 +43,13 @@ export interface SaveResourceEvent {
 
 }
 
+export const CloseResource = "EditorCloseResource";
+export interface CloseResourceEvent {
+
+  editor:Editor.ResourceEditor;
+  navigateToAvailableResource:boolean;
+
+}
 
 export const EditResource = "EditorEditResource";
 export interface EditResourceEvent {

+ 39 - 1
Script/AtomicEditor/resources/ResourceOps.ts

@@ -31,7 +31,45 @@ export function CreateNewFolder(resourcePath: string, reportError: boolean = tru
     var db = ToolCore.getAssetDatabase();
     db.scan();
 
-    // resourceOps.sendEvent(EditorEvents.ResourceFolderCreated, { path: resourcePath, navigate: true });
+    return true;
+
+}
+
+export function CreateNewComponent(resourcePath: string, componentName: string, reportError: boolean = true): boolean {
+
+    var title = "New Component Error";
+
+    var fs = Atomic.fileSystem;
+
+    if (fs.dirExists(resourcePath) || fs.fileExists(resourcePath)) {
+        if (reportError)
+            resourceOps.sendEvent(EditorEvents.ModalError, { title: title, message: "Already exists: " + resourcePath });
+        return false;
+
+    }
+
+    var templateFilename = "AtomicEditor/templates/template_component.js";
+    var file = Atomic.cache.getFile(templateFilename);
+
+    if (!file) {
+
+        if (reportError)
+            resourceOps.sendEvent(EditorEvents.ModalError, { title: title, message: "Failed to open template: " + templateFilename });
+        return false;
+
+    }
+
+    var out = new Atomic.File(resourcePath, Atomic.FILE_WRITE);
+    var success = out.copy(file);
+    out.close();
+
+    if (!success) {
+        if (reportError)
+            resourceOps.sendEvent(EditorEvents.ModalError, { title: title, message: "Failed template copy: " + templateFilename + " -> " + resourcePath });
+        return false;
+    }
+
+    ToolCore.assetDatabase.scan();
 
     return true;
 

+ 22 - 4
Script/AtomicEditor/ui/HierarchyFrame.ts

@@ -1,6 +1,7 @@
 
 import HierarchyFrameMenu = require("./HierarchyFrameMenu");
 import MenuItemSources = require("./menus/MenuItemSources");
+import EditorEvents = require("../editor/EditorEvents");
 
 class HierarchyFrame extends Atomic.UIWidget {
 
@@ -32,15 +33,28 @@ class HierarchyFrame extends Atomic.UIWidget {
 
         this.subscribeToEvent(this, "WidgetEvent", (data) => this.handleWidgetEvent(data));
 
-        this.subscribeToEvent("EditorActiveSceneChanged", (data) => this.handleActiveSceneChanged(data));
 
-        this.subscribeToEvent("EditorActiveNodeChange", (data) => {
+        this.subscribeToEvent(EditorEvents.ActiveNodeChange, (data) => {
 
             if (data.node)
                 this.hierList.selectItemByID(data.node.id.toString());
 
         });
 
+        this.subscribeToEvent(EditorEvents.ActiveSceneChange, (data) => this.handleActiveSceneChanged(data));
+
+        this.subscribeToEvent(EditorEvents.SceneClosed, (ev: EditorEvents.SceneClosedEvent) => {
+
+            if (ev.scene == this.scene) {
+
+                this.unsubscribeFromEvents(this.scene);
+                this.scene = null;
+                this.populate();
+
+            }
+
+        });
+
     }
 
     handleNodeAdded(ev: Atomic.NodeAddedEvent) {
@@ -63,6 +77,10 @@ class HierarchyFrame extends Atomic.UIWidget {
 
     handleNodeRemoved(ev: Atomic.NodeRemovedEvent) {
 
+        // on close
+        if (!this.scene)
+            return;
+
         var node = ev.node;
 
         if (this.filterNode(node))
@@ -75,7 +93,7 @@ class HierarchyFrame extends Atomic.UIWidget {
 
         this.hierList.deleteItemByID(node.id.toString());
 
-        this.sendEvent("EditorActiveNodeChange", { node: ev.parent ? ev.parent : this.scene });
+        this.sendEvent(EditorEvents.ActiveNodeChange, { node: ev.parent ? ev.parent : this.scene });
 
     }
 
@@ -231,7 +249,7 @@ class HierarchyFrame extends Atomic.UIWidget {
         var icon = "";
 
         if (node.isTemporary())
-          icon = "ComponentBitmap";
+            icon = "ComponentBitmap";
 
         var childItemID = this.hierList.addChildItem(parentID, name, icon, node.id.toString());
 

+ 15 - 0
Script/AtomicEditor/ui/MainFrameMenu.ts

@@ -14,6 +14,21 @@ class MainFrameMenu extends Atomic.ScriptObject {
         MenuItemSources.createMenuItemSource("menu edit", editItems);
         MenuItemSources.createMenuItemSource("menu file", fileItems);
 
+        this.subscribeToEvent("WidgetEvent", (ev: Atomic.UIWidgetEvent) => {
+
+            if (ev.type == Atomic.UI_EVENT_TYPE_SHORTCUT) {
+
+                if (ev.refid == "save") {
+
+                    this.sendEvent(EditorEvents.SaveResource);
+                    return true;
+
+                }
+
+            }
+
+        })
+
     }
 
     handlePopupMenu(target: Atomic.UIWidget, refid: string): boolean {

+ 7 - 6
Script/AtomicEditor/ui/ProjectFrameMenu.ts

@@ -27,18 +27,18 @@ class ProjectFrameMenus extends Atomic.ScriptObject {
             var asset = <ToolCore.Asset> target['asset'];
 
             if (refid == "delete_asset") {
-
                 EditorUI.getModelOps().showResourceDelete(asset);
-
                 return true;
             }
 
             if (refid == "create_folder") {
-
                 EditorUI.getModelOps().showCreateFolder(asset.path);
-
                 return true;
+            }
 
+            if (refid == "create_component") {
+                EditorUI.getModelOps().showCreateComponent(asset.path);
+                return true;
             }
 
         }
@@ -63,7 +63,6 @@ class ProjectFrameMenus extends Atomic.ScriptObject {
             srcName = "asset context general";
         }
 
-
         var src = MenuItemSources.getMenuItemSource(srcName);
         menu.show(src, x, y);
 
@@ -118,8 +117,10 @@ var assetGeneralContextItems = {
 
 var assetFolderContextItems = {
     "Create Folder": ["create_folder", undefined, "Folder.icon"],
-    "Reveal in Finder": ["reveal_folder", undefined, ""],
+    "Create Component": ["create_component", undefined, "ComponentBitmap"],
     "-1": null,
+    "Reveal in Finder": ["reveal_folder", undefined, ""],
+    "-2": null,
     "Delete": ["delete_asset", undefined, "FolderDeleteBitmap"]
 };
 

+ 22 - 5
Script/AtomicEditor/ui/ResourceFrame.ts

@@ -58,7 +58,7 @@ class ResourceFrame extends ScriptWidget {
             var sceneEditor3D = new Editor.SceneEditor3D(path, this.tabcontainer);
             editor = sceneEditor3D;
 
-            this.sendEvent("EditorActiveSceneChanged", { scene: sceneEditor3D.scene });
+            this.sendEvent(EditorEvents.ActiveSceneChange, { scene: sceneEditor3D.scene });
 
         }
 
@@ -112,10 +112,16 @@ class ResourceFrame extends ScriptWidget {
 
     }
 
-    handleCloseResourceEditor(data) {
+    handleCloseResource(ev: EditorEvents.CloseResourceEvent) {
 
-        var editor = <Editor.ResourceEditor> data.editor;
-        var navigate = <boolean> data.navigateToAvailableResource;
+        var editor = ev.editor;
+        var navigate = ev.navigateToAvailableResource;
+
+        if (!editor)
+            return;
+
+        if (this.currentResourceEditor == editor)
+            this.currentResourceEditor = null;
 
         editor.unsubscribeFromAllEvents();
 
@@ -151,6 +157,17 @@ class ResourceFrame extends ScriptWidget {
 
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
 
+        if (ev.type == Atomic.UI_EVENT_TYPE_SHORTCUT) {
+
+            if (ev.refid == "close") {
+                if (this.currentResourceEditor) {
+                    this.currentResourceEditor.close(true);
+                    return true;
+                }
+            }
+        }
+
+
         if (ev.type == Atomic.UI_EVENT_TYPE_TAB_CHANGED && ev.target == this.tabcontainer) {
             var w = <EditorRootContentWidget> this.tabcontainer.currentPageWidget;
 
@@ -187,7 +204,7 @@ class ResourceFrame extends ScriptWidget {
 
         this.subscribeToEvent(EditorEvents.EditResource, (data) => this.handleEditResource(data));
         this.subscribeToEvent(EditorEvents.SaveResource, (data) => this.handleSaveResource(data));
-        this.subscribeToEvent(UIEvents.CloseResourceEditor, (data) => this.handleCloseResourceEditor(data));
+        this.subscribeToEvent(EditorEvents.CloseResource, (ev: EditorEvents.CloseResourceEvent) => this.handleCloseResource(ev));
         this.subscribeToEvent(UIEvents.ResourceEditorChanged, (data) => this.handleResourceEditorChanged(data));
 
 

+ 0 - 3
Script/AtomicEditor/ui/UIEvents.ts

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

+ 10 - 0
Script/AtomicEditor/ui/modal/ModalOps.ts

@@ -39,6 +39,16 @@ class ModalOps extends Atomic.ScriptObject {
 
     }
 
+    showCreateComponent(resourcePath:string) {
+
+      if (this.show()) {
+
+          this.opWindow = new UIResourceOps.CreateComponent(resourcePath);
+
+      }
+
+    }
+
     showResourceDelete(asset:ToolCore.Asset) {
 
       if (this.show()) {

+ 22 - 22
Script/AtomicEditor/ui/modal/ModalWindow.ts

@@ -3,44 +3,44 @@ import EditorUI = require("../EditorUI");
 
 class ModalWindow extends Atomic.UIWindow {
 
-  constructor() {
+    constructor() {
 
-    super();
+        super();
 
-    var view = EditorUI.getView();
-    view.addChild(this);
+        var view = EditorUI.getView();
+        view.addChild(this);
 
-    this.setFocus();
+        this.setFocus();
 
-    this.subscribeToEvent(this, "WidgetDeleted", (event:Atomic.UIWidgetDeletedEvent) => {
+        this.subscribeToEvent(this, "WidgetDeleted", (event: Atomic.UIWidgetDeletedEvent) => {
 
-      this.hide();
+            this.hide();
 
-    });
+        });
 
-    this.subscribeToEvent(this, "WidgetEvent", (data) => this.handleWidgetEvent(data));
+        this.subscribeToEvent(this, "WidgetEvent", (data) => this.handleWidgetEvent(data));
 
-  }
+    }
 
-  hide() {
+    hide() {
 
-    var modalOps = EditorUI.getModelOps();
-    modalOps.hide();
+        var modalOps = EditorUI.getModelOps();
+        modalOps.hide();
 
-  }
+    }
 
-  handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
 
-  }
+    }
 
-  init(windowText:string, uifilename:string) {
+    init(windowText: string, uifilename: string) {
 
-    this.text = windowText;
-    this.load(uifilename);
-    this.resizeToFitContent();
-    this.center();
+        this.text = windowText;
+        this.load(uifilename);
+        this.resizeToFitContent();
+        this.center();
 
-  }
+    }
 
 }
 

+ 81 - 28
Script/AtomicEditor/ui/modal/UIResourceOps.ts

@@ -5,53 +5,53 @@ import ResourceOps = require("../../resources/ResourceOps");
 
 export class ResourceDelete extends ModalWindow {
 
-  constructor(asset: ToolCore.Asset) {
+    constructor(asset: ToolCore.Asset) {
 
-      super();
+        super();
 
-      this.asset = asset;
-      this.init("Delete Resource", "AtomicEditor/editor/ui/resourcedelete.tb.txt");
-      var message = <Atomic.UIEditField> this.getWidget("message");
+        this.asset = asset;
+        this.init("Delete Resource", "AtomicEditor/editor/ui/resourcedelete.tb.txt");
+        var message = <Atomic.UIEditField> this.getWidget("message");
 
-      var text = "Are you sure you want to delete resource:\n\n";
-      text += asset.path;
-      text += "\n\nThis operation cannot be undone";
+        var text = "Are you sure you want to delete resource:\n\n";
+        text += asset.path;
+        text += "\n\nThis operation cannot be undone";
 
-      message.text = text;
+        message.text = text;
 
-      this.resizeToFitContent();
-      this.center();
+        this.resizeToFitContent();
+        this.center();
 
-  }
+    }
 
-  handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
 
-      if (ev.type == Atomic.UI_EVENT_TYPE_CLICK) {
+        if (ev.type == Atomic.UI_EVENT_TYPE_CLICK) {
 
-          var id = ev.target.id;
+            var id = ev.target.id;
 
-          if (id == "delete") {
+            if (id == "delete") {
 
-              this.hide();
+                this.hide();
 
-              var db = ToolCore.getAssetDatabase();
-              db.deleteAsset(this.asset);
+                var db = ToolCore.getAssetDatabase();
+                db.deleteAsset(this.asset);
 
-              return true;
-          }
+                return true;
+            }
 
-          if (id == "cancel") {
+            if (id == "cancel") {
 
-              this.hide();
+                this.hide();
 
-              return true;
-          }
+                return true;
+            }
 
-      }
+        }
 
-  }
+    }
 
-  asset: ToolCore.Asset;
+    asset: ToolCore.Asset;
 
 }
 
@@ -101,3 +101,56 @@ export class CreateFolder extends ModalWindow {
     nameField: Atomic.UIEditField;
 
 }
+
+export class CreateComponent extends ModalWindow {
+
+    constructor(resourcePath: string) {
+
+        super();
+
+        this.resourcePath = resourcePath;
+        this.init("New Component", "AtomicEditor/editor/ui/resourcecreatecomponent.tb.txt");
+        this.nameField = <Atomic.UIEditField> this.getWidget("component_name");
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CLICK) {
+
+            var id = ev.target.id;
+
+            if (id == "create") {
+
+                var componentName = this.nameField.text;
+                var outputFile = Atomic.addTrailingSlash(this.resourcePath) + componentName;
+
+                if (outputFile.indexOf(".js") == -1) outputFile += ".js";
+
+
+                if (ResourceOps.CreateNewComponent(outputFile, componentName)) {
+
+                    this.hide();
+
+                    this.sendEvent(EditorEvents.EditResource, { path:outputFile});
+
+                }
+
+                return true;
+
+            }
+
+            if (id == "cancel") {
+
+                this.hide();
+
+                return true;
+            }
+
+        }
+
+    }
+
+    resourcePath: string;
+    nameField: Atomic.UIEditField;
+
+}

+ 2 - 0
Script/TypeScript/Atomic.d.ts

@@ -7824,6 +7824,8 @@ declare module Atomic {
       isPackaged(): boolean;
       // Return the fullpath to the file
       getFullPath(): string;
+      // Unlike FileSystem.Copy this copy works when the source file is in a package file
+      copy(srcFile: File): boolean;
       readText():string;
 
    }

+ 5 - 0
Script/TypeScript/AtomicWork.d.ts

@@ -153,6 +153,11 @@ declare module ToolCore {
 
     }
 
+    export var toolEnvironment: ToolEnvironment;
+    export var toolSystem: ToolSystem;
+    export var assetDatabase: AssetDatabase;
+    export var licenseSystem: LicenseSystem;
+
     export function getToolEnvironment(): ToolEnvironment;
     export function getToolSystem(): ToolSystem;
     export function getAssetDatabase(): AssetDatabase;

+ 2 - 0
Script/TypeScript/Editor.d.ts

@@ -71,6 +71,7 @@ declare module Editor {
       gotoLineNumber(lineNumber: number): void;
       setFocus(): void;
       hasUnsavedModifications(): boolean;
+      save(): boolean;
 
    }
 
@@ -105,6 +106,7 @@ declare module Editor {
       getScene(): Atomic.Scene;
       setFocus(): void;
       requiresInspector(): boolean;
+      close(navigateToAvailableResource?: boolean): void;
       save(): boolean;
 
    }

+ 21 - 0
Source/Atomic/IO/File.cpp

@@ -542,4 +542,25 @@ void File::ReadText(String& text)
     text[size_] = '\0';
 }
 
+// ATOMIC BEGIN
+bool File::Copy(File* srcFile)
+{
+    if (!srcFile || !srcFile->IsOpen() || srcFile->GetMode() != FILE_READ)
+        return false;
+
+    if (!IsOpen() || GetMode() != FILE_WRITE)
+        return false;
+
+    unsigned fileSize = srcFile->GetSize();
+    SharedArrayPtr<unsigned char> buffer(new unsigned char[fileSize]);
+
+    unsigned bytesRead = srcFile->Read(buffer.Get(), fileSize);
+    unsigned bytesWritten = Write(buffer.Get(), fileSize);
+    return bytesRead == fileSize && bytesWritten == fileSize;
+
+}
+
+// ATOMIC END
+
+
 }

+ 6 - 0
Source/Atomic/IO/File.h

@@ -101,6 +101,12 @@ public:
     bool IsPackaged() const { return offset_ != 0; }
     /// Return the fullpath to the file
     const String& GetFullPath() const { return fullPath_; }
+
+    // Atomic Begin
+    /// Copy a file from a source file, must be opened and FILE_WRITE
+    /// Unlike FileSystem.Copy this copy works when the source file is in a package file
+    bool Copy(File* srcFile);
+    // Atomic End
     
 
 private:

+ 16 - 1
Source/Atomic/UI/UIInput.cpp

@@ -197,7 +197,22 @@ static bool InvokeShortcut(int key, SPECIAL_KEY special_key, MODIFIER_KEYS modif
     TBWidgetEvent ev(EVENT_TYPE_SHORTCUT);
     ev.modifierkeys = modifierkeys;
     ev.ref_id = id;
-    return TBWidget::focused_widget->InvokeEvent(ev);
+
+    TBWidget* eventWidget = TBWidget::focused_widget;
+
+    if (id == TBIDC("save") || id == TBIDC("close")) {
+
+        while (eventWidget && !eventWidget->GetDelegate()) {
+
+            eventWidget = eventWidget->GetParent();
+        }
+
+    }
+
+    if (!eventWidget)
+        return false;
+
+    return eventWidget->InvokeEvent(ev);
 }
 
 static bool InvokeKey(TBWidget* root, unsigned int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys, bool keydown)

+ 2 - 2
Source/Atomic/UI/UITabContainer.cpp

@@ -58,8 +58,8 @@ void UITabContainer::SetCurrentPage(int page)
 }
 
 bool UITabContainer::OnEvent(const tb::TBWidgetEvent &ev)
-{
-    return false;
+{    
+    return UIWidget::OnEvent(ev);
 }
 
 }

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

@@ -605,6 +605,20 @@ bool UIWidget::OnEvent(const tb::TBWidgetEvent &ev)
 
         }
 
+    }
+    else if (ev.type == EVENT_TYPE_SHORTCUT)
+    {
+        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_TAB_CHANGED)
     {

+ 31 - 11
Source/AtomicEditorWork/Editors/JSResourceEditor.cpp

@@ -259,20 +259,20 @@ bool JSResourceEditor::OnEvent(const TBWidgetEvent &ev)
 
         }
 
-        if (ev.ref_id == TBIDC("save") && modified_)
+        if (ev.ref_id == TBIDC("save"))
         {
-            TBStr text;
-            styleEdit_->GetText(text);
-            File file(context_, fullpath_, FILE_WRITE);
-            file.Write((void*) text.CStr(), text.Length());
-            file.Close();
-
-            String filename = GetFileNameAndExtension(fullpath_);
-            button_->SetText(filename.CString());
-            modified_ = false;
+            //TBStr text;
+            //styleEdit_->GetText(text);
+            //File file(context_, fullpath_, FILE_WRITE);
+            //file.Write((void*) text.CStr(), text.Length());
+            //file.Close();
+
+            //String filename = GetFileNameAndExtension(fullpath_);
+            //button_->SetText(filename.CString());
+            //modified_ = false;
             //SendEvent(E_JAVASCRIPTSAVED);
 
-            return true;
+            return false;
         }
         else if (ev.ref_id == TBIDC("find"))
         {
@@ -617,4 +617,24 @@ bool JSResourceEditor::BeautifyJavascript(const char* source, String& output)
 
 }
 
+bool JSResourceEditor::Save()
+{
+    if (!modified_)
+        return true;
+
+    TBStr text;
+    styleEdit_->GetText(text);
+    File file(context_, fullpath_, FILE_WRITE);
+    file.Write((void*) text.CStr(), text.Length());
+    file.Close();
+
+    String filename = GetFileNameAndExtension(fullpath_);
+    button_->SetText(filename.CString());
+    modified_ = false;
+
+    return true;
+
+}
+
+
 }

+ 2 - 0
Source/AtomicEditorWork/Editors/JSResourceEditor.h

@@ -42,6 +42,8 @@ public:
 
     bool HasUnsavedModifications();
 
+    bool Save();
+
 private:
 
     bool ParseJavascriptToJSON(const char* source, String& json, bool loose = true);

+ 1 - 5
Source/AtomicEditorWork/Editors/ResourceEditor.cpp

@@ -118,11 +118,7 @@ void ResourceEditor::Close(bool navigateToAvailableResource)
     VariantMap data;
     data["Editor"] = this;
     data["NavigateToAvailableResource"] = navigateToAvailableResource;
-    SendEvent("CloseResourceEditor", data);
-
-    //MainFrame* frame = GetSubsystem<MainFrame>();
-    //ResourceFrame* rframe = frame->GetResourceFrame();
-    //rframe->CloseResourceEditor(this, navigateToAvailabeResource);
+    SendEvent("EditorCloseResource", data);
 }
 
 }

+ 12 - 6
Source/AtomicEditorWork/Editors/SceneEditor3D/SceneEditor3D.cpp

@@ -89,11 +89,6 @@ SceneEditor3D ::SceneEditor3D(Context* context, const String &fullpath, UITabCon
     IntRect rect = container_->GetContentRoot()->GetRect();
     rootContentWidget_->SetSize(rect.Width(), rect.Height());
 
-    // TODO: generate this event properly
-    VariantMap eventData;
-    eventData[EditorActiveSceneChange::P_SCENE] = scene_;
-    SendEvent(E_EDITORACTIVESCENECHANGE, eventData);
-
     SubscribeToEvent(E_EDITORPLAYSTARTED, HANDLER(SceneEditor3D, HandlePlayStarted));
     SubscribeToEvent(E_EDITORPLAYSTOPPED, HANDLER(SceneEditor3D, HandlePlayStopped));
 
@@ -109,6 +104,7 @@ bool SceneEditor3D::OnEvent(const TBWidgetEvent &ev)
 
     if (ev.type == EVENT_TYPE_SHORTCUT)
     {
+        /*
         if (ev.ref_id == TBIDC("save"))
         {
             File file(context_);
@@ -120,7 +116,7 @@ bool SceneEditor3D::OnEvent(const TBWidgetEvent &ev)
 
             return true;
         }
-        else if (ev.ref_id == TBIDC("copy"))
+        else */if (ev.ref_id == TBIDC("copy"))
         {
             if (selectedNode_.NotNull())
             {
@@ -208,6 +204,16 @@ void SceneEditor3D::HandleGizmoEditModeChanged(StringHash eventType, VariantMap&
     gizmo3D_->SetEditMode(mode);
 }
 
+void SceneEditor3D::Close(bool navigateToAvailableResource)
+{
+
+    VariantMap data;
+    data["Scene"] = scene_;
+    SendEvent("EditorSceneClosed", data);
+
+    ResourceEditor::Close(navigateToAvailableResource);
+}
+
 bool SceneEditor3D::Save()
 {
     File file(context_);

+ 1 - 0
Source/AtomicEditorWork/Editors/SceneEditor3D/SceneEditor3D.h

@@ -48,6 +48,7 @@ public:
 
     virtual bool RequiresInspector() { return true; }
 
+    void Close(bool navigateToAvailableResource = true);
     bool Save();
 
 private:

+ 0 - 6
Source/AtomicEditorWork/Editors/SceneEditor3D/SceneView3D.cpp

@@ -102,12 +102,6 @@ SceneView3D ::SceneView3D(Context* context, SceneEditor3D *sceneEditor) :
     SubscribeToEvent(E_DRAGEXITWIDGET, HANDLER(SceneView3D, HandleDragExitWidget));
     SubscribeToEvent(E_DRAGENDED, HANDLER(SceneView3D, HandleDragEnded));
 
-
-    // TODO: generate this event properly
-    VariantMap eventData;
-    eventData[EditorActiveSceneChange::P_SCENE] = scene_;
-    SendEvent(E_EDITORACTIVESCENECHANGE, eventData);
-
     SetIsFocusable(true);
 
 

+ 20 - 0
Source/AtomicJS/Javascript/JSAPI.cpp

@@ -310,6 +310,24 @@ static int variantmap_property_get(duk_context* ctx)
 
 }
 
+// removes all keys from the variant map proxy target, REGARDLESS of key given for delete
+// see (lengthy) note in JSEventDispatcher::EndSendEvent
+static int variantmap_property_deleteproperty(duk_context* ctx)
+{
+    // deleteProperty: function (targ, key)
+
+    duk_enum(ctx, 0, DUK_ENUM_OWN_PROPERTIES_ONLY);
+
+    while (duk_next(ctx, -1, 0)) {
+        duk_push_undefined(ctx);
+        duk_put_prop(ctx, 0);
+    }
+
+    duk_push_boolean(ctx, 1);
+    return 1;
+
+}
+
 
 void js_push_variantmap(duk_context* ctx, const VariantMap &vmap)
 {
@@ -343,6 +361,8 @@ void js_push_variantmap(duk_context* ctx, const VariantMap &vmap)
     duk_push_object(ctx);
     duk_push_c_function(ctx, variantmap_property_get, 3);
     duk_put_prop_string(ctx, -2, "get");
+    duk_push_c_function(ctx, variantmap_property_deleteproperty, 2);
+    duk_put_prop_string(ctx, -2, "deleteProperty");
 
     duk_new(ctx, 2);
 

+ 33 - 3
Source/AtomicJS/Javascript/JSEventHelper.cpp

@@ -38,10 +38,40 @@ void JSEventDispatcher::EndSendEvent(Context* context, Object* sender, StringHas
     duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_VARIANTMAP_CACHE);
 
     duk_push_pointer(ctx, (void*) &eventData);
-    duk_push_undefined(ctx);
-    duk_put_prop(ctx, -3);
+    duk_get_prop(ctx, -2);
+
+    // Ok, this is unfortunate, in an event callback it is possible
+    // to capture the Proxy object which represents the event VariantMap
+    // in a function() {} closure, which will keep the event data alive
+    // until the function happens to be gc'd (it is a member of the eventhandler)
+    // which will leave things like scenes up if there was a P_SCENE event data
+    // member, etc
+    // So, we need to check if we have an object in the variant map cache
+    // and thense call it's delete property method on the Proxy, which will clear
+    // all the data (the proxy can still be alive as a captured local, though
+    // the members won't be held
+    // This all makes it that much more important that the pointer to eventData
+    // is consistent across entire event, otherwise references may be held
+
+    // this would be a great use of weak references if duktape had them
+
+    if (duk_is_object(ctx, -1))
+    {
+        // deletes all properties, thus freeing references, even if
+        // the variant map object is held onto by script (it will be invalid, post
+        // event send)
+        // see JSAPI.cpp variantmap_property_deleteproperty
+        duk_del_prop_index(ctx, -1, 0);
+
+        duk_push_pointer(ctx, (void*) &eventData);
+        duk_push_undefined(ctx);
+
+        // clear the variant map object from the cache
+        duk_put_prop(ctx, -4);
+
+    }
 
-    duk_pop_2(ctx);
+    duk_pop_3(ctx);
 
 }
 

+ 12 - 0
Source/ToolCoreJS/ToolCoreJS.cpp

@@ -113,15 +113,27 @@ void jsapi_init_toolcore(JSVM* vm)
     duk_push_c_function(ctx, js_atomic_GetToolEnvironment, 0);
     duk_put_prop_string(ctx, -2, "getToolEnvironment");
 
+    js_push_class_object_instance(ctx, vm->GetSubsystem<ToolEnvironment>(), "ToolEnvironment");
+    duk_put_prop_string(ctx, -2, "toolEnvironment");
+
     duk_push_c_function(ctx, js_atomic_GetToolSystem, 0);
     duk_put_prop_string(ctx, -2, "getToolSystem");
 
+    js_push_class_object_instance(ctx, vm->GetSubsystem<ToolSystem>(), "ToolSystem");
+    duk_put_prop_string(ctx, -2, "toolSystem");
+
     duk_push_c_function(ctx, js_atomic_GetLicenseSystem, 0);
     duk_put_prop_string(ctx, -2, "getLicenseSystem");
 
+    js_push_class_object_instance(ctx, vm->GetSubsystem<LicenseSystem>(), "LicenseSystem");
+    duk_put_prop_string(ctx, -2, "licenseSystem");
+
     duk_push_c_function(ctx, js_atomic_GetAssetDatabase, 0);
     duk_put_prop_string(ctx, -2, "getAssetDatabase");
 
+    js_push_class_object_instance(ctx, vm->GetSubsystem<AssetDatabase>(), "AssetDatabase");
+    duk_put_prop_string(ctx, -2, "assetDatabase");
+
     duk_pop(ctx);
 
     js_class_get_prototype(ctx, "ToolCore", "AssetDatabase");