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';
10 final Definition definition;
12 String initialValueRuntime;
13 bool animates = false;
17 bool isNullable = false;
18 bool isRuntime = true;
20 bool isSetOverride = false;
21 bool isGetOverride = false;
22 FieldType typeRuntime;
24 factory Property(Definition type, String name, Map<String, dynamic> data) {
25 if (data['runtime'] is bool && data['runtime'] == false) {
30 FieldType.find(data["typeRuntime"]) ?? FieldType.find(data["type"]);
32 if (fieldType == null) {
33 color('Invalid field type ${data['type']} for $name.', front: Styles.RED);
36 return Property.make(type, name, fieldType, data);
40 this.definition, this.name, this.type, Map<String, dynamic> data) {
41 dynamic descriptionValue = data["description"];
42 if (descriptionValue is String) {
43 description = descriptionValue;
45 dynamic nullableValue = data['nullable'];
46 if (nullableValue is bool) {
47 isNullable = nullableValue;
49 dynamic init = data['initialValue'];
53 dynamic initRuntime = data['initialValueRuntime'];
54 if (initRuntime is String) {
55 initialValueRuntime = initRuntime;
57 dynamic overrideSet = data['overrideSet'];
58 if (overrideSet is bool && overrideSet) {
61 dynamic overrideGet = data['overrideGet'];
62 if (overrideGet is bool && overrideGet) {
65 dynamic a = data['animates'];
69 dynamic g = data['group'];
73 dynamic e = data['editorOnly'];
77 dynamic r = data['runtime'];
81 dynamic c = data['coop'];
85 dynamic rt = data['typeRuntime'];
87 typeRuntime = FieldType.find(rt);
89 key = Key.fromJSON(data["key"]) ?? Key.forProperty(this);
92 FieldType getExportType() => typeRuntime ?? type;
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}.',
101 if (initialValueRuntime != null || initialValue != null) {
103 '${exportType.cppName} _$name = ${initialValueRuntime ?? initialValue};');
105 code.writeln('${exportType.cppName} _$name;');
107 if (exportAnimates) {
108 code.writeln('${exportType.cppName} _${name}Animated;');
109 code.writeln('KeyState _${name}KeyState = KeyState.none;');
111 code.writeln('static const int $propertyKey = ${key.intValue};');
113 if (description != null) {
114 code.write(comment(description, indent: 1));
116 if (exportAnimates) {
118 'Get the [_$name] field value.'
119 'Note this may not match the core value '
120 'if animation mode is active.',
123 '${exportType.cppName} get $name => _${name}Animated ?? _$name;');
125 comment('Get the non-animation [_$name] field value.', indent: 1));
126 code.writeln('${exportType.cppName} get ${name}Core => _$name;');
128 code.writeln('${exportType.cppName} get $name => _$name;');
130 code.write(comment('Change the [_$name] field value.', indent: 1));
132 '[${name}Changed] will be invoked only if the '
133 'field\'\s value has changed.',
136 '''set $name${exportAnimates ? 'Core' : ''}(${exportType.cppName} value) {
137 if(${exportType.equalityCheck('_$name', 'value')}) { return; }
138 ${exportType.cppName} from = _$name;
140 // Property change callbacks to the context don't propagate at runtime.
142 code.writeln('onPropertyChanged($propertyKey, from, value);');
145 'context?.editorPropertyChanged(this, $propertyKey, from, value);');
148 // Change callbacks do as we use those to trigger dirty states.
150 ${name}Changed(from, value);
152 if (exportAnimates) {
153 code.writeln('''set $name(${exportType.cppName} value) {
154 if(context != null && context.isAnimating && $name != value) {
155 _${name}Animate(value, true);
162 '''void _${name}Animate(${exportType.cppName} value, bool autoKey) {
163 if (_${name}Animated == value) {
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);
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) {
182 _${name}KeyState = value;
183 // Force update anything listening on this property.
184 onAnimatedPropertyChanged($propertyKey, false, _${name}Animated, _${name}Animated);
187 code.writeln('void ${name}Changed('
188 '${exportType.cppName} from, ${exportType.cppName} to);\n');
190 return code.toString();
193 Map<String, dynamic> serialize() {
194 Map<String, dynamic> data = <String, dynamic>{'type': type.name};
195 if (typeRuntime != null) {
196 data['typeRuntime'] = typeRuntime.name;
199 if (initialValue != null) {
200 data['initialValue'] = initialValue;
202 if (initialValueRuntime != null) {
203 data['initialValueRuntime'] = initialValueRuntime;
206 data['overrideGet'] = true;
209 data['overrideSet'] = true;
212 data['animates'] = true;
215 data['group'] = group;
217 data['key'] = key.serialize();
218 if (description != null) {
219 data['description'] = description;
222 data['nullable'] = true;
225 data['runtime'] = false;
228 data['coop'] = false;
235 return '$name(${key.intValue})';
238 String get capitalizedName => '${name[0].toUpperCase()}${name.substring(1)}'
240 .replaceAll('>', '');