view builder update
This commit is contained in:
@@ -1,160 +0,0 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.resources.Resources.ResMap;
|
||||
import haxework.resources.IResources;
|
||||
import haxework.provider.Provider;
|
||||
import openfl.Assets;
|
||||
import haxework.locale.LString;
|
||||
import haxe.Json;
|
||||
import flash.errors.TypeError;
|
||||
import flash.errors.Error;
|
||||
|
||||
enum Value {
|
||||
SIMPLE(v:Dynamic);
|
||||
RESOURCE(res:ResMap<Dynamic>, key:String);
|
||||
LINK(key:String);
|
||||
BUILDER(v:Builder);
|
||||
ARRAY(v:Array<Value>);
|
||||
}
|
||||
|
||||
class Field {
|
||||
|
||||
public var key(default, null):String;
|
||||
public var value(default, null):Value;
|
||||
|
||||
public function new(key:String, value:Dynamic) {
|
||||
this.key = key;
|
||||
this.value = fieldValue(value);
|
||||
}
|
||||
|
||||
public static function buildValue(value:Value, ?links:Dynamic):Dynamic {
|
||||
return switch (value) {
|
||||
case Value.SIMPLE(v):
|
||||
v;
|
||||
case Value.BUILDER(v):
|
||||
v.build(links);
|
||||
case Value.ARRAY(v):
|
||||
v.map(function(i) return buildValue(i, links));
|
||||
case Value.RESOURCE(r, k):
|
||||
r.get(k);
|
||||
case Value.LINK(k):
|
||||
Reflect.field(links, k);
|
||||
}
|
||||
}
|
||||
|
||||
private static function specialValue(value):Value {
|
||||
var a = value.split(":");
|
||||
return switch (a[0]) {
|
||||
case "asset":
|
||||
switch (a[1]) {
|
||||
case "image":
|
||||
Value.SIMPLE(Assets.getBitmapData(a[2]));
|
||||
case _:
|
||||
Value.SIMPLE(value);
|
||||
}
|
||||
case "res":
|
||||
//Reflect.field(Provider.get(IResources), a[1]).bind(a[2], object, field);
|
||||
Value.RESOURCE(Reflect.field(Provider.get(IResources), a[1]), a[2]);
|
||||
case "locale":
|
||||
Value.SIMPLE(new LString(a[1]));
|
||||
case "class":
|
||||
Value.SIMPLE(Type.resolveClass(a[1]));
|
||||
case "layout":
|
||||
var data = Json.parse(Assets.getText(a[1]));
|
||||
Value.BUILDER(new Builder(data));
|
||||
case "link":
|
||||
Value.LINK(a[1]); //ToDo
|
||||
case _:
|
||||
throw new Error("Unsupported prefix \"" + a[0] + "\"");
|
||||
//Value.SIMPLE(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static function fieldValue(value:Dynamic):Value {
|
||||
return if (Std.is(value, Array)) {
|
||||
Value.ARRAY(value.map(fieldValue));
|
||||
} else if (Std.is(value, String)) {
|
||||
if (value.charAt(0) == "@") {
|
||||
specialValue(value.substr(1));
|
||||
} else if (~/0x[A-Fa-f\d]{6}/.match(value)) {
|
||||
Value.SIMPLE(Std.parseInt(value));
|
||||
} else {
|
||||
Value.SIMPLE(value);
|
||||
}
|
||||
} else if (Std.is(value, Float) || (Std.is(value, Bool))) {
|
||||
Value.SIMPLE(value);
|
||||
} else if (value != null) {
|
||||
if (Reflect.hasField(value, "type")) {
|
||||
Value.BUILDER(new Builder(value));
|
||||
} else {
|
||||
Value.SIMPLE(value);
|
||||
}
|
||||
} else {
|
||||
Value.SIMPLE(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@:deprecated("Use haxework.gui.ViewBuilder")
|
||||
class Builder {
|
||||
|
||||
private var factory:Class<Dynamic>;
|
||||
private var fields:Map<String, Field>;
|
||||
|
||||
public function new(data:Dynamic) {
|
||||
if (Reflect.hasField(data, "type")) {
|
||||
var type = Reflect.field(data, "type");
|
||||
this.factory = Type.resolveClass(type);
|
||||
if (factory == null) throw new TypeError("Class \"" + type + "\" not found");
|
||||
}
|
||||
this.fields = new Map<String, Field>();
|
||||
if (Reflect.hasField(data, "style")) {
|
||||
var style = Provider.get(IResources).styles.get(Reflect.field(data, "style"));
|
||||
for (key in Reflect.fields(style)) {
|
||||
fields.set(key, new Field(key, Reflect.field(style, key)));
|
||||
}
|
||||
}
|
||||
for (key in Reflect.fields(data)) {
|
||||
switch (key) {
|
||||
case "type":
|
||||
case "style":
|
||||
case _: fields.set(key, new Field(key, Reflect.field(data, key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function build(?links:Dynamic):Dynamic {
|
||||
var instance:Dynamic = Type.createInstance(factory, []);
|
||||
instance = fill(instance, links);
|
||||
var initMethod:Dynamic = Reflect.field(instance, "init");
|
||||
if (initMethod != null) Reflect.callMethod(instance, initMethod, []);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public function fill(instance:Dynamic, ?links:Dynamic):Dynamic {
|
||||
for (field in fields) {
|
||||
Reflect.setProperty(instance, field.key, Field.buildValue(field.value, links));
|
||||
switch (field.value) {
|
||||
case Value.RESOURCE(r, k):
|
||||
r.bind(k, instance, field.key);
|
||||
case _:
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
private static var cache:Map<String, Builder> = new Map<String, Builder>();
|
||||
|
||||
public static function createFromAsset(asset:String, ?key:String):Dynamic {
|
||||
var cacheKey = asset + ":" + key;
|
||||
if (!cache.exists(cacheKey)) {
|
||||
var data = Json.parse(openfl.Assets.getText(asset));
|
||||
if (key != null) {
|
||||
data = Reflect.field(data, key);
|
||||
}
|
||||
cache.set(cacheKey, new Builder(data));
|
||||
}
|
||||
return cache.get(cacheKey);
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
package haxework.gui;
|
||||
|
||||
|
||||
//ToDo:
|
||||
import haxework.locale.LString;
|
||||
import haxe.Json;
|
||||
import flash.errors.Error;
|
||||
import openfl.Assets;
|
||||
import flash.errors.TypeError;
|
||||
import flash.errors.ArgumentError;
|
||||
import haxework.resources.IResources;
|
||||
import haxework.provider.Provider;
|
||||
import haxework.gui.View;
|
||||
import haxework.gui.SpriteView;
|
||||
import haxework.gui.MovieView;
|
||||
import haxework.gui.GroupView;
|
||||
import haxework.gui.HGroupView;
|
||||
import haxework.gui.VGroupView;
|
||||
import haxework.gui.TextView;
|
||||
import haxework.gui.InputView;
|
||||
import haxework.gui.LabelView;
|
||||
import haxework.gui.ButtonView;
|
||||
import haxework.gui.ToggleButtonView;
|
||||
import haxework.gui.ProgressView;
|
||||
import haxework.gui.AnimateView;
|
||||
import haxework.gui.skin.ColorSkin;
|
||||
import haxework.gui.skin.BitmapSkin;
|
||||
import haxework.gui.skin.ButtonColorSkin;
|
||||
import haxework.gui.skin.ButtonBitmapSkin;
|
||||
import haxework.gui.skin.ProgressSkin;
|
||||
import haxework.gui.frame.FrameSwitcher;
|
||||
|
||||
@:deprecated("Use haxework.gui.ViewBuilder")
|
||||
class GuiBuilder {
|
||||
|
||||
private static var cache:Map<String, Dynamic> = new Map<String, Dynamic>();
|
||||
|
||||
private static function getResource(resource:String):Dynamic {
|
||||
if (!cache.exists(resource)) {
|
||||
cache.set(resource, Json.parse(Assets.getText(resource)));
|
||||
}
|
||||
return cache.get(resource);
|
||||
}
|
||||
|
||||
public static function build(data:Dynamic, ?links:Dynamic):Dynamic {
|
||||
return new GuiB(data, links, data._includes).build();
|
||||
}
|
||||
|
||||
public static function fill(object:Dynamic, data:Dynamic, ?links:Dynamic):Void {
|
||||
new GuiF(object, data, links, data._includes).fill();
|
||||
}
|
||||
|
||||
public static function buildFromAssets(resource:String, ?key:String = null, ?links:Dynamic = null):Dynamic {
|
||||
var form:Dynamic = getResource(resource);
|
||||
if (key != null) form = Reflect.field(form, key);
|
||||
return build(form, links);
|
||||
}
|
||||
|
||||
public static function fillFromAssets(object:Dynamic, resource:String, ?key:String = null, ?links:Dynamic = null):Void {
|
||||
var form:Dynamic = getResource(resource);
|
||||
if (key != null) form = Reflect.field(form, key);
|
||||
fill(object, form, links);
|
||||
}
|
||||
}
|
||||
|
||||
class GuiB {
|
||||
|
||||
private var data:Dynamic;
|
||||
private var links:Dynamic;
|
||||
private var includes:Dynamic;
|
||||
|
||||
public function new(data:Dynamic, ?links:Dynamic, ?includes:Dynamic) {
|
||||
this.data = data;
|
||||
this.links = links;
|
||||
this.includes = includes;
|
||||
}
|
||||
|
||||
public function build():Dynamic {
|
||||
if (Reflect.hasField(data, "type")) {
|
||||
var type:String = data.type;
|
||||
//Reflect.deleteField(data, "type");
|
||||
if (type.charAt(0) == "~") return Type.resolveClass(type.substr(1));
|
||||
var object:Dynamic = instance(type);
|
||||
new GuiF(object, data, links, includes).fill();
|
||||
var initMethod:Dynamic = Reflect.field(object, "init");
|
||||
if (initMethod != null) Reflect.callMethod(object, initMethod, []);
|
||||
return object;
|
||||
} else if (Std.is(data, String)) {
|
||||
return GuiF.convertString(data, links);
|
||||
} else {
|
||||
new GuiF(data, data, links, includes).fill();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
private static function instance(type:String):Dynamic {
|
||||
var clazz:Class<Dynamic> = Type.resolveClass(type);
|
||||
if (clazz == null) throw new TypeError("Class \"" + type + "\" not found");
|
||||
var instance:Dynamic = Type.createInstance(clazz, []);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
class GuiF {
|
||||
|
||||
private var object:Dynamic;
|
||||
private var data:Dynamic;
|
||||
private var links:Dynamic;
|
||||
private var includes:Dynamic;
|
||||
|
||||
public function new(object:Dynamic, data:Dynamic, ?links:Dynamic, ?includes:Dynamic) {
|
||||
this.object = object;
|
||||
this.data = data;
|
||||
this.links = links;
|
||||
this.includes = includes;
|
||||
}
|
||||
|
||||
public static function convertString(value:Dynamic, ?links:Dynamic):Dynamic {
|
||||
var s:String = cast(value, String);
|
||||
var c:String = s.charAt(0);
|
||||
if (c == "#") {
|
||||
value = Reflect.field(links, s.substr(1));
|
||||
} else if (c == "~") {
|
||||
var a:Array<String> = s.substr(1).split(":");
|
||||
var e:Enum<Dynamic> = Type.resolveEnum(a[0]);
|
||||
value = Type.createEnum(e, a[1]);
|
||||
} else if (c == "@") {
|
||||
if (s.charAt(1) == "~") {
|
||||
var a:Array<String> = s.substr(2).split(":");
|
||||
switch (a[0]) {
|
||||
case "image": value = Assets.getBitmapData(a[1]);
|
||||
case "layout": value = GuiBuilder.buildFromAssets(a[1], null, links);
|
||||
}
|
||||
} else {
|
||||
var a:Array<String> = s.substr(1).split(":");
|
||||
value = Reflect.field(Provider.get(IResources), a[0]).get(a[1]);
|
||||
}
|
||||
} else if (~/0x[A-Fa-f\d]{6}/.match(value)) {
|
||||
value = Std.parseInt(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public function fill() {
|
||||
if (Reflect.hasField(data, "style")) {
|
||||
var style = Provider.get(IResources).styles.get(Reflect.field(data, "style"));
|
||||
for (key in Reflect.fields(style)) if (!Reflect.hasField(data, key)) {
|
||||
Reflect.setField(data, key, Reflect.field(style, key));
|
||||
}
|
||||
}
|
||||
var fields:Array<String> = Reflect.fields(data);
|
||||
for (field in fields) {
|
||||
if (field == "type" || field == "style") continue;
|
||||
if (field == "_includes") continue;
|
||||
var value:Dynamic = Reflect.field(data, field);
|
||||
if (field == "_include") {
|
||||
var data:Dynamic = includes == null ? null : Reflect.field(includes, value);
|
||||
if (data != null) {
|
||||
new GuiF(object, data, links).fill();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (Std.is(value, Array)) {
|
||||
var a:Array<Dynamic> = [];
|
||||
for (o in cast(value, Array<Dynamic>)) a.push(new GuiB(o, links, includes).build());
|
||||
value = a;
|
||||
} else if (Std.is(value, String)) {
|
||||
var s:String = cast(value, String);
|
||||
var c:String = s.charAt(0);
|
||||
if (c == "#") {
|
||||
value = Reflect.field(links, s.substr(1));
|
||||
} else if (c == "~") {
|
||||
var a:Array<String> = s.substr(1).split(":");
|
||||
var e:Enum<Dynamic> = Type.resolveEnum(a[0]);
|
||||
value = Type.createEnum(e, a[1]);
|
||||
} else if (c == "@") {
|
||||
var c1 = s.charAt(1);
|
||||
if (c1 == "~") {
|
||||
var a:Array<String> = s.substr(2).split(":");
|
||||
switch (a[0]) {
|
||||
case "image": value = Assets.getBitmapData(a[1]);
|
||||
}
|
||||
} else if (c1 == "^") {
|
||||
value = new LString(s.substr(2));
|
||||
} else {
|
||||
var a:Array<String> = s.substr(1).split(":");
|
||||
//value = Reflect.field(Provider.get(IResources), a[0]).get(a[1]);
|
||||
Reflect.field(Provider.get(IResources), a[0]).bind(a[1], object, field);
|
||||
continue;
|
||||
}
|
||||
} else if (~/0x[A-Fa-f\d]{6}/.match(value)) {
|
||||
value = Std.parseInt(value);
|
||||
}
|
||||
} else if (Std.is(value, Float)) {
|
||||
|
||||
} else if (Std.is(value, Bool)) {
|
||||
|
||||
} else {
|
||||
var o:Dynamic = new GuiB(value, links, includes).build();
|
||||
new GuiF(o, value, links).fill();
|
||||
value = o;
|
||||
}
|
||||
try {
|
||||
Reflect.setProperty(object, field, value);
|
||||
} catch(error:Dynamic) {
|
||||
L.e("GuiBuilder", "", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,206 +1,10 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxe.Json;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import haxework.gui.build.Builder;
|
||||
|
||||
@:remove @:autoBuild(haxework.gui.ViewBuilderImpl.build())
|
||||
extern interface ViewBuilder {}
|
||||
|
||||
#if macro
|
||||
private class BuilderUtil {
|
||||
|
||||
public static function loadFile(path:String, json:Bool = true) {
|
||||
Context.registerModuleDependency(Context.getLocalModule(), path);
|
||||
var content = sys.io.File.getContent(path);
|
||||
return json ? Json.parse(content) : content;
|
||||
}
|
||||
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
|
||||
private 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.loadFile(templateFile);
|
||||
if (templateKey != null) template = Reflect.field(template, templateKey);
|
||||
|
||||
if (templateMeta[1] != null) {
|
||||
styleFile = Context.resolvePath(templateMeta[1]);
|
||||
style = BuilderUtil.loadFile(styleFile);
|
||||
}
|
||||
|
||||
fields = Context.getBuildFields();
|
||||
exprs = [];
|
||||
i = 0;
|
||||
}
|
||||
|
||||
private function getPosition():Position {
|
||||
return Context.makePosition({min:0, max:0, file:templateFile + (templateKey != null ? "@" + templateKey : "")});
|
||||
}
|
||||
|
||||
private function specialValue(name:String, key:String, a:Array<String>):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()));
|
||||
//res + ".get(\"" + a[2] + "\")";
|
||||
null;
|
||||
case "locale":
|
||||
"new haxework.locale.LString(\"" + a[1] + "\")";
|
||||
case "class":
|
||||
a[1];
|
||||
case "layout":
|
||||
var template = BuilderUtil.loadFile(a[1]);
|
||||
return getValue(name, key, template);
|
||||
case "link":
|
||||
"(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")";
|
||||
case _:
|
||||
throw "Unsupported prefix \"" + a[0] + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
private function getValue(name:String, key:String, value:Dynamic):Dynamic {
|
||||
return if (Std.is(value, Array)) {
|
||||
value.map(function(v) { return getValue(null, null, v); });
|
||||
} else if (Std.is(value, String)) {
|
||||
if (value.charAt(0) == "@") {
|
||||
specialValue(name, key, value.substring(1, value.length).split(":"));
|
||||
} 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");
|
||||
exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition()));
|
||||
createElement(value, n);
|
||||
n;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
} 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 (!Reflect.hasField(template, 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 type = switch (expr.expr) {
|
||||
case EVars(vars): vars[0].type;
|
||||
case _: null;
|
||||
}
|
||||
fields.push({
|
||||
name: id,
|
||||
access: [APublic],
|
||||
pos: getPosition(),
|
||||
kind: FProp("default", "null", type)
|
||||
});
|
||||
exprs.push(Context.parse("this." + id + " = " + name, getPosition()));
|
||||
}
|
||||
|
||||
for (key in Reflect.fields(template)) {
|
||||
if (["type", "style"].indexOf(key) > -1) continue;
|
||||
var value = getValue(name, key, Reflect.field(template, key));
|
||||
if (value != null) {
|
||||
exprs.push(Context.parse(name + "." + key + " = " + value, getPosition()));
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
class ViewBuilderImpl {
|
||||
#if macro
|
||||
public static function build() {
|
||||
|
||||
181
haxework/gui/build/Builder.hx
Executable file
181
haxework/gui/build/Builder.hx
Executable file
@@ -0,0 +1,181 @@
|
||||
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.loadFile(templateFile);
|
||||
if (templateKey != null) template = Reflect.field(template, templateKey);
|
||||
|
||||
if (templateMeta[1] != null) {
|
||||
styleFile = Context.resolvePath(templateMeta[1]);
|
||||
style = BuilderUtil.loadFile(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;
|
||||
return Context.makePosition({min:min, max:max, file:templateFile});
|
||||
}
|
||||
|
||||
private function specialValue(name:String, key:String, a:Array<String>):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()));
|
||||
//res + ".get(\"" + a[2] + "\")";
|
||||
null;
|
||||
case "locale":
|
||||
"new haxework.locale.LString(\"" + a[1] + "\")";
|
||||
case "class":
|
||||
a[1];
|
||||
case "layout":
|
||||
var template = BuilderUtil.loadFile(a[1]);
|
||||
return getValue(name, key, template);
|
||||
case "link":
|
||||
"(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")";
|
||||
case _:
|
||||
throw "Unsupported prefix \"" + a[0] + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
private function getValue(name:String, key:String, value:Dynamic):Dynamic {
|
||||
return if (Std.is(value, Array)) {
|
||||
value.map(function(v) { return getValue(null, null, v); });
|
||||
} else if (Std.is(value, String)) {
|
||||
if (value.charAt(0) == "@") {
|
||||
specialValue(name, key, value.substring(1, value.length).split(":"));
|
||||
} 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");
|
||||
exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition()));
|
||||
createElement(value, n);
|
||||
n;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
} 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 (!Reflect.hasField(template, 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 type = switch (expr.expr) {
|
||||
case EVars(vars): vars[0].type;
|
||||
case _: null;
|
||||
}
|
||||
fields.push({
|
||||
name: id,
|
||||
access: [APublic],
|
||||
pos: getPosition(),
|
||||
kind: FProp("default", "null", type)
|
||||
});
|
||||
exprs.push(Context.parse("this." + id + " = " + name, getPosition()));
|
||||
}
|
||||
|
||||
for (key in Reflect.fields(template)) {
|
||||
if (key.charAt(0) == "$" || ["type", "style"].indexOf(key) > -1) continue;
|
||||
var position = Reflect.field(template, "$" + key);
|
||||
var value = getValue(name, key, Reflect.field(template, key));
|
||||
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
|
||||
30
haxework/gui/build/BuilderUtil.hx
Executable file
30
haxework/gui/build/BuilderUtil.hx
Executable file
@@ -0,0 +1,30 @@
|
||||
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 loadFile(path:String, json:Bool = true) {
|
||||
Context.registerModuleDependency(Context.getLocalModule(), path);
|
||||
var content = sys.io.File.getContent(path);
|
||||
Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
|
||||
return json ? PositionJsonParser.parse(content) : content;
|
||||
}
|
||||
|
||||
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
|
||||
233
haxework/gui/build/PositionJsonParser.hx
Executable file
233
haxework/gui/build/PositionJsonParser.hx
Executable file
@@ -0,0 +1,233 @@
|
||||
package haxework.gui.build;
|
||||
|
||||
typedef JsonKeyPosition = {
|
||||
var min:Int;
|
||||
var max:Int;
|
||||
}
|
||||
|
||||
class PositionJsonParser {
|
||||
|
||||
static public inline function parse(str : String) : Dynamic {
|
||||
return new PositionJsonParser(str).parseRec();
|
||||
}
|
||||
|
||||
var str : String;
|
||||
var pos : Int;
|
||||
|
||||
function new( str : String ) {
|
||||
this.str = str;
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
function getKeyPosition():JsonKeyPosition {
|
||||
return {
|
||||
min: pos,
|
||||
max: pos
|
||||
}
|
||||
}
|
||||
|
||||
function parseRec() : Dynamic {
|
||||
while( true ) {
|
||||
var c = nextChar();
|
||||
switch( c ) {
|
||||
case ' '.code, '\r'.code, '\n'.code, '\t'.code:
|
||||
// loop
|
||||
case '{'.code:
|
||||
var obj = {}, field = null, comma : Null<Bool> = null;
|
||||
var position = null;
|
||||
while( true ) {
|
||||
var c = nextChar();
|
||||
switch( c ) {
|
||||
case ' '.code, '\r'.code, '\n'.code, '\t'.code:
|
||||
// loop
|
||||
case '}'.code:
|
||||
if( field != null || comma == false )
|
||||
invalidChar();
|
||||
return obj;
|
||||
case ':'.code:
|
||||
if( field == null )
|
||||
invalidChar();
|
||||
Reflect.setField(obj,field,parseRec());
|
||||
Reflect.setField(obj,"$" + field,position);
|
||||
field = null;
|
||||
comma = true;
|
||||
case ','.code:
|
||||
if( comma ) comma = false else invalidChar();
|
||||
case '"'.code:
|
||||
if( comma ) invalidChar();
|
||||
position = getKeyPosition();
|
||||
field = parseString();
|
||||
default:
|
||||
invalidChar();
|
||||
}
|
||||
}
|
||||
case '['.code:
|
||||
var arr = [], comma : Null<Bool> = null;
|
||||
while( true ) {
|
||||
var c = nextChar();
|
||||
switch( c ) {
|
||||
case ' '.code, '\r'.code, '\n'.code, '\t'.code:
|
||||
// loop
|
||||
case ']'.code:
|
||||
if( comma == false ) invalidChar();
|
||||
return arr;
|
||||
case ','.code:
|
||||
if( comma ) comma = false else invalidChar();
|
||||
default:
|
||||
if( comma ) invalidChar();
|
||||
pos--;
|
||||
arr.push(parseRec());
|
||||
comma = true;
|
||||
}
|
||||
}
|
||||
case 't'.code:
|
||||
var save = pos;
|
||||
if( nextChar() != 'r'.code || nextChar() != 'u'.code || nextChar() != 'e'.code ) {
|
||||
pos = save;
|
||||
invalidChar();
|
||||
}
|
||||
return true;
|
||||
case 'f'.code:
|
||||
var save = pos;
|
||||
if( nextChar() != 'a'.code || nextChar() != 'l'.code || nextChar() != 's'.code || nextChar() != 'e'.code ) {
|
||||
pos = save;
|
||||
invalidChar();
|
||||
}
|
||||
return false;
|
||||
case 'n'.code:
|
||||
var save = pos;
|
||||
if( nextChar() != 'u'.code || nextChar() != 'l'.code || nextChar() != 'l'.code ) {
|
||||
pos = save;
|
||||
invalidChar();
|
||||
}
|
||||
return null;
|
||||
case '"'.code:
|
||||
return parseString();
|
||||
case '0'.code, '1'.code,'2'.code,'3'.code,'4'.code,'5'.code,'6'.code,'7'.code,'8'.code,'9'.code,'-'.code:
|
||||
return parseNumber(c);
|
||||
default:
|
||||
invalidChar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseString() {
|
||||
var start = pos;
|
||||
var buf = null;
|
||||
while( true ) {
|
||||
var c = nextChar();
|
||||
if( c == '"'.code )
|
||||
break;
|
||||
if( c == '\\'.code ) {
|
||||
if (buf == null) {
|
||||
buf = new StringBuf();
|
||||
}
|
||||
buf.addSub(str,start, pos - start - 1);
|
||||
c = nextChar();
|
||||
switch( c ) {
|
||||
case "r".code: buf.addChar("\r".code);
|
||||
case "n".code: buf.addChar("\n".code);
|
||||
case "t".code: buf.addChar("\t".code);
|
||||
case "b".code: buf.addChar(8);
|
||||
case "f".code: buf.addChar(12);
|
||||
case "/".code, '\\'.code, '"'.code: buf.addChar(c);
|
||||
case 'u'.code:
|
||||
var uc = Std.parseInt("0x" + str.substr(pos, 4));
|
||||
pos += 4;
|
||||
#if (neko || php || cpp)
|
||||
if( uc <= 0x7F )
|
||||
buf.addChar(uc);
|
||||
else if( uc <= 0x7FF ) {
|
||||
buf.addChar(0xC0 | (uc >> 6));
|
||||
buf.addChar(0x80 | (uc & 63));
|
||||
} else if( uc <= 0xFFFF ) {
|
||||
buf.addChar(0xE0 | (uc >> 12));
|
||||
buf.addChar(0x80 | ((uc >> 6) & 63));
|
||||
buf.addChar(0x80 | (uc & 63));
|
||||
} else {
|
||||
buf.addChar(0xF0 | (uc >> 18));
|
||||
buf.addChar(0x80 | ((uc >> 12) & 63));
|
||||
buf.addChar(0x80 | ((uc >> 6) & 63));
|
||||
buf.addChar(0x80 | (uc & 63));
|
||||
}
|
||||
#else
|
||||
buf.addChar(uc);
|
||||
#end
|
||||
default:
|
||||
throw "Invalid escape sequence \\" + String.fromCharCode(c) + " at position " + (pos - 1);
|
||||
}
|
||||
start = pos;
|
||||
}
|
||||
#if (neko || php || cpp)
|
||||
// ensure utf8 chars are not cut
|
||||
else if( c >= 0x80 ) {
|
||||
pos++;
|
||||
if( c >= 0xFC ) pos += 4;
|
||||
else if( c >= 0xF8 ) pos += 3;
|
||||
else if( c >= 0xF0 ) pos += 2;
|
||||
else if( c >= 0xE0 ) pos++;
|
||||
}
|
||||
#end
|
||||
else if( StringTools.isEof(c) )
|
||||
throw "Unclosed string";
|
||||
}
|
||||
if (buf == null) {
|
||||
return str.substr(start, pos - start - 1);
|
||||
}
|
||||
else {
|
||||
buf.addSub(str,start, pos - start - 1);
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
inline function parseNumber( c : Int ) : Dynamic {
|
||||
var start = pos - 1;
|
||||
var minus = c == '-'.code, digit = !minus, zero = c == '0'.code;
|
||||
var point = false, e = false, pm = false, end = false;
|
||||
while( true ) {
|
||||
c = nextChar();
|
||||
switch( c ) {
|
||||
case '0'.code :
|
||||
if (zero && !point) invalidNumber(start);
|
||||
if (minus) {
|
||||
minus = false; zero = true;
|
||||
}
|
||||
digit = true;
|
||||
case '1'.code,'2'.code,'3'.code,'4'.code,'5'.code,'6'.code,'7'.code,'8'.code,'9'.code :
|
||||
if (zero && !point) invalidNumber(start);
|
||||
if (minus) minus = false;
|
||||
digit = true; zero = false;
|
||||
case '.'.code :
|
||||
if (minus || point) invalidNumber(start);
|
||||
digit = false; point = true;
|
||||
case 'e'.code, 'E'.code :
|
||||
if (minus || zero || e) invalidNumber(start);
|
||||
digit = false; e = true;
|
||||
case '+'.code, '-'.code :
|
||||
if (!e || pm) invalidNumber(start);
|
||||
digit = false; pm = true;
|
||||
default :
|
||||
if (!digit) invalidNumber(start);
|
||||
pos--;
|
||||
end = true;
|
||||
}
|
||||
if (end) break;
|
||||
}
|
||||
var f = Std.parseFloat(str.substr(start, pos - start));
|
||||
var i = Std.int(f);
|
||||
return if( i == f ) i else f;
|
||||
}
|
||||
|
||||
inline function nextChar() {
|
||||
return StringTools.fastCodeAt(str,pos++);
|
||||
}
|
||||
|
||||
function invalidChar() {
|
||||
pos--; // rewind
|
||||
throw "Invalid char "+StringTools.fastCodeAt(str,pos)+" at position "+pos;
|
||||
}
|
||||
|
||||
function invalidNumber( start : Int ) {
|
||||
throw "Invalid number at position "+start+": " + str.substr(start, pos - start);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user