diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..55929d4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*.dart] +max_line_length = 120 diff --git a/app/build_web.sh b/app/build_web.sh new file mode 100755 index 0000000..930ce73 --- /dev/null +++ b/app/build_web.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Downloads WASM locally and use local fonts +# Temporary solution until https://github.com/flutter/flutter/issues/70101 and 77580 provide a better way +flutter clean +flutter build web +wasmLocation=$(grep canvaskit-wasm build/web/main.dart.js | sed -e 's/.*https/https/' -e 's/\/bin.*/\/bin/' | uniq) +echo "Downloading WASM from $wasmLocation" +curl -o build/web/canvaskit.js "$wasmLocation/canvaskit.js" +curl -o build/web/canvaskit.wasm "$wasmLocation/canvaskit.wasm" +sed -i -e "s!$wasmLocation!.!" \ + -e "s!https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf!./assets/packages/amos_mobile_widgets/assets/google_fonts/Roboto-Regular.ttf!" \ + -e "s!https://fonts.googleapis.com/css2?family=Noto+Sans+Symbols!./assets/assets/css/Noto-Sans-Symbols.css!" \ + -e "s!https://fonts.googleapis.com/css2?family=Noto+Color+Emoji+Compat!./assets/assets/css/Noto-Color-Emoji-Compat.css!" \ + build/web/main.dart.js diff --git a/app/lib/builder/provider_generator.dart b/app/lib/builder/provider_generator.dart index 14fc6bc..cddb088 100644 --- a/app/lib/builder/provider_generator.dart +++ b/app/lib/builder/provider_generator.dart @@ -12,9 +12,13 @@ class AnnotationField { } class ProviderGenerator extends GeneratorForAnnotation { + var _preClassName; + @override generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) { var className = (element as ClassElement).source.shortName; + var needImports = className != _preClassName; + _preClassName = className; List fields = []; element.fields.forEach((field) { var annotation = field.metadata.firstWhereOrNull( @@ -29,11 +33,14 @@ class ProviderGenerator extends GeneratorForAnnotation { ), ); }); - return ''' + return [ + if (needImports) + ''' 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')} @@ -43,6 +50,7 @@ class ${element.displayName}Provider extends ${element.displayName} with ChangeN if (changed) notifyListeners(); } } - '''; + ''' + ]; } } diff --git a/app/lib/extensions/sout_extension.dart b/app/lib/extensions/sout_extension.dart index c1ad104..a6bd60c 100644 --- a/app/lib/extensions/sout_extension.dart +++ b/app/lib/extensions/sout_extension.dart @@ -5,6 +5,8 @@ extension SoutExtension on Object? { return print(this); case Null: return print(null); + case List: + return print('[${(this as List).join(', ')}]'); default: return print(this.toString()); } diff --git a/app/lib/models/solution.dart b/app/lib/models/solution.dart new file mode 100644 index 0000000..664f872 --- /dev/null +++ b/app/lib/models/solution.dart @@ -0,0 +1,90 @@ +import 'dart:convert'; + +import 'package:dde_gesture_manager/builder/provider_annotation.dart'; +import 'package:dde_gesture_manager/utils/helper.dart'; + +@ProviderModel() +class Solution { + @ProviderModelProp() + String? name; + + @ProviderModelProp() + String? description; + + @ProviderModelProp() + List? gestures; + + Solution.parse(solution) { + if (solution is String) solution = json.decode(solution); + assert(solution is Map); + name = solution['name']; + description = solution['desc']; + gestures = (solution['gestures'] as List? ?? []).map((ele) => GestureProp.parse(ele)).toList(); + } +} + +enum Gesture { + swipe, + tap, + pinch, +} + +enum GestureDirection { + up, + down, + left, + right, + pinch_in, + pinch_out, + none, +} + +enum GestureType { + built_in, + commandline, + shortcut, +} + +@ProviderModel() +class GestureProp { + @ProviderModelProp() + Gesture? gesture; + + @ProviderModelProp() + GestureDirection? direction; + + @ProviderModelProp() + int? fingers; + + @ProviderModelProp() + GestureType? type; + + @ProviderModelProp() + String? command; + + @ProviderModelProp() + String? remark; + + @override + bool operator ==(Object other) => + other is GestureProp && + other.gesture == this.gesture && + other.direction == this.direction && + other.fingers == this.fingers; + + @override + String toString() { + return 'GestureProp{gesture: $gesture, direction: $direction, fingers: $fingers, type: $type, command: $command, remark: $remark}'; + } + + GestureProp.parse(props) { + if (props is String) props = json.decode(props); + assert(props is Map); + gesture = H.getGestureByName(props['gesture']); + direction = H.getGestureDirectionByName(props['direction']); + fingers = props['fingers']; + type = H.getGestureTypeByName(props['type']); + command = props['command']; + remark = props['remark']; + } +} diff --git a/app/lib/pages/gesture_editor.dart b/app/lib/pages/gesture_editor.dart index 668456e..fcc4a70 100644 --- a/app/lib/pages/gesture_editor.dart +++ b/app/lib/pages/gesture_editor.dart @@ -1,6 +1,7 @@ import 'package:dde_gesture_manager/constants/constants.dart'; import 'package:dde_gesture_manager/extensions.dart'; import 'package:dde_gesture_manager/models/content_layout.provider.dart'; +import 'package:dde_gesture_manager/models/solution.provider.dart'; import 'package:dde_gesture_manager/utils/helper.dart'; import 'package:dde_gesture_manager/widgets/dde_button.dart'; import 'package:dde_gesture_manager/widgets/dde_data_table.dart'; @@ -13,6 +14,10 @@ class GestureEditor extends StatelessWidget { @override Widget build(BuildContext context) { var layoutProvider = context.watch(); + var solutionProvider = context.watch(); + solutionProvider.name.sout(); + solutionProvider.gestures.sout(); + return Flexible( child: Padding( padding: const EdgeInsets.all(10), @@ -22,7 +27,7 @@ class GestureEditor extends StatelessWidget { borderRadius: BorderRadius.circular(10), ), child: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.max, @@ -40,6 +45,13 @@ class GestureEditor extends StatelessWidget { ), ), ), + Text( + LocaleKeys.gesture_editor_label, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ).tr(), Visibility( visible: layoutProvider.marketOpened == false, child: DButton( @@ -52,6 +64,7 @@ class GestureEditor extends StatelessWidget { ), ], ), + Container(height: 10), Expanded( child: Container( decoration: BoxDecoration( @@ -72,6 +85,7 @@ class GestureEditor extends StatelessWidget { child: ConstrainedBox( constraints: BoxConstraints(minWidth: constraints.maxWidth), child: DDataTable( + headerBackgroundColor: context.t.dialogBackgroundColor, decoration: BoxDecoration( borderRadius: BorderRadius.circular(defaultBorderRadius), border: Border.all( @@ -79,71 +93,76 @@ class GestureEditor extends StatelessWidget { color: context.t.dividerColor, ), ), + dataRowColor: MaterialStateProperty.resolveWith((Set states) { + if (states.contains(MaterialState.hovered)) + return Colors.blue; + return null; + }), columns: [ - DDataColumn(label: Text('gesture')), - DDataColumn(label: Text('direction')), - DDataColumn(label: Text('fingers')), - DDataColumn(label: Text('type')), - DDataColumn(label: Text('command')), - DDataColumn(label: Text('remark')), + DDataColumn(label: Text(LocaleKeys.gesture_editor_gesture.tr())), + DDataColumn(label: Text(LocaleKeys.gesture_editor_direction.tr())), + DDataColumn(label: Text(LocaleKeys.gesture_editor_fingers.tr())), + DDataColumn(label: Text(LocaleKeys.gesture_editor_type.tr())), + DDataColumn(label: Text(LocaleKeys.gesture_editor_command.tr())), + DDataColumn(label: Text(LocaleKeys.gesture_editor_remark.tr())), ], rows: [ DDataRow( cells: [ - DDataCell(Text('swipe')), - DDataCell(Text('right')), + DDataCell(Text(LocaleKeys.gesture_editor_gestures_swipe).tr()), + DDataCell(Text(LocaleKeys.gesture_editor_directions_right).tr()), DDataCell(Text('3')), - DDataCell(Text('shortcut')), + DDataCell(Text(LocaleKeys.gesture_editor_types_shortcut).tr()), DDataCell(Text('ctrl+w')), DDataCell(Text('close current page.')), ], ), DDataRow( cells: [ - DDataCell(Text('swipe')), - DDataCell(Text('left')), + DDataCell(Text(LocaleKeys.gesture_editor_gestures_swipe).tr()), + DDataCell(Text(LocaleKeys.gesture_editor_directions_left).tr()), DDataCell(Text('3')), - DDataCell(Text('shortcut')), + DDataCell(Text(LocaleKeys.gesture_editor_types_shortcut).tr()), DDataCell(Text('ctrl+alt+t')), DDataCell(Text('reopen last closed page.')), ], ), DDataRow( cells: [ - DDataCell(Text('swipe')), - DDataCell(Text('left')), + DDataCell(Text(LocaleKeys.gesture_editor_gestures_swipe).tr()), + DDataCell(Text(LocaleKeys.gesture_editor_directions_left).tr()), DDataCell(Text('3')), - DDataCell(Text('shortcut')), + DDataCell(Text(LocaleKeys.gesture_editor_types_shortcut).tr()), DDataCell(Text('ctrl+alt+t')), DDataCell(Text('reopen last closed page.')), ], ), DDataRow( cells: [ - DDataCell(Text('swipe')), - DDataCell(Text('left')), + DDataCell(Text(LocaleKeys.gesture_editor_gestures_swipe).tr()), + DDataCell(Text(LocaleKeys.gesture_editor_directions_left).tr()), DDataCell(Text('3')), - DDataCell(Text('shortcut')), + DDataCell(Text(LocaleKeys.gesture_editor_types_shortcut).tr()), DDataCell(Text('ctrl+alt+t')), DDataCell(Text('reopen last closed page.')), ], ), DDataRow( cells: [ - DDataCell(Text('swipe')), - DDataCell(Text('left')), + DDataCell(Text(LocaleKeys.gesture_editor_gestures_swipe).tr()), + DDataCell(Text(LocaleKeys.gesture_editor_directions_left).tr()), DDataCell(Text('3')), - DDataCell(Text('shortcut')), + DDataCell(Text(LocaleKeys.gesture_editor_types_shortcut).tr()), DDataCell(Text('ctrl+alt+t')), DDataCell(Text('reopen last closed page.')), ], ), DDataRow( cells: [ - DDataCell(Text('swipe')), - DDataCell(Text('down')), + DDataCell(Text(LocaleKeys.gesture_editor_gestures_swipe).tr()), + DDataCell(Text(LocaleKeys.gesture_editor_directions_down).tr()), DDataCell(Text('3')), - DDataCell(Text('commandline')), + DDataCell(Text(LocaleKeys.gesture_editor_types_commandline).tr()), DDataCell(Text( 'dbus-send --type=method_call --dest=com.deepin.dde.Launcher /com/deepin/dde/Launcher com.deepin.dde.Launcher.Toggle')), DDataCell(TextButton( @@ -162,7 +181,7 @@ class GestureEditor extends StatelessWidget { ), Container(height: 10), Container( - height: 400, + height: 300, decoration: BoxDecoration( borderRadius: BorderRadius.circular(defaultBorderRadius), border: Border.all( diff --git a/app/lib/pages/home.dart b/app/lib/pages/home.dart index f1c3a4a..74c7ef6 100644 --- a/app/lib/pages/home.dart +++ b/app/lib/pages/home.dart @@ -1,3 +1,5 @@ +import 'package:dde_gesture_manager/extensions.dart'; +import 'package:dde_gesture_manager/models/solution.provider.dart'; import 'package:dde_gesture_manager/pages/content.dart'; import 'package:dde_gesture_manager/pages/footer.dart'; import 'package:flutter/material.dart'; @@ -13,18 +15,38 @@ class _HomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Content(), - ), - SizedBox( - height: 36, - child: Footer(), - ), + body: MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => SolutionProvider.parse(''' + { + "name": "test", + "desc": "some desc", + "gestures": [ + { + "gesture": "swipe", + "direction": "up", + "fingers": 3, + "type": "shortcut", + "command": "ctrl+w" + } + ] + } + ''')), + // ChangeNotifierProvider(create: (context) => GesturePropProvider()), ], + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Content(), + ), + SizedBox( + height: 36, + child: Footer(), + ), + ], + ), ), ); } diff --git a/app/lib/themes/dark.dart b/app/lib/themes/dark.dart index eddfa3c..5746339 100644 --- a/app/lib/themes/dark.dart +++ b/app/lib/themes/dark.dart @@ -22,4 +22,5 @@ var darkTheme = ThemeData.dark().copyWith( borderRadius: BorderRadius.circular(defaultBorderRadius), ), ), + dialogBackgroundColor: Color(0xff202020), ); diff --git a/app/lib/themes/light.dart b/app/lib/themes/light.dart index 07f5325..ffeee59 100644 --- a/app/lib/themes/light.dart +++ b/app/lib/themes/light.dart @@ -22,4 +22,5 @@ var lightTheme = ThemeData.light().copyWith( borderRadius: BorderRadius.circular(defaultBorderRadius), ), ), + dialogBackgroundColor: Color(0xfffefefe), ); diff --git a/app/lib/utils/helper.dart b/app/lib/utils/helper.dart index 0e67a88..3b471dd 100644 --- a/app/lib/utils/helper.dart +++ b/app/lib/utils/helper.dart @@ -1,4 +1,5 @@ import 'package:dde_gesture_manager/models/content_layout.provider.dart'; +import 'package:dde_gesture_manager/models/solution.dart'; import 'package:flutter/cupertino.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:dde_gesture_manager/constants/constants.dart'; @@ -47,6 +48,54 @@ class H { else return preferredPanelsStatus..marketPanelOpened = false; } + + static String getGestureName(Gesture gesture) => const { + Gesture.swipe: 'swipe', + Gesture.tap: 'tap', + Gesture.pinch: 'pinch', + }[gesture]!; + + static Gesture getGestureByName(String gestureName) => + const { + 'swipe': Gesture.swipe, + 'tap': Gesture.tap, + 'pinch': Gesture.pinch, + }[gestureName] ?? + Gesture.swipe; + + static String? getGestureDirectionName(GestureDirection direction) => const { + GestureDirection.up: 'up', + GestureDirection.down: 'down', + GestureDirection.left: 'left', + GestureDirection.right: 'right', + GestureDirection.pinch_in: 'in', + GestureDirection.pinch_out: 'out', + }[direction]; + + static GestureDirection getGestureDirectionByName(String? directionName) => + const { + 'up': GestureDirection.up, + 'down': GestureDirection.down, + 'left': GestureDirection.left, + 'right': GestureDirection.right, + 'in': GestureDirection.pinch_in, + 'out': GestureDirection.pinch_out, + }[directionName] ?? + GestureDirection.none; + + static String getGestureTypeName(GestureType type) => const { + GestureType.built_in: 'built_in', + GestureType.shortcut: 'shortcut', + GestureType.commandline: 'commandline', + }[type]!; + + static GestureType getGestureTypeByName(String typeName) => + const { + 'built_in': GestureType.built_in, + 'shortcut': GestureType.shortcut, + 'commandline': GestureType.commandline, + }[typeName] ?? + GestureType.built_in; } class PreferredPanelsStatus { diff --git a/app/lib/widgets/dde_data_table.dart b/app/lib/widgets/dde_data_table.dart index 32fd8b8..dcd6b06 100644 --- a/app/lib/widgets/dde_data_table.dart +++ b/app/lib/widgets/dde_data_table.dart @@ -6,6 +6,7 @@ import 'dart:math' as math; import 'package:flutter/foundation.dart'; +import 'package:flutter/painting.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/material.dart'; @@ -415,6 +416,8 @@ class DDataCell { /// provides controls for paging through the remainder of the data. /// * class DDataTable extends StatefulWidget { + final Color headerBackgroundColor; + /// Creates a widget describing a data table. /// /// The [columns] argument must be a list of as many [DataColumn] @@ -459,6 +462,7 @@ class DDataTable extends StatefulWidget { this.dividerThickness, required this.rows, this.checkboxHorizontalMargin, + required this.headerBackgroundColor, }) : assert(columns != null), assert(columns.isNotEmpty), assert(sortColumnIndex == null || (sortColumnIndex >= 0 && sortColumnIndex < columns.length)), @@ -1078,29 +1082,29 @@ class _DDataTableState extends State { } Future.microtask(() { - List _rects = []; - var changed = false; - if (tableRows.first.children != null) { - for (var i = 0; i < tableRows.first.children!.length; i++) { - _rects.add((tableRows.first.children![i] as RectGetter).getRect() ?? Rect.zero); - if (!changed && (_headersRect == null || (_headersRect != null && _headersRect![i] != _rects[i]))) { - changed = true; - } - } - if (changed) - setState(() { - _headersRect = _rects; - }); - } + _buildHeaderStack(tableRows); }); - var _skickyHeaders = []; - + List _skickyHeaders = []; + var _headerBackgroundHSLColor = HSLColor.fromColor(widget.headerBackgroundColor); + HSLColor.fromColor(widget.headerBackgroundColor).withSaturation(.1).toColor(); if (_headersRect != null && _headersRect!.length > 0) { for (var i = 0; i < _headersRect!.length; i++) { _skickyHeaders.add(Positioned( child: Container( - color: Colors.deepPurple, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + _headerBackgroundHSLColor + .withLightness( + _headerBackgroundHSLColor.lightness - 0.1 < 0 ? 0 : _headerBackgroundHSLColor.lightness - 0.1) + .toColor(), + widget.headerBackgroundColor, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), child: (tableRows.first.children![i] as RectGetter).clone(), ), left: _headersRect![i].left - _headersRect![0].left, @@ -1136,6 +1140,25 @@ class _DDataTableState extends State { ), ); } + + void _buildHeaderStack(List tableRows) { + List _rects = []; + var changed = false; + if (tableRows.first.children != null) { + for (var i = 0; i < tableRows.first.children!.length; i++) { + _rects.add((tableRows.first.children![i] as RectGetter).getRect() ?? Rect.zero); + if (!changed && (_headersRect == null || (_headersRect != null && _headersRect![i] != _rects[i]))) { + changed = true; + } + } + if (changed) + setState(() { + _headersRect = _rects; + }); + } + if (_rects == null || _rects.isEmpty || _rects.first == null) + Future.microtask(() => _buildHeaderStack(tableRows)); + } } /// A rectangular area of a Material that responds to touch but clips diff --git a/app/resources/langs/en.json b/app/resources/langs/en.json index 9eeed1b..3d356f7 100644 --- a/app/resources/langs/en.json +++ b/app/resources/langs/en.json @@ -24,5 +24,38 @@ }, "local_manager": { "title": "Local solution management" + }, + "gesture_editor": { + "label": "Gesture program editing", + "gesture": "gesture", + "direction": "direction", + "fingers": "fingers", + "type": "type", + "command": "command", + "remark": "remark", + "directions": { + "up": "up", + "down": "down", + "left": "left", + "right": "right", + "in": "pinch-in", + "out": "pinch-out" + }, + "gestures": { + "swipe": "swipe", + "pinch": "pinch", + "tap": "pinch" + }, + "types": { + "built_in": "built-in", + "commandline": "commandline", + "shortcut": "shortcut" + } + }, + "operation": { + "add": "Add", + "delete": "delete", + "duplicate": "duplicate", + "apply": "apply" } } \ No newline at end of file diff --git a/app/resources/langs/zh-CN.json b/app/resources/langs/zh-CN.json index 08196ef..5c8dca5 100644 --- a/app/resources/langs/zh-CN.json +++ b/app/resources/langs/zh-CN.json @@ -24,5 +24,38 @@ }, "local_manager": { "title": "本地方案管理" + }, + "gesture_editor": { + "label": "手势方案编辑", + "gesture": "手势", + "direction": "方向", + "fingers": "手指数", + "type": "类型", + "command": "命令", + "remark": "备注", + "directions": { + "up": "向上", + "down": "向下", + "left": "向左", + "right": "向右", + "in": "向内捏合", + "out": "向外展开" + }, + "gestures": { + "swipe": "滑动", + "pinch": "捏", + "tap": "点击" + }, + "types": { + "built_in": "内置操作", + "commandline": "命令行", + "shortcut": "快捷键" + } + }, + "operation": { + "add": "新增", + "delete": "删除", + "duplicate": "复制", + "apply": "应用" } } \ No newline at end of file