瀏覽代碼

filetree new/delete context menu

Nicolas Cannasse 8 年之前
父節點
當前提交
64b37caff0
共有 9 個文件被更改,包括 144 次插入18 次删除
  1. 11 11
      bin/style.css
  2. 0 1
      hide.hxproj
  3. 37 0
      hide/comp/ContextMenu.hx
  4. 29 1
      hide/comp/IconTree.hx
  5. 7 0
      hide/ui/Ide.hx
  6. 55 4
      hide/view/FileTree.hx
  7. 1 0
      hide/view/Particles3D.hx
  8. 1 0
      libs/nw/Menu.hx
  9. 3 1
      libs/nw/MenuItem.hx

+ 11 - 11
bin/style.css

@@ -7,7 +7,7 @@ body {
 ul, li {
 	margin : 0;
 	padding : 0;
-	list-style : none;	
+	list-style : none;
 }
 
 body, td, th, li, p, a, input, select {
@@ -32,13 +32,13 @@ a {
 	width: 5px;
 	height: 5px;
 }
- 
+
 .hide-scrollzone::-webkit-scrollbar-track {
 	background: #666;
 }
- 
+
 .hide-scrollzone::-webkit-scrollbar-thumb {
-	background: #ddd; 
+	background: #ddd;
 }
 
 .hide-loading {
@@ -97,7 +97,7 @@ a {
 	padding : 2px;
 	text-align: center;
 	border-radius: 1px;
-	text-shadow: 1px 2px 2px black; 
+	text-shadow: 1px 2px 2px black;
 	background: -webkit-linear-gradient(top, rgb(90,90,90), rgb(50,50,50));
 	cursor : pointer;
 	font-size: 16px;
@@ -111,12 +111,12 @@ a {
 .hide-toolbar-button:hover, .hide-toolbar-toggle:hover {
 	color : white;
 	border-color : white;
-	background-color : #666;	
+	background-color : #666;
 }
 
 .hide-toolbar-toggle.toggled {
-	color : #ddd;	
-	background : #777;	
+	color : #ddd;
+	background : #777;
 	padding-top: 2px;
 	padding-bottom: 1px;
 	text-shadow: none;
@@ -139,7 +139,7 @@ a {
 
 .hide-toolbar-select .hide-toolbar-icon {
 	font-size: 16px;
-	vertical-align: top;	
+	vertical-align: top;
 }
 
 .hide-toolbar-select > select {
@@ -148,7 +148,7 @@ a {
 }
 
 select {
-	background-color: rgb(34,34,34);	
+	background-color: rgb(34,34,34);
 }
 
 /* Golden Layout Fixes */
@@ -161,7 +161,7 @@ select {
 	box-shadow : none;
 }
 div.lm_close_tab { top : 0px !important; right : 0px !important; width : 20px !important; height : 18px !important; }
-div.lm_close_tab:hover { opacity : 0.7 !important; }
+div.lm_close_tab:hover { background-color : #555 !important; }
 
 /* JSTree fixes */
 

+ 0 - 1
hide.hxproj

@@ -36,7 +36,6 @@
   </compileTargets>
   <!-- Paths to exclude from the Project Explorer tree -->
   <hiddenPaths>
-    <hidden path="bin\style.css" />
     <hidden path="obj" />
     <hidden path="bin\ffmpegsumo.dll" />
     <hidden path="bin\libEGL.dll" />

+ 37 - 0
hide/comp/ContextMenu.hx

@@ -0,0 +1,37 @@
+package hide.comp;
+
+typedef ContextMenuItem = {
+	var label : String;
+	@:optional var menu : Array<ContextMenuItem>;
+	@:optional var click : Void -> Void;
+	@:optional var enabled : Bool;
+	@:optional var checked : Bool;
+	@:optional var isSeparator : Bool;
+}
+
+class ContextMenu {
+
+	public function new( config : Array<ContextMenuItem> ) {
+		var menu = makeMenu(config);
+		var ide = hide.ui.Ide.inst;
+		menu.popup(ide.mouseX, ide.mouseY);
+	}
+
+	function makeMenu( config : Array<ContextMenuItem> ) {
+		var m = new nw.Menu({type:ContextMenu});
+		for( i in config )
+			m.append(makeMenuItem(i));
+		return m;
+	}
+
+	function makeMenuItem(i:ContextMenuItem) {
+		var mconf : nw.MenuItem.MenuItemOptions = { label : i.label, type : i.checked ? Checkbox : i.isSeparator ? Separator : Normal };
+		if( i.menu != null ) mconf.submenu = makeMenu(i.menu);
+		var m = new nw.MenuItem(mconf);
+		if( i.checked != null ) m.checked = i.checked;
+		if( i.enabled != null ) m.enabled = i.enabled;
+		m.click = i.click;
+		return m;
+	}
+
+}

+ 29 - 1
hide/comp/IconTree.hx

@@ -14,6 +14,8 @@ typedef IconTreeItem = {
 
 class IconTree extends Component {
 
+	var waitRefresh = new Array<Void->Void>();
+
 	public dynamic function get( id : String ) : Array<IconTreeItem> {
 		return [{ id : id+"0", text : "get()", children : true }];
 	}
@@ -49,6 +51,32 @@ class IconTree extends Component {
 		root.on("close_node.jstree", function(event,e) {
 			onToggle(e.node.id, false);
 		});
+		root.on("refresh.jstree", function(_) {
+			var old = waitRefresh;
+			waitRefresh = [];
+			for( f in old ) f();
+		});
+	}
+
+	public function getCurrentOver() : Null<String> {
+		var id = root.find(":focus").attr("id");
+		if( id != null )
+			id = id.substr(0, -7); // remove _anchor
+		return id;
+	}
+
+	public function setSelection( ids : Array<String> ) {
+		(untyped root.jstree)('deselect_all');
+		(untyped root.jstree)('select_node',ids);
 	}
-	
+
+	public function refresh( ?onReady : Void -> Void ) {
+		if( onReady != null ) waitRefresh.push(onReady);
+		(untyped root.jstree)('refresh',true);
+	}
+
+	public function getSelection() : Array<String> {
+		return (untyped root.jstree)('get_selected');
+	}
+
 }

+ 7 - 0
hide/ui/Ide.hx

@@ -7,6 +7,9 @@ class Ide {
 	public var resourceDir(get,never) : String;
 	public var initializing(default,null) : Bool;
 
+	public var mouseX : Int = 0;
+	public var mouseY : Int = 0;
+
 	var window : nw.Window;
 
 	var layout : golden.Layout;
@@ -33,6 +36,10 @@ class Ide {
 		window.show(true);
 
 		setProject(props.global.currentProject);
+		window.window.document.addEventListener("mousemove", function(e) {
+			mouseX = e.x;
+			mouseY = e.y;
+		});
 		window.on('maximize', function() { maximized = true; onWindowChange(); });
 		window.on('restore', function() { maximized = false; onWindowChange(); });
 		window.on('move', function() haxe.Timer.delay(onWindowChange,100));

+ 55 - 4
hide/view/FileTree.hx

@@ -5,6 +5,12 @@ typedef ExtensionOptions = {
 	?createNew : String,
 };
 
+typedef ExtensionDesc = {
+	var component : String;
+	var extensions : Array<String>;
+	var options : ExtensionOptions;
+}
+
 class FileTree extends hide.ui.View<{ root : String, opened : Array<String> }> {
 
 	var tree : hide.comp.IconTree;
@@ -99,13 +105,36 @@ class FileTree extends hide.ui.View<{ root : String, opened : Array<String> }> {
 					lastOpen = null;
 				},1000);
 		});
-
+		e.contextmenu(function(e) {
+			var current = tree.getCurrentOver();
+			if( current == null ) return;
+			tree.setSelection([current]);
+			e.preventDefault();
+			new hide.comp.ContextMenu([
+				{ label : "New..", menu:[for( e in EXTENSIONS ) if( e.options.createNew != null ) { label : e.options.createNew, click : createNew.bind(current, e) }] },
+				{ label : "Delete", click : function() if( js.Browser.window.confirm("Delete " + current + "?") ) { onDeleteFile(current); tree.refresh(); } },
+			]);
+		});
 		tree.onDblClick = onOpenFile;
 		tree.init();
 	}
 
+	function onDeleteFile( path : String ) {
+		var fullPath = getPath(path);
+		if( sys.FileSystem.isDirectory(fullPath) ) {
+			for( f in sys.FileSystem.readDirectory(fullPath) )
+				onDeleteFile(path + "/" + f);
+			sys.FileSystem.deleteDirectory(fullPath);
+		} else
+			sys.FileSystem.deleteFile(fullPath);
+	}
+
+	function getPath(path:String) {
+		return ide.getPath(state.root) + path;
+	}
+
 	function onOpenFile( path : String ) {
-		var fullPath = ide.getPath(state.root) + path;
+		var fullPath = getPath(path);
 		if( sys.FileSystem.isDirectory(fullPath) )
 			return;
 		var ext = getExtension(fullPath);
@@ -119,17 +148,39 @@ class FileTree extends hide.ui.View<{ root : String, opened : Array<String> }> {
 		});
 	}
 
+	function createNew( basePath : String, ext : ExtensionDesc ) {
+		var fullPath = getPath(basePath);
+		if( !sys.FileSystem.isDirectory(fullPath) ) {
+			basePath = new haxe.io.Path(basePath).dir;
+			fullPath = getPath(basePath);
+		}
+		var file = js.Browser.window.prompt(ext.options.createNew + " name:");
+		if( file == null ) return;
+		if( file.indexOf(".") < 0 ) file += "." + ext.extensions[0].split(".").shift();
+
+		if( sys.FileSystem.exists(fullPath + "/" + file) ) {
+			js.Browser.alert("File '" + file+"' already exists");
+			createNew(basePath, ext);
+			return;
+		}
+
+		var view : hide.view.FileView = Type.createEmptyInstance(Type.resolveClass(ext.component));
+		sys.io.File.saveBytes(fullPath + "/" + file, view.getDefaultContent());
+		tree.refresh(function() tree.setSelection([basePath + "/" + file]));
+		onOpenFile(basePath+"/"+file);
+	}
+
 	function isIgnored( path : String, file : String ) {
 		if( file.charCodeAt(0) == ".".code )
 			return true;
 		return false;
 	}
 
-	static var EXTENSIONS = new Map<String,{ component : String, options : ExtensionOptions }>();
+	static var EXTENSIONS = new Map<String,ExtensionDesc>();
 	public static function registerExtension<T>( c : Class<hide.ui.View<T>>, extensions : Array<String>, ?options : ExtensionOptions ) {
 		hide.ui.View.register(c);
 		if( options == null ) options = {};
-		var obj = { component : Type.getClassName(c), options : options };
+		var obj = { component : Type.getClassName(c), options : options, extensions : extensions };
 		for( e in extensions )
 			EXTENSIONS.set(e, obj);
 		return null;

+ 1 - 0
hide/view/Particles3D.hx

@@ -23,6 +23,7 @@ class Particles3D extends FileView {
 
 	override function getDefaultContent() {
 		var p = new h3d.parts.GpuParticles();
+		p.addGroup().name = "Default";
 		return haxe.io.Bytes.ofString(haxe.Json.stringify(p.save(),"\t"));
 	}
 

+ 1 - 0
libs/nw/Menu.hx

@@ -9,5 +9,6 @@ extern class Menu {
 
 	public function new( ?options : { ?label : String, ?type : MenuType } ) : Void;
 	public function append( m : MenuItem ) : Void;
+	public function popup( x : Int, y : Int ) : Void;
 
 }

+ 3 - 1
libs/nw/MenuItem.hx

@@ -6,11 +6,13 @@ package nw;
 	var Separator = "separator";
 }
 
+typedef MenuItemOptions = { label : String, ?icon : String, ?type : MenuItemType, ?submenu : Menu };
+
 extern class MenuItem {
 
 	public var checked : Bool;
 	public var enabled : Bool;
 
-	public function new( options : { label : String, ?icon : String, ?type : MenuItemType, ?submenu : Menu } ) : Void;
+	public function new( options : MenuItemOptions ) : Void;
 	public dynamic function click() : Void;
 }