Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 086518ae42 | |||
| a53510d1d4 | |||
| 4a65cef02a | |||
| 56baf7cfdc | |||
| 97b7686694 | |||
| 5b4e8dbb5e | |||
| 9671a37d0d | |||
| 823f3ea596 | |||
| cc6717f46b | |||
| 6bc4f297d5 | |||
| a6bbcb408f |
15
README.md
15
README.md
@@ -14,8 +14,8 @@ form.json:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type":"haxework.gui.VGroupView",
|
"@type":"haxework.gui.VGroupView",
|
||||||
"skin":{"type":"haxework.gui.skin.ColorSkin", "color":"0xffff00"},
|
"skin":{"@type":"haxework.gui.skin.ColorSkin", "color":"0xffff00"},
|
||||||
"paddings":20,
|
"paddings":20,
|
||||||
"layoutMargin":10,
|
"layoutMargin":10,
|
||||||
"views":[
|
"views":[
|
||||||
@@ -23,13 +23,13 @@ form.json:
|
|||||||
"id":"view1",
|
"id":"view1",
|
||||||
"type":"haxework.gui.SpriteView",
|
"type":"haxework.gui.SpriteView",
|
||||||
"pWidth":100, "pHeight":100,
|
"pWidth":100, "pHeight":100,
|
||||||
"skin":{"type":"haxework.gui.skin.ColorSkin", "color":"0xff0000"}
|
"skin":{"@type":"haxework.gui.skin.ColorSkin", "color":"0xff0000"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"view2",
|
"id":"view2",
|
||||||
"type":"haxework.gui.SpriteView",
|
"type":"haxework.gui.SpriteView",
|
||||||
"pWidth":100, "height":50,
|
"pWidth":100, "height":50,
|
||||||
"skin":{"type":"haxework.gui.skin.ColorSkin", "color":"0x00ff00"}
|
"skin":{"@type":"haxework.gui.skin.ColorSkin", "color":"0x00ff00"}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -37,12 +37,15 @@ form.json:
|
|||||||
|
|
||||||
```haxe
|
```haxe
|
||||||
@:template("form.json")
|
@:template("form.json")
|
||||||
class FormView extends VGroupView implements ViewBuilder {}
|
class FormView extends VGroupView {
|
||||||
|
@:view public var view1:IView;
|
||||||
|
@:view("view2") public var anyVarName:IView;
|
||||||
|
}
|
||||||
|
|
||||||
var form = new FormView();
|
var form = new FormView();
|
||||||
Root.bind(form); // Add form to stage as root view element.
|
Root.bind(form); // Add form to stage as root view element.
|
||||||
trace(form.view1);
|
trace(form.view1);
|
||||||
trace(form.view2);
|
trace(form.anyVarName);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Loader
|
## Loader
|
||||||
|
|||||||
1
extraParams.hxml
Normal file
1
extraParams.hxml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
--macro haxework.parser.Parser.auto()
|
||||||
@@ -2,13 +2,15 @@
|
|||||||
"name": "haxework",
|
"name": "haxework",
|
||||||
"url" : "https://bitbucket.org/shmyga/haxework.git",
|
"url" : "https://bitbucket.org/shmyga/haxework.git",
|
||||||
"license": "BSD",
|
"license": "BSD",
|
||||||
"tags": ["flash"],
|
"tags": ["flash", "openfl"],
|
||||||
"description": "Framework.",
|
"description": "Framework.",
|
||||||
"version": "0.6.1",
|
"version": "0.8.0",
|
||||||
"releasenote": "Update.",
|
"releasenote": "Update.",
|
||||||
"contributors": ["shmyga"],
|
"contributors": ["shmyga"],
|
||||||
"classPath": "src/main",
|
"classPath": "src/main",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"promhx": ""
|
"promhx": "1.1.0",
|
||||||
|
"openfl": "7.0.0",
|
||||||
|
"yaml": "1.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
-cp src
|
-cp src
|
||||||
-lib haxework
|
-cp ../../src/main
|
||||||
|
-lib yaml
|
||||||
|
-lib promhx
|
||||||
-main ViewExample.hx
|
-main ViewExample.hx
|
||||||
|
--macro haxework.parser.Parser.auto()
|
||||||
|
|
||||||
-swf target/ViewExample.swf
|
-swf target/ViewExample.swf
|
||||||
|
#-as3 target
|
||||||
@@ -1,29 +1,39 @@
|
|||||||
package;
|
package;
|
||||||
|
|
||||||
import haxework.gui.ViewBuilder;
|
import haxework.gui.View;
|
||||||
import haxework.gui.VGroupView;
|
import haxework.gui.VGroupView;
|
||||||
import haxework.gui.ButtonView;
|
import haxework.gui.ButtonView;
|
||||||
import haxework.gui.Root;
|
import haxework.gui.Root;
|
||||||
|
|
||||||
@:template("form.json")
|
|
||||||
class FormView extends VGroupView implements ViewBuilder {}
|
@:template2("form.json")
|
||||||
|
class FormView extends VGroupView {
|
||||||
|
@:view public var panel(default, null):View;
|
||||||
|
@:view public var button1(default, null):View;
|
||||||
|
@:view public var button2(default, null):View;
|
||||||
|
@:view public var button3(default, null):View;
|
||||||
|
|
||||||
|
private function init() {
|
||||||
|
trace('Init');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ViewExample {
|
class ViewExample {
|
||||||
|
|
||||||
public static function main() {
|
public static function main() {
|
||||||
new ViewExample();
|
new ViewExample();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
var form = new FormView({listener:this});
|
var form:FormView = new FormView();
|
||||||
Root.bind(form);
|
Root.bind(form);
|
||||||
trace(form.panel);
|
trace(form.panel);
|
||||||
trace(form.button1);
|
trace(form.button1);
|
||||||
trace(form.button2);
|
trace(form.button2);
|
||||||
trace(form.button3);
|
trace(form.button3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onPress(view:ButtonView):Void {
|
public function onPress(view:ButtonView):Void {
|
||||||
trace("onPress: " + view.id);
|
trace("onPress: " + view.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,8 +37,7 @@
|
|||||||
"width":100,
|
"width":100,
|
||||||
"pHeight":100,
|
"pHeight":100,
|
||||||
"skin":{"@type":"haxework.gui.skin.ButtonColorSkin", "color":"0xcc0000"},
|
"skin":{"@type":"haxework.gui.skin.ButtonColorSkin", "color":"0xcc0000"},
|
||||||
"text":"Text1",
|
"text":"Text1"
|
||||||
"onPress":"@link:listener"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"button2",
|
"id":"button2",
|
||||||
@@ -47,8 +46,7 @@
|
|||||||
"skin":{"@type":"haxework.gui.skin.ButtonColorSkin", "color":"0x00cc00"},
|
"skin":{"@type":"haxework.gui.skin.ButtonColorSkin", "color":"0x00cc00"},
|
||||||
"text":"Text2",
|
"text":"Text2",
|
||||||
"fontFamily":"Georgia",
|
"fontFamily":"Georgia",
|
||||||
"fontColor":"0xffffff",
|
"fontColor":"0xffffff"
|
||||||
"onPress":"@link:listener"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"button3",
|
"id":"button3",
|
||||||
@@ -57,8 +55,7 @@
|
|||||||
"skin":{"@type":"haxework.gui.skin.ButtonColorSkin", "color":"0x00cccc"},
|
"skin":{"@type":"haxework.gui.skin.ButtonColorSkin", "color":"0x00cccc"},
|
||||||
"text":"Text 3333333333 ddd",
|
"text":"Text 3333333333 ddd",
|
||||||
"fontFamily":"Tahoma",
|
"fontFamily":"Tahoma",
|
||||||
"fontColor":"0xff0000",
|
"fontColor":"0xff0000"
|
||||||
"onPress":"@link:listener"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package haxework.gui;
|
package haxework.gui;
|
||||||
|
|
||||||
import flash.text.TextField;
|
import flash.text.TextField;
|
||||||
import haxework.gui.IView.Content;
|
import haxework.gui.IView;
|
||||||
import flash.text.TextFormatAlign;
|
import flash.text.TextFormatAlign;
|
||||||
|
|
||||||
interface ITextView extends IView extends HasPaddings {
|
interface ITextView extends IView extends HasPaddings {
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package haxework.gui;
|
|
||||||
|
|
||||||
import haxework.gui.build.Builder;
|
|
||||||
|
|
||||||
@:remove @:autoBuild(haxework.gui.ViewBuilderImpl.build())
|
|
||||||
extern interface ViewBuilder {}
|
|
||||||
|
|
||||||
class ViewBuilderImpl {
|
|
||||||
#if macro
|
|
||||||
public static function build() {
|
|
||||||
return new Builder().build();
|
|
||||||
}
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
package haxework.gui.build;
|
|
||||||
#if macro
|
|
||||||
|
|
||||||
import haxe.macro.Context;
|
|
||||||
import haxework.gui.build.PositionJsonParser;
|
|
||||||
import haxe.macro.Expr;
|
|
||||||
import haxe.macro.Expr.Field;
|
|
||||||
|
|
||||||
class Builder {
|
|
||||||
|
|
||||||
private var templateFile:String;
|
|
||||||
private var templateKey:String;
|
|
||||||
private var styleFile:String;
|
|
||||||
|
|
||||||
private var template:Dynamic;
|
|
||||||
private var fields:Array<Field>;
|
|
||||||
private var exprs:Array<Expr>;
|
|
||||||
private var style:Dynamic;
|
|
||||||
|
|
||||||
private var i:Int;
|
|
||||||
|
|
||||||
public function new() {
|
|
||||||
var templateMeta = BuilderUtil.getMeta(":template");
|
|
||||||
var templatePath = templateMeta[0].split("@");
|
|
||||||
templateFile = Context.resolvePath(templatePath[0]);
|
|
||||||
templateKey = templatePath[1];
|
|
||||||
|
|
||||||
template = BuilderUtil.loadJsonFile(templateFile);
|
|
||||||
if (templateKey != null) template = Reflect.field(template, templateKey);
|
|
||||||
|
|
||||||
if (templateMeta[1] != null) {
|
|
||||||
styleFile = Context.resolvePath(templateMeta[1]);
|
|
||||||
style = BuilderUtil.loadJsonFile(styleFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
fields = Context.getBuildFields();
|
|
||||||
exprs = [];
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getPosition(?position:JsonKeyPosition):Position {
|
|
||||||
var min = position == null ? 1 : position.min + 32; // :-(
|
|
||||||
var max = position == null ? 1 : position.max + 32;
|
|
||||||
var file = position == null || position.file == null ? templateFile : position.file;
|
|
||||||
return Context.makePosition({min:min, max:max, file:file});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function specialValue(name:String, key:String, a:Array<String>, position:JsonKeyPosition):Dynamic {
|
|
||||||
return switch (a[0]) {
|
|
||||||
case "asset":
|
|
||||||
switch (a[1]) {
|
|
||||||
case "image":
|
|
||||||
"openfl.Assets.getBitmapData(\"" + a[2] + "\")";
|
|
||||||
case _:
|
|
||||||
a[2];
|
|
||||||
}
|
|
||||||
case "res":
|
|
||||||
var res = "haxework.provider.Provider.get(haxework.resources.IResources)." + a[1];
|
|
||||||
var bindExpr = res + ".bind(\"" + a[2] + "\", " + name + ", \"" + key + "\")";
|
|
||||||
exprs.push(Context.parse(bindExpr, getPosition(position)));
|
|
||||||
//res + ".get(\"" + a[2] + "\")";
|
|
||||||
null;
|
|
||||||
case "locale":
|
|
||||||
"new haxework.locale.LString(\"" + a[1] + "\")";
|
|
||||||
case "class":
|
|
||||||
a[1];
|
|
||||||
case "layout":
|
|
||||||
var template = BuilderUtil.loadJsonFile(a[1]);
|
|
||||||
return getValue(name, key, template, position);
|
|
||||||
case "link":
|
|
||||||
"(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")";
|
|
||||||
case _:
|
|
||||||
Context.error("Unsupported prefix \"" + a[0] + "\"", getPosition(position));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getValue(name:String, key:String, value:Dynamic, position:JsonKeyPosition):Dynamic {
|
|
||||||
return if (Std.is(value, Array)) {
|
|
||||||
value.map(function(v) {
|
|
||||||
return getValue(null, null, v, position);
|
|
||||||
});
|
|
||||||
} else if (Std.is(value, String)) {
|
|
||||||
if (value.charAt(0) == "@") {
|
|
||||||
specialValue(name, key, value.substring(1, value.length).split(":"), position);
|
|
||||||
} else if (~/(0x|#)[A-Fa-f\d]{6}/.match(value)) {
|
|
||||||
Std.parseInt(StringTools.replace(Std.string(value), "#", "0x"));
|
|
||||||
} else {
|
|
||||||
"\"" + value + "\"";
|
|
||||||
}
|
|
||||||
} else if (Std.is(value, Float) || (Std.is(value, Bool))) {
|
|
||||||
value;
|
|
||||||
} else if (value != null) {
|
|
||||||
if (Reflect.hasField(value, "@type")) {
|
|
||||||
var n = "a" + i++;
|
|
||||||
var type = Reflect.field(value, "@type");
|
|
||||||
if (type == "Dynamic") {
|
|
||||||
//ToDo:
|
|
||||||
exprs.push(Context.parse("var " + n + " = cast {}", getPosition(position)));
|
|
||||||
} else {
|
|
||||||
exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition(position)));
|
|
||||||
}
|
|
||||||
createElement(value, n);
|
|
||||||
n;
|
|
||||||
} else {
|
|
||||||
Context.error("Need @type field", getPosition(position));
|
|
||||||
null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createElement(template:Dynamic, name:String):String {
|
|
||||||
if (Reflect.hasField(template, "@style")) {
|
|
||||||
var s = Reflect.field(style, Reflect.field(template, "@style"));
|
|
||||||
for (key in Reflect.fields(s)) {
|
|
||||||
if (key.charAt(0) != "$" && !Reflect.hasField(template, key)) {
|
|
||||||
Reflect.setField(template, key, Reflect.field(s, key));
|
|
||||||
Reflect.setField(template, "$" + key, Reflect.field(s, "$" + key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Reflect.hasField(template, "id")) {
|
|
||||||
var id = Reflect.field(template, "id");
|
|
||||||
var type = Reflect.field(template, "@type");
|
|
||||||
var expr = Context.parse("var a:" + type, getPosition());
|
|
||||||
var complexType = switch (expr.expr) {
|
|
||||||
case EVars(vars): vars[0].type;
|
|
||||||
case _: null;
|
|
||||||
}
|
|
||||||
fields.push({
|
|
||||||
name: id,
|
|
||||||
access: [APublic],
|
|
||||||
pos: getPosition(),
|
|
||||||
kind: FProp("default", "null", complexType)
|
|
||||||
});
|
|
||||||
exprs.push(Context.parse("this." + id + " = " + name, getPosition()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (key in Reflect.fields(template)) {
|
|
||||||
if (key.charAt(0) == "$" || key.charAt(0) == "@") continue;
|
|
||||||
var position = Reflect.field(template, "$" + key);
|
|
||||||
var value = getValue(name, key, Reflect.field(template, key), position);
|
|
||||||
if (value != null) {
|
|
||||||
exprs.push(Context.parse(name + "." + key + " = " + value, getPosition(position)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function build():Array<Field> {
|
|
||||||
createElement(template, "this");
|
|
||||||
|
|
||||||
var init = false;
|
|
||||||
for (f in fields) if (f.name == "init") {
|
|
||||||
init = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.push({
|
|
||||||
name: "build",
|
|
||||||
access: [APublic],
|
|
||||||
pos: getPosition(),
|
|
||||||
kind: FFun({
|
|
||||||
args: [{name:"links", type:TPath({name:"Dynamic", pack:[], params:[]}), opt:true, value:null}],
|
|
||||||
expr: macro $b{exprs},
|
|
||||||
params: [],
|
|
||||||
ret: null
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
var contstrExprs = [];
|
|
||||||
contstrExprs.push(macro super());
|
|
||||||
contstrExprs.push(macro build(links));
|
|
||||||
if (init) contstrExprs.push(macro init());
|
|
||||||
|
|
||||||
fields.push({
|
|
||||||
name: "new",
|
|
||||||
access: [APublic],
|
|
||||||
pos: getPosition(),
|
|
||||||
kind: FFun({
|
|
||||||
args: [{name:"links", type:TPath({name:"Dynamic", pack:[], params:[]}), opt:true, value:null}],
|
|
||||||
expr: macro $b{contstrExprs},
|
|
||||||
params: [],
|
|
||||||
ret: null
|
|
||||||
})
|
|
||||||
});
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#end
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package haxework.gui.build;
|
|
||||||
#if macro
|
|
||||||
|
|
||||||
import haxe.macro.Context;
|
|
||||||
import haxe.macro.Expr.Constant;
|
|
||||||
import haxe.macro.Expr.ExprDef;
|
|
||||||
|
|
||||||
class BuilderUtil {
|
|
||||||
|
|
||||||
public static function loadJsonFile(path:String) {
|
|
||||||
Context.registerModuleDependency(Context.getLocalModule(), path);
|
|
||||||
var content = sys.io.File.getContent(path);
|
|
||||||
var json = null;
|
|
||||||
try {
|
|
||||||
json = PositionJsonParser.parse(content, path);
|
|
||||||
} catch(error:Dynamic) {
|
|
||||||
Context.error(error, Context.makePosition({min:0, max:0, file:path}));
|
|
||||||
}
|
|
||||||
Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getMeta(key:String):Array<String> {
|
|
||||||
var c = Context.getLocalClass().get();
|
|
||||||
for (meta in c.meta.get()) {
|
|
||||||
if (meta.name == key) {
|
|
||||||
return meta.params.map(function(param) return switch(param.expr) {
|
|
||||||
case ExprDef.EConst(Constant.CString(value)): value;
|
|
||||||
case _: null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#end
|
|
||||||
@@ -39,7 +39,7 @@ class ListView<D> extends GroupView implements ScrollListener {
|
|||||||
private var mask:SpriteView;
|
private var mask:SpriteView;
|
||||||
private var itemSize:Float;
|
private var itemSize:Float;
|
||||||
|
|
||||||
private var items:Array<IListItemView<D>>;
|
public var items(default, null):Array<IListItemView<D>>;
|
||||||
private var itemsListeners:Map<IListItemView<D>, MouseEvent->Void>;
|
private var itemsListeners:Map<IListItemView<D>, MouseEvent->Void>;
|
||||||
|
|
||||||
public function new(layout:ILayout, otherLayout:ILayout) {
|
public function new(layout:ILayout, otherLayout:ILayout) {
|
||||||
@@ -241,7 +241,7 @@ interface IListItemView<D> extends IView {
|
|||||||
public var data(default, set):D;
|
public var data(default, set):D;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ListViewListener<D> {
|
typedef ListViewListener<D> = {
|
||||||
public function onListItemClick(item:IListItemView<D>):Void;
|
public function onListItemClick(item:IListItemView<D>):Void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package haxework.gui.skin;
|
package haxework.gui.skin;
|
||||||
|
|
||||||
import haxework.gui.IView.Content;
|
import haxework.gui.IView;
|
||||||
|
|
||||||
interface ISkin<V:IView> {
|
interface ISkin<V:IView> {
|
||||||
public function draw(view:V):Void;
|
public function draw(view:V):Void;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class SocketLogger extends BaseLogger {
|
|||||||
try {
|
try {
|
||||||
var s:String = p.fileName + ":" + p.lineNumber + ": " + text + "\n";
|
var s:String = p.fileName + ":" + p.lineNumber + ": " + text + "\n";
|
||||||
#if flash
|
#if flash
|
||||||
socket.writeUTF(s);
|
socket.writeUTFBytes(s);
|
||||||
socket.flush();
|
socket.flush();
|
||||||
#else
|
#else
|
||||||
socket.write(s);
|
socket.write(s);
|
||||||
|
|||||||
45
src/main/haxework/macro/FileUtil.hx
Normal file
45
src/main/haxework/macro/FileUtil.hx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package haxework.macro;
|
||||||
|
|
||||||
|
import yaml.Parser;
|
||||||
|
import yaml.Yaml;
|
||||||
|
import haxe.macro.Context;
|
||||||
|
|
||||||
|
|
||||||
|
class FileUtil {
|
||||||
|
|
||||||
|
public static function loadJsonFile(path:String):Dynamic {
|
||||||
|
Context.registerModuleDependency(Context.getLocalModule(), path);
|
||||||
|
var content = sys.io.File.getContent(path);
|
||||||
|
var json = null;
|
||||||
|
try {
|
||||||
|
json = PositionJsonParser.parse(content, path);
|
||||||
|
} catch (error:Dynamic) {
|
||||||
|
Context.error(error, Context.makePosition({min:0, max:0, file:path}));
|
||||||
|
}
|
||||||
|
Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function loadYamlFile(path:String):Dynamic {
|
||||||
|
Context.registerModuleDependency(Context.getLocalModule(), path);
|
||||||
|
var content = sys.io.File.getContent(path);
|
||||||
|
var result = null;
|
||||||
|
try {
|
||||||
|
// ToDo: extract poisiton info
|
||||||
|
result = Yaml.parse(content, Parser.options().useObjects());
|
||||||
|
} catch (error:Dynamic) {
|
||||||
|
Context.error(error, Context.makePosition({min:0, max:0, file:path}));
|
||||||
|
}
|
||||||
|
//Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function loadFile(path:String):Dynamic {
|
||||||
|
var ext = path.split('.').pop();
|
||||||
|
return switch(ext) {
|
||||||
|
case 'json': loadJsonFile(path);
|
||||||
|
case 'yml' | 'yaml': loadYamlFile(path);
|
||||||
|
case x: throw 'Unsupported file format: "${x}"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package haxework.gui.build;
|
package haxework.macro;
|
||||||
|
|
||||||
typedef JsonKeyPosition = {
|
typedef JsonKeyPosition = {
|
||||||
var min:Int;
|
var min:Int;
|
||||||
52
src/main/haxework/macro/ProvideMacro.hx
Normal file
52
src/main/haxework/macro/ProvideMacro.hx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package haxework.macro;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
|
||||||
|
|
||||||
|
class ProvideMacro {
|
||||||
|
|
||||||
|
public static function has(field:Field):Bool {
|
||||||
|
for (md in field.meta) if (md.name == ":provide") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var field:Field;
|
||||||
|
|
||||||
|
public function new(field:Field) {
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply():Array<Field> {
|
||||||
|
var result:Array<Field> = [];
|
||||||
|
var type:ComplexType = switch field.kind {
|
||||||
|
case FieldType.FVar(t): t;
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
var name:String = switch type {
|
||||||
|
case ComplexType.TPath(p): p.name;
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
result.push({
|
||||||
|
name: field.name,
|
||||||
|
access: [Access.APublic],
|
||||||
|
pos: field.pos,
|
||||||
|
kind: FieldType.FProp('get', 'never', type)
|
||||||
|
});
|
||||||
|
result.push({
|
||||||
|
name: 'get_${field.name}',
|
||||||
|
access: [Access.APrivate, Access.AInline],
|
||||||
|
pos: field.pos,
|
||||||
|
kind: FieldType.FFun({
|
||||||
|
args: [],
|
||||||
|
expr: Context.parse('return haxework.provider.Provider.get(${name})', field.pos),
|
||||||
|
params: [],
|
||||||
|
ret: type
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
232
src/main/haxework/macro/TemplateMacro.hx
Normal file
232
src/main/haxework/macro/TemplateMacro.hx
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
package haxework.macro;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
import haxe.macro.Type;
|
||||||
|
import haxework.macro.PositionJsonParser;
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateMacro {
|
||||||
|
|
||||||
|
private static inline var metaName:String = ':template';
|
||||||
|
|
||||||
|
public static function has(classType:ClassType):Bool {
|
||||||
|
for (md in classType.meta.get()) if (md.name == metaName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var classType:ClassType;
|
||||||
|
private var fields:Array<Field>;
|
||||||
|
private var bindings:Map<String, String>;
|
||||||
|
|
||||||
|
private var meta(get, never):MetadataEntry;
|
||||||
|
private var templateFile:String;
|
||||||
|
private var template:Dynamic;
|
||||||
|
private var style:Dynamic;
|
||||||
|
private var i:Int;
|
||||||
|
|
||||||
|
private function get_meta():MetadataEntry {
|
||||||
|
for (md in classType.meta.get()) if (md.name == metaName) {
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new(classType:ClassType, fields:Array<Field>) {
|
||||||
|
this.classType = classType;
|
||||||
|
this.fields = fields;
|
||||||
|
var params = Util.getMetaParams(meta);
|
||||||
|
templateFile = Context.resolvePath(params[0]);
|
||||||
|
template = FileUtil.loadFile(templateFile);
|
||||||
|
if (params.length > 1) {
|
||||||
|
var styleFile = params.length > 1 ? Context.resolvePath(params[1]) : null;
|
||||||
|
style = FileUtil.loadFile(styleFile);
|
||||||
|
}
|
||||||
|
bindings = findViewsBindings(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static function getSpecField(object:Dynamic, field:String):Dynamic {
|
||||||
|
if (Reflect.hasField(object, "@" + field)) {
|
||||||
|
return Reflect.field(object, "@" + field);
|
||||||
|
} else if (Reflect.hasField(object, "$" + field)) {
|
||||||
|
return Reflect.field(object, "$" + field);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPosition(?position:JsonKeyPosition):Position {
|
||||||
|
var min = position == null ? 1 : position.min + 32; // :-(
|
||||||
|
var max = position == null ? 1 : position.max + 32;
|
||||||
|
var file = position == null || position.file == null ? templateFile : position.file;
|
||||||
|
return Context.makePosition({min:min, max:max, file:file});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function specialValue(name:String, key:String, a:Array<String>, position:JsonKeyPosition, exprs:Array<Expr>):Dynamic {
|
||||||
|
return switch (a[0]) {
|
||||||
|
case "asset":
|
||||||
|
switch (a[1]) {
|
||||||
|
case "image":
|
||||||
|
"openfl.Assets.getBitmapData(\"" + a[2] + "\")";
|
||||||
|
case _:
|
||||||
|
a[2];
|
||||||
|
}
|
||||||
|
case "res":
|
||||||
|
var res = "haxework.provider.Provider.get(haxework.resources.IResources)." + a[1];
|
||||||
|
var bindExpr = res + ".bind(\"" + a[2] + "\", " + name + ", \"" + key + "\")";
|
||||||
|
exprs.push(Context.parse(bindExpr, getPosition(position)));
|
||||||
|
//res + ".get(\"" + a[2] + "\")";
|
||||||
|
null;
|
||||||
|
case "locale":
|
||||||
|
"new haxework.locale.LString(\"" + a[1] + "\")";
|
||||||
|
case "class":
|
||||||
|
a[1];
|
||||||
|
case "layout":
|
||||||
|
var template = FileUtil.loadJsonFile(a[1]);
|
||||||
|
return createValue(name, key, template, position, exprs);
|
||||||
|
case "link":
|
||||||
|
"(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")";
|
||||||
|
case _:
|
||||||
|
Context.error("Unsupported prefix \"" + a[0] + "\"", getPosition(position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getType(value:Dynamic, position:Position):String {
|
||||||
|
var type:String = getSpecField(value, "type");
|
||||||
|
if (type == null) {
|
||||||
|
Context.error("Need @type field", position);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createValue(name:String, key:String, value:Dynamic, position:JsonKeyPosition, exprs:Array<Expr>):Dynamic {
|
||||||
|
return if (Std.is(value, Array)) {
|
||||||
|
value.map(function(v) {
|
||||||
|
return createValue(null, null, v, position, exprs);
|
||||||
|
});
|
||||||
|
} else if (Std.is(value, String)) {
|
||||||
|
if (value.charAt(0) == "@" || value.charAt(0) == "$") {
|
||||||
|
specialValue(name, key, value.substring(1, value.length).split(":"), position, exprs);
|
||||||
|
} else if (~/(0x|#)[A-Fa-f\d]{6}/.match(value)) {
|
||||||
|
Std.parseInt(StringTools.replace(Std.string(value), "#", "0x"));
|
||||||
|
} else {
|
||||||
|
"\"" + value + "\"";
|
||||||
|
}
|
||||||
|
} else if (Std.is(value, Float) || (Std.is(value, Bool))) {
|
||||||
|
value;
|
||||||
|
} else if (value != null) {
|
||||||
|
var type = getType(value, getPosition(position));
|
||||||
|
if (type != null) {
|
||||||
|
var n = 'a${i++}';
|
||||||
|
if (type == "Dynamic") {
|
||||||
|
//ToDo:
|
||||||
|
exprs.push(Context.parse("var " + n + " = cast {}", getPosition(position)));
|
||||||
|
} else {
|
||||||
|
exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition(position)));
|
||||||
|
}
|
||||||
|
createElement(n, value, exprs);
|
||||||
|
n;
|
||||||
|
} else {
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createElement(name:String, data:Dynamic, exprs:Array<Expr>):String {
|
||||||
|
var s = getSpecField(data, "style");
|
||||||
|
if (s != null) {
|
||||||
|
var s = Reflect.field(style, s);
|
||||||
|
for (key in Reflect.fields(s)) {
|
||||||
|
if (key.charAt(0) != "$" && !Reflect.hasField(data, key)) {
|
||||||
|
Reflect.setField(data, key, Reflect.field(s, key));
|
||||||
|
Reflect.setField(data, "$" + key, Reflect.field(s, "$" + key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Reflect.hasField(data, "id")) {
|
||||||
|
var id = Reflect.field(data, "id");
|
||||||
|
if (bindings.exists(id)) {
|
||||||
|
var bind = bindings.get(id);
|
||||||
|
exprs.push(Context.parse('this.${bind} = ${name}', getPosition()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key in Reflect.fields(data)) {
|
||||||
|
if (key.charAt(0) == "$" || key.charAt(0) == "@") continue;
|
||||||
|
var position = Reflect.field(data, "$" + key);
|
||||||
|
var value = createValue(name, key, Reflect.field(data, key), position, exprs);
|
||||||
|
if (value != null) {
|
||||||
|
exprs.push(Context.parse(name + "." + key + " = " + value, getPosition(position)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildBuild(exprs:Array<Expr>):Field {
|
||||||
|
return {
|
||||||
|
name: "build",
|
||||||
|
access: [Access.APrivate],
|
||||||
|
pos: getPosition(),
|
||||||
|
kind: FieldType.FFun({
|
||||||
|
args: [],
|
||||||
|
expr: macro $b{exprs},
|
||||||
|
params: [],
|
||||||
|
ret: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildConstructor(init:Bool):Field {
|
||||||
|
var contstrExprs = [];
|
||||||
|
contstrExprs.push(macro super());
|
||||||
|
contstrExprs.push(macro build());
|
||||||
|
if (init) contstrExprs.push(macro init());
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "new",
|
||||||
|
access: [Access.APublic],
|
||||||
|
pos: getPosition(),
|
||||||
|
kind: FieldType.FFun({
|
||||||
|
args: [],
|
||||||
|
expr: macro $b{contstrExprs},
|
||||||
|
params: [],
|
||||||
|
ret: null
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function findViewsBindings(fields:Array<Field>):Map<String, String> {
|
||||||
|
var result:Map<String, String> = new Map();
|
||||||
|
for (field in fields) if (field.meta != null) {
|
||||||
|
for (meta in field.meta) {
|
||||||
|
if (meta.name == ':view') {
|
||||||
|
var viewId:String = meta.params.length == 0 ? field.name : switch meta.params[0].expr {
|
||||||
|
case ExprDef.EConst(Constant.CString(value)): value;
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
result.set(viewId, field.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply():Array<Field> {
|
||||||
|
i = 0;
|
||||||
|
var result:Array<Field> = fields.slice(0);
|
||||||
|
var exprs:Array<Expr> = [];
|
||||||
|
var init = Lambda.exists(result, function(f) return f.name == 'init');
|
||||||
|
createElement("this", template, exprs);
|
||||||
|
result.push(buildBuild(exprs));
|
||||||
|
result.push(buildConstructor(init));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/haxework/macro/Util.hx
Normal file
18
src/main/haxework/macro/Util.hx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package haxework.macro;
|
||||||
|
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
|
||||||
|
|
||||||
|
class Util {
|
||||||
|
|
||||||
|
public static function getMetaParams(meta:MetadataEntry):Array<String> {
|
||||||
|
return meta.params.map(function(param:Expr) return switch(param.expr) {
|
||||||
|
case ExprDef.EConst(Constant.CString(value)): value;
|
||||||
|
case _: null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline static function DynamicType():ComplexType {
|
||||||
|
return ComplexType.TPath({name:'Dynamic', pack:[], params:[]});
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/main/haxework/parser/Parser.hx
Normal file
50
src/main/haxework/parser/Parser.hx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package haxework.parser;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
import haxe.macro.Type.ClassType;
|
||||||
|
import haxe.macro.Type.Ref;
|
||||||
|
import haxe.macro.Type;
|
||||||
|
import haxework.macro.ProvideMacro;
|
||||||
|
import haxework.macro.TemplateMacro;
|
||||||
|
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
|
||||||
|
private static function auto():Void {
|
||||||
|
haxe.macro.Compiler.addGlobalMetadata("", "@:build(haxework.parser.Parser.autoRun())", true, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static macro function autoRun():Array<Field> {
|
||||||
|
var t:Type = Context.getLocalType();
|
||||||
|
switch (t) {
|
||||||
|
case null: return null;
|
||||||
|
case Type.TInst(_.get() => ct, _):
|
||||||
|
var modify:Bool = false;
|
||||||
|
var fields:Array<Field> = Context.getBuildFields();
|
||||||
|
var result:Array<Field> = [];
|
||||||
|
var appends:Array<Field> = [];
|
||||||
|
// process fields meta
|
||||||
|
for (field in fields) {
|
||||||
|
if (ProvideMacro.has(field)) {
|
||||||
|
modify = true;
|
||||||
|
var provide = new ProvideMacro(field);
|
||||||
|
result = result.concat(provide.apply());
|
||||||
|
} else {
|
||||||
|
result.push(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modify) {
|
||||||
|
fields = result;
|
||||||
|
}
|
||||||
|
// process class meta
|
||||||
|
if (TemplateMacro.has(ct)) {
|
||||||
|
modify = true;
|
||||||
|
var template = new TemplateMacro(ct, fields);
|
||||||
|
fields = template.apply();
|
||||||
|
}
|
||||||
|
return modify ? fields : null;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +1,36 @@
|
|||||||
package haxework.provider;
|
package haxework.provider;
|
||||||
|
|
||||||
import haxe.ds.ObjectMap;
|
|
||||||
|
|
||||||
class Provider {
|
class Provider {
|
||||||
|
|
||||||
//private static var factories:ObjectMap<Dynamic, Class<Dynamic>> = new ObjectMap<Dynamic, Class<Dynamic>>();
|
private static function key<T>(i:Class<T>, ?type:Dynamic):String {
|
||||||
private static var factories:ObjectMap<Dynamic, Class<Dynamic>> = cast [
|
var result = Type.getClassName(i);
|
||||||
|
if (type != null) result += ':${type}';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//private static var factories:Map<String, Class<Dynamic>> = new Map();
|
||||||
|
private static var factories:Map<String, Class<Dynamic>> = cast [
|
||||||
#if (!neko)
|
#if (!neko)
|
||||||
haxework.net.manage.LoaderManager => haxework.net.manage.ILoaderManager
|
key(haxework.net.manage.LoaderManager) => haxework.net.manage.ILoaderManager
|
||||||
#end
|
#end
|
||||||
];
|
];
|
||||||
|
|
||||||
private static var args:ObjectMap<Dynamic, Array<Dynamic>> = new ObjectMap<Dynamic, Array<Dynamic>>();
|
private static var args:Map<String, Array<Dynamic>> = new Map();
|
||||||
private static var instances:ObjectMap<Dynamic, Dynamic> = new ObjectMap<Dynamic, Dynamic>();
|
private static var instances:Map<String, Dynamic> = new Map();
|
||||||
|
|
||||||
public static function setFactory<T>(i:Class<T>, clazz:Class<T>, ?type:Dynamic, ?args:Array<Dynamic>):Void {
|
public static function setFactory<T>(i:Class<T>, clazz:Class<T>, ?type:Dynamic, ?args:Array<Dynamic>):Void {
|
||||||
factories.set(type == null ? i : i + type, clazz);
|
var key = key(i, type);
|
||||||
if (args != null) Provider.args.set(type == null ? i : i + type, args);
|
factories.set(key, clazz);
|
||||||
|
if (args != null) Provider.args.set(key, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function set<T>(i:Class<T>, instance:T, ?type:Dynamic):Void {
|
public static function set<T>(i:Class<T>, instance:T, ?type:Dynamic):Void {
|
||||||
instances.set(type == null ? i : i + type, instance);
|
var key = key(i, type);
|
||||||
|
instances.set(key, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get<T>(i:Class<T>, ?type:Dynamic):T {
|
public static function get<T>(i:Class<T>, ?type:Dynamic):T {
|
||||||
var key:Dynamic = (type == null) ? i : i + type;
|
var key = key(i, type);
|
||||||
if (instances.exists(key)) {
|
if (instances.exists(key)) {
|
||||||
return instances.get(key);
|
return instances.get(key);
|
||||||
} else if (factories.exists(key)) {
|
} else if (factories.exists(key)) {
|
||||||
@@ -32,17 +38,17 @@ class Provider {
|
|||||||
instances.set(key, instance);
|
instances.set(key, instance);
|
||||||
return instance;
|
return instance;
|
||||||
} else {
|
} else {
|
||||||
throw "Factory for\" " + i + "\" not found";
|
throw 'Factory for "${key}" not found';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function build<T>(i:Class<T>, ?type:Dynamic):T {
|
public static function build<T>(i:Class<T>, ?type:Dynamic):T {
|
||||||
var key:Dynamic = (type == null) ? i : type;
|
var key = key(i, type);
|
||||||
if (factories.exists(key)) {
|
if (factories.exists(key)) {
|
||||||
var instance:T = Type.createInstance(factories.get(key), args.exists(key) ? args.get(key) : []);
|
var instance:T = Type.createInstance(factories.get(key), args.exists(key) ? args.get(key) : []);
|
||||||
return instance;
|
return instance;
|
||||||
} else {
|
} else {
|
||||||
throw "Factory for\"" + i + "\" not found";
|
throw 'Factory for "${key}" not found';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user