浏览代码

type definition + macro type value, layout save/load/switch, props local/global/project

Nicolas Cannnasse 8 年之前
父节点
当前提交
bcdd13bddc
共有 17 个文件被更改,包括 1020 次插入126 次删除
  1. 5 0
      .vscode/settings.json
  2. 7 0
      .vscode/tasks.json
  3. 17 0
      bin/app.html
  4. 517 32
      bin/hide.js
  5. 3 0
      bin/style.css
  6. 5 1
      bin/style.hss
  7. 1 0
      hide.hxml
  8. 61 61
      hide.hxproj
  9. 28 0
      hide/HType.hx
  10. 99 0
      hide/Macros.hx
  11. 115 32
      hide/ui/Main.hx
  12. 55 0
      hide/ui/Menu.hx
  13. 92 0
      hide/ui/Props.hx
  14. 6 0
      hide/ui/View.hx
  15. 2 0
      hide/view/About.hx
  16. 6 0
      libs/golden/Layout.hx
  17. 1 0
      libs/nw/MenuItem.hx

+ 5 - 0
.vscode/settings.json

@@ -0,0 +1,5 @@
+{
+    "haxe.displayConfigurations": [
+        ["hide.hxml"]
+    ]
+}

+ 7 - 0
.vscode/tasks.json

@@ -0,0 +1,7 @@
+{
+    "version": "2.0.0",
+    "command": "haxe",
+    "args": ["hide.hxml","-cmd","bin\\hide.bat"],
+    "problemMatcher": "$haxe",
+    "showOutput": "never"
+}

+ 17 - 0
bin/app.html

@@ -1,3 +1,4 @@
+<!DOCTYPE html>
 <html>
 <html>
 <body>
 <body>
 <script src="libs/jquery-3.2.1.js"></script>
 <script src="libs/jquery-3.2.1.js"></script>
@@ -5,6 +6,22 @@
 <link rel="stylesheet" type="text/css" href="libs/goldenlayout-base.css"/>
 <link rel="stylesheet" type="text/css" href="libs/goldenlayout-base.css"/>
 <link rel="stylesheet" type="text/css" href="libs/goldenlayout-light-theme.css"/>
 <link rel="stylesheet" type="text/css" href="libs/goldenlayout-light-theme.css"/>
 <link rel="stylesheet" type="text/css" href="style.css"/>
 <link rel="stylesheet" type="text/css" href="style.css"/>
+<xml id="mainmenu">
+	<menu label="View">
+		<separator></separator>
+		<menu label="About" component="hide.view.About"></menu>
+		<menu label="Debug" class="debug"></menu>
+	</menu>
+	<menu label="Layout" class="layout">
+		<div class="content">
+		</div>
+		<separator></separator>
+		<menu label="Keep on close" class="autosave" type="checkbox"></menu>
+		<menu label="Save" class="save"></menu>
+		<menu label="Save As..." class="saveas"></menu>
+		<menu label="Manage" class="manage" disabled="disabled"></menu>
+	</menu>
+</xml>
 <script src="hide.js"></script>
 <script src="hide.js"></script>
 </body>
 </body>
 </html>
 </html>

+ 517 - 32
bin/hide.js

@@ -1,5 +1,7 @@
 // Generated by Haxe 4.0.0
 // Generated by Haxe 4.0.0
+if (process.version < "v4.0.0") console.warn("Module " + (typeof(module) == "undefined" ? "" : module.filename) + " requires node.js version 4.0.0 or higher");
 (function ($global) { "use strict";
 (function ($global) { "use strict";
+var $hxClasses = {};
 function $extend(from, fields) {
 function $extend(from, fields) {
 	function Inherit() {} Inherit.prototype = from; var proto = new Inherit();
 	function Inherit() {} Inherit.prototype = from; var proto = new Inherit();
 	for (var name in fields) proto[name] = fields[name];
 	for (var name in fields) proto[name] = fields[name];
@@ -7,7 +9,37 @@ function $extend(from, fields) {
 	return proto;
 	return proto;
 }
 }
 Math.__name__ = ["Math"];
 Math.__name__ = ["Math"];
+var Reflect = function() { };
+$hxClasses["Reflect"] = Reflect;
+Reflect.__name__ = ["Reflect"];
+Reflect.field = function(o,field) {
+	try {
+		return o[field];
+	} catch( e ) {
+		return null;
+	}
+};
+Reflect.fields = function(o) {
+	var a = [];
+	if(o != null) {
+		var hasOwnProperty = Object.prototype.hasOwnProperty;
+		for( var f in o ) {
+		if(f != "__id__" && f != "hx__closures__" && hasOwnProperty.call(o,f)) {
+			a.push(f);
+		}
+		}
+	}
+	return a;
+};
+Reflect.deleteField = function(o,field) {
+	if(!Object.prototype.hasOwnProperty.call(o,field)) {
+		return false;
+	}
+	delete(o[field]);
+	return true;
+};
 var Type = function() { };
 var Type = function() { };
+$hxClasses["Type"] = Type;
 Type.__name__ = ["Type"];
 Type.__name__ = ["Type"];
 Type.getClassName = function(c) {
 Type.getClassName = function(c) {
 	var a = c.__name__;
 	var a = c.__name__;
@@ -16,6 +48,13 @@ Type.getClassName = function(c) {
 	}
 	}
 	return a.join(".");
 	return a.join(".");
 };
 };
+Type.resolveClass = function(name) {
+	var cl = $hxClasses[name];
+	if(cl == null || !cl.__name__) {
+		return null;
+	}
+	return cl;
+};
 Type.createInstance = function(cl,args) {
 Type.createInstance = function(cl,args) {
 	var _g = args.length;
 	var _g = args.length;
 	switch(_g) {
 	switch(_g) {
@@ -53,53 +92,399 @@ Type.createInstance = function(cl,args) {
 		throw new js__$Boot_HaxeError("Too many arguments");
 		throw new js__$Boot_HaxeError("Too many arguments");
 	}
 	}
 };
 };
+var haxe_IMap = function() { };
+$hxClasses["haxe.IMap"] = haxe_IMap;
+haxe_IMap.__name__ = ["haxe","IMap"];
+var haxe_Timer = function(time_ms) {
+	var me = this;
+	this.id = setInterval(function() {
+		me.run();
+	},time_ms);
+};
+$hxClasses["haxe.Timer"] = haxe_Timer;
+haxe_Timer.__name__ = ["haxe","Timer"];
+haxe_Timer.delay = function(f,time_ms) {
+	var t = new haxe_Timer(time_ms);
+	t.run = function() {
+		t.stop();
+		f();
+	};
+	return t;
+};
+haxe_Timer.prototype = {
+	stop: function() {
+		if(this.id == null) {
+			return;
+		}
+		clearInterval(this.id);
+		this.id = null;
+	}
+	,run: function() {
+	}
+	,__class__: haxe_Timer
+};
+var haxe_ds_StringMap = function() { };
+$hxClasses["haxe.ds.StringMap"] = haxe_ds_StringMap;
+haxe_ds_StringMap.__name__ = ["haxe","ds","StringMap"];
+haxe_ds_StringMap.__interfaces__ = [haxe_IMap];
+var haxe_io_Bytes = function() { };
+$hxClasses["haxe.io.Bytes"] = haxe_io_Bytes;
+haxe_io_Bytes.__name__ = ["haxe","io","Bytes"];
+haxe_io_Bytes.prototype = {
+	__class__: haxe_io_Bytes
+};
+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;
+hide_HTypeDef.TInt = ["TInt",1];
+hide_HTypeDef.TInt.__enum__ = hide_HTypeDef;
+hide_HTypeDef.TBool = ["TBool",2];
+hide_HTypeDef.TBool.__enum__ = hide_HTypeDef;
+hide_HTypeDef.TFloat = ["TFloat",3];
+hide_HTypeDef.TFloat.__enum__ = hide_HTypeDef;
+hide_HTypeDef.TString = ["TString",4];
+hide_HTypeDef.TString.__enum__ = hide_HTypeDef;
+hide_HTypeDef.TAlias = function(name,t) { var $x = ["TAlias",5,name,t]; $x.__enum__ = hide_HTypeDef; return $x; };
+hide_HTypeDef.TArray = function(t) { var $x = ["TArray",6,t]; $x.__enum__ = hide_HTypeDef; return $x; };
+hide_HTypeDef.TEither = function(values) { var $x = ["TEither",7,values]; $x.__enum__ = hide_HTypeDef; return $x; };
+hide_HTypeDef.TFlags = function(values) { var $x = ["TFlags",8,values]; $x.__enum__ = hide_HTypeDef; return $x; };
+hide_HTypeDef.TFile = ["TFile",9];
+hide_HTypeDef.TFile.__enum__ = hide_HTypeDef;
+hide_HTypeDef.TTile = ["TTile",10];
+hide_HTypeDef.TTile.__enum__ = hide_HTypeDef;
+hide_HTypeDef.TDynamic = ["TDynamic",11];
+hide_HTypeDef.TDynamic.__enum__ = hide_HTypeDef;
+hide_HTypeDef.TStruct = function(fields) { var $x = ["TStruct",12,fields]; $x.__enum__ = hide_HTypeDef; return $x; };
+hide_HTypeDef.TEnum = function(constructors) { var $x = ["TEnum",13,constructors]; $x.__enum__ = hide_HTypeDef; return $x; };
+var hide_HTypeProp = { __ename__ : true, __constructs__ : ["PNull","PIsColor"] };
+hide_HTypeProp.PNull = ["PNull",0];
+hide_HTypeProp.PNull.__enum__ = hide_HTypeProp;
+hide_HTypeProp.PIsColor = ["PIsColor",1];
+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_Main = function() {
-	var _gthis = this;
-	var config = { content : []};
-	this.layout = new GoldenLayout(config);
-	this.layout.init();
+	var _hide_HType = { def : null};
+	var _hide_HTypeDef = { def : null};
+	var _tmp = { def : hide_HTypeDef.TFlags(["PIsColor","PNull"])};
+	_tmp.props |= 1 << hide_HTypeProp.PNull[1];
+	var inlobj_def = hide_HTypeDef.TStruct([{ name : "def", t : _hide_HTypeDef},{ name : "props", t : _tmp}]);
+	_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();
 	this.window = nw.Window.get();
 	this.window = nw.Window.get();
-	this.mainMenu = new nw.Menu({ type : "menubar"});
-	this.viewMenu = new nw.Menu({ type : "contextmenu"});
-	this.viewMenu.append(new nw.MenuItem({ label : null, type : "separator"}));
-	this.addGlobalView(hide_view_About);
-	var view = new nw.MenuItem({ label : "View", submenu : this.viewMenu});
-	var debug = new nw.MenuItem({ label : "Debug"});
-	debug.click = function() {
-		_gthis.window.showDevTools();
-	};
-	this.viewMenu.append(debug);
-	this.mainMenu.append(view);
-	nw.Window.get().menu = this.mainMenu;
+	this.initMenu();
+	this.initLayout();
 };
 };
+$hxClasses["hide.ui.Main"] = hide_ui_Main;
 hide_ui_Main.__name__ = ["hide","ui","Main"];
 hide_ui_Main.__name__ = ["hide","ui","Main"];
 hide_ui_Main.main = function() {
 hide_ui_Main.main = function() {
 	new hide_ui_Main();
 	new hide_ui_Main();
 };
 };
 hide_ui_Main.prototype = {
 hide_ui_Main.prototype = {
-	addGlobalView: function(c) {
+	initLayout: function(state) {
 		var _gthis = this;
 		var _gthis = this;
-		var cname = Type.getClassName(c);
-		var i = new nw.MenuItem({ label : cname.split(".").pop()});
-		i.click = function() {
-			if(_gthis.layout.root.contentItems.length == 0) {
-				_gthis.layout.root.addChild({ type : "row"});
-			}
-			_gthis.layout.root.contentItems[0].addChild({ type : "component", componentName : cname});
-		};
-		this.viewMenu.append(i);
-		this.layout.registerComponent(cname,function(cont,state) {
-			var view = Type.createInstance(c,[state]);
-			cont.setTitle(view.getTitle());
-			view.onDisplay(cont.getElement());
+		if(this.layout != null) {
+			this.layout.destroy();
+			this.layout = null;
+		}
+		var defaultLayout = null;
+		var _g = 0;
+		var _g1 = this.props.current.layouts;
+		while(_g < _g1.length) {
+			var p = _g1[_g];
+			++_g;
+			if(p.name == "Default") {
+				defaultLayout = p;
+				break;
+			}
+		}
+		if(defaultLayout == null) {
+			defaultLayout = { name : "Default", state : []};
+			if(this.props.local.layouts == null) {
+				this.props.local.layouts = [];
+			}
+			this.props.local.layouts.push(defaultLayout);
+			this.props.save();
+		}
+		if(state == null) {
+			state = defaultLayout;
+		}
+		this.currentLayout = state;
+		var config = { content : state.state};
+		this.layout = new GoldenLayout(config);
+		var _g2 = 0;
+		var _g11 = hide_ui_View.viewClasses;
+		while(_g2 < _g11.length) {
+			var cl = [_g11[_g2]];
+			++_g2;
+			this.layout.registerComponent(Type.getClassName(cl[0]),(function(cl1) {
+				return function(cont,state1) {
+					var view = Type.createInstance(cl1[0],[state1]);
+					cont.setTitle(view.getTitle());
+					view.onDisplay(cont.getElement());
+				};
+			})(cl));
+		}
+		this.layout.init();
+		this.layout.on("stateChanged",function() {
+			if(!_gthis.props.current.autoSaveLayout) {
+				return;
+			}
+			defaultLayout.state = _gthis.saveLayout();
+			_gthis.props.save();
 		});
 		});
+		haxe_Timer.delay(function() {
+			if(_gthis.layout.isInitialised) {
+				return;
+			}
+			state.state = [];
+			_gthis.initLayout();
+		},1000);
+	}
+	,saveLayout: function() {
+		return this.layout.toConfig().content;
+	}
+	,initMenu: function() {
+		var _gthis = this;
+		var firstInit = false;
+		if(this.menu == null) {
+			this.menu = $("#mainmenu");
+			firstInit = true;
+		}
+		var layouts = this.menu.find(".layout .content");
+		layouts.html("");
+		var _g = 0;
+		var _g1 = this.props.current.layouts;
+		while(_g < _g1.length) {
+			var l = [_g1[_g]];
+			++_g;
+			if(l[0].name == "Default") {
+				continue;
+			}
+			$("<menu>").attr("label",l[0].name).addClass(l[0].name).appendTo(layouts).click((function(l1) {
+				return function(_) {
+					_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.window.menu = new hide_ui_Menu(this.menu).root;
 	}
 	}
 	,__class__: hide_ui_Main
 	,__class__: hide_ui_Main
 };
 };
+var hide_ui_Menu = function(menu) {
+	this.root = new nw.Menu({ type : "menubar"});
+	this.buildMenuRec(this.root,"",menu);
+};
+$hxClasses["hide.ui.Menu"] = hide_ui_Menu;
+hide_ui_Menu.__name__ = ["hide","ui","Menu"];
+hide_ui_Menu.prototype = {
+	buildMenuRec: function(menu,path,e) {
+		var cl = e.attr("class");
+		if(cl != null) {
+			if(path == "") {
+				path = cl;
+			} else {
+				path = path + "." + cl;
+			}
+		}
+		var elt = e.get(0);
+		var _g = elt.nodeName;
+		switch(_g) {
+		case "MENU":
+			var submenu = null;
+			if(elt.firstElementChild != null) {
+				submenu = new nw.Menu({ type : "contextmenu"});
+				var _g_i = 0;
+				var _g_j = e.children();
+				while(_g_i < _g_j.length) {
+					var e1 = $(_g_j[_g_i++]);
+					this.buildMenuRec(submenu,path,e1);
+				}
+			}
+			var type;
+			var _g1 = e.attr("type");
+			type = _g1 == "checkbox" ? "checkbox" : "normal";
+			var label = e.attr("label");
+			if(label == null) {
+				label = "???";
+			}
+			var checked = e.attr("checked") == "checked";
+			var m = new nw.MenuItem(submenu == null ? { label : label, type : type} : { label : label, type : type, submenu : submenu});
+			if(type == "checkbox") {
+				m.checked = checked;
+			}
+			if(e.attr("disabled") == "disabled") {
+				m.enabled = false;
+			}
+			m.click = function() {
+				if(type == "checkbox") {
+					checked = !checked;
+					e.attr("checked",checked ? "checked" : "");
+					m.checked = checked;
+				}
+				e.click();
+			};
+			menu.append(m);
+			break;
+		case "SEPARATOR":
+			menu.append(new nw.MenuItem({ label : null, type : "separator"}));
+			break;
+		default:
+			var _g_i1 = 0;
+			var _g_j1 = e.children();
+			while(_g_i1 < _g_j1.length) {
+				var e2 = $(_g_j1[_g_i1++]);
+				this.buildMenuRec(menu,path,e2);
+			}
+		}
+	}
+	,__class__: hide_ui_Menu
+};
+var hide_ui_Props = function() {
+	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();
+	this.paths = { global : globalPath, local : localPath, project : projectPath};
+};
+$hxClasses["hide.ui.Props"] = hide_ui_Props;
+hide_ui_Props.__name__ = ["hide","ui","Props"];
+hide_ui_Props.prototype = {
+	load: function() {
+		var tmp;
+		try {
+			tmp = JSON.parse(js_node_Fs.readFileSync(this.paths.global,{ encoding : "utf8"}));
+		} catch( e ) {
+			tmp = null;
+		}
+		this.global = tmp;
+		var tmp1;
+		try {
+			tmp1 = JSON.parse(js_node_Fs.readFileSync(this.paths.project,{ encoding : "utf8"}));
+		} catch( e1 ) {
+			tmp1 = null;
+		}
+		this.project = tmp1;
+		var tmp2;
+		try {
+			tmp2 = JSON.parse(window.localStorage.getItem(this.paths.local));
+		} catch( e2 ) {
+			tmp2 = null;
+		}
+		this.local = tmp2;
+		if(this.global == null) {
+			this.global = { };
+		}
+		if(this.project == null) {
+			this.project = { };
+		}
+		if(this.local == null) {
+			this.local = { };
+		}
+		this.sync();
+	}
+	,sync: function() {
+		this.current = { autoSaveLayout : true, layouts : []};
+		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 == "{}") {
+			try {
+				js_node_Fs.unlinkSync(this.paths.project);
+			} catch( e ) {
+			}
+		} else {
+			js_node_Fs.writeFileSync(this.paths.project,str1);
+		}
+		var str2 = JSON.stringify(this.local);
+		window.localStorage.setItem(this.paths.local,str2);
+	}
+	,merge: function(props) {
+		var _g = 0;
+		var _g1 = Reflect.fields(props);
+		while(_g < _g1.length) {
+			var f = _g1[_g];
+			++_g;
+			var v = Reflect.field(props,f);
+			if(v == null) {
+				Reflect.deleteField(props,f);
+				continue;
+			}
+			if(props == this.global && v == Reflect.field(this.current,f)) {
+				Reflect.deleteField(props,f);
+				continue;
+			}
+			this.current[f] = v;
+		}
+	}
+	,__class__: hide_ui_Props
+};
 var hide_ui_View = function(state) {
 var hide_ui_View = function(state) {
 	this.state = state;
 	this.state = state;
 };
 };
+$hxClasses["hide.ui.View"] = hide_ui_View;
 hide_ui_View.__name__ = ["hide","ui","View"];
 hide_ui_View.__name__ = ["hide","ui","View"];
+hide_ui_View.register = function(cl) {
+	hide_ui_View.viewClasses.push(cl);
+	return null;
+};
 hide_ui_View.prototype = {
 hide_ui_View.prototype = {
 	getTitle: function() {
 	getTitle: function() {
 		var name = Type.getClassName(js_Boot.getClass(this));
 		var name = Type.getClassName(js_Boot.getClass(this));
@@ -113,11 +498,12 @@ hide_ui_View.prototype = {
 var hide_view_About = function(state) {
 var hide_view_About = function(state) {
 	hide_ui_View.call(this,state);
 	hide_ui_View.call(this,state);
 };
 };
+$hxClasses["hide.view.About"] = hide_view_About;
 hide_view_About.__name__ = ["hide","view","About"];
 hide_view_About.__name__ = ["hide","view","About"];
 hide_view_About.__super__ = hide_ui_View;
 hide_view_About.__super__ = hide_ui_View;
 hide_view_About.prototype = $extend(hide_ui_View.prototype,{
 hide_view_About.prototype = $extend(hide_ui_View.prototype,{
 	onDisplay: function(j) {
 	onDisplay: function(j) {
-		j.html("\r\n\t\t<p>\r\n\t\t\tHeaps IDE v0.1<br/>\r\n\t\t\t(c)2017 Nicolas Cannasse\r\n\t\t</p>\r\n\t\t");
+		j.html("\n\t\t<p>\n\t\t\tHeaps IDE v0.1<br/>\n\t\t\t(c)2017 Nicolas Cannasse\n\t\t</p>\n\t\t");
 	}
 	}
 	,__class__: hide_view_About
 	,__class__: hide_view_About
 });
 });
@@ -129,6 +515,7 @@ var js__$Boot_HaxeError = function(val) {
 		Error.captureStackTrace(this,js__$Boot_HaxeError);
 		Error.captureStackTrace(this,js__$Boot_HaxeError);
 	}
 	}
 };
 };
+$hxClasses["js._Boot.HaxeError"] = js__$Boot_HaxeError;
 js__$Boot_HaxeError.__name__ = ["js","_Boot","HaxeError"];
 js__$Boot_HaxeError.__name__ = ["js","_Boot","HaxeError"];
 js__$Boot_HaxeError.wrap = function(val) {
 js__$Boot_HaxeError.wrap = function(val) {
 	if((val instanceof Error)) {
 	if((val instanceof Error)) {
@@ -142,6 +529,7 @@ js__$Boot_HaxeError.prototype = $extend(Error.prototype,{
 	__class__: js__$Boot_HaxeError
 	__class__: js__$Boot_HaxeError
 });
 });
 var js_Boot = function() { };
 var js_Boot = function() { };
+$hxClasses["js.Boot"] = js_Boot;
 js_Boot.__name__ = ["js","Boot"];
 js_Boot.__name__ = ["js","Boot"];
 js_Boot.getClass = function(o) {
 js_Boot.getClass = function(o) {
 	if((o instanceof Array) && o.__enum__ == null) {
 	if((o instanceof Array) && o.__enum__ == null) {
@@ -158,6 +546,90 @@ js_Boot.getClass = function(o) {
 		return null;
 		return null;
 	}
 	}
 };
 };
+js_Boot.__string_rec = function(o,s) {
+	if(o == null) {
+		return "null";
+	}
+	if(s.length >= 5) {
+		return "<...>";
+	}
+	var t = typeof(o);
+	if(t == "function" && (o.__name__ || o.__ename__)) {
+		t = "object";
+	}
+	switch(t) {
+	case "function":
+		return "<function>";
+	case "object":
+		if(o instanceof Array) {
+			if(o.__enum__) {
+				if(o.length == 2) {
+					return o[0];
+				}
+				var str = o[0] + "(";
+				s += "\t";
+				var _g1 = 2;
+				var _g = o.length;
+				while(_g1 < _g) {
+					var i = _g1++;
+					if(i != 2) {
+						str += "," + js_Boot.__string_rec(o[i],s);
+					} else {
+						str += js_Boot.__string_rec(o[i],s);
+					}
+				}
+				return str + ")";
+			}
+			var l = o.length;
+			var i1;
+			var str1 = "[";
+			s += "\t";
+			var _g11 = 0;
+			var _g2 = l;
+			while(_g11 < _g2) {
+				var i2 = _g11++;
+				str1 += (i2 > 0 ? "," : "") + js_Boot.__string_rec(o[i2],s);
+			}
+			str1 += "]";
+			return str1;
+		}
+		var tostr;
+		try {
+			tostr = o.toString;
+		} catch( e ) {
+			return "???";
+		}
+		if(tostr != null && tostr != Object.toString && typeof(tostr) == "function") {
+			var s2 = o.toString();
+			if(s2 != "[object Object]") {
+				return s2;
+			}
+		}
+		var k = null;
+		var str2 = "{\n";
+		s += "\t";
+		var hasp = o.hasOwnProperty != null;
+		for( var k in o ) {
+		if(hasp && !o.hasOwnProperty(k)) {
+			continue;
+		}
+		if(k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__") {
+			continue;
+		}
+		if(str2.length != 2) {
+			str2 += ", \n";
+		}
+		str2 += s + k + " : " + js_Boot.__string_rec(o[k],s);
+		}
+		s = s.substring(1);
+		str2 += "\n" + s + "}";
+		return str2;
+	case "string":
+		return o;
+	default:
+		return String(o);
+	}
+};
 js_Boot.__nativeClassName = function(o) {
 js_Boot.__nativeClassName = function(o) {
 	var name = js_Boot.__toStr.call(o).slice(8,-1);
 	var name = js_Boot.__toStr.call(o).slice(8,-1);
 	if(name == "Object" || name == "Function" || name == "Math" || name == "JSON") {
 	if(name == "Object" || name == "Function" || name == "Math" || name == "JSON") {
@@ -168,9 +640,22 @@ js_Boot.__nativeClassName = function(o) {
 js_Boot.__resolveNativeClass = function(name) {
 js_Boot.__resolveNativeClass = function(name) {
 	return $global[name];
 	return $global[name];
 };
 };
-String.prototype.__class__ = String;
+var js_Browser = function() { };
+$hxClasses["js.Browser"] = js_Browser;
+js_Browser.__name__ = ["js","Browser"];
+js_Browser.alert = function(v) {
+	window.alert(js_Boot.__string_rec(v,""));
+};
+var js_node_Fs = require("fs");
+var js_node_buffer_Buffer = require("buffer").Buffer;
+$hxClasses["Math"] = Math;
+String.prototype.__class__ = $hxClasses["String"] = String;
 String.__name__ = ["String"];
 String.__name__ = ["String"];
+$hxClasses["Array"] = Array;
 Array.__name__ = ["Array"];
 Array.__name__ = ["Array"];
+var __map_reserved = {};
+hide_ui_View.viewClasses = [];
+hide_view_About._ = hide_ui_View.register(hide_view_About);
 js_Boot.__toStr = ({ }).toString;
 js_Boot.__toStr = ({ }).toString;
 hide_ui_Main.main();
 hide_ui_Main.main();
 })(typeof window != "undefined" ? window : typeof global != "undefined" ? global : typeof self != "undefined" ? self : this);
 })(typeof window != "undefined" ? window : typeof global != "undefined" ? global : typeof self != "undefined" ? self : this);

+ 3 - 0
bin/style.css

@@ -15,6 +15,7 @@ body, td, th, li, p, a, input, select {
 a {
 a {
 	text-decoration : none;
 	text-decoration : none;
 }
 }
+#mainmenu { display : none }
 .lm_header .lm_tabs {
 .lm_header .lm_tabs {
 	z-index : 1;
 	z-index : 1;
 }
 }
@@ -23,3 +24,5 @@ a {
 	-webkit-box-shadow : none;
 	-webkit-box-shadow : none;
 	box-shadow : none;
 	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; }

+ 5 - 1
bin/style.hss

@@ -19,6 +19,8 @@ a {
 	text-decoration : none;
 	text-decoration : none;
 }
 }
 
 
+#mainmenu { display : none }
+
 // golden layout fixes
 // golden layout fixes
 
 
 // prevent buttons getting over title with small panel width
 // prevent buttons getting over title with small panel width
@@ -26,4 +28,6 @@ a {
 // no shadow in drag'n drop
 // no shadow in drag'n drop
 .lm_dropTargetIndicator { box-shadow: none; }
 .lm_dropTargetIndicator { box-shadow: none; }
 
 
-
+// larger close tab icon
+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; }

+ 1 - 0
hide.hxml

@@ -2,3 +2,4 @@
 -js bin/hide.js
 -js bin/hide.js
 -main hide.ui.Main
 -main hide.ui.Main
 -lib hxnodejs
 -lib hxnodejs
+--macro include('hide.view')

+ 61 - 61
hide.hxproj

@@ -1,62 +1,62 @@
-<?xml version="1.0" encoding="utf-8"?>
-<project version="2">
-  <!-- Output SWF options -->
-  <output>
-    <movie outputType="Application" />
-    <movie input="" />
-    <movie path="bin\hide.js" />
-    <movie fps="30" />
-    <movie width="800" />
-    <movie height="600" />
-    <movie version="0" />
-    <movie minorVersion="0" />
-    <movie platform="JavaScript" />
-    <movie background="#FFFFFF" />
-  </output>
-  <!-- Other classes to be compiled into your SWF -->
-  <classpaths>
-    <class path="libs" />
-  </classpaths>
-  <!-- Build options -->
-  <build>
-    <option directives="" />
-    <option flashStrict="False" />
-    <option noInlineOnDebug="False" />
-    <option mainClass="hide.ui.Main" />
-    <option enabledebug="False" />
-    <option additional="-lib hxnodejs" />
-  </build>
-  <!-- haxelib libraries -->
-  <haxelib>
-    <!-- example: <library name="..." /> -->
-  </haxelib>
-  <!-- Class files to compile (other referenced classes will automatically be included) -->
-  <compileTargets>
-    <!-- example: <compile path="..." /> -->
-  </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" />
-    <hidden path="bin\libGLESv2.dll" />
-    <hidden path="bin\nw.exe" />
-    <hidden path="bin\nw.pak" />
-    <hidden path="bin\nwsnapshot.exe" />
-    <hidden path="bin\icudt.dll" />
-    <hidden path="bin\credits.html" />
-  </hiddenPaths>
-  <!-- Executed before build -->
-  <preBuildCommand />
-  <!-- Executed after build -->
-  <postBuildCommand alwaysRun="False" />
-  <!-- Other project options -->
-  <options>
-    <option showHiddenPaths="False" />
-    <option testMovie="OpenDocument" />
-    <option testMovieCommand="bin/hide.bat" />
-  </options>
-  <!-- Plugin storage -->
-  <storage />
+<?xml version="1.0" encoding="utf-8"?>
+<project version="2">
+  <!-- Output SWF options -->
+  <output>
+    <movie outputType="Application" />
+    <movie input="" />
+    <movie path="bin\hide.js" />
+    <movie fps="30" />
+    <movie width="800" />
+    <movie height="600" />
+    <movie version="0" />
+    <movie minorVersion="0" />
+    <movie platform="JavaScript" />
+    <movie background="#FFFFFF" />
+  </output>
+  <!-- Other classes to be compiled into your SWF -->
+  <classpaths>
+    <class path="libs" />
+  </classpaths>
+  <!-- Build options -->
+  <build>
+    <option directives="" />
+    <option flashStrict="False" />
+    <option noInlineOnDebug="False" />
+    <option mainClass="hide.ui.Main" />
+    <option enabledebug="False" />
+    <option additional="-lib hxnodejs" />
+  </build>
+  <!-- haxelib libraries -->
+  <haxelib>
+    <!-- example: <library name="..." /> -->
+  </haxelib>
+  <!-- Class files to compile (other referenced classes will automatically be included) -->
+  <compileTargets>
+    <!-- example: <compile path="..." /> -->
+  </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" />
+    <hidden path="bin\libGLESv2.dll" />
+    <hidden path="bin\nw.exe" />
+    <hidden path="bin\nw.pak" />
+    <hidden path="bin\nwsnapshot.exe" />
+    <hidden path="bin\icudt.dll" />
+    <hidden path="bin\credits.html" />
+  </hiddenPaths>
+  <!-- Executed before build -->
+  <preBuildCommand />
+  <!-- Executed after build -->
+  <postBuildCommand alwaysRun="False" />
+  <!-- Other project options -->
+  <options>
+    <option showHiddenPaths="False" />
+    <option testMovie="OpenDocument" />
+    <option testMovieCommand="bin/hide.bat" />
+  </options>
+  <!-- Plugin storage -->
+  <storage />
 </project>
 </project>

+ 28 - 0
hide/HType.hx

@@ -0,0 +1,28 @@
+package hide;
+
+enum HTypeDef {
+	TId;
+	TInt;
+	TBool;
+	TFloat;
+	TString;
+	TAlias( name : String, t : HType );
+	TArray( t : HType );
+	TEither( values : Array<String> );
+	TFlags( values : Array<String> );
+	TFile;
+	TTile;
+	TDynamic;
+	TStruct( fields : Array<{ name : String, t : HType }> );
+	TEnum( constructors : Array<{ name : String, args : Array<{ name : String, t : HType }> }> );
+}
+
+enum HTypeProp {
+	PNull; // can be null
+	PIsColor; // TInt only
+}
+
+typedef HType = {
+	var def : HTypeDef;
+	@:optional var props : haxe.EnumFlags<HTypeProp>;
+}

+ 99 - 0
hide/Macros.hx

@@ -0,0 +1,99 @@
+package hide;
+import haxe.macro.Expr;
+import haxe.macro.Context;
+import haxe.macro.Type;
+using haxe.macro.ExprTools;
+
+class Macros {
+
+	public static macro function makeTypeDef( e : Expr ) {
+		var t = Context.getType(e.toString());
+		return buildTypeDef(t);
+	}
+
+	#if macro
+
+	static function buildTypeDef( t : Type ) : Expr {
+		var m = new Map();
+		var e = buildTypeDefRec(t,m);
+		var block = [];
+		for( t in m )
+			block.push(t.decl);
+		for( t in m )
+			block.push(t.init);
+		block.push(e);
+		return macro ({$a{block}} : hide.HType);
+	}
+
+	static function buildTypeDefRec( t : Type, knownTypes : Map<String,{ value : Expr, decl : Expr, init : Expr }> ) : Expr {
+		switch( t ) {
+		case TType(t,[pt]) if( t.toString() == "Null" ):
+			var e = buildTypeDefRec(pt,knownTypes);
+			return macro { var _tmp : hide.HType = $e; _tmp.props.set(PNull); _tmp; };
+		case TType(t,[]):
+			var key = t.toString();
+			var def = knownTypes.get(key);
+			if( def != null )
+				return def.value;
+			var vname = "_"+key.split(".").join("_"); 
+			def = {
+				decl : (macro var $vname : hide.HType = { def : null }),
+				init : null,
+				value : macro $i{vname},
+			};
+			knownTypes.set(key, def);
+			def.init = macro $i{vname}.def = (${buildTypeDefRec(t.get().type,knownTypes)} : hide.HType).def;
+			return def.value;
+		case TEnum(e,[]):
+			var key = e.toString();
+			var def = knownTypes.get(key);
+			if( def != null )
+				return def.value;
+			var vname = "_"+key.split(".").join("_"); 
+			def = {
+				decl : (macro var $vname : hide.HType = { def : null }),
+				init : null,
+				value : macro $i{vname},
+			};
+			knownTypes.set(key, def);
+			var edef = e.get();
+			var constructs = [for( c in edef.names ) {
+				var c = edef.constructs.get(c);
+				var args = switch( c.type ) {
+				case TFun(args,_): [for( a in args ) macro { name : $v{a.name}, t : ${buildTypeDefRec(a.t,knownTypes)} }];
+				default: [];
+				};
+				macro { name : $v{c.name}, args : [$a{args}] };
+			}];
+			def.init = macro $i{vname}.def = TEnum([$a{constructs}]);
+			return def.value;
+		case TAnonymous(a):
+			var fields = [for( f in a.get().fields ) macro { name : $v{f.name}, t : ${buildTypeDefRec(f.type,knownTypes)} }];
+			return macro { def : TStruct([$a{fields}]) };
+		case TAbstract(a,pl):
+			var a = a.get();
+			switch( a.name ) {
+			case "EnumFlags":
+				switch( Context.follow(pl[0]) ) {
+				case TEnum(e,_):
+					var flags = [for( c in e.get().constructs ) macro $v{c.name}];
+					return macro { def : TFlags([$a{flags}]) };
+				default:
+				}
+			default:
+			}
+		case TInst(c,pl):
+			switch( c.toString() ) {
+			case "String": return macro { def : TString };
+			case "Array": return macro { def : TArray(${buildTypeDefRec(pl[0],knownTypes)}) };
+			default:
+			}
+		default:
+		}
+		Context.error("Unsupported type "+Std.string(t),Context.currentPos());
+		return null;
+	}
+
+	#end
+
+}

+ 115 - 32
hide/ui/Main.hx

@@ -1,58 +1,141 @@
 package hide.ui;
 package hide.ui;
+import js.jquery.Helper.*;
 
 
 class Main {
 class Main {
 
 
 	var window : nw.Window;
 	var window : nw.Window;
-	var mainMenu : nw.Menu;
-	var viewMenu : nw.Menu;
 
 
 	var layout : golden.Layout;
 	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() {
 	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 = {
 		var config : golden.Config = {
-			content: []
+			content: state.state,
 		};
 		};
 		layout = new golden.Layout(config);
 		layout = new golden.Layout(config);
-		layout.init();
 
 
-		window = nw.Window.get();
-		mainMenu = new nw.Menu({type: Menubar});
-		viewMenu = new nw.Menu({type: ContextMenu});
+		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();
+		});
 
 
-		viewMenu.append(new nw.MenuItem({label:null, type:Separator}));
-		addGlobalView(hide.view.About);
+		// error recovery if invalid component
+		haxe.Timer.delay(function() {
+			if( layout.isInitialised ) return;
+			state.state = [];
+			initLayout();
+		}, 1000);
+	}
 
 
-		var view = new nw.MenuItem({label:"View", submenu:viewMenu});
+	function saveLayout() {
+		return layout.toConfig().content;
+	}
 
 
-		var debug = new nw.MenuItem({label:"Debug"});
-		debug.click = function() window.showDevTools();
-		viewMenu.append(debug);
+	function initMenu() {
+		var firstInit = false;
 
 
-		mainMenu.append(view);
-		nw.Window.get().menu = mainMenu;
-	}
+		if( menu == null ) {
+			menu = J("#mainmenu");
+			firstInit = true;
+		}
 
 
-	function addGlobalView( c : Class<View<Dynamic>> ) {
-		var cname = Type.getClassName(c);
-		var i = new nw.MenuItem({ label : cname.split(".").pop() });
-		i.click = function() {
-			if( layout.root.contentItems.length == 0 )
-				layout.root.addChild({ type : Row });
+		// states
 
 
-			layout.root.contentItems[0].addChild({
-				type : Component,
-				componentName : cname,
+		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);
 			});
 			});
-		};
-		viewMenu.append(i);
-		layout.registerComponent(cname, function(cont, state) {
-			var view = Type.createInstance(c, [state]);
-			cont.setTitle(view.getTitle());
-			view.onDisplay(cont.getElement());
-		});
+		}
+		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() {
 	static function main() {
 		new Main();
 		new Main();
 	}
 	}

+ 55 - 0
hide/ui/Menu.hx

@@ -0,0 +1,55 @@
+package hide.ui;
+
+class Menu {
+
+	public var root : nw.Menu;
+
+	public function new( menu : js.jquery.JQuery ) {
+		root = new nw.Menu({type: Menubar});
+		buildMenuRec(root,"",menu);
+	}
+
+	function buildMenuRec( menu : nw.Menu, path : String, e : js.jquery.JQuery ) {
+		var cl = e.attr("class");
+		if( cl != null ) {
+			if( path == "" ) path = cl else path = path + "." + cl; 
+		}
+		var elt = e.get(0);
+		switch( elt.nodeName ) {
+		case "MENU":
+			var submenu = null;
+			if( elt.firstElementChild != null ) {
+				submenu = new nw.Menu({type:ContextMenu});
+				for( e in e.children().elements() )
+					buildMenuRec(submenu, path, e);
+			}
+			var type : nw.MenuItem.MenuItemType = switch( e.attr("type") ) {
+			case "checkbox": Checkbox;
+			default: Normal;
+			}
+			var label = e.attr("label");
+			if( label == null ) label = "???";
+			var checked = e.attr("checked") == "checked";
+			var m = new nw.MenuItem(submenu == null ? { label : label, type : type } : { label : label, type : type, submenu : submenu });
+			if( type == Checkbox )
+				m.checked = checked;
+			if( e.attr("disabled") == "disabled" )
+				m.enabled = false;
+			m.click = function() {
+				if( type == Checkbox ) {
+					checked = !checked;
+					e.attr("checked", checked ? "checked" : "");
+					m.checked = checked;
+				}
+				e.click();
+			};
+			menu.append(m);
+		case "SEPARATOR":
+			menu.append(new nw.MenuItem({ label : null, type : Separator }));
+		default:
+			for( e in e.children().elements() )
+				buildMenuRec(menu, path, e);
+		}
+	}
+
+}

+ 92 - 0
hide/ui/Props.hx

@@ -0,0 +1,92 @@
+package hide.ui;
+
+typedef PropsDef = {
+
+	public var autoSaveLayout : Null<Bool>;
+	public var layouts : Array<{ name : String, state : Dynamic }>;
+
+}
+
+class Props {
+
+	
+	var paths : {
+		global : String,
+		local : String,
+		project : String,
+	};
+
+	// per user, all project
+	public var global : PropsDef;
+	// per project, all  users
+	public var project : PropsDef;
+	// per user, per project
+	public var local : PropsDef;
+	
+	// current merge
+	public var current : PropsDef;
+
+	public function new() {
+		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();
+		paths = {
+			global : globalPath,
+			local : localPath,
+			project : projectPath
+		};
+	}
+
+	public 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;
+		if( global == null ) global = cast {};
+		if( project == null ) project = cast {};
+		if( local == null ) local = cast {};
+		sync();
+	}
+
+	public function sync() {
+		current = {
+			autoSaveLayout : true,
+			layouts : [],
+		};
+		merge(global);
+		merge(project);
+		merge(local);		
+	}
+
+	public function save() {
+		sync();
+		var str = haxe.Json.stringify(global);
+		sys.io.File.saveContent(paths.global, str);
+		var str = haxe.Json.stringify(project);
+		if( str == '{}' )
+			try sys.FileSystem.deleteFile(paths.project) catch(e:Dynamic) {}
+		else
+			sys.io.File.saveContent(paths.project, str);
+		var str = haxe.Json.stringify(local);
+		js.Browser.window.localStorage.setItem(paths.local, str);
+	}
+
+	function merge( props : PropsDef ) {
+		for( f in Reflect.fields(props) ) {
+			var v = Reflect.field(props,f);
+			if( v == null ) {
+				Reflect.deleteField(props,f);
+				continue;
+			}
+			// remove if we are on default
+			if( props == global && v == Reflect.field(current,f) ) {
+				Reflect.deleteField(props,f);
+				continue;
+			}
+			Reflect.setField(current, f, v);
+		}
+	}
+
+}

+ 6 - 0
hide/ui/View.hx

@@ -17,4 +17,10 @@ class View<T> {
 		j.html(Type.getClassName(Type.getClass(this)));
 		j.html(Type.getClassName(Type.getClass(this)));
 	}
 	}
 
 
+	public static var viewClasses = new Array<Class<View<Dynamic>>>();
+	public static function register<T>( cl : Class<View<T>> ) {
+		viewClasses.push(cl);
+		return null;
+	}
+
 }
 }

+ 2 - 0
hide/view/About.hx

@@ -15,4 +15,6 @@ class About extends hide.ui.View<{}> {
 		');
 		');
 	}
 	}
 
 
+	static var _ = hide.ui.View.register(About);
+
 }
 }

+ 6 - 0
libs/golden/Layout.hx

@@ -6,10 +6,16 @@ extern class Layout {
 
 
 	var root : ContentItem;
 	var root : ContentItem;
 	var selectedItem : ContentItem;
 	var selectedItem : ContentItem;
+	var isInitialised : Bool;
+	var config : Config;
 
 
 	function new( config : Config ) : Void;
 	function new( config : Config ) : Void;
 	function init() : Void;
 	function init() : Void;
+	function destroy() : Void;
 
 
 	function registerComponent( name : String, callb : Container -> Dynamic -> Void ) : Void;
 	function registerComponent( name : String, callb : Container -> Dynamic -> Void ) : Void;
+	function on( event : String, f : Void -> Void ) : Void;
+
+	function toConfig() : Config;
 
 
 }
 }

+ 1 - 0
libs/nw/MenuItem.hx

@@ -9,6 +9,7 @@ package nw;
 extern class MenuItem {
 extern class MenuItem {
 
 
 	public var checked : Bool;
 	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 : { label : String, ?icon : String, ?type : MenuItemType, ?submenu : Menu } ) : Void;
 	public dynamic function click() : Void;
 	public dynamic function click() : Void;