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