Browse Source

Merge pull request #366 from AtomicGameEngine/JME-ATOMIC-363

Adding support for renaming/moving assets
JoshEngebretson 10 years ago
parent
commit
7b28da8728

+ 15 - 0
Resources/EditorData/AtomicEditor/editor/ui/renameasset.tb.txt

@@ -0,0 +1,15 @@
+TBLayout: axis: y, distribution: gravity, position: left
+	TBLayout: gravity: left right, distribution-position: right bottom
+		TBTextField: text: "Current Name:"
+		TBTextField: id: current_name
+			lp: min-width: 180
+	TBSeparator: gravity: left right, skin: AESeparator
+	TBLayout: gravity: left right, distribution-position: right bottom
+		TBTextField: text: "New Name:"
+		TBEditField: id: new_name, autofocus: 1
+			lp: min-width: 180
+	TBSeparator: gravity: left right, skin: AESeparator
+	TBLayout: 
+		TBButton: text: Rename, id: rename
+		TBButton: text: Cancel, id: cancel
+		

+ 50 - 11
Script/AtomicEditor/ui/frames/ProjectFrame.ts

@@ -49,6 +49,7 @@ class ProjectFrame extends ScriptWidget {
 
         this.subscribeToEvent("ResourceAdded", (ev: ToolCore.ResourceAddedEvent) => this.handleResourceAdded(ev));
         this.subscribeToEvent("ResourceRemoved", (ev: ToolCore.ResourceRemovedEvent) => this.handleResourceRemoved(ev));
+        this.subscribeToEvent("AssetRenamed", (ev: ToolCore.AssetRenamedEvent) => this.handleAssetRenamed(ev));
 
         // this.subscribeToEvent(EditorEvents.ResourceFolderCreated, (ev: EditorEvents.ResourceFolderCreatedEvent) => this.handleResourceFolderCreated(ev));
 
@@ -61,12 +62,32 @@ class ProjectFrame extends ScriptWidget {
 
     }
 
+    handleAssetRenamed(ev: ToolCore.AssetRenamedEvent) {
+
+        var container: Atomic.UILayout = <Atomic.UILayout>this.getWidget("contentcontainer");
+
+        for (var widget = container.firstChild; widget; widget = widget.next) {
+
+            if (widget.id == ev.asset.guid) {
+
+                if (widget['assetButton'])
+                {
+                    widget['assetButton'].text = ev.asset.name + ev.asset.extension;
+                    widget['assetButton'].dragObject = new Atomic.UIDragObject(ev.asset, ev.asset.name);
+                }
+
+                break;
+            }
+        }
+
+    }
+
     handleResourceRemoved(ev: ToolCore.ResourceRemovedEvent) {
 
         var folderList = this.folderList;
         folderList.deleteItemByID(ev.guid);
 
-        var container: Atomic.UILayout = <Atomic.UILayout> this.getWidget("contentcontainer");
+        var container: Atomic.UILayout = <Atomic.UILayout>this.getWidget("contentcontainer");
 
         for (var widget = container.firstChild; widget; widget = widget.next) {
 
@@ -106,7 +127,7 @@ class ProjectFrame extends ScriptWidget {
 
         } else if (parent == this.currentFolder) {
 
-            var container: Atomic.UILayout = <Atomic.UILayout> this.getWidget("contentcontainer");
+            var container: Atomic.UILayout = <Atomic.UILayout>this.getWidget("contentcontainer");
             container.addChild(this.createButtonLayout(asset));
 
         }
@@ -161,7 +182,7 @@ class ProjectFrame extends ScriptWidget {
 
                 if (id == "folderList_") {
 
-                    var list = <Atomic.UISelectList> data.target;
+                    var list = <Atomic.UISelectList>data.target;
 
                     var selectedId = list.selectedItemID;
 
@@ -230,12 +251,15 @@ class ProjectFrame extends ScriptWidget {
 
         if (data.target) {
 
-            var container: Atomic.UILayout = <Atomic.UILayout> this.getWidget("contentcontainer");
+            var container: Atomic.UILayout = <Atomic.UILayout>this.getWidget("contentcontainer");
 
             if (data.target.id == "contentcontainerscroll" || container.isAncestorOf(data.target)) {
 
-                asset = this.currentFolder;
+                if (data.target["asset"])
+                  asset = <ToolCore.Asset> data.target["asset"];
 
+                if (!asset || !asset.isFolder)
+                  asset = this.currentFolder;
             }
 
         }
@@ -258,21 +282,21 @@ class ProjectFrame extends ScriptWidget {
             return;
 
         var dragObject = data.dragObject;
+
         if (dragObject.object && dragObject.object.typeName == "Node") {
 
-            var node = <Atomic.Node> dragObject.object;
+            var node = <Atomic.Node>dragObject.object;
 
-            var prefabComponent = <Atomic.PrefabComponent> node.getComponent("PrefabComponent");
+            var prefabComponent = <Atomic.PrefabComponent>node.getComponent("PrefabComponent");
 
             if (prefabComponent) {
 
-              prefabComponent.savePrefab();
+                prefabComponent.savePrefab();
 
             }
             else {
                 var destFilename = Atomic.addTrailingSlash(asset.path);
                 destFilename += node.name + ".prefab";
-
                 var file = new Atomic.File(destFilename, Atomic.FILE_WRITE);
                 node.saveXML(file);
                 file.close();
@@ -282,6 +306,18 @@ class ProjectFrame extends ScriptWidget {
 
             return;
 
+        } else if (dragObject.object && dragObject.object.typeName == "Asset") {
+
+            var dragAsset = <ToolCore.Asset> dragObject.object;
+
+            // get the folder we dragged on
+            var destPath = Atomic.addTrailingSlash(asset.path);
+
+            dragAsset.move(destPath + dragAsset.name + dragAsset.extension);
+
+            this.refreshContent(this.currentFolder);
+
+            return true;
         }
 
         // dropped some files?
@@ -324,7 +360,7 @@ class ProjectFrame extends ScriptWidget {
         this.folderList.deleteAllItems();
         this.resourceFolder = null;
 
-        var container: Atomic.UILayout = <Atomic.UILayout> this.getWidget("contentcontainer");
+        var container: Atomic.UILayout = <Atomic.UILayout>this.getWidget("contentcontainer");
         container.deleteAllChildren();
 
     }
@@ -341,7 +377,7 @@ class ProjectFrame extends ScriptWidget {
 
         var db = ToolCore.getAssetDatabase();
 
-        var container: Atomic.UILayout = <Atomic.UILayout> this.getWidget("contentcontainer");
+        var container: Atomic.UILayout = <Atomic.UILayout>this.getWidget("contentcontainer");
         container.deleteAllChildren();
 
         var assets = db.getFolderAssets(folder.path);
@@ -405,12 +441,15 @@ class ProjectFrame extends ScriptWidget {
         image.rect = [0, 0, 12, 12];
         image.gravity = Atomic.UI_GRAVITY_RIGHT;
         blayout.addChild(image);
+        image["asset"] = asset;
 
         button.id = asset.guid;
         button.layoutParams = lp;
         button.fontDescription = fd;
         button.text = asset.name + asset.extension;
         button.skinBg = "TBButton.flat";
+        button["asset"] = asset;
+        blayout['assetButton'] = button;
         blayout.addChild(button);
 
         return blayout;

+ 8 - 0
Script/AtomicEditor/ui/frames/inspector/InspectorFrame.ts

@@ -15,6 +15,7 @@ import MaterialInspector = require("./MaterialInspector");
 import ModelInspector = require("./ModelInspector");
 import NodeInspector = require("./NodeInspector");
 import AssemblyInspector = require("./AssemblyInspector");
+import PrefabInspector = require("./PrefabInspector");
 
 class InspectorFrame extends ScriptWidget {
 
@@ -124,6 +125,13 @@ class InspectorFrame extends ScriptWidget {
 
         }
 
+        if (asset.importerTypeName == "PrefabImporter") {
+
+            var prefabInspector = new PrefabInspector();
+            container.addChild(prefabInspector);
+            
+            prefabInspector.inspect(asset);
+        }
 
     }
 

+ 90 - 0
Script/AtomicEditor/ui/frames/inspector/PrefabInspector.ts

@@ -0,0 +1,90 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+import InspectorWidget = require("./InspectorWidget");
+import ArrayEditWidget = require("./ArrayEditWidget");
+import InspectorUtils = require("./InspectorUtils");
+
+class PrefabInspector extends InspectorWidget {
+
+    constructor() {
+
+        super();
+
+        this.fd.id = "Vera";
+        this.fd.size = 11;
+
+        this.subscribeToEvent(this, "WidgetEvent", (data) => this.handleWidgetEvent(data));
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+
+    }
+
+    onApply() {
+
+        var nameText = this.nameEdit.text;
+        if (nameText != this.asset.name) {
+            if (!this.asset.rename(nameText)) {
+                this.nameEdit.text = this.asset.name;
+            }
+        }
+    }
+
+    inspect(asset: ToolCore.Asset) {
+
+        this.asset = asset;
+        this.importer = <ToolCore.PrefabImporter>asset.importer;
+
+        // node attr layout
+        var rootLayout = this.rootLayout;
+
+        // Assembly Section
+        var prefabLayout = this.createSection(rootLayout, "Prefab", 1);
+
+        var container = InspectorUtils.createContainer();
+        container.gravity = Atomic.UI_GRAVITY_ALL;
+        prefabLayout.addChild(container);
+
+        var panel = new Atomic.UILayout();
+        panel.axis = Atomic.UI_AXIS_Y;
+        panel.layoutSize = Atomic.UI_LAYOUT_SIZE_PREFERRED;
+        panel.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
+        container.addChild(panel);
+
+        // Name Edit
+        this.nameEdit = this.createAttrEditField("Name", panel);
+        this.nameEdit.text = this.asset.name;
+
+        var applyButton = this.createApplyButton();
+        panel.addChild(applyButton);
+
+        /*
+        var editButton = new Atomic.UIButton();
+        editButton.fontDescription = this.fd;
+        editButton.text = "Edit";
+
+        editButton.onClick = function() {
+
+        }.bind(this);
+
+        prefabLayout.addChild(editButton);
+        */
+
+
+    }
+
+    nameEdit: Atomic.UIEditField;
+
+    asset: ToolCore.Asset;
+    importer: ToolCore.PrefabImporter;
+    fd: Atomic.UIFontDescription = new Atomic.UIFontDescription();
+
+}
+
+export = PrefabInspector;

+ 6 - 0
Script/AtomicEditor/ui/frames/menus/ProjectFrameMenu.ts

@@ -39,6 +39,11 @@ class ProjectFrameMenus extends Atomic.ScriptObject {
                 path = this.contentFolder;
             }
 
+            if (refid == "rename_asset") {
+                EditorUI.getModelOps().showRenameAsset(asset);
+                return true;
+            }
+
             if (refid == "delete_asset") {
                 EditorUI.getModelOps().showResourceDelete(asset);
                 return true;
@@ -127,6 +132,7 @@ export = ProjectFrameMenus;
 var StringID = strings.StringID;
 
 var assetGeneralContextItems = {
+    "Rename": ["rename_asset", undefined, ""],
     "Reveal in Finder": ["reveal_folder", undefined, ""],
     "-1": null,
     "Delete": ["delete_asset", undefined, ""]

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

@@ -107,6 +107,17 @@ class ModalOps extends Atomic.ScriptObject {
 
     }
 
+    showRenameAsset(asset: ToolCore.Asset) {
+
+        if (this.show()) {
+
+            this.opWindow = new UIResourceOps.RenameAsset(asset);
+
+        }
+
+    }
+
+
     showResourceSelection(windowText: string, importerType: string, callback: (asset: ToolCore.Asset, args: any) => void, args: any = undefined) {
 
         if (this.show()) {

+ 62 - 10
Script/AtomicEditor/ui/modal/UIResourceOps.ts

@@ -18,7 +18,7 @@ export class ResourceDelete extends ModalWindow {
 
         this.asset = asset;
         this.init("Delete Resource", "AtomicEditor/editor/ui/resourcedelete.tb.txt");
-        var message = <Atomic.UIEditField> this.getWidget("message");
+        var message = <Atomic.UIEditField>this.getWidget("message");
 
         var text = "Are you sure you want to delete resource:\n\n";
         text += asset.path;
@@ -70,7 +70,7 @@ export class CreateFolder extends ModalWindow {
 
         this.resourcePath = resourcePath;
         this.init("New Folder", "AtomicEditor/editor/ui/resourcenewfolder.tb.txt");
-        this.nameField = <Atomic.UIEditField> this.getWidget("folder_name");
+        this.nameField = <Atomic.UIEditField>this.getWidget("folder_name");
     }
 
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
@@ -117,7 +117,7 @@ export class CreateComponent extends ModalWindow {
 
         this.resourcePath = resourcePath;
         this.init("New Component", "AtomicEditor/editor/ui/resourcecreatecomponent.tb.txt");
-        this.nameField = <Atomic.UIEditField> this.getWidget("component_name");
+        this.nameField = <Atomic.UIEditField>this.getWidget("component_name");
     }
 
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
@@ -138,7 +138,7 @@ export class CreateComponent extends ModalWindow {
 
                     this.hide();
 
-                    this.sendEvent(EditorEvents.EditResource, { path:outputFile});
+                    this.sendEvent(EditorEvents.EditResource, { path: outputFile });
 
                 }
 
@@ -170,7 +170,7 @@ export class CreateScript extends ModalWindow {
 
         this.resourcePath = resourcePath;
         this.init("New Script", "AtomicEditor/editor/ui/resourcecreatecomponent.tb.txt");
-        this.nameField = <Atomic.UIEditField> this.getWidget("component_name");
+        this.nameField = <Atomic.UIEditField>this.getWidget("component_name");
     }
 
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
@@ -191,7 +191,7 @@ export class CreateScript extends ModalWindow {
 
                     this.hide();
 
-                    this.sendEvent(EditorEvents.EditResource, { path:outputFile});
+                    this.sendEvent(EditorEvents.EditResource, { path: outputFile });
 
                 }
 
@@ -223,7 +223,7 @@ export class CreateScene extends ModalWindow {
 
         this.resourcePath = resourcePath;
         this.init("New Scene", "AtomicEditor/editor/ui/resourcecreatecomponent.tb.txt");
-        this.nameField = <Atomic.UIEditField> this.getWidget("component_name");
+        this.nameField = <Atomic.UIEditField>this.getWidget("component_name");
     }
 
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
@@ -243,7 +243,7 @@ export class CreateScene extends ModalWindow {
 
                     this.hide();
 
-                    this.sendEvent(EditorEvents.EditResource, { path:outputFile});
+                    this.sendEvent(EditorEvents.EditResource, { path: outputFile });
 
                 }
 
@@ -275,7 +275,7 @@ export class CreateMaterial extends ModalWindow {
 
         this.resourcePath = resourcePath;
         this.init("New Material", "AtomicEditor/editor/ui/resourcecreatecomponent.tb.txt");
-        this.nameField = <Atomic.UIEditField> this.getWidget("component_name");
+        this.nameField = <Atomic.UIEditField>this.getWidget("component_name");
     }
 
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
@@ -295,7 +295,7 @@ export class CreateMaterial extends ModalWindow {
 
                     this.hide();
 
-                    this.sendEvent(EditorEvents.EditResource, { path:outputFile});
+                    this.sendEvent(EditorEvents.EditResource, { path: outputFile });
 
                 }
 
@@ -318,3 +318,55 @@ export class CreateMaterial extends ModalWindow {
     nameField: Atomic.UIEditField;
 
 }
+
+export class RenameAsset extends ModalWindow {
+
+    constructor(asset: ToolCore.Asset) {
+
+        super();
+
+        this.asset = asset;
+        this.init("Rename Resource", "AtomicEditor/editor/ui/renameasset.tb.txt");
+
+        var currentName = <Atomic.UITextField>this.getWidget("current_name");
+        this.nameEdit = <Atomic.UIEditField>this.getWidget("new_name");
+
+        currentName.text = asset.name;
+        this.nameEdit.text = asset.name;
+
+        this.resizeToFitContent();
+        this.center();
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CLICK) {
+
+            var id = ev.target.id;
+
+            if (id == "rename") {
+
+                this.hide();
+
+                if (this.asset.name != this.nameEdit.text)
+                    this.asset.rename(this.nameEdit.text);
+
+                return true;
+            }
+
+            if (id == "cancel") {
+
+                this.hide();
+
+                return true;
+            }
+
+        }
+
+    }
+
+    nameEdit: Atomic.UIEditField;
+    asset: ToolCore.Asset;
+
+}

+ 23 - 10
Script/TypeScript/AtomicWork.d.ts

@@ -213,8 +213,8 @@ declare module AtomicNET {
 
     export interface CSComponentClassChangedEvent {
 
-      cscomponent: CSComponent;
-      classname: string;
+        cscomponent: CSComponent;
+        classname: string;
 
     }
 
@@ -242,30 +242,43 @@ declare module ToolCore {
 
     export interface AssetImportErrorEvent {
 
-        path:string;
-        guid:string;
+        path: string;
+        guid: string;
         error: string;
+    }
+
+    export interface AssetRenamedEvent {
+
+        asset: Asset;
+
+    }
+
+    export interface AssetMovedEvent {
+
+        asset: Asset;
+        oldPath: string;
 
     }
 
+
     export interface PlatformChangedEvent {
 
-        platform:ToolCore.Platform;
+        platform: ToolCore.Platform;
 
     }
 
     export interface BuildOutputEvent {
 
-        text:string;
+        text: string;
 
     }
 
     export interface BuildCompleteEvent {
 
-      platformID:number;
-      message:string;
-      success:boolean;
-      buildFolder:string;
+        platformID: number;
+        message: string;
+        success: boolean;
+        buildFolder: string;
 
     }
 

+ 38 - 0
Source/ToolCore/Assets/Asset.cpp

@@ -367,6 +367,44 @@ bool Asset::SetPath(const String& path)
 
 }
 
+bool Asset::Move(const String& newPath)
+{
+    if (importer_.Null())
+        return false;
+
+    String oldPath = path_;
+
+    bool result = importer_->Move(newPath);
+
+    if (result)
+    {
+        VariantMap eventData;
+        eventData[AssetMoved::P_ASSET] = this;
+        eventData[AssetMoved::P_OLDPATH] = oldPath;
+        SendEvent(E_ASSETMOVED, eventData);
+    }
+
+    return result;
+
+}
+
+bool Asset::Rename(const String& newName)
+{
+    if (importer_.Null())
+        return false;
+
+    bool result = importer_->Rename(newName);
+
+    if (result)
+    {
+        VariantMap eventData;
+        eventData[AssetRenamed::P_ASSET] = this;
+        SendEvent(E_ASSETRENAMED, eventData);
+    }
+
+    return result;
+}
+
 Resource* Asset::GetResource(const String &typeName)
 {
     if (importer_)

+ 8 - 0
Source/ToolCore/Assets/Asset.h

@@ -23,6 +23,7 @@ namespace ToolCore
 class Asset : public Object
 {
     friend class AssetDatabase;
+    friend class AssetImporter;
 
     OBJECT(Asset);
 
@@ -40,6 +41,7 @@ public:
     const String& GetGUID() const { return guid_; }
     const String& GetName() const { return name_; }
     const String& GetPath() const { return path_; }
+
     String GetExtension() const;
     /// Get the path relative to project
     String GetRelativePath();
@@ -68,6 +70,12 @@ public:
     // get the .asset filename
     String GetDotAssetFilename();
 
+    /// Rename the asset, which depending on the asset type may be nontrivial
+    bool Rename(const String& newName);
+
+    /// Move the asset, which depending on the asset type may be nontrivial
+    bool Move(const String& newPath);
+
     bool IsFolder() const { return isFolder_; }
 
     // load .asset

+ 11 - 0
Source/ToolCore/Assets/AssetEvents.h

@@ -28,5 +28,16 @@ EVENT(E_ASSETIMPORTERROR, AssetImportError)
     PARAM(P_ERROR, Error);                  // string
 }
 
+EVENT(E_ASSETRENAMED, AssetRenamed)
+{
+    PARAM(P_ASSET, Asset);                  // asset ptr
+}
+
+EVENT(E_ASSETMOVED, AssetMoved)
+{
+    PARAM(P_ASSET, Asset);                  // asset ptr
+    PARAM(P_OLDPATH, OldPath);                  // string
+
+}
 
 }

+ 62 - 0
Source/ToolCore/Assets/AssetImporter.cpp

@@ -5,6 +5,7 @@
 // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
 //
 
+#include <Atomic/IO/Log.h>
 #include <Atomic/IO/File.h>
 #include <Atomic/IO/FileSystem.h>
 #include "AssetDatabase.h"
@@ -52,5 +53,66 @@ bool AssetImporter::SaveSettingsInternal(JSONValue& jsonRoot)
     return true;
 }
 
+bool AssetImporter::Move(const String& newPath)
+{
+    FileSystem* fs = GetSubsystem<FileSystem>();
+
+    if (newPath == asset_->path_)
+        return false;
+
+    String oldPath = asset_->path_;
+    String oldName = asset_->name_;
+
+    String pathName, newName, ext;
+
+    SplitPath(newPath, pathName, newName, ext);
+
+    // rename asset first, ahead of the filesystem watcher, so the assetdatabase doesn't see a new asset
+    asset_->name_ = newName;
+    asset_->path_ = newPath;
+
+    // first rename the .asset file
+    if (!fs->Rename(oldPath + ".asset", newPath + ".asset"))
+    {
+        asset_->name_ = oldName;
+        asset_->path_ = oldPath;
+
+        LOGERRORF("Unable to rename asset: %s to %s", GetNativePath(oldPath + ".asset").CString(), GetNativePath(newPath + ".asset").CString());
+        return false;
+    }
+
+    // now rename the asset file itself
+    if (!fs->Rename(oldPath, newPath))
+    {
+        asset_->name_ = oldName;
+        asset_->path_ = oldPath;
+
+        // restore .asset
+        fs->Rename(newPath + ".asset", oldPath + ".asset");
+
+        LOGERRORF("Unable to rename: %s to %s", GetNativePath(oldPath).CString(), GetNativePath(newPath).CString());
+        return false;
+    }
+
+    return true;
+}
+
+bool AssetImporter::Rename(const String& newName)
+{
+    String pathName, fileName, ext;
+
+    SplitPath(asset_->path_, pathName, fileName, ext);
+
+    String newPath = pathName + newName + ext;
+
+    FileSystem* fs = GetSubsystem<FileSystem>();
+
+    if (fs->FileExists(newPath) || fs->DirExists(newPath))
+        return false;
+
+    return Move(newPath);
+
+}
+
 
 }

+ 3 - 0
Source/ToolCore/Assets/AssetImporter.h

@@ -48,6 +48,9 @@ public:
     /// Instantiate a node from the asset
     virtual Node* InstantiateNode(Node* parent, const String& name) { return 0; }
 
+    virtual bool Rename(const String& newName);
+    virtual bool Move(const String& newPath);
+
 protected:
 
     virtual bool Import() { return true; }