adding packages
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type_system.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
import 'store_class_visitor.dart';
|
||||
import 'template/widget_class_gen.dart';
|
||||
import 'template/store_file.dart';
|
||||
import 'type_names.dart';
|
||||
|
||||
class WidgetGenerator extends Generator {
|
||||
//GeneratorForAnnotation<WidgetClass> {
|
||||
@override
|
||||
FutureOr<String> generate(LibraryReader library, BuildStep buildStep) async {
|
||||
if (library.allElements.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final typeSystem = await library.allElements.first.session.typeSystem;
|
||||
final file = StoreFileTemplate()
|
||||
..storeSources = _generateCodeForLibrary(library, typeSystem).toSet();
|
||||
return file.toString();
|
||||
}
|
||||
|
||||
Iterable<String> _generateCodeForLibrary(
|
||||
LibraryReader library,
|
||||
TypeSystem typeSystem,
|
||||
) sync* {
|
||||
for (final classElement in library.classes) {
|
||||
yield* _generateCodeForMixinStore(
|
||||
library,
|
||||
classElement,
|
||||
typeSystem,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Iterable<String> _generateCodeForMixinStore(
|
||||
LibraryReader library,
|
||||
ClassElement baseClass,
|
||||
TypeSystem typeSystem,
|
||||
) sync* {
|
||||
final typeNameFinder = LibraryScopedNameFinder(library.element);
|
||||
String _base;
|
||||
double width, height;
|
||||
|
||||
if (isWidgetClass(baseClass)) {
|
||||
width = getPreferredWidth(baseClass);
|
||||
height = getPreferredHeight(baseClass);
|
||||
if (width != null || height != null) {
|
||||
_base = 'WidgetPreferredSizedBase';
|
||||
} else {
|
||||
_base = 'WidgetBase';
|
||||
}
|
||||
}
|
||||
if (isPropertyClass(baseClass)) {
|
||||
_base = 'PropertyBase';
|
||||
}
|
||||
if (_base != null) {
|
||||
final _template = MixinStoreTemplate(_base)
|
||||
..width = width
|
||||
..height = height;
|
||||
yield _generateCodeFromTemplate(
|
||||
baseClass.name,
|
||||
baseClass,
|
||||
_template,
|
||||
typeNameFinder,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _generateCodeFromTemplate(
|
||||
String publicTypeName,
|
||||
ClassElement userStoreClass,
|
||||
StoreTemplate template,
|
||||
LibraryScopedNameFinder typeNameFinder,
|
||||
) {
|
||||
final visitor = StoreClassVisitor(publicTypeName, userStoreClass, template);
|
||||
userStoreClass
|
||||
..accept(visitor)
|
||||
..visitChildren(visitor);
|
||||
return visitor.source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
abstract class CodegenError {
|
||||
bool get hasErrors;
|
||||
String get message;
|
||||
}
|
||||
|
||||
class StoreClassCodegenErrors implements CodegenError {
|
||||
StoreClassCodegenErrors(this.name) {
|
||||
_errorCategories = [
|
||||
// nonAbstractStoreMixinDeclarations,
|
||||
// invalidComputedAnnotations,
|
||||
// invalidObservableAnnotations,
|
||||
invalidActionAnnotations,
|
||||
// staticObservables,
|
||||
// finalObservables,
|
||||
];
|
||||
}
|
||||
|
||||
final String name;
|
||||
|
||||
final NonAbstractStoreMixinDeclarations nonAbstractStoreMixinDeclarations =
|
||||
NonAbstractStoreMixinDeclarations();
|
||||
|
||||
// final PropertyErrors finalObservables = FinalObservableFields();
|
||||
final PropertyErrors staticObservables = StaticObservableFields();
|
||||
final PropertyErrors invalidObservableAnnotations =
|
||||
InvalidObservableAnnotations();
|
||||
|
||||
final PropertyErrors invalidComputedAnnotations =
|
||||
InvalidComputedAnnotations();
|
||||
|
||||
final PropertyErrors invalidActionAnnotations = InvalidActionAnnotations();
|
||||
|
||||
List<CodegenError> _errorCategories;
|
||||
|
||||
@override
|
||||
String get message {
|
||||
final errors = _errorCategories
|
||||
.where((category) => category.hasErrors)
|
||||
.toList(growable: false)
|
||||
.asMap()
|
||||
.map((i, category) => MapEntry(i, ' ${i + 1}. ${category.message}'))
|
||||
.values
|
||||
.join('\n');
|
||||
|
||||
return 'Could not make class "$name" observable. Changes needed:\n$errors';
|
||||
}
|
||||
|
||||
@override
|
||||
bool get hasErrors => _errorCategories.any((category) => category.hasErrors);
|
||||
}
|
||||
|
||||
final _fieldPluralizer = Pluralize('the field', 'fields');
|
||||
final _methodPluralizer = Pluralize('the method', 'methods');
|
||||
final _memberPluralizer = Pluralize('the member', 'members');
|
||||
|
||||
abstract class _InvalidStoreDeclarations implements CodegenError {
|
||||
final NameList _classNames = NameList();
|
||||
|
||||
// ignore: avoid_positional_boolean_parameters
|
||||
bool addIf(bool condition, String className) {
|
||||
if (condition) {
|
||||
_classNames.add(className);
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get hasErrors => _classNames.isNotEmpty;
|
||||
}
|
||||
|
||||
class NonAbstractStoreMixinDeclarations extends _InvalidStoreDeclarations {
|
||||
@override
|
||||
String get message =>
|
||||
'Classes that use the SettingsStore mixin must be marked abstract. Affected classes: $_classNames.';
|
||||
}
|
||||
|
||||
abstract class PropertyErrors implements CodegenError {
|
||||
final NameList _properties = NameList();
|
||||
|
||||
// ignore: avoid_positional_boolean_parameters
|
||||
bool addIf(bool condition, String propertyName) {
|
||||
if (condition) {
|
||||
_properties.add(propertyName);
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
String get propertyList => _properties.toString();
|
||||
|
||||
Pluralize propertyPlural = _fieldPluralizer;
|
||||
|
||||
String get property => propertyPlural(_properties.length);
|
||||
|
||||
@override
|
||||
bool get hasErrors => _properties.isNotEmpty;
|
||||
}
|
||||
|
||||
// class FinalObservableFields extends PropertyErrors {
|
||||
// @override
|
||||
// String get message => 'Remove final modifier from $property $propertyList.';
|
||||
// }
|
||||
|
||||
class StaticObservableFields extends PropertyErrors {
|
||||
@override
|
||||
String get message => 'Remove static modifier from $property $propertyList.';
|
||||
}
|
||||
|
||||
class AsyncGeneratorActionMethods extends PropertyErrors {
|
||||
@override
|
||||
// ignore: overridden_fields
|
||||
Pluralize propertyPlural = _methodPluralizer;
|
||||
|
||||
@override
|
||||
String get message =>
|
||||
'Replace async* modifier with async from $property $propertyList.';
|
||||
}
|
||||
|
||||
class NonAsyncMethods extends PropertyErrors {
|
||||
@override
|
||||
// ignore: overridden_fields
|
||||
Pluralize propertyPlural = _methodPluralizer;
|
||||
|
||||
@override
|
||||
String get message =>
|
||||
'Return a Future or a Stream from $property $propertyList.';
|
||||
}
|
||||
|
||||
class InvalidComputedAnnotations extends PropertyErrors {
|
||||
@override
|
||||
// ignore: overridden_fields
|
||||
Pluralize propertyPlural = _memberPluralizer;
|
||||
|
||||
@override
|
||||
String get message =>
|
||||
'Remove @computed annotation for $property $propertyList. They only apply to property-getters.';
|
||||
}
|
||||
|
||||
class InvalidObservableAnnotations extends PropertyErrors {
|
||||
@override
|
||||
// ignore: overridden_fields
|
||||
Pluralize propertyPlural = _memberPluralizer;
|
||||
|
||||
@override
|
||||
String get message =>
|
||||
'Remove @observable annotation for $property $propertyList. They only apply to fields.';
|
||||
}
|
||||
|
||||
class InvalidActionAnnotations extends PropertyErrors {
|
||||
@override
|
||||
// ignore: overridden_fields
|
||||
Pluralize propertyPlural = _memberPluralizer;
|
||||
|
||||
@override
|
||||
String get message =>
|
||||
'Remove @action annotation for $property $propertyList. They only apply to methods.';
|
||||
}
|
||||
|
||||
class InvalidStaticMethods extends PropertyErrors {
|
||||
@override
|
||||
// ignore: overridden_fields
|
||||
Pluralize propertyPlural = _methodPluralizer;
|
||||
|
||||
@override
|
||||
String get message => 'Remove static modifier from $property $propertyList.';
|
||||
}
|
||||
|
||||
class NameList {
|
||||
final List<String> _names = [];
|
||||
|
||||
void add(String name) => _names.add(name);
|
||||
|
||||
int get length => _names.length;
|
||||
|
||||
bool get isNotEmpty => _names.isNotEmpty;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (_names.length == 1) {
|
||||
return '"${_names[0]}"';
|
||||
}
|
||||
|
||||
final buf = StringBuffer();
|
||||
for (var i = 0; i < _names.length; i++) {
|
||||
final name = _names[i];
|
||||
buf.write('"$name"');
|
||||
if (i < _names.length - 2) {
|
||||
buf.write(', ');
|
||||
} else if (i == _names.length - 2) {
|
||||
buf.write(' and ');
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class Pluralize {
|
||||
Pluralize(this._single, this._multiple);
|
||||
|
||||
final String _single;
|
||||
final String _multiple;
|
||||
|
||||
String call(int count) => count == 1 ? _single : _multiple;
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/dart/element/visitor.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
import '../widget_gen_annotations.dart';
|
||||
import 'errors.dart';
|
||||
import 'template/properties/base.dart';
|
||||
import 'template/properties/color.dart';
|
||||
import 'template/properties/edge_insets_geometry.dart';
|
||||
import 'template/properties/enum.dart';
|
||||
import 'template/properties/function.dart';
|
||||
import 'template/properties/key.dart';
|
||||
import 'template/properties/list_widget.dart';
|
||||
import 'template/properties/matrix_4.dart';
|
||||
import 'template/properties/offset.dart';
|
||||
import 'template/properties/size.dart';
|
||||
import 'template/properties/supported.dart';
|
||||
import 'template/properties/widget.dart';
|
||||
import 'template/util.dart';
|
||||
import 'template/widget_class_gen.dart';
|
||||
import 'type_names.dart';
|
||||
|
||||
class StoreClassVisitor extends SimpleElementVisitor {
|
||||
StoreClassVisitor(
|
||||
String publicTypeName,
|
||||
ClassElement userClass,
|
||||
StoreTemplate template,
|
||||
) : _errors = StoreClassCodegenErrors(publicTypeName) {
|
||||
_storeTemplate = template
|
||||
..typeParams.templates.addAll(userClass.typeParameters
|
||||
.map((type) => typeParamTemplate(type, typeNameFinder)))
|
||||
..typeArgs.templates.addAll(userClass.typeParameters.map((t) => t.name))
|
||||
..parentTypeName = userClass.name
|
||||
..publicTypeName = publicTypeName
|
||||
..widgetName = getClassName(userClass)
|
||||
..isAnimated = getWidgetIsAnimated(userClass)
|
||||
..allowTap = getAllowTap(userClass)
|
||||
..animatedDurationMilliseconds = getAnimationDuration(userClass);
|
||||
}
|
||||
|
||||
StoreTemplate _storeTemplate;
|
||||
DartType className;
|
||||
Map<String, DartType> fields = {};
|
||||
Map<String, dynamic> metaData = {};
|
||||
LibraryScopedNameFinder typeNameFinder;
|
||||
|
||||
final StoreClassCodegenErrors _errors;
|
||||
|
||||
String get source {
|
||||
if (_errors.hasErrors) {
|
||||
log.severe(_errors.message);
|
||||
return '';
|
||||
}
|
||||
return _storeTemplate.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
visitConstructorElement(ConstructorElement element) {
|
||||
className = element.type.returnType;
|
||||
return super.visitConstructorElement(element);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitClassElement(ClassElement element) {
|
||||
if (isWidgetClass(element) || isPropertyClass(element)) {
|
||||
_errors.nonAbstractStoreMixinDeclarations
|
||||
.addIf(!element.isAbstract, element.name);
|
||||
}
|
||||
// if the class is annotated to generate toString() method we add the information to the _storeTemplate
|
||||
// _storeTemplate.generateToString = hasGeneratedToString(element);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitFieldElement(FieldElement element) {
|
||||
fields[element.name] = element.type;
|
||||
metaData[element.name] = element.metadata;
|
||||
if (_fieldIsNotValid(element)) {
|
||||
return;
|
||||
}
|
||||
final _enumKey = const TypeChecker.fromRuntime(EnumKey);
|
||||
if (_enumKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _enumKey.firstAnnotationOfExact(element);
|
||||
final template = EnumOptionTemplate()
|
||||
..defaultValue = annotation.getField('defaultValue').toStringValue()
|
||||
..key = annotation.getField('key').toStringValue()
|
||||
..values = annotation
|
||||
.getField('values')
|
||||
.toListValue()
|
||||
.map((e) => e.toStringValue())
|
||||
.toList()
|
||||
..isPrivate = element.isPrivate
|
||||
..propertyType =
|
||||
annotation?.getField('propertyType')?.toStringValue() ??
|
||||
element.type.toString()
|
||||
..name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString() == 'Color') {
|
||||
final template = ColorOptionTemplate();
|
||||
final _colorKey = const TypeChecker.fromRuntime(ColorKey);
|
||||
if (_colorKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _colorKey.firstAnnotationOfExact(element);
|
||||
template.defaultValue =
|
||||
annotation.getField('defaultValue').toIntValue();
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
final _supportedKey = const TypeChecker.fromRuntime(SupportedKey);
|
||||
if (_supportedKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final template = SupportedOptionTemplate();
|
||||
final annotation = _supportedKey.firstAnnotationOfExact(element);
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString() == 'Widget') {
|
||||
final template = WidgetOptionTemplate();
|
||||
final _widgetKey = const TypeChecker.fromRuntime(WidgetKey);
|
||||
if (_widgetKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _widgetKey.firstAnnotationOfExact(element);
|
||||
template.fallback = annotation.getField('defaultValue').toStringValue();
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
template.acceptType = annotation.getField('acceptType').toStringValue();
|
||||
template.acceptWidth =
|
||||
annotation.getField('acceptWidth').toDoubleValue();
|
||||
template.acceptHeight =
|
||||
annotation.getField('acceptHeight').toDoubleValue();
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString() == 'Matrix4') {
|
||||
final template = Matrix4OptionTemplate();
|
||||
final _widgetKey = const TypeChecker.fromRuntime(Matrix4Key);
|
||||
if (_widgetKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _widgetKey.firstAnnotationOfExact(element);
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString() == 'EdgeInsets') {
|
||||
final template = EdgeInsetsOptionTemplate();
|
||||
final _widgetKey = const TypeChecker.fromRuntime(EdgeInsetsKey);
|
||||
if (_widgetKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _widgetKey.firstAnnotationOfExact(element);
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
template.defaultValue =
|
||||
annotation.getField('defaultValue').toDoubleValue();
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString() == 'Offset') {
|
||||
final template = OffsetOptionTemplate();
|
||||
final _widgetKey = const TypeChecker.fromRuntime(OffsetKey);
|
||||
if (_widgetKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _widgetKey.firstAnnotationOfExact(element);
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString() == 'Size') {
|
||||
final template = SizeOptionTemplate();
|
||||
final _widgetKey = const TypeChecker.fromRuntime(SizeKey);
|
||||
if (_widgetKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _widgetKey.firstAnnotationOfExact(element);
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString().contains('List')) {
|
||||
final template = ListWidgetOptionTemplate();
|
||||
final _widgetKey = const TypeChecker.fromRuntime(ListWidgetKey);
|
||||
if (_widgetKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _widgetKey.firstAnnotationOfExact(element);
|
||||
template.fallback = annotation.getField('fallback').toStringValue();
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
template.empty = annotation.getField('empty').toBoolValue();
|
||||
template.acceptType = annotation.getField('acceptType').toStringValue();
|
||||
}
|
||||
template.acceptType ??= 'WidgetBaseData';
|
||||
template.empty ??= true;
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString() == 'Function') {
|
||||
final template = FunctionOptionTemplate();
|
||||
final _widgetKey = const TypeChecker.fromRuntime(FunctionKey);
|
||||
if (_widgetKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _widgetKey.firstAnnotationOfExact(element);
|
||||
template.defaultValue = annotation.getField('fallback').toStringValue();
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.type.toString() == 'Key') {
|
||||
final template = KeyOptionTemplate();
|
||||
final _widgetKey = const TypeChecker.fromRuntime(TreeKey);
|
||||
if (_widgetKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _widgetKey.firstAnnotationOfExact(element);
|
||||
template.defaultValue =
|
||||
annotation.getField('defaultValue').toStringValue();
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
const _ignoreFields = [
|
||||
'widgetData',
|
||||
'widgetContext',
|
||||
'widgetRender',
|
||||
];
|
||||
if (_ignoreFields.contains(element.name)) {
|
||||
return;
|
||||
}
|
||||
const _allowedTypes = [
|
||||
'double',
|
||||
'int',
|
||||
'num',
|
||||
'String',
|
||||
'bool',
|
||||
'Object',
|
||||
];
|
||||
if (!_allowedTypes.contains(element.type.toString())) {
|
||||
print('Not Implemented -> ${element.type}');
|
||||
return;
|
||||
}
|
||||
final template = BaseOptionTemplate();
|
||||
final _baseKey = const TypeChecker.fromRuntime(PropertyKey);
|
||||
if (_baseKey.hasAnnotationOfExact(element, throwOnUnresolved: false)) {
|
||||
final annotation = _baseKey.firstAnnotationOfExact(element);
|
||||
template.defaultValue =
|
||||
annotation.getField('defaultValue').toStringValue();
|
||||
template.key = annotation.getField('key').toStringValue();
|
||||
template.tryParse = annotation.getField('tryParse').toBoolValue();
|
||||
} else {
|
||||
template.tryParse = false;
|
||||
}
|
||||
template.isPrivate = element.isPrivate;
|
||||
template.propertyType = element.type.toString();
|
||||
template.name = element.name;
|
||||
_storeTemplate.settings.add(template);
|
||||
return;
|
||||
}
|
||||
|
||||
bool _fieldIsNotValid(FieldElement element) => _any([
|
||||
_errors.staticObservables.addIf(element.isStatic, element.name),
|
||||
// _errors.finalObservables.addIf(element.isFinal, element.name)
|
||||
]);
|
||||
}
|
||||
|
||||
const _widgetChecker = TypeChecker.fromRuntime(WidgetClass);
|
||||
const _propertyChecker = TypeChecker.fromRuntime(PropertyClass);
|
||||
|
||||
// Checks if the class as a toString annotation
|
||||
bool isWidgetClass(ClassElement classElement) =>
|
||||
_widgetChecker.hasAnnotationOfExact(classElement);
|
||||
bool isPropertyClass(ClassElement classElement) =>
|
||||
_propertyChecker.hasAnnotationOfExact(classElement);
|
||||
|
||||
String getClassName(ClassElement classElement) {
|
||||
if (isWidgetClass(classElement)) {
|
||||
final annotation = _widgetChecker.firstAnnotationOfExact(classElement);
|
||||
return annotation.getField('name').toStringValue();
|
||||
}
|
||||
if (isPropertyClass(classElement)) {
|
||||
final annotation = _propertyChecker.firstAnnotationOfExact(classElement);
|
||||
return annotation.getField('name').toStringValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool getWidgetIsAnimated(ClassElement classElement) {
|
||||
if (isWidgetClass(classElement)) {
|
||||
final annotation = _widgetChecker.firstAnnotationOfExact(classElement);
|
||||
return annotation.getField('animated').toBoolValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int getAnimationDuration(ClassElement classElement) {
|
||||
if (isWidgetClass(classElement)) {
|
||||
final annotation = _widgetChecker.firstAnnotationOfExact(classElement);
|
||||
return annotation.getField('durationMilliseconds').toIntValue();
|
||||
}
|
||||
return 250;
|
||||
}
|
||||
|
||||
double getPreferredHeight(ClassElement classElement) {
|
||||
if (isWidgetClass(classElement)) {
|
||||
final annotation = _widgetChecker.firstAnnotationOfExact(classElement);
|
||||
return annotation.getField('preferredHeight').toDoubleValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
double getPreferredWidth(ClassElement classElement) {
|
||||
if (isWidgetClass(classElement)) {
|
||||
final annotation = _widgetChecker.firstAnnotationOfExact(classElement);
|
||||
return annotation.getField('preferredWidth').toDoubleValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool getAllowTap(ClassElement classElement) {
|
||||
if (isWidgetClass(classElement)) {
|
||||
final annotation = _widgetChecker.firstAnnotationOfExact(classElement);
|
||||
return annotation.getField('allowTap').toBoolValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _any(List<bool> list) => list.any(_identity);
|
||||
|
||||
T _identity<T>(T value) => value;
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'util.dart';
|
||||
|
||||
class CommaList<T> {
|
||||
CommaList(this.templates) : assert(templates != null);
|
||||
|
||||
final List<T> templates;
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
templates.map((t) => t.toString()).where((s) => s.isNotEmpty).join(', ');
|
||||
}
|
||||
|
||||
class SurroundedCommaList<T> {
|
||||
SurroundedCommaList(this.prefix, this.suffix, this.templates)
|
||||
: assert(prefix != null),
|
||||
assert(suffix != null),
|
||||
assert(templates != null);
|
||||
|
||||
final String prefix;
|
||||
final String suffix;
|
||||
final List<T> templates;
|
||||
|
||||
@override
|
||||
String toString() => surroundNonEmpty(prefix, suffix, CommaList(templates));
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
class ParamTemplate {
|
||||
String name;
|
||||
String type;
|
||||
String defaultValue;
|
||||
bool hasRequiredAnnotation = false;
|
||||
|
||||
String get asArgument => name;
|
||||
|
||||
NamedArgTemplate get asNamedArgument => NamedArgTemplate()..name = name;
|
||||
|
||||
String get metadata => hasRequiredAnnotation ? '@required ' : '';
|
||||
|
||||
@override
|
||||
String toString() => defaultValue == null
|
||||
? '$metadata$type $name'
|
||||
: '$type $name = $defaultValue';
|
||||
}
|
||||
|
||||
class TypeParamTemplate {
|
||||
String name;
|
||||
String bound;
|
||||
|
||||
String get asArgument => name;
|
||||
|
||||
@override
|
||||
String toString() => bound == null ? name : '$name extends $bound';
|
||||
}
|
||||
|
||||
class NamedArgTemplate {
|
||||
String name;
|
||||
|
||||
@override
|
||||
String toString() => '$name: $name';
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class BaseOptionTemplate extends SettingsImpl {
|
||||
String defaultValue;
|
||||
bool isPrivate;
|
||||
bool tryParse;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.write("if (params[${name}Key] != null) ");
|
||||
sb.writeln('{');
|
||||
sb.write('return ');
|
||||
if (tryParse) {
|
||||
sb.write("$propertyType.tryParse(params[${name}Key].toString())");
|
||||
if (defaultValue != null) {
|
||||
sb.write(' ?? $defaultValue');
|
||||
}
|
||||
} else {
|
||||
sb.write('params[${name}Key] as $propertyType');
|
||||
}
|
||||
sb.writeln(';');
|
||||
sb.writeln('}');
|
||||
if (defaultValue != null) {
|
||||
if (propertyType == 'String') {
|
||||
sb.writeln("return '$defaultValue';");
|
||||
} else {
|
||||
sb.writeln("return $defaultValue;");
|
||||
}
|
||||
} else {
|
||||
sb.writeln("return null;");
|
||||
}
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.writeln('params[${name}Key] = val;');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class ColorOptionTemplate extends SettingsImpl {
|
||||
int defaultValue;
|
||||
bool isPrivate;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
// Getter
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.write("if (params[${name}Key] != null) ");
|
||||
sb.writeln('{');
|
||||
sb.writeln('int _value = $defaultValue;');
|
||||
sb.write("""
|
||||
String description = params[${name}Key].toString();
|
||||
if (description.startsWith('#')) {
|
||||
description = description.replaceAll('#$propertyType(', '').replaceAll(')', '');
|
||||
_value = int.tryParse(description);
|
||||
} else if (params[${name}Key] is Map) {
|
||||
if (params[${name}Key]['name'] == '$propertyType'
|
||||
&& params[${name}Key]['params'] != null
|
||||
&& params[${name}Key]['params']['0'] != null) {
|
||||
_value = int.tryParse(params[${name}Key]['params']['0']);
|
||||
}
|
||||
}
|
||||
""");
|
||||
sb.writeln('if (_value != null) {');
|
||||
sb.write('return $propertyType(_value);');
|
||||
sb.writeln('}');
|
||||
sb.writeln('}');
|
||||
if (defaultValue != null) {
|
||||
sb.writeln("return $propertyType($defaultValue);");
|
||||
} else {
|
||||
sb.writeln("return null;");
|
||||
}
|
||||
sb.writeln('}');
|
||||
// Setter
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.write('params[${name}Key] = "');
|
||||
sb.write('#$propertyType(\${val.value})');
|
||||
sb.writeln('";');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class EdgeInsetsOptionTemplate extends SettingsImpl {
|
||||
bool isPrivate;
|
||||
double defaultValue;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.writeln("""
|
||||
EdgeInsets _spacing = EdgeInsets.all(0.0);
|
||||
if (params[${name}Key] != null) {
|
||||
double top = 0;
|
||||
double bottom = 0;
|
||||
double left = 0;
|
||||
double right = 0;
|
||||
Map<String, dynamic> _spacingParams = params[${name}Key]['params'];
|
||||
top = _spacingParams['top'] ?? 0;
|
||||
bottom = _spacingParams['bottom'] ?? 0;
|
||||
left = _spacingParams['left'] ?? 0;
|
||||
right = _spacingParams['right'] ?? 0;
|
||||
_spacing = EdgeInsets.fromLTRB(left, top, right, bottom);
|
||||
}
|
||||
return _spacing;
|
||||
""");
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.write('''
|
||||
params[${name}Key] = {
|
||||
"name" : "EdgeInsets.only",
|
||||
"id" : "${name}KeyEdgeInsets",
|
||||
"params" : {
|
||||
"top" : val.top,
|
||||
"bottom" : val.bottom,
|
||||
"left" : val.left,
|
||||
"right" : val.right,
|
||||
}
|
||||
};
|
||||
''');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class EnumOptionTemplate extends SettingsImpl {
|
||||
String defaultValue;
|
||||
bool isPrivate;
|
||||
List values;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln("List<$propertyType> get ${name}Values => [");
|
||||
for (final item in values) {
|
||||
sb.writeln("${_getEnumValueFromString(item)},");
|
||||
}
|
||||
sb.writeln("];");
|
||||
sb.writeln('');
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.write("if (params[${name}Key] != null) ");
|
||||
sb.writeln('{');
|
||||
sb.write('final _value = ');
|
||||
sb.writeln("params[${name}Key].toString().replaceAll('#', '');");
|
||||
final _fallback =
|
||||
defaultValue == null ? null : _getEnumValueFromString(defaultValue);
|
||||
sb.writeln("""
|
||||
return ${name}Values.firstWhere(
|
||||
(element) => element.toString() == _value,
|
||||
orElse: () => $_fallback,
|
||||
)
|
||||
""");
|
||||
sb.writeln(';');
|
||||
sb.writeln('}');
|
||||
sb.writeln("return $_fallback;");
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.writeln('params[${name}Key] = "\$val";');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String _getEnumValueFromString(item) {
|
||||
if (item == null) return '';
|
||||
String description = item.toString().replaceAll('#', '');
|
||||
// if (!description.contains('(')) {
|
||||
// return "$propertyType." + describeEnum(item);
|
||||
// }
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
T getEnum<T>(String val, {T fallback, List<T> values}) {
|
||||
if (val == null) return fallback;
|
||||
final _value = val.replaceAll('#', '');
|
||||
return values.firstWhere(
|
||||
(element) => element.toString() == _value,
|
||||
orElse: () => null,
|
||||
);
|
||||
}
|
||||
|
||||
String describeEnum(String enumEntry) {
|
||||
final String description = enumEntry.toString();
|
||||
if (!description.contains('.')) {
|
||||
return description;
|
||||
}
|
||||
final int indexOfDot = description.indexOf('.');
|
||||
assert(indexOfDot != -1 && indexOfDot < description.length - 1);
|
||||
return description.substring(indexOfDot + 1);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class FunctionOptionTemplate extends SettingsImpl {
|
||||
String defaultValue;
|
||||
bool isPrivate;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('String get ${name}Val {');
|
||||
sb.write("if (params[${name}Key] != null) ");
|
||||
sb.writeln('{');
|
||||
sb.write('return ');
|
||||
sb.write('params[${name}Key] as String');
|
||||
sb.writeln(';');
|
||||
sb.writeln('}');
|
||||
if (defaultValue != null) {
|
||||
sb.writeln("return $defaultValue;");
|
||||
} else {
|
||||
sb.writeln("return null;");
|
||||
}
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val(String val) {');
|
||||
sb.writeln('params[${name}Key] = val;');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
String constructor() {
|
||||
final sb = StringBuffer();
|
||||
sb.write(' ');
|
||||
if (key != null && int.tryParse(key) != null) {
|
||||
sb.write('');
|
||||
} else if (key != null) {
|
||||
sb.write("$key: ");
|
||||
} else {
|
||||
sb.write("$name: ");
|
||||
}
|
||||
sb.writeln("() => onAction(context, ${name}Val)");
|
||||
sb.writeln(',');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
abstract class SettingsImpl {
|
||||
@override
|
||||
String toString() {
|
||||
final sb = StringBuffer();
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String access();
|
||||
|
||||
String constructor() {
|
||||
final sb = StringBuffer();
|
||||
sb.write(' ');
|
||||
if (key != null && int.tryParse(key) != null) {
|
||||
sb.write('${name}Val');
|
||||
} else if (key != null) {
|
||||
sb.write("$key: ${name}Val");
|
||||
} else {
|
||||
sb.write("$name: ${name}Val");
|
||||
}
|
||||
sb.writeln(',');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String keyValue() {
|
||||
final sb = StringBuffer();
|
||||
sb.write(' ');
|
||||
sb.writeln("String ${name}Key = '${key ?? name}';");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String get name;
|
||||
|
||||
String get key;
|
||||
|
||||
String get propertyType;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class KeyOptionTemplate extends SettingsImpl {
|
||||
String defaultValue;
|
||||
bool isPrivate;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.write("if (params[${name}Key] != null) ");
|
||||
sb.writeln('{');
|
||||
sb.write("""
|
||||
String _val = params[${name}Key].toString();
|
||||
if (_val.startsWith('#')) {
|
||||
_val = _val.substring(1);
|
||||
if (_val.startsWith('ValueKey')) {
|
||||
_val = _val.replaceAll('ValueKey', '');
|
||||
_val = _val.replaceAll('<String>', '');
|
||||
_val = _val.replaceAll('(', '');
|
||||
_val = _val.replaceAll(')', '');
|
||||
}
|
||||
}
|
||||
return ValueKey('\$_val')
|
||||
""");
|
||||
sb.writeln(';');
|
||||
sb.writeln('}');
|
||||
if (defaultValue != null) {
|
||||
sb.writeln("return ValueKey<String>('$defaultValue');");
|
||||
} else {
|
||||
sb.writeln("return null;");
|
||||
}
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.write("""
|
||||
if (val == null) {
|
||||
params[${name}Key] = null;
|
||||
} else {
|
||||
params[${name}Key] = "#ValueKey('\$val')";
|
||||
}
|
||||
""");
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class ListWidgetOptionTemplate extends SettingsImpl {
|
||||
String fallback;
|
||||
bool isPrivate;
|
||||
bool empty;
|
||||
String acceptType;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('final _${name}Listen = ValueNotifier<bool>(false);');
|
||||
sb.writeln('List<WidgetBase> get ${name}Val {');
|
||||
sb.write("if (params[${name}Key] != null) ");
|
||||
sb.writeln('{');
|
||||
sb.write("""
|
||||
final _children = <WidgetBase>[];
|
||||
final _list = List.from(params[${name}Key]);
|
||||
for (final item in _list) {
|
||||
if (item is Map<String, dynamic>) {
|
||||
_children.add(widgetRender(widgetContext, item));
|
||||
}
|
||||
}
|
||||
return _children;
|
||||
""");
|
||||
sb.writeln('}');
|
||||
if (fallback != null) {
|
||||
final random = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
sb.writeln("""
|
||||
return [
|
||||
widgetRender({
|
||||
'id': '$random',
|
||||
'name': '$fallback',
|
||||
'params': {},
|
||||
})
|
||||
];
|
||||
""");
|
||||
} else {
|
||||
sb.writeln("return null;");
|
||||
}
|
||||
sb.writeln('}');
|
||||
sb.writeln('void ${name}ValUpdate(Map<String, dynamic> val) {');
|
||||
sb.write("""
|
||||
if (params[${name}Key] == null) {
|
||||
params[${name}Key] = [];
|
||||
}
|
||||
params[${name}Key].add(val);
|
||||
""");
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
String constructor() {
|
||||
final sb = StringBuffer();
|
||||
sb.write(' ');
|
||||
if (key != null && int.tryParse(key) != null) {
|
||||
sb.write('');
|
||||
} else if (key != null) {
|
||||
sb.write("$key: ");
|
||||
} else {
|
||||
sb.write("$name: ");
|
||||
}
|
||||
sb.write("""
|
||||
${name}Val == null && !widgetContext.isDragging ? ${empty ? '[]' : 'null'} : [
|
||||
if (${name}Val != null)
|
||||
for (final item in ${name}Val) item.build(context),
|
||||
""");
|
||||
if (acceptType != null && acceptType.isNotEmpty) {
|
||||
sb.write("""
|
||||
if (widgetContext.isDragging)
|
||||
DragTarget<$acceptType>(
|
||||
onAccept: (val) {
|
||||
_${name}Listen.value = false;
|
||||
if (val != null) {
|
||||
${name}ValUpdate(val?.data);
|
||||
}
|
||||
},
|
||||
onLeave: (val) {
|
||||
_${name}Listen.value = false;
|
||||
},
|
||||
onWillAccept: (val) {
|
||||
_${name}Listen.value = true;
|
||||
return _${name}Listen.value;
|
||||
},
|
||||
builder: (context, accepted, rejected) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: _${name}Listen,
|
||||
builder: (context, _accepting, child) => SizedBox.fromSize(
|
||||
size: Size(${30}, ${30}),
|
||||
child: Placeholder(
|
||||
color: !_accepting ?
|
||||
Colors.grey :
|
||||
Theme.of(context).accentColor,
|
||||
),
|
||||
));
|
||||
},
|
||||
),
|
||||
""");
|
||||
}
|
||||
sb.writeln('],');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class Matrix4OptionTemplate extends SettingsImpl {
|
||||
bool isPrivate;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.writeln("""
|
||||
final _matrix = Matrix4.identity();
|
||||
if (params[${name}Key] != null) {
|
||||
if (params[${name}Key] is List) {
|
||||
final values = List.from(params[${name}Key]);
|
||||
_matrix.setValues(
|
||||
values[0],
|
||||
values[1],
|
||||
values[2],
|
||||
values[3],
|
||||
values[4],
|
||||
values[5],
|
||||
values[6],
|
||||
values[7],
|
||||
values[8],
|
||||
values[9],
|
||||
values[10],
|
||||
values[11],
|
||||
values[12],
|
||||
values[13],
|
||||
values[14],
|
||||
values[15],
|
||||
);
|
||||
} else if (params[${name}Key] is String) {
|
||||
final description = params[${name}Key].toString();
|
||||
final _entryMatches = 'setEntry('.allMatches(description).toList();
|
||||
final _rotateXMatches = 'rotateX('.allMatches(description).toList();
|
||||
final _rotateYMatches = 'rotateY('.allMatches(description).toList();
|
||||
final _rotateZMatches = 'rotateZ('.allMatches(description).toList();
|
||||
for (final idx in _entryMatches) {
|
||||
int start = idx.end;
|
||||
int end = description.indexOf(')', start);
|
||||
final _values = description
|
||||
.substring(start, end)
|
||||
.split(',')
|
||||
.map((e) => num.tryParse(e.trim()))
|
||||
.toList();
|
||||
_matrix.setEntry(
|
||||
_values[0].toInt(),
|
||||
_values[1].toInt(),
|
||||
_values[2].toDouble(),
|
||||
);
|
||||
}
|
||||
for (final idx in _rotateXMatches) {
|
||||
int start = idx.end;
|
||||
int end = description.indexOf(')', start);
|
||||
final _value = num.tryParse(description.substring(start, end).trim());
|
||||
_matrix.rotateX(_value.toDouble());
|
||||
}
|
||||
for (final idx in _rotateYMatches) {
|
||||
int start = idx.end;
|
||||
int end = description.indexOf(')', start);
|
||||
final _value = num.tryParse(description.substring(start, end).trim());
|
||||
_matrix.rotateY(_value.toDouble());
|
||||
}
|
||||
for (final idx in _rotateZMatches) {
|
||||
int start = idx.end;
|
||||
int end = description.indexOf(')', start);
|
||||
final _value = num.tryParse(description.substring(start, end).trim());
|
||||
_matrix.rotateZ(_value.toDouble());
|
||||
}
|
||||
}
|
||||
}
|
||||
return _matrix;
|
||||
""");
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.writeln('params[${name}Key] = val.storage.toList();');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class OffsetOptionTemplate extends SettingsImpl {
|
||||
bool isPrivate;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.writeln("""
|
||||
Offset _offset = Offset(0.0, 0.0);
|
||||
if (params[${name}Key] != null) {
|
||||
double dx = 0;
|
||||
double dy = 0;
|
||||
Map<String, dynamic> _offsetParams = params[${name}Key]['params'];
|
||||
dx = _offsetParams['0'] ?? 0;
|
||||
dy = _offsetParams['1'] ?? 0;
|
||||
_offset = Offset(dx, dy);
|
||||
}
|
||||
return _offset;
|
||||
""");
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.write('''
|
||||
params[${name}Key] = {
|
||||
"name" : "Offset",
|
||||
"id" : "${name}KeyOffset",
|
||||
"params" : {
|
||||
"0" : val.dx,
|
||||
"1" : val.dy,
|
||||
}
|
||||
};
|
||||
''');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class SizeOptionTemplate extends SettingsImpl {
|
||||
bool isPrivate;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.writeln("""
|
||||
Size _size = Size(0.0, 0.0);
|
||||
if (params[${name}Key] != null) {
|
||||
double width = 0;
|
||||
double height = 0;
|
||||
Map<String, dynamic> _sizeParams = params[${name}Key]['params'];
|
||||
width = _sizeParams['0'] ?? 0;
|
||||
height = _sizeParams['1'] ?? 0;
|
||||
_size = Size(width, height);
|
||||
}
|
||||
return _size;
|
||||
""");
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.write('''
|
||||
params[${name}Key] = {
|
||||
"name" : "Size",
|
||||
"id" : "${name}KeySize",
|
||||
"params" : {
|
||||
"0" : val.width,
|
||||
"1" : val.height,
|
||||
}
|
||||
};
|
||||
''');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'impl.dart';
|
||||
|
||||
class SupportedOptionTemplate extends SettingsImpl {
|
||||
bool isPrivate;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('$propertyType get ${name}Val {');
|
||||
sb.write("if (params[${name}Key] != null) ");
|
||||
sb.writeln('{');
|
||||
sb.write('return ');
|
||||
sb.write('$propertyType(params[${name}Key], widgetContext, widgetRender)');
|
||||
sb.writeln(';');
|
||||
sb.writeln('}');
|
||||
sb.writeln("return null;");
|
||||
sb.writeln('}');
|
||||
sb.writeln('set ${name}Val($propertyType val) {');
|
||||
sb.writeln('params[${name}Key] = val;');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
String constructor() {
|
||||
final sb = StringBuffer();
|
||||
sb.write(' ');
|
||||
if (key != null && int.tryParse(key) != null) {
|
||||
sb.write('');
|
||||
} else if (key != null) {
|
||||
sb.write("$key: ");
|
||||
} else {
|
||||
sb.write("$name: ");
|
||||
}
|
||||
sb.writeln("${name}Val?.build(context)");
|
||||
sb.writeln(',');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import 'impl.dart';
|
||||
|
||||
import 'package:shortid/shortid.dart';
|
||||
|
||||
class WidgetOptionTemplate extends SettingsImpl {
|
||||
String fallback;
|
||||
bool isPrivate;
|
||||
String acceptType;
|
||||
double acceptWidth;
|
||||
double acceptHeight;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
String key;
|
||||
|
||||
@override
|
||||
String propertyType;
|
||||
|
||||
@override
|
||||
String access() {
|
||||
final sb = StringBuffer();
|
||||
if (acceptType != null) {
|
||||
sb.writeln('final _${name}Listen = ValueNotifier<bool>(false);');
|
||||
}
|
||||
sb.writeln('WidgetBase get ${name}Val {');
|
||||
sb.write("if (params[${name}Key] != null) ");
|
||||
sb.writeln('{');
|
||||
sb.write('return ');
|
||||
sb.write('widgetRender(widgetContext, params[${name}Key])');
|
||||
sb.writeln(';');
|
||||
sb.writeln('}');
|
||||
sb.writeln("return null;");
|
||||
sb.writeln('}');
|
||||
sb.writeln('void ${name}ValUpdate(Map<String, dynamic> val) {');
|
||||
sb.write("""
|
||||
final _data = val;
|
||||
_data['id'] = '${shortid.generate()}';
|
||||
if (_data['name'] == 'Text') {
|
||||
_data['params']['style']['id'] = '${shortid.generate()}';
|
||||
}
|
||||
if (_data['name'] == 'Icon') {
|
||||
_data['params']['0']['id'] = '${shortid.generate()}';
|
||||
}
|
||||
""");
|
||||
sb.writeln('params[${name}Key] = _data;');
|
||||
sb.writeln('widgetContext.onUpdate(id, widgetData);');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
String constructor() {
|
||||
final sb = StringBuffer();
|
||||
sb.write(' ');
|
||||
if (key != null && int.tryParse(key) != null) {
|
||||
sb.write('');
|
||||
} else if (key != null) {
|
||||
sb.write("$key: ");
|
||||
} else {
|
||||
sb.write("$name: ");
|
||||
}
|
||||
if (acceptType == null) {
|
||||
sb.writeln('${name}Val?.build(context)');
|
||||
} else {
|
||||
sb.write("""
|
||||
!widgetContext.isDragging || (widgetContext.isDragging && ${name}Val?.build(context) != null) ?
|
||||
(
|
||||
${name}Val?.build(context)
|
||||
|
||||
""");
|
||||
if (fallback != null) {
|
||||
sb.write("""
|
||||
?? (widgetRender(widgetContext, json.decode(json.encode({
|
||||
'id': '${shortid.generate()}',
|
||||
'name': '$fallback',
|
||||
'params': {},
|
||||
})))).build(context)
|
||||
""");
|
||||
}
|
||||
sb.write("""
|
||||
)
|
||||
""");
|
||||
sb.write("""
|
||||
:
|
||||
PreferredSize(
|
||||
preferredSize: Size(${acceptWidth ?? 30}, ${acceptHeight ?? 30}),
|
||||
child: DragTarget<$acceptType>(
|
||||
onAccept: (val) {
|
||||
_${name}Listen.value = false;
|
||||
if (val != null) {
|
||||
${name}ValUpdate(val?.data);
|
||||
}
|
||||
},
|
||||
onLeave: (val) {
|
||||
_${name}Listen.value = false;
|
||||
},
|
||||
onWillAccept: (val) {
|
||||
_${name}Listen.value = true;
|
||||
return _${name}Listen.value;
|
||||
},
|
||||
builder: (context, accepted, rejected) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: _${name}Listen,
|
||||
builder: (context, _accepting, child) => SizedBox.fromSize(
|
||||
size: Size(${acceptWidth ?? 30}, ${acceptHeight ?? 30}),
|
||||
child: Placeholder(
|
||||
color: !_accepting ?
|
||||
Colors.grey :
|
||||
Theme.of(context).accentColor,
|
||||
),
|
||||
));
|
||||
},
|
||||
),
|
||||
)
|
||||
""");
|
||||
}
|
||||
sb.writeln(',');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Rows<T> {
|
||||
final List<T> _templates = [];
|
||||
|
||||
void add(T template) => _templates.add(template);
|
||||
|
||||
bool get isEmpty => _templates.isEmpty;
|
||||
List get templates => _templates;
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
_templates.map((t) => t.toString()).where((s) => s.isNotEmpty).join('\n');
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
const _analyzerIgnores =
|
||||
'// ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, avoid_init_to_null';
|
||||
|
||||
class StoreFileTemplate {
|
||||
Iterable<String> storeSources;
|
||||
|
||||
@override
|
||||
String toString() => storeSources.isEmpty
|
||||
? ''
|
||||
: '''
|
||||
$_analyzerIgnores
|
||||
|
||||
${storeSources.join('\n\n')}
|
||||
''';
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
import '../type_names.dart';
|
||||
import 'params.dart';
|
||||
|
||||
// ignore: avoid_annotating_with_dynamic
|
||||
String surroundNonEmpty(String prefix, String suffix, dynamic content) {
|
||||
final contentStr = content.toString();
|
||||
return contentStr.isEmpty ? '' : '$prefix$contentStr$suffix';
|
||||
}
|
||||
|
||||
const _streamChecker = TypeChecker.fromRuntime(Stream);
|
||||
|
||||
class AsyncMethodChecker {
|
||||
AsyncMethodChecker([TypeChecker checkStream]) {
|
||||
_checkStream = checkStream ?? _streamChecker;
|
||||
}
|
||||
|
||||
TypeChecker _checkStream;
|
||||
|
||||
bool returnsFuture(MethodElement method) =>
|
||||
method.returnType.isDartAsyncFuture ||
|
||||
(method.isAsynchronous &&
|
||||
!method.isGenerator &&
|
||||
method.returnType.isDynamic);
|
||||
|
||||
bool returnsStream(MethodElement method) =>
|
||||
_checkStream.isAssignableFromType(method.returnType) ||
|
||||
(method.isAsynchronous &&
|
||||
method.isGenerator &&
|
||||
method.returnType.isDynamic);
|
||||
}
|
||||
|
||||
TypeParamTemplate typeParamTemplate(
|
||||
TypeParameterElement param,
|
||||
LibraryScopedNameFinder typeNameFinder,
|
||||
) =>
|
||||
TypeParamTemplate()
|
||||
..name = param.name
|
||||
..bound = param.bound != null
|
||||
? typeNameFinder.findTypeParameterBoundsTypeName(param)
|
||||
: null;
|
||||
@@ -0,0 +1,106 @@
|
||||
import 'comma_list.dart';
|
||||
import 'params.dart';
|
||||
import 'properties/impl.dart';
|
||||
|
||||
class MixinStoreTemplate extends StoreTemplate {
|
||||
final String extendsClass;
|
||||
double width, height;
|
||||
|
||||
MixinStoreTemplate(this.extendsClass);
|
||||
String get typeName => '_\$$publicTypeName';
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('abstract class $typeName$typeParams extends $extendsClass {');
|
||||
for (final setting in settings) {
|
||||
sb.write(setting.keyValue());
|
||||
}
|
||||
sb.writeln('');
|
||||
final _hasPreferredSize = width != null || height != null;
|
||||
if (_hasPreferredSize) {
|
||||
sb.writeln('@override');
|
||||
sb.write('Size get preferredSize => ');
|
||||
if (width != null && height != null) {
|
||||
sb.write('Size($width, $height)');
|
||||
} else if (width != null) {
|
||||
sb.write('Size.fromWidth($width)');
|
||||
} else if (height != null) {
|
||||
sb.write('Size.fromHeight($height)');
|
||||
}
|
||||
sb.writeln(';');
|
||||
}
|
||||
sb.writeln('');
|
||||
sb.writeln('@override');
|
||||
sb.writeln('Map<String, String> get properties => {');
|
||||
for (final setting in settings) {
|
||||
sb.write("'${setting?.key ?? setting.name}'");
|
||||
sb.write(':');
|
||||
sb.write("'${setting.propertyType}'");
|
||||
sb.writeln(',');
|
||||
}
|
||||
sb.writeln('};');
|
||||
sb.writeln('');
|
||||
for (final setting in settings) {
|
||||
sb.writeln(setting.access());
|
||||
}
|
||||
sb.writeln('');
|
||||
sb.writeln('@override');
|
||||
sb.writeln('Object build(BuildContext context) {');
|
||||
sb.write(' return ');
|
||||
if (allowTap) {
|
||||
if (_hasPreferredSize) {
|
||||
sb.writeln('PreferredSize(');
|
||||
sb.writeln('preferredSize: preferredSize,');
|
||||
sb.writeln('child: ');
|
||||
}
|
||||
sb.writeln('GestureDetector(');
|
||||
sb.writeln("onTap: () => widgetContext.onTap(id, widgetData),");
|
||||
sb.writeln('child: ');
|
||||
}
|
||||
if (isAnimated) {
|
||||
sb.write('Animated');
|
||||
}
|
||||
sb.writeln('$widgetName(');
|
||||
settings.sort((a, b) => (a?.key ?? a.name).compareTo((b?.key ?? b.name)));
|
||||
if (isAnimated) {
|
||||
sb.writeln(
|
||||
'duration: const Duration(milliseconds: $animatedDurationMilliseconds),');
|
||||
}
|
||||
for (final setting in settings) {
|
||||
sb.writeln(setting.constructor());
|
||||
}
|
||||
sb.writeln(')');
|
||||
if (allowTap) {
|
||||
if (_hasPreferredSize) {
|
||||
sb.write(',');
|
||||
sb.writeln(')');
|
||||
}
|
||||
sb.write(',');
|
||||
sb.writeln(')');
|
||||
}
|
||||
sb.write(';');
|
||||
sb.writeln('}');
|
||||
sb.writeln('');
|
||||
sb.writeln('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class StoreTemplate {
|
||||
final SurroundedCommaList<TypeParamTemplate> typeParams =
|
||||
SurroundedCommaList('<', '>', []);
|
||||
final SurroundedCommaList<String> typeArgs =
|
||||
SurroundedCommaList('<', '>', []);
|
||||
String publicTypeName;
|
||||
String parentTypeName;
|
||||
String widgetName;
|
||||
bool isAnimated;
|
||||
int animatedDurationMilliseconds;
|
||||
bool allowTap;
|
||||
|
||||
final List<SettingsImpl> settings = [];
|
||||
|
||||
@override
|
||||
String toString();
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
|
||||
import 'template/comma_list.dart';
|
||||
|
||||
/// Determines the names of types within the context of a library, determining
|
||||
/// prefixes if applicable.
|
||||
///
|
||||
/// For example, if a library has been imported with a name, references to types
|
||||
/// contained or exported by that library must be prefixed by that name.
|
||||
///
|
||||
/// ```dart
|
||||
/// import 'dart:io' as io;
|
||||
///
|
||||
/// io.File someFile;
|
||||
/// ```
|
||||
///
|
||||
/// If we had a reference to `someFile`'s [Element], [findVariableTypeName]
|
||||
/// would return `"io.File"`.
|
||||
class LibraryScopedNameFinder {
|
||||
LibraryScopedNameFinder(this.library);
|
||||
|
||||
final LibraryElement library;
|
||||
|
||||
Map<Element, String> _namesByElement;
|
||||
Map<Element, String> get namesByElement {
|
||||
if (_namesByElement != null) {
|
||||
return _namesByElement;
|
||||
}
|
||||
|
||||
_namesByElement = {};
|
||||
|
||||
// Add all of this library's type-defining elements to the name map
|
||||
final libraryElements =
|
||||
library.topLevelElements.whereType<TypeDefiningElement>();
|
||||
for (final element in libraryElements) {
|
||||
_namesByElement[element] = element.name;
|
||||
}
|
||||
|
||||
// Reverse each import's export namespace so we can map elements to their
|
||||
// library-local names. Note that the definedNames include a prefix if there
|
||||
// is one.
|
||||
for (final import in library.imports) {
|
||||
for (final entry in import.namespace.definedNames.entries) {
|
||||
_namesByElement[entry.value] = entry.key;
|
||||
}
|
||||
}
|
||||
|
||||
return _namesByElement;
|
||||
}
|
||||
|
||||
String findVariableTypeName(VariableElement variable) =>
|
||||
_getDartTypeName(variable.type);
|
||||
|
||||
String findGetterTypeName(PropertyAccessorElement getter) {
|
||||
assert(getter.isGetter);
|
||||
return findReturnTypeName(getter);
|
||||
}
|
||||
|
||||
String findParameterTypeName(ParameterElement parameter) =>
|
||||
_getDartTypeName(parameter.type);
|
||||
|
||||
String findReturnTypeName(FunctionTypedElement executable) =>
|
||||
_getDartTypeName(executable.returnType);
|
||||
|
||||
List<String> findReturnTypeArgumentTypeNames(ExecutableElement executable) {
|
||||
final returnType = executable.returnType;
|
||||
return returnType is ParameterizedType
|
||||
? returnType.typeArguments.map(_getDartTypeName).toList()
|
||||
: [];
|
||||
}
|
||||
|
||||
String findTypeParameterBoundsTypeName(TypeParameterElement typeParameter) {
|
||||
assert(typeParameter.bound != null);
|
||||
return _getDartTypeName(typeParameter.bound);
|
||||
}
|
||||
|
||||
/// Calculates a type name, including its type arguments
|
||||
///
|
||||
/// The returned string will include import prefixes on all applicable types.
|
||||
String _getDartTypeName(DartType type) {
|
||||
var typeElement = type.element;
|
||||
if (type is FunctionType) {
|
||||
// If we're dealing with a typedef, we let it undergo the standard name
|
||||
// lookup. Otherwise, we special case the function naming.
|
||||
if (typeElement?.enclosingElement is GenericTypeAliasElement) {
|
||||
typeElement = typeElement.enclosingElement;
|
||||
} else {
|
||||
return _getFunctionTypeName(type);
|
||||
}
|
||||
} else if (
|
||||
// Some types don't have associated elements, like void
|
||||
typeElement == null ||
|
||||
// This is a bare type param, like "T"
|
||||
type is TypeParameterType) {
|
||||
// TODO(shyndman): This ignored deprecation can be removed when we
|
||||
// increase the analyzer dependency's lower bound to 0.39.2, and
|
||||
// migrate to using `DartType.getDisplayString`.
|
||||
// ignore: deprecated_member_use
|
||||
return type.displayName;
|
||||
}
|
||||
|
||||
return _getNamedElementTypeName(typeElement, type);
|
||||
}
|
||||
|
||||
String _getFunctionTypeName(FunctionType type) {
|
||||
final returnTypeName = _getDartTypeName(type.returnType);
|
||||
|
||||
final normalParameterTypeNames =
|
||||
CommaList(type.normalParameterTypes.map(_getDartTypeName).toList());
|
||||
final optionalParameterTypeNames = SurroundedCommaList(
|
||||
'[', ']', type.optionalParameterTypes.map(_getDartTypeName).toList());
|
||||
final namedParameterPairs = type.namedParameterTypes.entries
|
||||
.map((entry) => '${_getDartTypeName(entry.value)} ${entry.key}')
|
||||
.toList();
|
||||
final namedParameterTypeNames =
|
||||
SurroundedCommaList('{', '}', namedParameterPairs);
|
||||
|
||||
final parameterTypeNames = CommaList([
|
||||
normalParameterTypeNames,
|
||||
optionalParameterTypeNames,
|
||||
namedParameterTypeNames,
|
||||
]);
|
||||
|
||||
return '$returnTypeName Function($parameterTypeNames)';
|
||||
}
|
||||
|
||||
String _getNamedElementTypeName(Element typeElement, DartType type) {
|
||||
// Determine the name of the type, without type arguments.
|
||||
assert(namesByElement.containsKey(typeElement));
|
||||
|
||||
// If the type is parameterized, we recursively name its type arguments
|
||||
if (type is ParameterizedType && type.typeArguments.isNotEmpty) {
|
||||
final typeArgNames = SurroundedCommaList(
|
||||
'<', '>', type.typeArguments.map(_getDartTypeName).toList());
|
||||
return '${namesByElement[typeElement]}$typeArgNames';
|
||||
}
|
||||
|
||||
return namesByElement[typeElement];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user