From 3a491144e868eddae7f6197f0d13c73ed0c6a874 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Sun, 29 Dec 2024 19:33:29 +0100 Subject: [PATCH] Add grid and ruler selections --- api/lib/src/models/tool.dart | 2 +- api/lib/src/models/tool.freezed.dart | 23 ++++++++-------- api/lib/src/models/tool.g.dart | 21 ++++++++++---- app/lib/cubits/current_index.dart | 5 ++-- app/lib/dialogs/import/add.dart | 18 ++++++++++++ app/lib/handlers/hand.dart | 4 +++ app/lib/handlers/handler.dart | 4 +-- app/lib/handlers/import.dart | 4 +-- app/lib/l10n/app_en.arb | 5 ++-- app/lib/selections/selection.dart | 2 ++ app/lib/selections/tools/grid.dart | 40 +++++++++++++++++++++++++++ app/lib/selections/tools/ruler.dart | 41 ++++++++++++++++++++++++++++ app/lib/selections/tools/tool.dart | 2 ++ app/lib/services/import.dart | 3 +- app/lib/visualizer/tool.dart | 9 +++++- 15 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 app/lib/selections/tools/grid.dart create mode 100644 app/lib/selections/tools/ruler.dart diff --git a/api/lib/src/models/tool.dart b/api/lib/src/models/tool.dart index b5906b923b09..437ecc2d66eb 100644 --- a/api/lib/src/models/tool.dart +++ b/api/lib/src/models/tool.dart @@ -195,7 +195,7 @@ sealed class Tool with _$Tool { factory Tool.ruler({ @Default('') String name, @Default('') String displayIcon, - @Default(SRGBColor.black) @ColorJsonConverter() SRGBColor gridColor, + @ColorJsonConverter() SRGBColor? color, }) = RulerTool; factory Tool.grid({ diff --git a/api/lib/src/models/tool.freezed.dart b/api/lib/src/models/tool.freezed.dart index de96650c80d6..b7f09f735a68 100644 --- a/api/lib/src/models/tool.freezed.dart +++ b/api/lib/src/models/tool.freezed.dart @@ -2799,7 +2799,7 @@ abstract class _$$RulerToolImplCopyWith<$Res> implements $ToolCopyWith<$Res> { $Res call( {String name, String displayIcon, - @ColorJsonConverter() SRGBColor gridColor}); + @ColorJsonConverter() SRGBColor? color}); } /// @nodoc @@ -2817,7 +2817,7 @@ class __$$RulerToolImplCopyWithImpl<$Res> $Res call({ Object? name = null, Object? displayIcon = null, - Object? gridColor = null, + Object? color = freezed, }) { return _then(_$RulerToolImpl( name: null == name @@ -2828,10 +2828,10 @@ class __$$RulerToolImplCopyWithImpl<$Res> ? _value.displayIcon : displayIcon // ignore: cast_nullable_to_non_nullable as String, - gridColor: null == gridColor - ? _value.gridColor - : gridColor // ignore: cast_nullable_to_non_nullable - as SRGBColor, + color: freezed == color + ? _value.color + : color // ignore: cast_nullable_to_non_nullable + as SRGBColor?, )); } } @@ -2842,7 +2842,7 @@ class _$RulerToolImpl extends RulerTool { _$RulerToolImpl( {this.name = '', this.displayIcon = '', - @ColorJsonConverter() this.gridColor = SRGBColor.black, + @ColorJsonConverter() this.color, final String? $type}) : $type = $type ?? 'ruler', super._(); @@ -2857,16 +2857,15 @@ class _$RulerToolImpl extends RulerTool { @JsonKey() final String displayIcon; @override - @JsonKey() @ColorJsonConverter() - final SRGBColor gridColor; + final SRGBColor? color; @JsonKey(name: 'type') final String $type; @override String toString() { - return 'Tool.ruler(name: $name, displayIcon: $displayIcon, gridColor: $gridColor)'; + return 'Tool.ruler(name: $name, displayIcon: $displayIcon, color: $color)'; } /// Create a copy of Tool @@ -2889,7 +2888,7 @@ abstract class RulerTool extends Tool { factory RulerTool( {final String name, final String displayIcon, - @ColorJsonConverter() final SRGBColor gridColor}) = _$RulerToolImpl; + @ColorJsonConverter() final SRGBColor? color}) = _$RulerToolImpl; RulerTool._() : super._(); factory RulerTool.fromJson(Map json) = @@ -2900,7 +2899,7 @@ abstract class RulerTool extends Tool { @override String get displayIcon; @ColorJsonConverter() - SRGBColor get gridColor; + SRGBColor? get color; /// Create a copy of Tool /// with the given fields replaced by the non-null parameter values. diff --git a/api/lib/src/models/tool.g.dart b/api/lib/src/models/tool.g.dart index 460514d1ecf7..f87f64628ad5 100644 --- a/api/lib/src/models/tool.g.dart +++ b/api/lib/src/models/tool.g.dart @@ -427,10 +427,8 @@ Map _$$TextureToolImplToJson(_$TextureToolImpl instance) => _$RulerToolImpl _$$RulerToolImplFromJson(Map json) => _$RulerToolImpl( name: json['name'] as String? ?? '', displayIcon: json['displayIcon'] as String? ?? '', - gridColor: json['gridColor'] == null - ? SRGBColor.black - : const ColorJsonConverter() - .fromJson((json['gridColor'] as num).toInt()), + color: _$JsonConverterFromJson( + json['color'], const ColorJsonConverter().fromJson), $type: json['type'] as String?, ); @@ -438,10 +436,23 @@ Map _$$RulerToolImplToJson(_$RulerToolImpl instance) => { 'name': instance.name, 'displayIcon': instance.displayIcon, - 'gridColor': const ColorJsonConverter().toJson(instance.gridColor), + 'color': _$JsonConverterToJson( + instance.color, const ColorJsonConverter().toJson), 'type': instance.$type, }; +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + _$GridToolImpl _$$GridToolImplFromJson(Map json) => _$GridToolImpl( name: json['name'] as String? ?? '', displayIcon: json['displayIcon'] as String? ?? '', diff --git a/app/lib/cubits/current_index.dart b/app/lib/cubits/current_index.dart index bd0e9e794261..d1991b5abaae 100644 --- a/app/lib/cubits/current_index.dart +++ b/app/lib/cubits/current_index.dart @@ -319,7 +319,7 @@ class CurrentIndexCubit extends Cubit { Handler? handler; bool needsDispose = false; if (state.index == index) { - handler = fetchHandler>(); + handler = fetchHandler>(disableTemporary: true); } else if (state.toggleableHandlers.containsKey(index)) { handler = state.toggleableHandlers[index]; } @@ -327,7 +327,8 @@ class CurrentIndexCubit extends Cubit { List tools = const []; final blocState = bloc.state; if (blocState is DocumentLoaded) tools = blocState.info.tools; - handler = Handler.fromTool(tools.elementAtOrNull(index)); + final tool = tools.elementAtOrNull(index) ?? HandTool(); + handler = Handler.fromTool(tool); needsDispose = true; } final result = callback(handler); diff --git a/app/lib/dialogs/import/add.dart b/app/lib/dialogs/import/add.dart index c6ab323a375b..81ca6d3a7761 100644 --- a/app/lib/dialogs/import/add.dart +++ b/app/lib/dialogs/import/add.dart @@ -209,6 +209,16 @@ class _AddDialogState extends State { .toLowerCase() .contains(search.toLowerCase())) .toList(); + final view = [ + Tool.ruler, + Tool.grid, + ] + .map((e) => e()) + .where((e) => e + .getLocalizedName(context) + .toLowerCase() + .contains(search.toLowerCase())) + .toList(); return ListView( shrinkWrap: true, children: [ @@ -306,6 +316,14 @@ class _AddDialogState extends State { ), const SizedBox(height: 16), ], + if (view.isNotEmpty) ...[ + _ToolsListView( + isMobile: isMobile, + title: AppLocalizations.of(context).view, + children: view.map(buildTool).toList(), + ), + const SizedBox(height: 16), + ], ], ); }), diff --git a/app/lib/handlers/hand.dart b/app/lib/handlers/hand.dart index 3a4365aec4b0..f2f43813a670 100644 --- a/app/lib/handlers/hand.dart +++ b/app/lib/handlers/hand.dart @@ -23,6 +23,10 @@ class GeneralHandHandler extends Handler { _moved ? SystemMouseCursors.grabbing : SystemMouseCursors.grab; } +class FallbackHandler extends GeneralHandHandler { + FallbackHandler(super.data); +} + class HandHandler extends GeneralHandHandler { HandHandler([HandTool? tool]) : super(tool ?? HandTool()); } diff --git a/app/lib/handlers/handler.dart b/app/lib/handlers/handler.dart index 7ab3294275f8..a725c0b94336 100644 --- a/app/lib/handlers/handler.dart +++ b/app/lib/handlers/handler.dart @@ -221,7 +221,7 @@ abstract class Handler { return Handler.fromTool(tool); } - static Handler fromTool(T? tool) { + static Handler fromTool(T tool) { return switch (tool) { HandTool() => HandHandler(tool), SelectTool() => SelectHandler(tool), @@ -244,7 +244,7 @@ abstract class Handler { AssetTool() => AssetHandler(tool), EyeDropperTool() => EyeDropperHandler(tool), ExportTool() => ExportHandler(tool), - _ => GeneralHandHandler(tool), + _ => FallbackHandler(tool), } as Handler; } diff --git a/app/lib/handlers/import.dart b/app/lib/handlers/import.dart index d36a36aa1d03..3540e3dde3d6 100644 --- a/app/lib/handlers/import.dart +++ b/app/lib/handlers/import.dart @@ -61,8 +61,8 @@ class ImportHandler extends Handler { .copyWith(id: createUniqueId())) .nonNulls .toList())); - context.refresh(); - context.bake(); + await context.refresh(); + await context.bake(); } @override diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 0d1390c723c3..e6428d052459 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -626,7 +626,7 @@ "renderResolution": "Render resolution", "translate": "Translate", "unencrypted": "Unencrypted", - "encrypted": "Decrypted", + "encrypted": "Encrypted", "encryptDocumentMessage": "Click to encrypt the document", "unencryptDocumentMessage": "Click to unencrypt the document", "unencrypt": "Unencrypt", @@ -634,5 +634,6 @@ "encryptWarning": "This will encrypt the document. You will need to remember the password to decrypt it.", "unencryptWarning": "This will unencrypt the document. The password will be removed and everyone with access will be able to open it.", "confirmPassword": "Confirm password", - "passwordMismatch": "The passwords do not match" + "passwordMismatch": "The passwords do not match", + "action": "Action" } diff --git a/app/lib/selections/selection.dart b/app/lib/selections/selection.dart index 72b4a480518d..0abf448a3bf3 100644 --- a/app/lib/selections/selection.dart +++ b/app/lib/selections/selection.dart @@ -31,10 +31,12 @@ part 'tools/tool.dart'; part 'tools/hand.dart'; part 'tools/area.dart'; part 'tools/eraser.dart'; +part 'tools/grid.dart'; part 'tools/label.dart'; part 'tools/laser.dart'; part 'tools/path_eraser.dart'; part 'tools/pen.dart'; +part 'tools/ruler.dart'; part 'tools/shape.dart'; part 'tools/stamp.dart'; part 'tools/texture.dart'; diff --git a/app/lib/selections/tools/grid.dart b/app/lib/selections/tools/grid.dart new file mode 100644 index 000000000000..c393b3957594 --- /dev/null +++ b/app/lib/selections/tools/grid.dart @@ -0,0 +1,40 @@ +part of '../selection.dart'; + +class GridToolSelection extends ToolSelection { + GridToolSelection(super.selected); + + @override + List buildProperties(BuildContext context) { + return [ + ...super.buildProperties(context), + const SizedBox(height: 8), + OffsetPropertyView( + title: Text(AppLocalizations.of(context).size), + value: Offset(selected.first.xSize, selected.first.ySize), + onChanged: (value) => update( + context, + selected + .map((e) => e.copyWith(xSize: value.dx, ySize: value.dy)) + .toList(), + ), + ), + const SizedBox(height: 8), + ColorField( + value: selected.first.color, + onChanged: (value) => update( + context, + selected.map((e) => e.copyWith(color: value)).toList(), + ), + title: Text(LeapLocalizations.of(context).color), + ), + ]; + } + + @override + Selection insert(dynamic element) { + if (element is GridTool) { + return GridToolSelection([...selected, element]); + } + return super.insert(element); + } +} diff --git a/app/lib/selections/tools/ruler.dart b/app/lib/selections/tools/ruler.dart new file mode 100644 index 000000000000..dab240fac245 --- /dev/null +++ b/app/lib/selections/tools/ruler.dart @@ -0,0 +1,41 @@ +part of '../selection.dart'; + +class RulerToolSelection extends ToolSelection { + RulerToolSelection(super.selected); + + @override + List buildProperties(BuildContext context) { + return [ + ...super.buildProperties(context), + ColorField( + title: Text(LeapLocalizations.of(context).color), + value: + selected.first.color?.withValues(a: 255) ?? SRGBColor.transparent, + subtitle: selected.first.color == null + ? Text(AppLocalizations.of(context).notSet) + : null, + leading: selected.first.color == null + ? null + : IconButton( + icon: const Icon(PhosphorIconsLight.trash), + onPressed: () => update( + context, + selected.map((e) => e.copyWith(color: null)).toList(), + ), + ), + onChanged: (value) => update( + context, + selected.map((e) => e.copyWith(color: value)).toList(), + ), + ), + ]; + } + + @override + Selection insert(dynamic element) { + if (element is RulerTool) { + return RulerToolSelection([...selected, element]); + } + return super.insert(element); + } +} diff --git a/app/lib/selections/tools/tool.dart b/app/lib/selections/tools/tool.dart index 14aa9662b25e..8e26018f6451 100644 --- a/app/lib/selections/tools/tool.dart +++ b/app/lib/selections/tools/tool.dart @@ -10,7 +10,9 @@ class ToolSelection extends Selection { EraserTool e => EraserToolSelection([e]), PathEraserTool e => PathEraserToolSelection([e]), AreaTool e => AreaToolSelection([e]), + GridTool e => GridToolSelection([e]), LaserTool e => LaserToolSelection([e]), + RulerTool e => RulerToolSelection([e]), ShapeTool e => ShapeToolSelection([e]), StampTool e => StampToolSelection([e]), TextureTool e => TextureToolSelection([e]), diff --git a/app/lib/services/import.dart b/app/lib/services/import.dart index b8c1d404438a..3490da121f0d 100644 --- a/app/lib/services/import.dart +++ b/app/lib/services/import.dart @@ -229,7 +229,8 @@ class ImportService { password = await showDialog( context: context, builder: (context) => NameDialog( - title: AppLocalizations.of(context).password, + title: AppLocalizations.of(context).encrypted, + hint: AppLocalizations.of(context).password, button: AppLocalizations.of(context).open, obscureText: true, ), diff --git a/app/lib/visualizer/tool.dart b/app/lib/visualizer/tool.dart index ea4fb1ec7cfe..f638ac859453 100644 --- a/app/lib/visualizer/tool.dart +++ b/app/lib/visualizer/tool.dart @@ -19,9 +19,16 @@ extension ToolCategoryVisualizer on ToolCategory { ToolCategory.normal => PhosphorIcons.paintBrush, ToolCategory.import => PhosphorIcons.arrowSquareIn, ToolCategory.surface => PhosphorIcons.monitor, - ToolCategory.action => PhosphorIcons.arrowClockwise, + ToolCategory.action => PhosphorIcons.play, ToolCategory.view => PhosphorIcons.eye, }; + String getLocalizedName(BuildContext context) => switch (this) { + ToolCategory.normal => AppLocalizations.of(context).normal, + ToolCategory.import => AppLocalizations.of(context).import, + ToolCategory.surface => AppLocalizations.of(context).surface, + ToolCategory.action => AppLocalizations.of(context).action, + ToolCategory.view => AppLocalizations.of(context).view, + }; } extension ToolVisualizer on Tool {