up-to-date submodule(rive-cpp)
[platform/core/uifw/rive-tizen.git] / submodule / dev / core_generator / lib / src / property.dart
1 import 'package:colorize/colorize.dart';
2 import 'package:core_generator/src/comment.dart';
3 import 'package:core_generator/src/definition.dart';
4 import 'package:core_generator/src/field_type.dart';
5 import 'package:core_generator/src/key.dart';
6
7 class Property {
8   final String name;
9   final FieldType type;
10   final Definition definition;
11   String initialValue;
12   String initialValueRuntime;
13   bool animates = false;
14   String group;
15   Key key;
16   String description;
17   bool isNullable = false;
18   bool isRuntime = true;
19   bool isCoop = true;
20   bool isSetOverride = false;
21   bool isGetOverride = false;
22   FieldType typeRuntime;
23
24   factory Property(Definition type, String name, Map<String, dynamic> data) {
25     if (data['runtime'] is bool && data['runtime'] == false) {
26       return null;
27     }
28
29     var fieldType =
30         FieldType.find(data["typeRuntime"]) ?? FieldType.find(data["type"]);
31
32     if (fieldType == null) {
33       color('Invalid field type ${data['type']} for $name.', front: Styles.RED);
34       return null;
35     }
36     return Property.make(type, name, fieldType, data);
37   }
38
39   Property.make(
40       this.definition, this.name, this.type, Map<String, dynamic> data) {
41     dynamic descriptionValue = data["description"];
42     if (descriptionValue is String) {
43       description = descriptionValue;
44     }
45     dynamic nullableValue = data['nullable'];
46     if (nullableValue is bool) {
47       isNullable = nullableValue;
48     }
49     dynamic init = data['initialValue'];
50     if (init is String) {
51       initialValue = init;
52     }
53     dynamic initRuntime = data['initialValueRuntime'];
54     if (initRuntime is String) {
55       initialValueRuntime = initRuntime;
56     }
57     dynamic overrideSet = data['overrideSet'];
58     if (overrideSet is bool && overrideSet) {
59       isSetOverride = true;
60     }
61     dynamic overrideGet = data['overrideGet'];
62     if (overrideGet is bool && overrideGet) {
63       isGetOverride = true;
64     }
65     dynamic a = data['animates'];
66     if (a is bool) {
67       animates = a;
68     }
69     dynamic g = data['group'];
70     if (g is String) {
71       group = g;
72     }
73     dynamic e = data['editorOnly'];
74     if (e is bool && e) {
75       isCoop = false;
76     }
77     dynamic r = data['runtime'];
78     if (r is bool) {
79       isRuntime = r;
80     }
81     dynamic c = data['coop'];
82     if (c is bool) {
83       isCoop = c;
84     }
85     dynamic rt = data['typeRuntime'];
86     if (rt is String) {
87       typeRuntime = FieldType.find(rt);
88     }
89     key = Key.fromJSON(data["key"]) ?? Key.forProperty(this);
90   }
91
92   FieldType getExportType() => typeRuntime ?? type;
93
94   String generateCode(bool forRuntime) {
95     bool exportAnimates = false;
96     var exportType = getExportType();
97     String propertyKey = '${name}PropertyKey';
98     var code = StringBuffer('  /// ${'-' * 74}\n');
99     code.write(comment('${capitalize(name)} field with key ${key.intValue}.',
100         indent: 1));
101     if (initialValueRuntime != null || initialValue != null) {
102       code.writeln(
103           '${exportType.cppName} _$name = ${initialValueRuntime ?? initialValue};');
104     } else {
105       code.writeln('${exportType.cppName} _$name;');
106     }
107     if (exportAnimates) {
108       code.writeln('${exportType.cppName} _${name}Animated;');
109       code.writeln('KeyState _${name}KeyState = KeyState.none;');
110     }
111     code.writeln('static const int $propertyKey = ${key.intValue};');
112
113     if (description != null) {
114       code.write(comment(description, indent: 1));
115     }
116     if (exportAnimates) {
117       code.write(comment(
118           'Get the [_$name] field value.'
119           'Note this may not match the core value '
120           'if animation mode is active.',
121           indent: 1));
122       code.writeln(
123           '${exportType.cppName} get $name => _${name}Animated ?? _$name;');
124       code.write(
125           comment('Get the non-animation [_$name] field value.', indent: 1));
126       code.writeln('${exportType.cppName} get ${name}Core => _$name;');
127     } else {
128       code.writeln('${exportType.cppName} get $name => _$name;');
129     }
130     code.write(comment('Change the [_$name] field value.', indent: 1));
131     code.write(comment(
132         '[${name}Changed] will be invoked only if the '
133         'field\'\s value has changed.',
134         indent: 1));
135     code.writeln(
136         '''set $name${exportAnimates ? 'Core' : ''}(${exportType.cppName} value) {
137         if(${exportType.equalityCheck('_$name', 'value')}) { return; }
138         ${exportType.cppName} from = _$name;
139         _$name = value;''');
140     // Property change callbacks to the context don't propagate at runtime.
141     if (!forRuntime) {
142       code.writeln('onPropertyChanged($propertyKey, from, value);');
143       if (!isCoop) {
144         code.writeln(
145             'context?.editorPropertyChanged(this, $propertyKey, from, value);');
146       }
147     }
148     // Change callbacks do as we use those to trigger dirty states.
149     code.writeln('''
150         ${name}Changed(from, value);
151       }''');
152     if (exportAnimates) {
153       code.writeln('''set $name(${exportType.cppName} value) {
154         if(context != null && context.isAnimating && $name != value) {
155           _${name}Animate(value, true);
156           return;
157         }
158         ${name}Core = value;
159       }''');
160
161       code.writeln(
162           '''void _${name}Animate(${exportType.cppName} value, bool autoKey) {
163         if (_${name}Animated == value) {
164           return;
165         }
166         ${exportType.cppName} from = $name;
167         _${name}Animated = value;
168         ${exportType.cppName} to = $name;
169         onAnimatedPropertyChanged($propertyKey, autoKey, from, to);
170         ${name}Changed(from, to);
171       }''');
172
173       code.writeln(
174           '${exportType.cppName} get ${name}Animated => _${name}Animated;');
175       code.writeln('''set ${name}Animated(${exportType.cppName} value) =>
176                         _${name}Animate(value, false);''');
177       code.writeln('KeyState get ${name}KeyState => _${name}KeyState;');
178       code.writeln('''set ${name}KeyState(KeyState value) {
179         if (_${name}KeyState == value) {
180           return;
181         }
182         _${name}KeyState = value;
183         // Force update anything listening on this property.
184         onAnimatedPropertyChanged($propertyKey, false, _${name}Animated, _${name}Animated);
185       }''');
186     }
187     code.writeln('void ${name}Changed('
188         '${exportType.cppName} from, ${exportType.cppName} to);\n');
189
190     return code.toString();
191   }
192
193   Map<String, dynamic> serialize() {
194     Map<String, dynamic> data = <String, dynamic>{'type': type.name};
195     if (typeRuntime != null) {
196       data['typeRuntime'] = typeRuntime.name;
197     }
198
199     if (initialValue != null) {
200       data['initialValue'] = initialValue;
201     }
202     if (initialValueRuntime != null) {
203       data['initialValueRuntime'] = initialValueRuntime;
204     }
205     if (isGetOverride) {
206       data['overrideGet'] = true;
207     }
208     if (isSetOverride) {
209       data['overrideSet'] = true;
210     }
211     if (animates) {
212       data['animates'] = true;
213     }
214     if (group != null) {
215       data['group'] = group;
216     }
217     data['key'] = key.serialize();
218     if (description != null) {
219       data['description'] = description;
220     }
221     if (isNullable) {
222       data['nullable'] = true;
223     }
224     if (!isRuntime) {
225       data['runtime'] = false;
226     }
227     if (!isCoop) {
228       data['coop'] = false;
229     }
230     return data;
231   }
232
233   @override
234   String toString() {
235     return '$name(${key.intValue})';
236   }
237
238   String get capitalizedName => '${name[0].toUpperCase()}${name.substring(1)}'
239       .replaceAll('<', '')
240       .replaceAll('>', '');
241 }