浏览代码

window save/restore pos

Nicolas Cannnasse 8 年之前
父节点
当前提交
dfac76298f
共有 10 个文件被更改,包括 569 次插入227 次删除
  1. 14 1
      bin/app.html
  2. 276 75
      bin/hide.js
  3. 2 1
      bin/package.json
  4. 1 1
      hide.hxml
  5. 218 0
      hide/ui/Ide.hx
  6. 0 143
      hide/ui/Main.hx
  7. 19 6
      hide/ui/Props.hx
  8. 2 0
      hide/ui/View.hx
  9. 17 0
      hide/view/FileTree.hx
  10. 20 0
      libs/nw/Window.hx

+ 14 - 1
bin/app.html

@@ -7,7 +7,20 @@
 <link rel="stylesheet" type="text/css" href="libs/goldenlayout-light-theme.css"/>
 <link rel="stylesheet" type="text/css" href="style.css"/>
 <xml id="mainmenu">
-	<menu label="View">
+	<menu label="Project" class="project">
+		<menu label="Open..." class="open"></menu>
+		<menu label="Recently opened">
+			<div class="recents">
+				<menu label="Empty" disabled="disabled"></menu>
+			</div>
+			<separator></separator>
+			<menu label="Clear Recents" class="clear"></menu>
+		</menu>
+		<separator></separator>
+		<menu label="Exit" class="exit"></menu>
+	</menu>
+	<menu label="View" class="view">
+		<menu label="Resources" component="hide.view.FileTree" state='{"root":""}'></menu>
 		<separator></separator>
 		<menu label="About" component="hide.view.About"></menu>
 		<menu label="Debug" class="debug"></menu>

+ 276 - 75
bin/hide.js

@@ -8,6 +8,29 @@ function $extend(from, fields) {
 	if( fields.toString !== Object.prototype.toString ) proto.toString = fields.toString;
 	return proto;
 }
+var HxOverrides = function() { };
+$hxClasses["HxOverrides"] = HxOverrides;
+HxOverrides.__name__ = ["HxOverrides"];
+HxOverrides.substr = function(s,pos,len) {
+	if(len == null) {
+		len = s.length;
+	} else if(len < 0) {
+		if(pos == 0) {
+			len = s.length + len;
+		} else {
+			return "";
+		}
+	}
+	return s.substr(pos,len);
+};
+HxOverrides.remove = function(a,obj) {
+	var i = a.indexOf(obj);
+	if(i == -1) {
+		return false;
+	}
+	a.splice(i,1);
+	return true;
+};
 Math.__name__ = ["Math"];
 var Reflect = function() { };
 $hxClasses["Reflect"] = Reflect;
@@ -38,6 +61,31 @@ Reflect.deleteField = function(o,field) {
 	delete(o[field]);
 	return true;
 };
+var Std = function() { };
+$hxClasses["Std"] = Std;
+Std.__name__ = ["Std"];
+Std.string = function(s) {
+	return js_Boot.__string_rec(s,"");
+};
+var StringTools = function() { };
+$hxClasses["StringTools"] = StringTools;
+StringTools.__name__ = ["StringTools"];
+StringTools.startsWith = function(s,start) {
+	if(s.length >= start.length) {
+		return HxOverrides.substr(s,0,start.length) == start;
+	} else {
+		return false;
+	}
+};
+StringTools.endsWith = function(s,end) {
+	var elen = end.length;
+	var slen = s.length;
+	if(slen >= elen) {
+		return HxOverrides.substr(s,slen - elen,elen) == end;
+	} else {
+		return false;
+	}
+};
 var Type = function() { };
 $hxClasses["Type"] = Type;
 Type.__name__ = ["Type"];
@@ -133,6 +181,21 @@ haxe_io_Bytes.__name__ = ["haxe","io","Bytes"];
 haxe_io_Bytes.prototype = {
 	__class__: haxe_io_Bytes
 };
+var haxe_io_Path = function() { };
+$hxClasses["haxe.io.Path"] = haxe_io_Path;
+haxe_io_Path.__name__ = ["haxe","io","Path"];
+haxe_io_Path.isAbsolute = function(path) {
+	if(StringTools.startsWith(path,"/")) {
+		return true;
+	}
+	if(path.charAt(1) == ":") {
+		return true;
+	}
+	if(StringTools.startsWith(path,"\\\\")) {
+		return true;
+	}
+	return false;
+};
 var hide_HTypeDef = { __ename__ : true, __constructs__ : ["TId","TInt","TBool","TFloat","TString","TAlias","TArray","TEither","TFlags","TFile","TTile","TDynamic","TStruct","TEnum"] };
 hide_HTypeDef.TId = ["TId",0];
 hide_HTypeDef.TId.__enum__ = hide_HTypeDef;
@@ -164,7 +227,7 @@ hide_HTypeProp.PIsColor.__enum__ = hide_HTypeProp;
 var hide_Macros = function() { };
 $hxClasses["hide.Macros"] = hide_Macros;
 hide_Macros.__name__ = ["hide","Macros"];
-var hide_ui_Main = function() {
+var hide_ui_Ide = function() {
 	var _hide_HType = { def : null};
 	var _hide_HTypeDef = { def : null};
 	var _tmp = { def : hide_HTypeDef.TFlags(["PIsColor","PNull"])};
@@ -173,19 +236,58 @@ var hide_ui_Main = function() {
 	_hide_HType.def = inlobj_def;
 	_hide_HTypeDef.def = hide_HTypeDef.TEnum([{ name : "TId", args : []},{ name : "TInt", args : []},{ name : "TBool", args : []},{ name : "TFloat", args : []},{ name : "TString", args : []},{ name : "TAlias", args : [{ name : "name", t : { def : hide_HTypeDef.TString}},{ name : "t", t : _hide_HType}]},{ name : "TArray", args : [{ name : "t", t : _hide_HType}]},{ name : "TEither", args : [{ name : "values", t : { def : hide_HTypeDef.TArray({ def : hide_HTypeDef.TString})}}]},{ name : "TFlags", args : [{ name : "values", t : { def : hide_HTypeDef.TArray({ def : hide_HTypeDef.TString})}}]},{ name : "TFile", args : []},{ name : "TTile", args : []},{ name : "TDynamic", args : []},{ name : "TStruct", args : [{ name : "fields", t : { def : hide_HTypeDef.TArray({ def : hide_HTypeDef.TStruct([{ name : "name", t : { def : hide_HTypeDef.TString}},{ name : "t", t : _hide_HType}])})}}]},{ name : "TEnum", args : [{ name : "constructors", t : { def : hide_HTypeDef.TArray({ def : hide_HTypeDef.TStruct([{ name : "args", t : { def : hide_HTypeDef.TArray({ def : hide_HTypeDef.TStruct([{ name : "name", t : { def : hide_HTypeDef.TString}},{ name : "t", t : _hide_HType}])})}},{ name : "name", t : { def : hide_HTypeDef.TString}}])})}}]}]);
 	this.typeDef = _hide_HType;
-	this.props = new hide_ui_Props();
-	this.props.load();
+	var _gthis = this;
+	hide_ui_Ide.inst = this;
 	this.window = nw.Window.get();
-	this.initMenu();
-	this.initLayout();
+	this.props = new hide_ui_Props(process.cwd());
+	if(this.props.global.currentProject == null) {
+		this.props.global.currentProject = process.cwd();
+	}
+	var wp = this.props.global.windowPos;
+	if(wp != null) {
+		this.window.resizeBy(wp.w - (this.window.window.outerWidth | 0),wp.h - (this.window.window.outerHeight | 0));
+		this.window.moveTo(wp.x,wp.y);
+		if(wp.max) {
+			this.window.maximize();
+		}
+	}
+	this.window.show(true);
+	this.setProject(this.props.global.currentProject);
+	this.window.on("maximize",function() {
+		_gthis.maximized = true;
+		_gthis.onWindowChange();
+	});
+	this.window.on("restore",function() {
+		_gthis.maximized = false;
+		_gthis.onWindowChange();
+	});
+	this.window.on("move",function() {
+		haxe_Timer.delay($bind(_gthis,_gthis.onWindowChange),100);
+	});
+	this.window.on("resize",function() {
+		haxe_Timer.delay($bind(_gthis,_gthis.onWindowChange),100);
+	});
 };
-$hxClasses["hide.ui.Main"] = hide_ui_Main;
-hide_ui_Main.__name__ = ["hide","ui","Main"];
-hide_ui_Main.main = function() {
-	new hide_ui_Main();
+$hxClasses["hide.ui.Ide"] = hide_ui_Ide;
+hide_ui_Ide.__name__ = ["hide","ui","Ide"];
+hide_ui_Ide.main = function() {
+	new hide_ui_Ide();
 };
-hide_ui_Main.prototype = {
-	initLayout: function(state) {
+hide_ui_Ide.prototype = {
+	onWindowChange: function() {
+		if(this.props.global.windowPos == null) {
+			this.props.global.windowPos = { x : 0, y : 0, w : 0, h : 0, max : false};
+		}
+		this.props.global.windowPos.max = this.maximized;
+		if(!this.maximized) {
+			this.props.global.windowPos.x = this.window.x;
+			this.props.global.windowPos.y = this.window.y;
+			this.props.global.windowPos.w = this.window.window.outerWidth | 0;
+			this.props.global.windowPos.h = this.window.window.outerHeight | 0;
+		}
+		this.props.saveGlobals();
+	}
+	,initLayout: function(state) {
 		var _gthis = this;
 		if(this.layout != null) {
 			this.layout.destroy();
@@ -248,77 +350,139 @@ hide_ui_Main.prototype = {
 	,saveLayout: function() {
 		return this.layout.toConfig().content;
 	}
+	,get_projectDir: function() {
+		return this.props.global.currentProject;
+	}
+	,get_resourceDir: function() {
+		return this.props.global.currentProject + "/res";
+	}
+	,setProject: function(dir) {
+		if(dir != this.props.global.currentProject) {
+			this.props.global.currentProject = dir;
+			if(this.props.global.recentProjects == null) {
+				this.props.global.recentProjects = [];
+			}
+			HxOverrides.remove(this.props.global.recentProjects,dir);
+			this.props.global.recentProjects.unshift(dir);
+			if(this.props.global.recentProjects.length > 10) {
+				this.props.global.recentProjects.pop();
+			}
+			this.props.save();
+		}
+		this.window.title = "HIDE - " + dir;
+		this.props = new hide_ui_Props(dir);
+		this.initMenu();
+		this.initLayout();
+	}
 	,initMenu: function() {
 		var _gthis = this;
-		var firstInit = false;
 		if(this.menu == null) {
-			this.menu = $("#mainmenu");
-			firstInit = true;
+			this.menu = $($("#mainmenu").get(0).outerHTML);
+		}
+		if(this.props.current.recentProjects.length > 0) {
+			this.menu.find(".project .recents").html("");
 		}
-		var layouts = this.menu.find(".layout .content");
-		layouts.html("");
 		var _g = 0;
-		var _g1 = this.props.current.layouts;
+		var _g1 = this.props.current.recentProjects.slice();
 		while(_g < _g1.length) {
-			var l = [_g1[_g]];
+			var v = [_g1[_g]];
 			++_g;
+			if(!sys_FileSystem.exists(v[0])) {
+				HxOverrides.remove(this.props.current.recentProjects,v[0]);
+				this.props.save();
+				continue;
+			}
+			$("<menu>").attr("label",v[0]).appendTo(this.menu.find(".project .recents")).click((function(v1) {
+				return function(_) {
+					_gthis.setProject(v1[0]);
+				};
+			})(v));
+		}
+		this.menu.find(".project .open").click(function(_1) {
+			$("<input type=\"file\" nwdirectory/>").change(function(e) {
+				var dir = $(this).val();
+				if(StringTools.endsWith(dir,"/res") || StringTools.endsWith(dir,"\\res")) {
+					dir = HxOverrides.substr(dir,0,-4);
+				}
+				_gthis.setProject(dir);
+			}).click();
+		});
+		this.menu.find(".project .clear").click(function(_2) {
+			_gthis.props.global.recentProjects = [];
+			_gthis.props.save();
+			_gthis.initMenu();
+		});
+		this.menu.find(".project .exit").click(function(_3) {
+			process.exit(0);
+		});
+		this.menu.find(".debug").click(function(_4) {
+			_gthis.window.showDevTools();
+		});
+		var comps = this.menu.find("[component]");
+		var _g_i = 0;
+		var _g_j = comps;
+		while(_g_i < _g_j.length) {
+			var c = $(_g_j[_g_i++]);
+			var cname = [c.attr("component")];
+			var cl = Type.resolveClass(cname[0]);
+			if(cl == null) {
+				js_Browser.alert("Missing component class " + cname[0]);
+			}
+			var state = [c.attr("state")];
+			if(state[0] != null) {
+				try {
+					JSON.parse(state[0]);
+				} catch( e1 ) {
+					if (e1 instanceof js__$Boot_HaxeError) e1 = e1.val;
+					js_Browser.alert("Invalid state " + state[0] + " (" + Std.string(e1) + ")");
+				}
+			}
+			c.click((function(state1,cname1) {
+				return function(_5) {
+					if(_gthis.layout.root.contentItems.length == 0) {
+						_gthis.layout.root.addChild({ type : "row"});
+					}
+					_gthis.layout.root.contentItems[0].addChild({ type : "component", componentName : cname1[0], componentState : state1[0] == null ? null : JSON.parse(state1[0])});
+				};
+			})(state,cname));
+		}
+		var layouts = this.menu.find(".layout .content");
+		layouts.html("");
+		var _g2 = 0;
+		var _g11 = this.props.current.layouts;
+		while(_g2 < _g11.length) {
+			var l = [_g11[_g2]];
+			++_g2;
 			if(l[0].name == "Default") {
 				continue;
 			}
 			$("<menu>").attr("label",l[0].name).addClass(l[0].name).appendTo(layouts).click((function(l1) {
-				return function(_) {
+				return function(_6) {
 					_gthis.initLayout(l1[0]);
 				};
 			})(l));
 		}
-		if(firstInit) {
-			this.menu.find(".layout .autosave").click(function(_1) {
-				_gthis.props.local.autoSaveLayout = !_gthis.props.local.autoSaveLayout;
-				_gthis.props.save();
-			});
-			this.menu.find(".layout .saveas").click(function(_2) {
-				var name = window.prompt("Please enter a layout name:");
-				if(name == null || name == "") {
-					return;
-				}
-				_gthis.props.local.layouts.push({ name : name, state : _gthis.saveLayout()});
-				_gthis.props.save();
-				_gthis.initMenu();
-			});
-			this.menu.find(".layout .save").click(function(_3) {
-				_gthis.currentLayout.state = _gthis.saveLayout();
-				_gthis.props.save();
-			});
-		}
 		var tmp = this.props.local.autoSaveLayout ? "checked" : "";
-		this.menu.find(".layout .autosave").attr("checked",tmp);
-		if(firstInit) {
-			this.menu.find(".debug").click(function(_4) {
-				_gthis.window.showDevTools();
-			});
-			var comps = this.menu.find("[component]");
-			var _g_i = 0;
-			var _g_j = comps;
-			while(_g_i < _g_j.length) {
-				var c = $(_g_j[_g_i++]);
-				var cname = [c.attr("component")];
-				var cl = Type.resolveClass(cname[0]);
-				if(cl == null) {
-					js_Browser.alert("Missing component class " + cname[0]);
-				}
-				c.click((function(cname1) {
-					return function(_5) {
-						if(_gthis.layout.root.contentItems.length == 0) {
-							_gthis.layout.root.addChild({ type : "row"});
-						}
-						_gthis.layout.root.contentItems[0].addChild({ type : "component", componentName : cname1[0]});
-					};
-				})(cname));
+		this.menu.find(".layout .autosave").click(function(_7) {
+			_gthis.props.local.autoSaveLayout = !_gthis.props.local.autoSaveLayout;
+			_gthis.props.save();
+		}).attr("checked",tmp);
+		this.menu.find(".layout .saveas").click(function(_8) {
+			var name = window.prompt("Please enter a layout name:");
+			if(name == null || name == "") {
+				return;
 			}
-		}
+			_gthis.props.local.layouts.push({ name : name, state : _gthis.saveLayout()});
+			_gthis.props.save();
+			_gthis.initMenu();
+		});
+		this.menu.find(".layout .save").click(function(_9) {
+			_gthis.currentLayout.state = _gthis.saveLayout();
+			_gthis.props.save();
+		});
 		this.window.menu = new hide_ui_Menu(this.menu).root;
 	}
-	,__class__: hide_ui_Main
+	,__class__: hide_ui_Ide
 };
 var hide_ui_Menu = function(menu) {
 	this.root = new nw.Menu({ type : "menubar"});
@@ -389,14 +553,15 @@ hide_ui_Menu.prototype = {
 	}
 	,__class__: hide_ui_Menu
 };
-var hide_ui_Props = function() {
+var hide_ui_Props = function(projectDir) {
 	var name = "hideProps.json";
 	var path = process.argv[0].split("\\").join("/").split("/");
 	path.pop();
 	var globalPath = path.join("/") + "/" + name;
-	var projectPath = process.cwd() + "/" + name;
-	var localPath = projectPath.split("\\").join("/").toLowerCase();
+	var projectPath = projectDir + "/" + name;
+	var localPath = projectDir.split("\\").join("/").toLowerCase();
 	this.paths = { global : globalPath, local : localPath, project : projectPath};
+	this.load();
 };
 $hxClasses["hide.ui.Props"] = hide_ui_Props;
 hide_ui_Props.__name__ = ["hide","ui","Props"];
@@ -435,26 +600,29 @@ hide_ui_Props.prototype = {
 		this.sync();
 	}
 	,sync: function() {
-		this.current = { autoSaveLayout : true, layouts : []};
+		this.current = { autoSaveLayout : true, layouts : [], currentProject : null, recentProjects : [], windowPos : null};
 		this.merge(this.global);
 		this.merge(this.project);
 		this.merge(this.local);
 	}
 	,save: function() {
 		this.sync();
-		var str = JSON.stringify(this.global);
-		js_node_Fs.writeFileSync(this.paths.global,str);
-		var str1 = JSON.stringify(this.project);
-		if(str1 == "{}") {
+		this.saveGlobals();
+		var str = JSON.stringify(this.project);
+		if(str == "{}") {
 			try {
 				js_node_Fs.unlinkSync(this.paths.project);
 			} catch( e ) {
 			}
 		} else {
-			js_node_Fs.writeFileSync(this.paths.project,str1);
+			js_node_Fs.writeFileSync(this.paths.project,str);
 		}
-		var str2 = JSON.stringify(this.local);
-		window.localStorage.setItem(this.paths.local,str2);
+		var str1 = JSON.stringify(this.local);
+		window.localStorage.setItem(this.paths.local,str1);
+	}
+	,saveGlobals: function() {
+		var str = JSON.stringify(this.global);
+		js_node_Fs.writeFileSync(this.paths.global,str);
 	}
 	,merge: function(props) {
 		var _g = 0;
@@ -478,6 +646,7 @@ hide_ui_Props.prototype = {
 };
 var hide_ui_View = function(state) {
 	this.state = state;
+	this.ide = hide_ui_Ide.inst;
 };
 $hxClasses["hide.ui.View"] = hide_ui_View;
 hide_ui_View.__name__ = ["hide","ui","View"];
@@ -507,6 +676,24 @@ hide_view_About.prototype = $extend(hide_ui_View.prototype,{
 	}
 	,__class__: hide_view_About
 });
+var hide_view_FileTree = function(state) {
+	hide_ui_View.call(this,state);
+};
+$hxClasses["hide.view.FileTree"] = hide_view_FileTree;
+hide_view_FileTree.__name__ = ["hide","view","FileTree"];
+hide_view_FileTree.__super__ = hide_ui_View;
+hide_view_FileTree.prototype = $extend(hide_ui_View.prototype,{
+	getPath: function() {
+		if(haxe_io_Path.isAbsolute(this.state.root)) {
+			return this.state.root;
+		}
+		return this.ide.get_resourceDir() + "/" + this.state.root;
+	}
+	,onDisplay: function(j) {
+		j.text(this.getPath());
+	}
+	,__class__: hide_view_FileTree
+});
 var js__$Boot_HaxeError = function(val) {
 	Error.call(this);
 	this.val = val;
@@ -648,6 +835,19 @@ js_Browser.alert = function(v) {
 };
 var js_node_Fs = require("fs");
 var js_node_buffer_Buffer = require("buffer").Buffer;
+var sys_FileSystem = function() { };
+$hxClasses["sys.FileSystem"] = sys_FileSystem;
+sys_FileSystem.__name__ = ["sys","FileSystem"];
+sys_FileSystem.exists = function(path) {
+	try {
+		js_node_Fs.accessSync(path);
+		return true;
+	} catch( _ ) {
+		return false;
+	}
+};
+var $_, $fid = 0;
+function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $fid++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; o.hx__closures__[m.__id__] = f; } return f; }
 $hxClasses["Math"] = Math;
 String.prototype.__class__ = $hxClasses["String"] = String;
 String.__name__ = ["String"];
@@ -656,6 +856,7 @@ Array.__name__ = ["Array"];
 var __map_reserved = {};
 hide_ui_View.viewClasses = [];
 hide_view_About._ = hide_ui_View.register(hide_view_About);
+hide_view_FileTree._ = hide_ui_View.register(hide_view_FileTree);
 js_Boot.__toStr = ({ }).toString;
-hide_ui_Main.main();
+hide_ui_Ide.main();
 })(typeof window != "undefined" ? window : typeof global != "undefined" ? global : typeof self != "undefined" ? self : this);

+ 2 - 1
bin/package.json

@@ -4,7 +4,8 @@
 		"title" : "HIDE",
 		"icon" : "icon.png",
 		"width" : 800,
-		"height" : 600
+		"height" : 600,
+		"show" : false
 	},
 	"main" : "app.html"
 }

+ 1 - 1
hide.hxml

@@ -1,5 +1,5 @@
 -cp libs
 -js bin/hide.js
--main hide.ui.Main
+-main hide.ui.Ide
 -lib hxnodejs
 --macro include('hide.view')

+ 218 - 0
hide/ui/Ide.hx

@@ -0,0 +1,218 @@
+package hide.ui;
+import js.jquery.Helper.*;
+
+class Ide {
+
+	public var props : Props;
+	public var projectDir(get,never) : String;
+	public var resourceDir(get,never) : String;
+
+	var window : nw.Window;
+
+	var layout : golden.Layout;
+	var types : Map<String,hide.HType>;
+	var typeDef = Macros.makeTypeDef(hide.HType);
+	
+	var menu : js.jquery.JQuery;
+	var currentLayout : { name : String, state : Dynamic };
+	var maximized : Bool;
+
+	function new() {
+		inst = this;
+		window = nw.Window.get();
+		props = new Props(Sys.getCwd());
+		if( props.global.currentProject == null ) props.global.currentProject = Sys.getCwd();
+
+		var wp = props.global.windowPos;
+		if( wp != null ) {
+			window.resizeBy(wp.w - Std.int(window.window.outerWidth), wp.h - Std.int(window.window.outerHeight));
+			window.moveTo(wp.x, wp.y);
+			if( wp.max ) window.maximize();
+		}
+		window.show(true);
+	
+		setProject(props.global.currentProject);
+		window.on('maximize', function() { maximized = true; onWindowChange(); });
+		window.on('restore', function() { maximized = false; onWindowChange(); });
+		window.on('move', function() haxe.Timer.delay(onWindowChange,100));
+		window.on('resize', function() haxe.Timer.delay(onWindowChange,100));
+	}
+
+	function onWindowChange() {
+		if( props.global.windowPos == null ) props.global.windowPos = { x : 0, y : 0, w : 0, h : 0, max : false };
+		props.global.windowPos.max = maximized;
+		if( !maximized ) {
+			props.global.windowPos.x = window.x;
+			props.global.windowPos.y = window.y;
+			props.global.windowPos.w = Std.int(window.window.outerWidth);
+			props.global.windowPos.h = Std.int(window.window.outerHeight);
+		}
+		props.saveGlobals();	
+	}
+
+	function initLayout( ?state : { name : String, state : Dynamic } ) {
+
+		if( layout != null ) {
+			layout.destroy();
+			layout = null;
+		}
+
+		var defaultLayout = null;
+		for( p in props.current.layouts )
+			if( p.name == "Default" ) {
+				defaultLayout = p;
+				break;
+			}
+		if( defaultLayout == null ) {
+			defaultLayout = { name : "Default", state : [] };
+			if( props.local.layouts == null ) props.local.layouts = [];
+			props.local.layouts.push(defaultLayout);
+			props.save();
+		}
+		if( state == null )
+			state = defaultLayout;
+
+		this.currentLayout = state;
+
+		var config : golden.Config = {
+			content: state.state,
+		};
+		layout = new golden.Layout(config);
+
+		for( cl in @:privateAccess View.viewClasses )
+			layout.registerComponent(Type.getClassName(cl),function(cont,state) {
+				var view = Type.createInstance(cl,[state]);
+				cont.setTitle(view.getTitle());
+				view.onDisplay(cont.getElement());
+			});
+
+		layout.init();
+		layout.on('stateChanged', function() {
+			if( !props.current.autoSaveLayout )
+				return;
+			defaultLayout.state = saveLayout();
+			props.save();
+		});
+
+		// error recovery if invalid component
+		haxe.Timer.delay(function() {
+			if( layout.isInitialised ) return;
+			state.state = [];
+			initLayout();
+		}, 1000);
+	}
+
+	function saveLayout() {
+		return layout.toConfig().content;
+	}
+
+
+	function get_projectDir() return props.global.currentProject;
+	function get_resourceDir() return props.global.currentProject+"/res";
+
+	function setProject( dir : String ) {
+		if( dir != props.global.currentProject ) {
+			props.global.currentProject = dir;
+			if( props.global.recentProjects == null ) props.global.recentProjects = [];
+			props.global.recentProjects.remove(dir);
+			props.global.recentProjects.unshift(dir);
+			if( props.global.recentProjects.length > 10 ) props.global.recentProjects.pop();
+			props.save();
+		}
+		window.title = "HIDE - " + dir;
+		props = new Props(dir);
+		initMenu();
+		initLayout();
+	}
+
+	function initMenu() {
+		if( menu == null )
+			menu = J(J("#mainmenu").get(0).outerHTML);
+
+		// project
+		if( props.current.recentProjects.length > 0 )
+			menu.find(".project .recents").html("");
+		for( v in props.current.recentProjects.copy() ) {
+			if( !sys.FileSystem.exists(v) ) {
+				props.current.recentProjects.remove(v);
+				props.save();
+				continue;
+			}
+			J("<menu>").attr("label",v).appendTo(menu.find(".project .recents")).click(function(_){
+				setProject(v);
+			});
+		}
+		menu.find(".project .open").click(function(_) {
+			J('<input type="file" nwdirectory/>').change(function(e) {
+				var dir = JTHIS.val();
+				if( StringTools.endsWith(dir,"/res") || StringTools.endsWith(dir,"\\res") )
+					dir = dir.substr(0,-4);
+				setProject(dir);
+			}).click();
+		});
+		menu.find(".project .clear").click(function(_) {
+			props.global.recentProjects = [];
+			props.save();
+			initMenu();
+		});
+		menu.find(".project .exit").click(function(_) {
+			Sys.exit(0);
+		});
+
+		// view
+		menu.find(".debug").click(function(_) window.showDevTools());
+		var comps = menu.find("[component]");
+		for( c in comps.elements() ) {
+			var cname = c.attr("component");
+			var cl = Type.resolveClass(cname);
+			if( cl == null ) js.Browser.alert("Missing component class "+cname);
+			var state = c.attr("state");
+			if( state != null ) try haxe.Json.parse(state) catch( e : Dynamic ) js.Browser.alert("Invalid state "+state+" ("+e+")");
+			c.click(function(_) {
+				if( layout.root.contentItems.length == 0 )
+					layout.root.addChild({ type : Row });
+				layout.root.contentItems[0].addChild({
+					type : Component,
+					componentName : cname,
+					componentState : state == null ? null : haxe.Json.parse(state),
+				});
+			});
+		}
+
+		// layout
+		var layouts = menu.find(".layout .content");
+		layouts.html("");
+		for( l in props.current.layouts ) {
+			if( l.name == "Default" ) continue;
+			J("<menu>").attr("label",l.name).addClass(l.name).appendTo(layouts).click(function(_) {
+				initLayout(l);
+			});
+		}
+		menu.find(".layout .autosave").click(function(_) {
+			props.local.autoSaveLayout = !props.local.autoSaveLayout;
+			props.save();
+		}).attr("checked",props.local.autoSaveLayout?"checked":"");
+
+		menu.find(".layout .saveas").click(function(_) {
+			var name = js.Browser.window.prompt("Please enter a layout name:");
+			if( name == null || name == "" ) return;
+			props.local.layouts.push({ name : name, state : saveLayout() });
+			props.save();
+			initMenu();
+		});
+		menu.find(".layout .save").click(function(_) {
+			currentLayout.state = saveLayout();
+			props.save();
+		});
+
+		window.menu = new Menu(menu).root;
+	}
+
+
+	public static var inst : Ide;
+
+	static function main() {
+		new Ide();
+	}
+
+}

+ 0 - 143
hide/ui/Main.hx

@@ -1,143 +0,0 @@
-package hide.ui;
-import js.jquery.Helper.*;
-
-class Main {
-
-	var window : nw.Window;
-
-	var layout : golden.Layout;
-	var types : Map<String,hide.HType>;
-	var typeDef = Macros.makeTypeDef(hide.HType);
-
-	var props : Props;
-	var menu : js.jquery.JQuery;
-	var currentLayout : { name : String, state : Dynamic };
-
-	function new() {
-		props = new Props();
-		props.load();
-		window = nw.Window.get();
-		initMenu();
-		initLayout();
-	}
-
-	function initLayout( ?state : { name : String, state : Dynamic } ) {
-
-		if( layout != null ) {
-			layout.destroy();
-			layout = null;
-		}
-
-		var defaultLayout = null;
-		for( p in props.current.layouts )
-			if( p.name == "Default" ) {
-				defaultLayout = p;
-				break;
-			}
-		if( defaultLayout == null ) {
-			defaultLayout = { name : "Default", state : [] };
-			if( props.local.layouts == null ) props.local.layouts = [];
-			props.local.layouts.push(defaultLayout);
-			props.save();
-		}
-		if( state == null )
-			state = defaultLayout;
-
-		this.currentLayout = state;
-
-		var config : golden.Config = {
-			content: state.state,
-		};
-		layout = new golden.Layout(config);
-
-		for( cl in @:privateAccess View.viewClasses )
-			layout.registerComponent(Type.getClassName(cl),function(cont,state) {
-				var view = Type.createInstance(cl,[state]);
-				cont.setTitle(view.getTitle());
-				view.onDisplay(cont.getElement());
-			});
-
-		layout.init();
-		layout.on('stateChanged', function() {
-			if( !props.current.autoSaveLayout )
-				return;
-			defaultLayout.state = saveLayout();
-			props.save();
-		});
-
-		// error recovery if invalid component
-		haxe.Timer.delay(function() {
-			if( layout.isInitialised ) return;
-			state.state = [];
-			initLayout();
-		}, 1000);
-	}
-
-	function saveLayout() {
-		return layout.toConfig().content;
-	}
-
-	function initMenu() {
-		var firstInit = false;
-
-		if( menu == null ) {
-			menu = J("#mainmenu");
-			firstInit = true;
-		}
-
-		// states
-
-		var layouts = menu.find(".layout .content");
-		layouts.html("");
-		for( l in props.current.layouts ) {
-			if( l.name == "Default" ) continue;
-			J("<menu>").attr("label",l.name).addClass(l.name).appendTo(layouts).click(function(_) {
-				initLayout(l);
-			});
-		}
-		if( firstInit ) {
-			menu.find(".layout .autosave").click(function(_) {
-				props.local.autoSaveLayout = !props.local.autoSaveLayout;
-				props.save();
-			});
-			menu.find(".layout .saveas").click(function(_) {
-				var name = js.Browser.window.prompt("Please enter a layout name:");
-				if( name == null || name == "" ) return;
-				props.local.layouts.push({ name : name, state : saveLayout() });
-				props.save();
-				initMenu();
-			});
-			menu.find(".layout .save").click(function(_) {
-				currentLayout.state = saveLayout();
-				props.save();
-			});
-		}
-		menu.find(".layout .autosave").attr("checked",props.local.autoSaveLayout?"checked":"");
-
-		// view
-		if( firstInit ) {
-			menu.find(".debug").click(function(_) window.showDevTools());
-			var comps = menu.find("[component]");
-			for( c in comps.elements() ) {
-				var cname = c.attr("component");
-				var cl = Type.resolveClass(cname);
-				if( cl == null ) js.Browser.alert("Missing component class "+cname);
-				c.click(function(_) {
-					if( layout.root.contentItems.length == 0 )
-						layout.root.addChild({ type : Row });
-					layout.root.contentItems[0].addChild({
-						type : Component,
-						componentName : cname,
-					});
-				});
-			}
-		}
-		window.menu = new Menu(menu).root;
-	}
-
-
-	static function main() {
-		new Main();
-	}
-
-}

+ 19 - 6
hide/ui/Props.hx

@@ -5,6 +5,11 @@ typedef PropsDef = {
 	public var autoSaveLayout : Null<Bool>;
 	public var layouts : Array<{ name : String, state : Dynamic }>;
 
+	public var currentProject : String;
+	public var recentProjects : Array<String>;
+
+	public var windowPos : { x : Int, y : Int, w : Int, h : Int, max : Bool };
+
 }
 
 class Props {
@@ -26,21 +31,22 @@ class Props {
 	// current merge
 	public var current : PropsDef;
 
-	public function new() {
+	public function new( projectDir : String ) {
 		var name = "hideProps.json";
 		var path = js.Node.process.argv[0].split("\\").join("/").split("/");
 		path.pop();
 		var globalPath = path.join("/") + "/" + name;
-		var projectPath = Sys.getCwd() + "/" + name;
-		var localPath = projectPath.split("\\").join("/").toLowerCase();
+		var projectPath = projectDir + "/" + name;
+		var localPath = projectDir.split("\\").join("/").toLowerCase();
 		paths = {
 			global : globalPath,
 			local : localPath,
 			project : projectPath
 		};
+		load();
 	}
 
-	public function load() {
+	function load() {
 		global = try haxe.Json.parse(sys.io.File.getContent(paths.global)) catch( e : Dynamic ) null;
 		project = try haxe.Json.parse(sys.io.File.getContent(paths.project)) catch( e : Dynamic ) null;
 		local = try haxe.Json.parse(js.Browser.window.localStorage.getItem(paths.local)) catch( e : Dynamic ) null;
@@ -54,6 +60,9 @@ class Props {
 		current = {
 			autoSaveLayout : true,
 			layouts : [],
+			currentProject : null,
+			recentProjects : [],
+			windowPos : null,
 		};
 		merge(global);
 		merge(project);
@@ -62,8 +71,7 @@ class Props {
 
 	public function save() {
 		sync();
-		var str = haxe.Json.stringify(global);
-		sys.io.File.saveContent(paths.global, str);
+		saveGlobals();
 		var str = haxe.Json.stringify(project);
 		if( str == '{}' )
 			try sys.FileSystem.deleteFile(paths.project) catch(e:Dynamic) {}
@@ -73,6 +81,11 @@ class Props {
 		js.Browser.window.localStorage.setItem(paths.local, str);
 	}
 
+	public function saveGlobals() {		
+		var str = haxe.Json.stringify(global);
+		sys.io.File.saveContent(paths.global, str);
+	}
+
 	function merge( props : PropsDef ) {
 		for( f in Reflect.fields(props) ) {
 			var v = Reflect.field(props,f);

+ 2 - 0
hide/ui/View.hx

@@ -2,10 +2,12 @@ package hide.ui;
 
 class View<T> {
 
+	var ide : Ide;
 	var state : T;
 
 	public function new(state:T) {
 		this.state = state;
+		ide = Ide.inst;
 	}
 
 	public function getTitle() {

+ 17 - 0
hide/view/FileTree.hx

@@ -0,0 +1,17 @@
+package hide.view;
+
+class FileTree extends hide.ui.View<{ root : String }> {
+
+	function getPath() {
+		if( haxe.io.Path.isAbsolute(state.root) )
+			return state.root;
+		return ide.resourceDir+"/"+state.root;
+	}
+
+	override function onDisplay( j : js.jquery.JQuery ) {
+		j.text(getPath());
+	}
+
+	static var _ = hide.ui.View.register(FileTree);
+
+}

+ 20 - 0
libs/nw/Window.hx

@@ -2,10 +2,30 @@ package nw;
 
 extern class Window {
 
+	public var x : Int;
+	public var y : Int;
+	public var width : Int;
+	public var height : Int;
+
+	public var window : js.html.Window;
+
 	public var menu : Menu;
+	public var title : String;
 
 	public function showDevTools() : Void;
 
+	public function moveTo( x : Int, y : Int ) : Void;
+	public function moveBy( dx : Int, dy : Int ) : Void;
+	public function resizeTo( w : Int, h : Int ) : Void;
+	public function resizeBy( dw : Int, dh : Int ) : Void;
+
+	public function maximize() : Void;
+	public function minimize() : Void;
+	public function restore() : Void;
+	public function on( event : String, callb : Void -> Void ) : Void;
+
+	public function show( b : Bool ) : Void;
+
 	public static function get() : Window;
 
 }