Advertisement
Guest User

Untitled

a guest
Apr 15th, 2019
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Haxe 13.75 KB | None | 0 0
  1. package haxe.ui.macros;
  2.  
  3. #if macro
  4. import haxe.macro.Context;
  5. import haxe.macro.Expr;
  6. import haxe.macro.TypeTools;
  7. import haxe.ui.macros.helpers.ClassBuilder;
  8. import haxe.ui.macros.helpers.CodeBuilder;
  9. import haxe.ui.macros.helpers.CodePos;
  10. import haxe.ui.macros.helpers.FieldBuilder;
  11. import haxe.ui.util.StringUtil;
  12. #end
  13.  
  14. class Macros {
  15.     #if macro
  16.  
  17.     macro static function build():Array<Field> {
  18.         var builder = new ClassBuilder(Context.getBuildFields(), Context.getLocalType(), Context.currentPos());
  19.        
  20.         if (builder.hasClassMeta(["xml"])) {
  21.             buildFromXmlMeta(builder);
  22.         }
  23.        
  24.         if (builder.hasClassMeta(["composite"])) {
  25.             buildComposite(builder);
  26.         }
  27.  
  28.         buildStyles(builder);
  29.         buildBindings(builder);
  30.        
  31.         if (builder.hasInterface("haxe.ui.core.IClonable")) {
  32.             buildClonable(builder);
  33.         }
  34.        
  35.         return builder.fields;
  36.     }
  37.    
  38.     static function buildFromXmlMeta(builder:ClassBuilder) {
  39.         if (builder.hasSuperClass("haxe.ui.core.Component") == false) {
  40.             Context.error("Must have a superclass of haxe.ui.core.Component", Context.currentPos());
  41.         }
  42.        
  43.         if (builder.constructor == null) {
  44.             Context.error("A class building component must have a constructor", Context.currentPos());
  45.         }
  46.        
  47.         var xml = builder.getClassMetaValue("xml");
  48.         var namedComponents:Map<String, String> = new Map<String, String>();
  49.         var codeBuilder = new CodeBuilder(ComponentMacros.buildComponentFromString([], xml, namedComponents));
  50.         codeBuilder.add(macro
  51.             addComponent(c0)
  52.         );
  53.        
  54.         var createChildrenFn = builder.findFunction("createChildren");
  55.         if (createChildrenFn == null) {
  56.             createChildrenFn = builder.addFunction("createChildren", macro {
  57.                 super.createChildren();
  58.             }, [APrivate, AOverride]);
  59.         }
  60.         createChildrenFn.add(codeBuilder);
  61.        
  62.         for (id in namedComponents.keys()) {
  63.             var safeId:String = StringUtil.capitalizeHyphens(id);
  64.             var cls:String = namedComponents.get(id);
  65.             builder.addVar(safeId, TypeTools.toComplexType(Context.getType(cls)));
  66.             builder.constructor.add(macro
  67.                 $i{safeId} = findComponent($v{id}, $p{cls.split(".")}, true)
  68.             , 1);
  69.         }
  70.     }
  71.    
  72.     static function buildComposite(builder:ClassBuilder) {
  73.         var registerCompositeFn = builder.findFunction("registerComposite");
  74.         if (registerCompositeFn == null) {
  75.             registerCompositeFn = builder.addFunction("registerComposite", macro {
  76.                 super.registerComposite();
  77.             }, [APrivate, AOverride]);
  78.         }
  79.        
  80.         for (param in builder.getClassMetaValues("composite")) {
  81.             // probably a better way to do this
  82.             if (param.indexOf("Event") != -1) {
  83.                 registerCompositeFn.add(macro
  84.                     _internalEventsClass = $p{param.split(".")}
  85.                 );
  86.             } else if (param.indexOf("Builder") != -1) {
  87.                 registerCompositeFn.add(macro
  88.                     _compositeBuilderClass = $p{param.split(".")}
  89.                 );
  90.             } else if (param.indexOf("Layout") != -1) {
  91.                 registerCompositeFn.add(macro
  92.                     _defaultLayoutClass = $p{param.split(".")}
  93.                 );
  94.             }
  95.         }
  96.     }
  97.  
  98.     static function buildStyles(builder:ClassBuilder) {
  99.         for (f in builder.getFieldsWithMeta("style")) {
  100.             f.remove();
  101.            
  102.             var defaultValue:Dynamic = null;
  103.             if (f.isNumeric == true) {
  104.                 defaultValue = 0;
  105.             } else if (f.isBool == true) {
  106.                 defaultValue = false;
  107.             }
  108.            
  109.             var getter = builder.addGetter(f.name, f.type, macro {
  110.                 if ($p{["customStyle", f.name]} != $v{defaultValue}) {
  111.                     return $p{["customStyle", f.name]};
  112.                 }
  113.                 if (style == null || $p{["style", f.name]} == null) {
  114.                     return $v{defaultValue};
  115.                 }
  116.                 return $p{["style", f.name]};
  117.             });
  118.             getter.addMeta(":style");
  119.             getter.addMeta(":clonable");
  120.             getter.addMeta(":dox", [macro group = "Style properties"]);
  121.            
  122.             var codeBuilder = new CodeBuilder(macro {
  123.                 if ($p{["customStyle", f.name]} == value) {
  124.                     return value;
  125.                 }
  126.                 if (_style == null) {
  127.                     _style = new haxe.ui.styles.Style();
  128.                 }
  129.                 $p{["customStyle", f.name]} = value;
  130.                 return value;
  131.             });
  132.             if (f.hasMetaParam("style", "layout")) {
  133.                 codeBuilder.add(macro
  134.                     invalidateComponentLayout()
  135.                 );
  136.             }
  137.             if (f.hasMetaParam("style", "layoutparent")) {
  138.                 codeBuilder.add(macro
  139.                     if (parentComponent != null) parentComponent.invalidateComponentLayout()
  140.                 );
  141.             }
  142.             var setter = builder.addSetter(f.name, f.type, codeBuilder.expr);
  143.         }
  144.     }
  145.    
  146.     static function buildBindings(builder:ClassBuilder) {
  147.         for (f in builder.getFieldsWithMeta("bindable")) {
  148.             var setFn = builder.findFunction("set_" + f.name);
  149.             if (setFn != null) {
  150.                 setFn.add(macro
  151.                     haxe.ui.binding.BindingManager.instance.componentPropChanged(this, $v{f.name})
  152.                 );
  153.             }
  154.         }
  155.        
  156.         var bindFields = builder.getFieldsWithMeta("bind");
  157.         if (bindFields.length > 0) {
  158.             if (builder.hasSuperClass("haxe.ui.core.Component") == false) {
  159.                 Context.error("Must have a superclass of haxe.ui.core.Component", Context.currentPos());
  160.             }
  161.            
  162.             if (builder.constructor == null) {
  163.                 Context.error("A class building component must have a constructor", Context.currentPos());
  164.             }
  165.            
  166.             for (f in bindFields) {
  167.                 var param1 = f.getMetaValueString("bind", 0);
  168.                 var param2 = f.getMetaValueString("bind", 1);
  169.                 if (param1 != null && param2 == null) { // one param, lets assume binding to component prop
  170.                     f.remove();
  171.                    
  172.                     var variable:String = param1.split(".")[0];
  173.                     var field:String = param1.split(".")[1];
  174.                     if (field == null) {
  175.                         field = "value";
  176.                     }
  177.                    
  178.                     builder.addGetter(f.name, f.type, macro {
  179.                         return Reflect.getProperty(findComponent($v{variable}), $v{field});
  180.                     });
  181.                     builder.addSetter(f.name, f.type, macro {
  182.                         if (value != $i{f.name}) {
  183.                             Reflect.setProperty(findComponent($v{variable}), $v{field}, value);
  184.                         }
  185.                         return value;
  186.                     });
  187.                     if (f.expr != null) {
  188.                         builder.constructor.add(macro
  189.                             $i{f.name} = $e{f.expr}
  190.                         , 1);
  191.                     }
  192.                 } else if (param1 != null && param2 != null) { // two params, lets assume event binding
  193.                     builder.constructor.add(macro
  194.                         findComponent($v{param1}, haxe.ui.core.Component).registerEvent($p{param2.split(".")}, $i{f.name})
  195.                     , 1);
  196.                 }
  197.             }
  198.         }
  199.     }
  200.    
  201.     static function buildClonable(builder:ClassBuilder) {
  202.         var useSelf:Bool = (builder.fullPath == "haxe.ui.core.Component");
  203.        
  204.         var cloneFn = builder.findFunction("cloneComponent");
  205.         if (cloneFn == null) { // add new clone fn
  206.             var access:Array<Access> = [APublic];
  207.             if (useSelf == false) {
  208.                 access.push(AOverride);
  209.             }
  210.             cloneFn = builder.addFunction("cloneComponent", builder.path, access);
  211.         }
  212.        
  213.         var cloneLineExpr = null;
  214.         var typePath = TypeTools.toComplexType(builder.type);
  215.         if (useSelf == false) {
  216.             cloneLineExpr = macro var c:$typePath = cast super.cloneComponent();
  217.         } else {
  218.             cloneLineExpr = macro var c:$typePath = self();
  219.         }
  220.         cloneFn.add(cloneLineExpr, CodePos.Start);
  221.        
  222.         var n = 1;
  223.         for (f in builder.getFieldsWithMeta("clonable")) {
  224.             if (f.isNullable == true) {
  225.                 cloneFn.add(macro
  226.                     if ($p{["this", f.name]} != null) $p{["c", f.name]} = $p{["this", f.name]}
  227.                 , n);
  228.             } else {
  229.                 cloneFn.add(macro
  230.                     $p{["c", f.name]} = $p{["this", f.name]}
  231.                 , n);
  232.             }
  233.             n++;
  234.         }
  235.         cloneFn.add(macro return c);
  236.  
  237.         // add "self" function
  238.         var access:Array<Access> = [APrivate];
  239.         if (useSelf == false) {
  240.             access.push(AOverride);
  241.         }
  242.         var typePath = builder.typePath;
  243.         builder.addFunction("self", macro {
  244.             return new $typePath();
  245.         }, builder.path, access);
  246.     }
  247.    
  248.     private static function buildBehaviours():Array<Field> {
  249.         var builder = new ClassBuilder(haxe.macro.Context.getBuildFields(), Context.getLocalType(), Context.currentPos());
  250.         var registerBehavioursFn = builder.findFunction("registerBehaviours");
  251.         if (registerBehavioursFn == null) {
  252.             registerBehavioursFn = builder.addFunction("registerBehaviours", macro {
  253.                 super.registerBehaviours();
  254.             }, [APrivate, AOverride]);
  255.         }
  256.        
  257.         var valueField = builder.getFieldMetaValue("value");
  258.         for (f in builder.getFieldsWithMeta("behaviour")) {
  259.             f.remove();
  260.             if (builder.hasField(f.name, true) == false) { // check to see if it already exists, possibly in a super class
  261.                 var newField:FieldBuilder = null;
  262.                 if (f.isDynamic == true) { // add a getter that can return dynamic
  263.                     newField = builder.addGetter(f.name, f.type, macro {
  264.                         return behaviours.getDynamic($v{f.name});
  265.                     }, f.access);
  266.                 } else { // add a normal (Variant) getter
  267.                     newField = builder.addGetter(f.name, f.type, macro {
  268.                         return behaviours.get($v{f.name});
  269.                     }, f.access);
  270.                 }
  271.                
  272.                 if (f.name == valueField) {
  273.                     newField = builder.addSetter(f.name, f.type, macro { // add a normal (Variant) setter but let the binding manager know that the value has changed
  274.                         behaviours.set($v{f.name}, value);
  275.                         haxe.ui.binding.BindingManager.instance.componentPropChanged(this, "value");
  276.                         return value;
  277.                     }, f.access);
  278.                 } else {
  279.                     newField = builder.addSetter(f.name, f.type, macro { // add a normal (Variant) setter
  280.                         behaviours.set($v{f.name}, value);
  281.                         return value;
  282.                     }, f.access);
  283.                 }
  284.                
  285.                 newField.doc = f.doc;
  286.                 newField.addMeta(":behaviour");
  287.                 newField.addMeta(":bindable");
  288.                 newField.mergeMeta(f.meta, ["behaviour"]);
  289.             }
  290.            
  291.             if (f.getMetaValueExpr("behaviour", 1) == null) {
  292.                 registerBehavioursFn.add(macro
  293.                     behaviours.register($v{f.name}, $p{f.getMetaValueString("behaviour", 0).split(".")})
  294.                 );
  295.             } else {
  296.                 registerBehavioursFn.add(macro
  297.                     behaviours.register($v{f.name}, $p{f.getMetaValueString("behaviour", 0).split(".")}, $e{f.getMetaValueExpr("behaviour", 1)})
  298.                 );
  299.             }
  300.         }
  301.        
  302.         for (f in builder.findFunctionsWithMeta("call")) {
  303.             var arg0 = '${f.getArgName(0)}';
  304.             if (f.isVoid == true) {
  305.                 f.set(macro {
  306.                     behaviours.call($v{f.name}, $i{arg0});
  307.                 });
  308.             } else {
  309.                 f.set(macro {
  310.                     return behaviours.call($v{f.name}, $i{arg0});
  311.                 });
  312.             }
  313.            
  314.             if (f.getMetaValueExpr("call", 1) == null) {
  315.                 registerBehavioursFn.add(macro
  316.                     behaviours.register($v{f.name}, $p{f.getMetaValueString("call", 0).split(".")})
  317.                 );
  318.             } else {
  319.                 registerBehavioursFn.add(macro
  320.                     behaviours.register($v{f.name}, $p{f.getMetaValueString("call", 0).split(".")}, $e{f.getMetaValueExpr("call", 1)})
  321.                 );
  322.             }
  323.         }
  324.        
  325.         for (f in builder.getFieldsWithMeta("value")) {
  326.             f.remove();
  327.            
  328.             var propName = f.getMetaValueString("value");
  329.             builder.addGetter(f.name, macro: Dynamic, macro {
  330.                 return $i{propName};
  331.             }, false, true);
  332.            
  333.             builder.addSetter(f.name, macro: Dynamic, macro {
  334.                 $i{propName} = value;
  335.                 haxe.ui.binding.BindingManager.instance.componentPropChanged(this, $v{propName});
  336.                 return value;
  337.             }, false, true);
  338.         }
  339.        
  340.         return builder.fields;
  341.     }
  342.    
  343.     #end
  344. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement