diff --git a/app/README.md b/app/README.md index d446fd0..a6629fe 100644 --- a/app/README.md +++ b/app/README.md @@ -1,16 +1,47 @@ -# dde_gesture_manager +# DDE Gesture Manager +专为 DDE 桌面环境打造的触摸板手势管理工具 -A new Flutter project. +## ProviderGenerator +利用 [source_gen](https://pub.dev/packages/source_gen) 和 [build_runner](https://pub.flutter-io.cn/packages/build_runner) 生成 [provider](https://pub.flutter-io.cn/packages/provider) 的模板代码: +1. 在 `lib/models/` 下编写模型类 +```dart +import 'package:dde_gesture_manager/builder/provider_annotation.dart'; -## Getting Started +@ProviderModel() +class Test { + @ProviderModelProp() + bool? tested; -This project is a starting point for a Flutter application. + @ProviderModelProp() + String? name; +} -A few resources to get you started if this is your first Flutter project: +``` -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) +2. `app` 项目目录下执行 `flutter packages pub run build_runner build` -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +3. 将在 `lib/models/test.provider.dart` 生成如下代码: +```dart +import 'package:flutter/foundation.dart'; +import 'package:dde_gesture_manager/extensions/compare_extension.dart'; +import 'test.dart'; + +class TestProvider extends Test with ChangeNotifier { + void setProps({ + bool? tested, + String? name, + }) { + bool changed = false; + if (tested.diff(this.tested)) { + this.tested = tested; + changed = true; + } + if (name.diff(this.name)) { + this.name = name; + changed = true; + } + if (changed) notifyListeners(); + } +} + +``` diff --git a/app/build.yaml b/app/build.yaml new file mode 100644 index 0000000..710187a --- /dev/null +++ b/app/build.yaml @@ -0,0 +1,11 @@ +builders: + provider_builder: + import: 'lib/builder/provider_builder.dart' + builder_factories: [ 'providerBuilder' ] + build_extensions: { '.dart': [ '.provider.dart' ] } + auto_apply: root_package + build_to: source + defaults: + generate_for: + include: + - lib/models/** diff --git a/app/lib/builder/provider_annotation.dart b/app/lib/builder/provider_annotation.dart new file mode 100644 index 0000000..3f66ed0 --- /dev/null +++ b/app/lib/builder/provider_annotation.dart @@ -0,0 +1,9 @@ +class ProviderModel { + const ProviderModel(); +} + +class ProviderModelProp { + const ProviderModelProp({this.nullable = true}); + + final bool nullable; +} diff --git a/app/lib/builder/provider_builder.dart b/app/lib/builder/provider_builder.dart new file mode 100644 index 0000000..42f92e8 --- /dev/null +++ b/app/lib/builder/provider_builder.dart @@ -0,0 +1,6 @@ +import 'package:dde_gesture_manager/builder/provider_generator.dart'; +import 'package:source_gen/source_gen.dart'; +import 'package:build/build.dart'; + +Builder providerBuilder(BuilderOptions options) => + LibraryBuilder(ProviderGenerator(), generatedExtension: '.provider.dart'); diff --git a/app/lib/builder/provider_generator.dart b/app/lib/builder/provider_generator.dart new file mode 100644 index 0000000..14fc6bc --- /dev/null +++ b/app/lib/builder/provider_generator.dart @@ -0,0 +1,48 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:build/src/builder/build_step.dart'; +import 'package:dde_gesture_manager/builder/provider_annotation.dart'; +import 'package:source_gen/source_gen.dart'; +import 'package:collection/collection.dart'; + +class AnnotationField { + String name; + String type; + + AnnotationField(this.name, this.type); +} + +class ProviderGenerator extends GeneratorForAnnotation { + @override + generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) { + var className = (element as ClassElement).source.shortName; + List fields = []; + element.fields.forEach((field) { + var annotation = field.metadata.firstWhereOrNull( + (m) => m.computeConstantValue()?.type?.getDisplayString(withNullability: true) == 'ProviderModelProp'); + if (annotation != null) + fields.add( + AnnotationField( + field.displayName, + field.type.getDisplayString( + withNullability: annotation.computeConstantValue()?.getField('nullable')?.toBoolValue() ?? true, + ), + ), + ); + }); + return ''' +import 'package:flutter/foundation.dart'; +import 'package:dde_gesture_manager/extensions/compare_extension.dart'; +import '$className'; + +class ${element.displayName}Provider extends ${element.displayName} with ChangeNotifier { + void setProps({ + ${fields.map((f) => '${f.type.endsWith('?') ? '' : 'required '}${f.type} ${f.name},').join('\n')} + }) { + bool changed = false; + ${fields.map((f) => 'if (${f.name}.diff(this.${f.name})) {this.${f.name} = ${f.name}; changed = true; }').join('\n')} + if (changed) notifyListeners(); + } +} + '''; + } +} diff --git a/app/lib/extensions/compare_extension.dart b/app/lib/extensions/compare_extension.dart new file mode 100644 index 0000000..7d54d64 --- /dev/null +++ b/app/lib/extensions/compare_extension.dart @@ -0,0 +1,3 @@ +extension CompareExtension on Object? { + bool diff(other) => this != null && this != other; +} diff --git a/app/lib/extensions/sout_extension.dart b/app/lib/extensions/sout_extension.dart new file mode 100644 index 0000000..c1ad104 --- /dev/null +++ b/app/lib/extensions/sout_extension.dart @@ -0,0 +1,12 @@ +extension SoutExtension on Object? { + void sout() { + switch (this.runtimeType) { + case String: + return print(this); + case Null: + return print(null); + default: + return print(this.toString()); + } + } +} diff --git a/app/lib/main.dart b/app/lib/main.dart index 1318877..8382e84 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,29 +1,13 @@ -import 'package:dde_gesture_manager/models/settings.dart'; +import 'package:dde_gesture_manager/models/settings.provider.dart'; +import 'package:dde_gesture_manager/utils/init.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:gsettings/gsettings.dart'; import 'package:provider/provider.dart'; -import 'package:window_manager/window_manager.dart'; -import 'package:xdg_directories/xdg_directories.dart' as xdgDir; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - if (!kIsWeb) { - print(await xdgDir.configHome); - print(await xdgDir.cacheHome); - print(await xdgDir.dataHome); - print('------'); - print(await xdgDir.configDirs.join('\n')); - print('------'); - print(await xdgDir.dataDirs.join('\n')); - print('------'); - print(await xdgDir.runtimeDir); - print('------'); - var windowManager = WindowManager.instance; - windowManager.setTitle('Gesture Manager For DDE'); - windowManager.setMinimumSize(const Size(800, 600)); - } + await initConfigs(); runApp(MyApp()); } @@ -36,41 +20,32 @@ class MyApp extends StatelessWidget { ], builder: (context, child) { var isDarkMode = context.watch().isDarkMode; - return AnimatedCrossFade( - crossFadeState: isDarkMode != null ? CrossFadeState.showFirst : CrossFadeState.showSecond, - alignment: Alignment.center, - layoutBuilder: (topChild, topChildKey, bottomChild, bottomChildKey) => Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Positioned(key: bottomChildKey, child: bottomChild), - Positioned(key: topChildKey, child: topChild), - ], + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: isDarkMode == true ? Colors.blue : Colors.blue, + brightness: isDarkMode == true ? Brightness.dark : Brightness.light, ), - firstChild: MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: isDarkMode == true ? Colors.blue : Colors.blue, - brightness: isDarkMode == true ? Brightness.dark : Brightness.light, + home: AnimatedCrossFade( + crossFadeState: isDarkMode != null ? CrossFadeState.showFirst : CrossFadeState.showSecond, + alignment: Alignment.center, + layoutBuilder: (topChild, topChildKey, bottomChild, bottomChildKey) => Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Positioned(key: bottomChildKey, child: bottomChild), + Positioned(key: topChildKey, child: topChild), + ], ), - home: MyHomePage(title: 'Flutter Demo Home Page'), - ), - secondChild: Builder(builder: (_) { - var xsettings = GSettings('com.deepin.xsettings'); - xsettings.get('theme-name').then((value) { - Future.delayed( - Duration(seconds: 1), - () => context.read().setProps(isDarkMode: value.toString().contains('dark')), + firstChild: MyHomePage(title: 'Flutter Demo Home Page'), + secondChild: Builder(builder: (context) { + initEvents(context); + return Center( + child: CircularProgressIndicator(), ); - }); - xsettings.keysChanged.listen((event) { - xsettings.get('theme-name').then((value) { - context.read().setProps(isDarkMode: value.toString().contains('dark')); - }); - }); - return CircularProgressIndicator(); - }), - duration: Duration(seconds: 1), + }), + duration: Duration(seconds: 1), + ), ); }, ); diff --git a/app/lib/models/settings.dart b/app/lib/models/settings.dart index 12ee534..06b4c8f 100644 --- a/app/lib/models/settings.dart +++ b/app/lib/models/settings.dart @@ -1,20 +1,10 @@ -import 'package:flutter/foundation.dart'; +import 'package:dde_gesture_manager/builder/provider_annotation.dart'; +@ProviderModel() class Settings { - bool? _isDarkMode; + @ProviderModelProp() + bool? isDarkMode; - bool? get isDarkMode => _isDarkMode; -} - -class SettingsProvider extends Settings with ChangeNotifier { - void setProps({ - bool? isDarkMode, - }) { - bool changed = false; - if (this._isDarkMode != isDarkMode) { - this._isDarkMode = isDarkMode; - changed = true; - } - if (changed) notifyListeners(); - } + @ProviderModelProp() + String? name; } diff --git a/app/lib/utils/helper.dart b/app/lib/utils/helper.dart index 682c2c1..f306e6a 100644 --- a/app/lib/utils/helper.dart +++ b/app/lib/utils/helper.dart @@ -1,2 +1,17 @@ +import 'package:shared_preferences/shared_preferences.dart'; + class H { -} \ No newline at end of file + H._(); + + static final _h = H._(); + + factory H() => _h; + + late SharedPreferences _sp; + + SharedPreferences get sp => _sp; + + initSharedPreference() async { + _sp = await SharedPreferences.getInstance(); + } +} diff --git a/app/lib/utils/init.dart b/app/lib/utils/init.dart new file mode 100644 index 0000000..edafa2a --- /dev/null +++ b/app/lib/utils/init.dart @@ -0,0 +1 @@ +export 'init_web.dart' if (dart.library.io) 'init_linux.dart'; diff --git a/app/lib/utils/init_linux.dart b/app/lib/utils/init_linux.dart new file mode 100644 index 0000000..4291835 --- /dev/null +++ b/app/lib/utils/init_linux.dart @@ -0,0 +1,34 @@ +import 'package:dde_gesture_manager/utils/helper.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:gsettings/gsettings.dart'; +import 'package:provider/provider.dart'; +import 'package:window_manager/window_manager.dart'; +import 'package:dde_gesture_manager/models/settings.provider.dart'; + +Future initEvents(BuildContext context) async { + var isDark = MediaQuery.of(context).platformBrightness == Brightness.dark; + if (isDark) { + context.read().setProps(isDarkMode: isDark); + } else { + var xsettings = GSettings('com.deepin.xsettings'); + xsettings.get('theme-name').then((value) { + Future.delayed( + Duration(seconds: 1), + () => context.read().setProps(isDarkMode: value.toString().contains('dark')), + ); + }); + xsettings.keysChanged.listen((event) { + xsettings.get('theme-name').then((value) { + context.read().setProps(isDarkMode: value.toString().contains('dark')); + }); + }); + } +} + +Future initConfigs() async { + await H().initSharedPreference(); + var windowManager = WindowManager.instance; + windowManager.setTitle('Gesture Manager For DDE'); + windowManager.setMinimumSize(const Size(800, 600)); +} diff --git a/app/lib/utils/init_web.dart b/app/lib/utils/init_web.dart new file mode 100644 index 0000000..8fe0d79 --- /dev/null +++ b/app/lib/utils/init_web.dart @@ -0,0 +1,13 @@ +import 'package:dde_gesture_manager/models/settings.provider.dart'; +import 'package:dde_gesture_manager/utils/helper.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +Future initEvents(BuildContext context) async { + var isDark = MediaQuery.of(context).platformBrightness == Brightness.dark; + context.read().setProps(isDarkMode: isDark); +} + +Future initConfigs() async { + await H().initSharedPreference(); +} diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 446340f..25d41f5 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -38,6 +38,8 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + build_runner: 2.1.2 + source_gen: 1.1.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec