1 module dlangui.widgets.metadata;
2 
3 import dlangui.widgets.widget;
4 
5 version = GENERATE_PROPERTY_METADATA;
6 
7 interface WidgetMetadataDef {
8     Widget create();
9     /// short class name, e.g. "EditLine"
10     string className();
11     /// module name, e.g. "dlangui.widgets.editors"
12     string moduleName();
13     /// full class name, e.g. "dlangui.widgets.editors.EditLine"
14     string fullName();
15     /// property list, e.g. "backgroundColor"
16     WidgetPropertyMetadata[] properties();
17 }
18 
19 struct WidgetSignalMetadata {
20     string name;
21     string typeString;
22     //TypeTuple
23     TypeInfo returnType;
24     TypeInfo paramsType;
25 }
26 
27 /**
28 * Stores information about property
29 *
30 */
31 struct WidgetPropertyMetadata {
32     TypeInfo type;
33     string name;
34 }
35 
36 private __gshared WidgetMetadataDef[string] _registeredWidgets;
37 
38 string[] getRegisteredWidgetsList()
39 {
40     return _registeredWidgets.keys;
41 }
42 
43 WidgetMetadataDef findWidgetMetadata(string name) {
44     if (auto p = name in _registeredWidgets)
45         return *p;
46     return null;
47 }
48 
49 void registerWidgetMetadata(string name, WidgetMetadataDef metadata) {
50     _registeredWidgets[name] = metadata;
51 }
52 
53 WidgetSignalMetadata[] getSignalList(alias T)() {
54     WidgetSignalMetadata[] res;
55     foreach(m; __traits(allMembers, T)) {
56         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
57             // skip non-public members
58             static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
59                 static if (__traits(compiles, __traits(getMember, T, m).params_t ) && __traits(compiles, __traits(getMember, T, m).return_t)) {
60                     alias ti = typeof(__traits(getMember, T, m));
61                     res ~= WidgetSignalMetadata(m,
62                                                 __traits(getMember, T, m).return_t.stringof ~ __traits(getMember, T, m).params_t.stringof,
63                                                 typeid(__traits(getMember, T, m).return_t),
64                                                 typeid(__traits(getMember, T, m).params_t));
65                 }
66             }
67         }
68     }
69     return res;
70 }
71 
72 template isMarkupType(T)
73 {
74     enum isMarkupType = is(T==int) ||
75                     is(T==float) ||
76                     is(T==double) ||
77                     is(T==bool) ||
78                     is(T==Rect) ||
79                     is(T==string) ||
80                     is(T==dstring) ||
81                     is(T==UIString) ||
82                     is(T==UIString[]) ||
83                     is(T==StringListValue[]);
84 }
85 
86 private bool hasPropertyAnnotation(alias ti)() {
87     bool res = false;
88     foreach ( attr; __traits(getFunctionAttributes, ti)) {
89         static if (attr == "@property") {
90             res = true;
91         }
92     }
93     return res;
94 }
95 /*
96 string markupPropertyGetterType(alias overload)() {
97     static if (__traits(getProtection, overload) == "public") {
98         import std.traits;
99         static if (is(typeof(overload) == function) && hasPropertyAnnotation!overload) {
100             alias ret = ReturnType!overload;
101             //alias params = Parameters!overload;
102             alias params = ParameterTypeTuple!overload;
103             static if (params.length == 0 && isMarkupType!ret && !isTemplate!ret) {
104                 return ret.stringof;
105             } else {
106                 return null;
107             }
108         } else {
109             return null;
110         }
111     }
112 }
113 
114 string markupPropertySetterType(alias overload)() {
115     static if (__traits(getProtection, overload) == "public") {
116         import std.traits;
117         static if (is(typeof(overload) == function) && hasPropertyAnnotation!overload) {
118             //alias params = Parameters!overload;
119             alias params = ParameterTypeTuple!overload;
120             static if (params.length == 1 && isMarkupType!(params[0]) && !isTemplate!(params[0])) {
121                 return params[0].stringof;
122             } else {
123                 return null;
124             }
125         } else {
126             return null;
127         }
128     }
129 }
130 */
131 
132 private template isPublicPropertyFunction(alias overload) {
133     static if (__traits(getProtection, overload) == "public") {
134         static if (hasPropertyAnnotation!overload) {
135             enum isPublicPropertyFunction = true;
136         } else {
137             enum isPublicPropertyFunction = false;
138         }
139     } else {
140         enum isPublicPropertyFunction = false;
141     }
142     //pragma(msg, is(typeof(overload) == function).stringof);
143     //enum isPublicPropertyFunction = (__traits(getProtection, overload) == "public") && is(typeof(overload) == function);// && hasPropertyAnnotation!overload;
144 }
145 
146 private template markupPropertyType(alias overload) {
147     import std.traits : ReturnType, ParameterTypeTuple;
148     alias ret = ReturnType!overload;
149     alias params = ParameterTypeTuple!overload;
150     static if (params.length == 0 && isMarkupType!ret /* && !isTemplate!ret*/) {
151         enum string markupPropertyType = ret.stringof;
152     } else static if (params.length == 1 && isMarkupType!(params[0]) /* && !isTemplate!(params[0])*/) {
153         enum string markupPropertyType = params[0].stringof;
154     } else {
155         enum string markupPropertyType = null;
156     }
157 }
158 
159 private string[] generatePropertyTypeList(alias T)() {
160     import std.meta;
161     string[] properties;
162     properties ~= "[";
163     foreach(m; __traits(allMembers, T)) {
164         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
165             //static if (is (typeof(__traits(getMember, T, m)) == function)) {
166             static if (__traits(isVirtualMethod, __traits(getMember, T, m))) {//
167                 import std.traits : MemberFunctionsTuple;
168                 alias overloads = typeof(__traits(getVirtualMethods, T, m));
169                 static if (overloads.length == 2) {
170                     static if (isPublicPropertyFunction!(__traits(getVirtualMethods, T, m)[0]) && isPublicPropertyFunction!(__traits(getVirtualMethods, T, m)[1])) {
171                         //pragma(msg, m ~ " isPublicPropertyFunction0=" ~ isPublicPropertyFunction!(__traits(getVirtualFunctions, T, m)[0]).stringof);
172                         //pragma(msg, m ~ " isPublicPropertyFunction1=" ~ isPublicPropertyFunction!(__traits(getVirtualFunctions, T, m)[1]).stringof);
173                         immutable getterType = markupPropertyType!(__traits(getVirtualMethods, T, m)[0]);
174                         immutable setterType = markupPropertyType!(__traits(getVirtualMethods, T, m)[1]);
175                         static if (getterType && setterType && getterType == setterType) {
176                             //pragma(msg, "markup property found: " ~ getterType ~ " " ~ m.stringof);
177                             properties ~= "WidgetPropertyMetadata( typeid(" ~ getterType ~ "), " ~ m.stringof ~ " ), ";
178                         }
179                     }
180                 }
181             }
182         }
183     }
184     properties ~= "]";
185     return properties;
186 }
187 
188 string joinStrings(string[] lines) {
189     if (lines.length == 0)
190         return "";
191     if (lines.length == 1)
192         return lines[0];
193     else
194         return joinStrings(lines[0 .. $/2]) ~ joinStrings(lines[$/2 .. $]);
195 }
196 
197 private string generatePropertiesMetadata(alias T)() if (is(T : Widget)) {
198     version (GENERATE_PROPERTY_METADATA) {
199         //import std.algorithm.searching;
200         //import std.traits : MemberFunctionsTuple;
201         //import std.meta;
202         auto properties = generatePropertyTypeList!T;
203         return joinStrings(properties);
204     } else {
205         return "[]";
206     }
207 }
208 
209 string generateMetadataClass(alias t)() if (is(t : Widget)) {
210     //pragma(msg, moduleName!t);
211     import std.traits;
212     //pragma(msg, getSignalList!t);
213     //pragma(msg, generatePropertiesMetadata!t);
214     immutable string metadataClassName = t.stringof ~ "Metadata";
215     return "static class " ~ metadataClassName ~ " : WidgetMetadataDef { \n" ~
216         "    override Widget create() {\n" ~
217         "        return new " ~ moduleName!t ~ "." ~ t.stringof ~ "();\n" ~
218         "    }\n" ~
219         "    override string className() {\n" ~
220         "        return \"" ~ t.stringof ~ "\";\n" ~
221         "    }\n" ~
222         "    override string moduleName() {\n" ~
223         "        return \"" ~ moduleName!t ~ "\";\n" ~
224         "    }\n" ~
225         "    override string fullName() {\n" ~
226         "        return \"" ~ moduleName!t ~ "." ~ t.stringof ~ "\";\n" ~
227         "    }\n" ~
228         "    override WidgetPropertyMetadata[] properties() {\n" ~
229         "        return " ~ generatePropertiesMetadata!t ~ ";\n" ~
230         "    }\n" ~
231         "}\n";
232 }
233 
234 string generateRegisterMetadataClass(alias t)() if (is(t : Widget)) {
235     immutable string metadataClassName = t.stringof ~ "Metadata";
236     //pragma(msg, metadataClassName);
237     return "registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ t.stringof ~ "Metadata" ~ "());\n";
238 }
239 
240 template registerWidgetMetadataClass(alias t) if (is(t : Widget)) {
241     //pragma(msg, t.stringof);
242     //pragma(msg, generateMetadataClass!t);
243     immutable string classDef = generateMetadataClass!t;
244     immutable string registerDef = "registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ t.stringof ~ "Metadata" ~ "());\n";
245     enum registerWidgetMetadataClass = classDef ~ registerDef;
246     //mixin(classDef);
247 
248     //pragma(msg, "registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ t.stringof ~ "Metadata" ~ "());\n");
249     //mixin("registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ t.stringof ~ "Metadata" ~ "());\n");
250 }
251 
252 string registerWidgetsFunction(string registerFunctionName = "__gshared static this", T...)() {
253     pragma(msg, registerFunctionName);
254     pragma(msg, T);
255     string[] registerDefs;
256     foreach(t; T) {
257         pragma(msg, t.stringof);
258         //pragma(msg, moduleName!t);
259         //
260         static if (is(t : Widget)) {
261             //pragma(msg, classdef);
262             immutable string registerdef = generateRegisterMetadataClass!t;
263             pragma(msg, registerdef);
264             registerDefs ~= registerdef;
265         } else {
266             pragma(msg, "Skipping non-widget class: " ~ t.stringof);
267         }
268         //registerWidgetMetadata(T.stringof, new Metadata());
269     }
270     return registerFunctionName ~ "() {\n" ~ joinStrings(registerDefs) ~ "}\n";
271 }
272 
273 string registerWidgets(string registerFunctionName = "__gshared static this", T...)() {
274     pragma(msg, registerFunctionName);
275     pragma(msg, T);
276     string[] classDefs;
277     string[] registerDefs;
278     foreach(t; T) {
279         pragma(msg, t.stringof);
280         //pragma(msg, moduleName!t);
281         //
282         static if (is(t : Widget)) {
283             immutable string classdef = generateMetadataClass!t;
284             //pragma(msg, classdef);
285             immutable string registerdef = generateRegisterMetadataClass!t;
286             pragma(msg, registerdef);
287             classDefs ~= classdef;
288             registerDefs ~= registerdef;
289         } else {
290             pragma(msg, "Skipping non-widget class: " ~ t.stringof);
291         }
292         //registerWidgetMetadata(T.stringof, new Metadata());
293     }
294     return joinStrings(classDefs) ~ "\n" ~ registerFunctionName ~ "() {\n" ~ joinStrings(registerDefs) ~ "}\n";
295 }
296 
297 /// returns true if passed name is identifier of registered widget class
298 bool isWidgetClassName(string name) {
299     return (name in _registeredWidgets) !is null;
300 }
301