浏览代码

DragDrop: rework drag & drop and fix drag and drop frop file browser to scene tree

LeoVgr 1 月之前
父节点
当前提交
1566848df1
共有 5 个文件被更改,包括 131 次插入95 次删除
  1. 15 2
      hide/Ide.hx
  2. 8 8
      hide/comp/FancyTree.hx
  3. 94 38
      hide/comp/SceneEditor.hx
  4. 2 2
      hide/view/FXEditor.hx
  5. 12 45
      hide/view/FileBrowser.hx

+ 15 - 2
hide/Ide.hx

@@ -41,6 +41,8 @@ class Ide extends hide.tools.IdeData {
 	var hasReloaded = false;
 	public var thumbnailMode : Bool = false;
 
+	var dataTransfer : Map<String, Dynamic> = new Map();
+
 	var hideRoot : hide.Element;
 	var statusBar : hide.Element;
 	var goldenContainer : hide.Element;
@@ -229,8 +231,8 @@ class Ide extends hide.tools.IdeData {
 			syncMousePosition(e);
 			var view = getViewAt(mouseX, mouseY);
 			var items : Array<String> = [for(f in e.dataTransfer.files) Reflect.field(f, "path")];
-			if (e.dataTransfer.types.contains(hide.view.FileBrowser.dragKey)) {
-				var data = e.dataTransfer.getData(hide.view.FileBrowser.dragKey);
+			if (e.dataTransfer.types.contains("application/x.filemove")) {
+				var data = e.dataTransfer.getData("application/x.filemove");
 				// when in the middle of a drag (and not a drop) getData return nothing
 				if (data.length > 0) {
 					var moreItems : Array<String> = haxe.Json.parse(data);
@@ -629,6 +631,7 @@ class Ide extends hide.tools.IdeData {
 		};
 	}
 
+
 	public function setClipboard( data : String, type: nw.Clipboard.ClipboardType = Text ) {
 		nw.Clipboard.get().set([{data: data, type: type }]);
 	}
@@ -641,6 +644,16 @@ class Ide extends hide.tools.IdeData {
 		return nw.Clipboard.get().get(type);
 	}
 
+
+	public function setData(key: String,  data: Dynamic) {
+		dataTransfer.set(key, data);
+	}
+
+	public function getData(key: String) {
+		return dataTransfer.get(key);
+	}
+
+
 	public function registerUpdate( updateFun ) {
 		updates.push(updateFun);
 	}

+ 8 - 8
hide/comp/FancyTree.hx

@@ -219,23 +219,23 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 	{
 		/**
 			Called when the user starts a drag and drop operation on `item`.
-			Fill dataTransfer with the information you want to transfer, you can use getSelectedItems to handle dragging more than
+			Fill event.dataTransfer with the information you want to transfer, you can use getSelectedItems to handle dragging more than
 			one item at a time.
 			Return `true` if the drag operation is allowed, and `false` to cancel it
 		**/
-		onDragStart: (item: TreeItem, dataTransfer: js.html.DataTransfer) -> Bool,
+		onDragStart: (item: TreeItem, event : js.html.DragEvent) -> Bool,
 
 		/**
 			Called when the user hovers on `target` with a drag and drop operation. You need to return what drop orperation is allowed
 			on the given object
 		**/
-		getItemDropFlags: (target: TreeItem, dataTransfer: js.html.DataTransfer) -> DropFlags,
+		getItemDropFlags: (target: TreeItem, event : js.html.DragEvent) -> DropFlags,
 
 		/**
 			Called when the user drops an item on `target` and getItemDropFlags returned at least one valid flag.
-			`where` tells you where the item was dropped, and you can use `dataTransfer` to know what was dropped
+			`where` tells you where the item was dropped, and you can use `event.dataTransfer` to know what was dropped
 		**/
-		onDrop: (target: TreeItem, where: DropOperation, dataTransfer: js.html.DataTransfer) -> Void
+		onDrop: (target: TreeItem, where: DropOperation, event : js.html.DragEvent) -> Void
 	} = null;
 
 	/**
@@ -827,7 +827,7 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 
 				moveLastDragOver = null;
 
-				if (dragAndDropInterface.onDragStart(data.item, e.dataTransfer)) {
+				if (dragAndDropInterface.onDragStart(data.item, e)) {
 					e.dataTransfer.effectAllowed = "move";
 					e.dataTransfer.setDragImage(data.element, 0, 0);
 				} else {
@@ -889,7 +889,7 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 
 				var operation = getDragOperation(data,e);
 				if (operation != null) {
-					dragAndDropInterface.onDrop(data.item, operation, e.dataTransfer);
+					dragAndDropInterface.onDrop(data.item, operation, e);
 				}
 				e.preventDefault();
 				e.stopPropagation();
@@ -905,7 +905,7 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 
 	function getDragOperation(data: TreeItemData<TreeItem>, event: js.html.DragEvent) : DropOperation {
 		var element = data.element;
-		var flags = dragAndDropInterface.getItemDropFlags(data.item, event.dataTransfer);
+		var flags = dragAndDropInterface.getItemDropFlags(data.item, event);
 		if (flags == DropFlags.ofInt(0)) {
 			return null;
 		}

+ 94 - 38
hide/comp/SceneEditor.hx

@@ -1098,22 +1098,16 @@ class SceneEditor {
 			onResize();
 		};
 
-		sceneEl.get(0).ondragover = (e: js.html.DragEvent) -> {
-			if (e.dataTransfer.types.contains(hide.view.FileBrowser.dragKey)) {
-				e.preventDefault();
-				return;
-			}
-		}
-
 		sceneEl.get(0).ondrop = (e: js.html.DragEvent) -> {
-			if (e.dataTransfer.types.contains(hide.view.FileBrowser.dragKey)) {
-				var files : Array<String> = haxe.Json.parse(e.dataTransfer.getData(hide.view.FileBrowser.dragKey));
-				@:privateAccess scene.canvas.focus();
-				onDragDrop(files, true, e);
-				e.preventDefault();
-				e.stopPropagation();
+			var fileEntries : Array<hide.tools.FileManager.FileEntry> = cast ide.getData("drag/filetree");
+			if (fileEntries == null)
 				return;
-			}
+
+			var files = [for (f in fileEntries) f.relPath];
+			@:privateAccess scene.canvas.focus();
+			onDragDrop(files, true, e);
+			e.preventDefault();
+			e.stopPropagation();
 		}
 
 		editorDisplay = true;
@@ -2057,32 +2051,99 @@ class SceneEditor {
 			return buttons;
 		}
 
-		var movedPrefabs : Array<hrt.prefab.Prefab> = [];
 		tree.dragAndDropInterface =
 		{
-			onDragStart: function(p: hrt.prefab.Prefab, dataTransfer: js.html.DataTransfer) : Bool {
+			onDragStart: function(p: hrt.prefab.Prefab, e: js.html.DragEvent) : Bool {
 				var selection = tree.getSelectedItems();
 				if (selection.length <= 0)
 					return false;
-				movedPrefabs = selection;
+				ide.setData("drag/scenetree", cast selection);
 				return true;
 			},
-			getItemDropFlags: function(target: hrt.prefab.Prefab, dataTransfer: js.html.DataTransfer) : hide.comp.FancyTree.DropFlags {
-				for (p in movedPrefabs) {
-					if (checkAllowParent({prefabClass : Type.getClass(p), inf : p.getHideProps()}, target))
-						return (Reorder:hide.comp.FancyTree.DropFlags) | Reparent;
+			getItemDropFlags: function(target: hrt.prefab.Prefab, e: js.html.DragEvent) : hide.comp.FancyTree.DropFlags {
+				var prefabs : Array<hrt.prefab.Prefab> = cast ide.getData("drag/scenetree");
+				if (prefabs != null) {
+					for (p in prefabs) {
+						if (checkAllowParent({prefabClass : Type.getClass(p), inf : p.getHideProps()}, target))
+							return (Reorder:hide.comp.FancyTree.DropFlags) | Reparent;
+					}
 				}
+
+				var files : Array<hide.tools.FileManager.FileEntry> = cast ide.getData("drag/filetree");
+				if (files != null) {
+					for (f in files) {
+						var ptype = hrt.prefab.Prefab.getPrefabType(f.relPath);
+						var ext = f.relPath.substring(f.relPath.lastIndexOf(".") + 1);
+
+						if (ptype != null) {
+							return (Reorder:hide.comp.FancyTree.DropFlags) | Reparent;
+						}
+						else if (ext == "fbx" || ext == "hmd") {
+							var model = new hrt.prefab.Model(null, null);
+							if (checkAllowParent({prefabClass : hrt.prefab.Model, inf : model.getHideProps()}, target))
+								return (Reorder:hide.comp.FancyTree.DropFlags) | Reparent;
+						}
+					}
+				}
+
 				return Reorder;
 			},
-			onDrop: function(target: hrt.prefab.Prefab, operation: hide.comp.FancyTree.DropOperation, dataTransfer: js.html.DataTransfer) : Bool {
+			onDrop: function(target: hrt.prefab.Prefab, operation: hide.comp.FancyTree.DropOperation, e: js.html.DragEvent) : Bool {
 				var parent = operation.match(hide.comp.FancyTree.DropOperation.Inside) ? target : target.parent;
 				var tChildren = target.parent.children;
-				var idx = tChildren.indexOf(target);
-				if (operation.match(hide.comp.FancyTree.DropOperation.After))
-					idx++;
-				reparentElement(movedPrefabs, parent, idx);
-				refreshTree(targetTree);
-				return true;
+
+				var prefabs : Array<hrt.prefab.Prefab> = cast ide.getData("drag/scenetree");
+				if (prefabs != null) {
+					var idx = tChildren.indexOf(target);
+					if (operation.match(hide.comp.FancyTree.DropOperation.After))
+						idx++;
+					reparentElement(prefabs, parent, idx);
+					refreshTree(targetTree);
+					return true;
+				}
+
+				var files : Array<hide.tools.FileManager.FileEntry> = cast ide.getData("drag/filetree");
+				if (files != null) {
+					var createdPrefab : Array<{ p : hrt.prefab.Prefab, idx: Int }> = [];
+					for (f in files) {
+						var idx = switch (operation) {
+							case hide.comp.FancyTree.DropOperation.Inside:
+								parent.children.length;
+							case hide.comp.FancyTree.DropOperation.After:
+								tChildren.indexOf(target) + 1;
+							case hide.comp.FancyTree.DropOperation.Before:
+								tChildren.indexOf(target);
+						}
+
+						var p = createDroppedElement(f.relPath, parent, e.shiftKey);
+						parent.children.remove(p);
+						parent.children.insert(idx, p);
+						queueRebuild(p);
+						createdPrefab.push({ p : p, idx : idx });
+					}
+
+					undo.change(Custom((undo) -> {
+						if (undo) {
+							for (p in createdPrefab) {
+								parent.children.remove(p.p);
+								p.p.editorRemoveInstanceObjects();
+							}
+						}
+						else {
+							for (p in createdPrefab) {
+								parent.children.insert(p.idx, p.p);
+								queueRebuild(p.p);
+							}
+						}
+
+						refreshTree(targetTree);
+					}));
+
+					refreshTree(targetTree);
+					return true;
+				}
+
+				return false;
 			}
 		}
 		function ctxMenu(p: hrt.prefab.Prefab, e: js.html.Event) {
@@ -3908,17 +3969,12 @@ class SceneEditor {
 		return true;
 	}
 
-	function createDroppedElement(path: String, defaultParent: PrefabElement, event: js.html.DragEvent) : hrt.prefab.Prefab {
+	function createDroppedElement(path: String, parent: PrefabElement, inlinePrefab : Bool) : hrt.prefab.Prefab {
 		var prefab : hrt.prefab.Prefab = null;
 		var relative = ide.makeRelative(path);
 
 		var ptype = hrt.prefab.Prefab.getPrefabType(path);
-		var hover = null;//tree.getItemByElement(js.Browser.document.elementFromPoint(event.clientX, event.clientY));
-		var parent = hover ?? defaultParent;
-		var index = 0;
-		if (parent == defaultParent) {
-			index = defaultParent.children.length;
-		}
+		var index = parent.children.length;
 		if (ptype == "shgraph") {
 			var p = parent;
 			while (p != null && Std.downcast(p, Object3D) == null && Std.downcast(p, Object2D) == null && Std.downcast(p, hrt.prefab.Material) == null) {
@@ -3938,7 +3994,7 @@ class SceneEditor {
 		}
 		else if(ptype != null) {
 			// Inline reference if shift is held
-			if (event.shiftKey) {
+			if (inlinePrefab) {
 				var inlineRef = hxd.res.Loader.currentInstance.load(relative).toPrefab().load();
 				// create a root group
 				var root = new hrt.prefab.Object3D(null, parent.shared);
@@ -3983,6 +4039,7 @@ class SceneEditor {
 			return null;
 		}
 
+		autoName(prefab);
 		return prefab;
 	}
 
@@ -4012,7 +4069,7 @@ class SceneEditor {
 
 		var elts: Array<PrefabElement> = [];
 		for(path in paths) {
-			var prefab = createDroppedElement(path, parent, event);
+			var prefab = createDroppedElement(path, parent, event.shiftKey);
 			if (prefab == null) {
 				return;
 			}
@@ -4020,7 +4077,6 @@ class SceneEditor {
 			if (obj3d != null) {
 				obj3d.setTransform(localMat);
 			}
-			autoName(prefab);
 			elts.push(prefab);
 		}
 

+ 2 - 2
hide/view/FXEditor.hx

@@ -99,7 +99,7 @@ private class FXSceneEditor extends hide.comp.SceneEditor {
 		}
 	}
 
-	override function createDroppedElement(path:String, parent:PrefabElement, event: js.html.DragEvent): hrt.prefab.Prefab {
+	override function createDroppedElement(path:String, parent:PrefabElement, inlinePrefab: Bool): hrt.prefab.Prefab {
 		var type = hrt.prefab.Prefab.getPrefabType(path);
 		if(type == "fx") {
 			var relative = ide.makeRelative(path);
@@ -108,7 +108,7 @@ private class FXSceneEditor extends hide.comp.SceneEditor {
 			ref.name = new haxe.io.Path(relative).file;
 			return ref;
 		}
-		return super.createDroppedElement(path, parent, event);
+		return super.createDroppedElement(path, parent, inlinePrefab);
 	}
 
 	override function updateGrid() {

+ 12 - 45
hide/view/FileBrowser.hx

@@ -114,7 +114,6 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 		return menu;
 	}
 
-	public static final dragKey = "application/x.filemove";
 
 	var currentFolder : FileEntry;
 	var currentSearch : Array<FileEntry> = [];
@@ -444,63 +443,31 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 
 		fancyTree.dragAndDropInterface =
 		{
-			onDragStart: function(file: FileEntry, dataTransfer: js.html.DataTransfer) : Bool {
+			onDragStart: function(file: FileEntry, e: js.html.DragEvent) : Bool {
 				var selection = fancyTree.getSelectedItems();
 				if (selection.length <= 0)
 					return false;
-				var ser = [];
-				ser.push(file.getPath());
-				for (item in selection) {
-					if (item == file)
-						continue;
-					ser.push(item.getPath());
-				}
-				dataTransfer.setData(dragKey, haxe.Json.stringify(ser));
+				ide.setData("drag/filetree", cast selection);
 				return true;
 			},
-			getItemDropFlags: function(target: FileEntry, dataTransfer: js.html.DataTransfer) : hide.comp.FancyTree.DropFlags {
-				var containsFiles = false;
-				if (dataTransfer.types.contains("Files")) {
-					containsFiles = true;
-				}
-				if (dataTransfer.types.contains(dragKey)) {
-					containsFiles = true;
-				}
+			getItemDropFlags: function(target: FileEntry, e: js.html.DragEvent) : hide.comp.FancyTree.DropFlags {
+				var fileEntries : Array<FileEntry> = cast ide.getData("drag/filetree");
+				var containsFiles = fileEntries.length > 0;
 
-				if (!containsFiles) {
+				if (!containsFiles)
 					return hide.comp.FancyTree.DropFlags.ofInt(0);
-				}
 
-				if (target.kind == Dir) {
+				if (target.kind == Dir)
 					return (Reorder:hide.comp.FancyTree.DropFlags) | Reparent;
-				}
+
 				return Reorder;
 			},
-			onDrop: function(target: FileEntry, operation: hide.comp.FancyTree.DropOperation, dataTransfer: js.html.DataTransfer) : Bool {
+			onDrop: function(target: FileEntry, operation: hide.comp.FancyTree.DropOperation, e: js.html.DragEvent) : Bool {
 				if (target.kind != Dir)
 					target = target.parent;
 
-				var files : Array<String> = [];
-				for (file in dataTransfer.files) {
-					var path : String = untyped file.path; //file.path is an extension from nwjs or node
-					path = StringTools.replace(path, "\\", "/");
-					var rel = ide.getRelPath(path);
-					files.push(rel);
-				}
-
-				var fileMoveData = dataTransfer.getData(dragKey);
-				if (fileMoveData.length > 0) {
-					try {
-						var unser = haxe.Json.parse(fileMoveData);
-						for (file in (unser:Array<String>)) {
-							var rel = ide.getRelPath(file);
-							files.push(rel);
-						}
-					} catch (e) {
-						trace("Invalid data " + e);
-					}
-				}
-
+				var fileEntries : Array<FileEntry> = cast ide.getData("drag/filetree");
+				var files = [ for (f in fileEntries) f.path ];
 				if (files.length == 0)
 					return false;
 
@@ -580,7 +547,7 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 		fancyGallery.dragAndDropInterface = {
 			onDragStart: (item: FileEntry, dataTransfer: js.html.DataTransfer) -> {
 				var selection = getItemAndSelection(item, true);
-				dataTransfer.setData(dragKey, haxe.Json.stringify([for (item in selection) item.getPath()]));
+				ide.setData("drag/filetree", cast selection);
 				return true;
 			}
 		}