From 8d1676d916160d6ab561161002867e8ed638f313 Mon Sep 17 00:00:00 2001 From: Amit-Matth Date: Thu, 14 Aug 2025 15:00:35 +0530 Subject: [PATCH 1/4] PAINTROID-796 initial setup --- .../command_factory/command_factory.dart | 7 ++ .../graphic/dashed_path_command.dart | 66 +++++++++++ .../graphic/dashed_path_command.g.dart | 24 ++++ .../versioning/serializer_version.dart | 2 + .../versioning/version_strategy.dart | 6 + .../object/tools/clipping_tool_provider.dart | 22 ++++ .../tools/clipping_tool_provider.g.dart | 27 +++++ .../state/toolbox_state_provider.dart | 4 + .../state/toolbox_state_provider.g.dart | 2 +- .../tools/implementation/clipping_tool.dart | 110 ++++++++++++++++++ .../bottom_bar/tool_options/tool_options.dart | 1 + pubspec.lock | 2 +- pubspec.yaml | 1 + .../utils/dummy_version_strategy.dart | 6 + 14 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 lib/core/commands/command_implementation/graphic/dashed_path_command.dart create mode 100644 lib/core/commands/command_implementation/graphic/dashed_path_command.g.dart create mode 100644 lib/core/providers/object/tools/clipping_tool_provider.dart create mode 100644 lib/core/providers/object/tools/clipping_tool_provider.g.dart create mode 100644 lib/core/tools/implementation/clipping_tool.dart diff --git a/lib/core/commands/command_factory/command_factory.dart b/lib/core/commands/command_factory/command_factory.dart index 5dbfe01c..46cd96b1 100644 --- a/lib/core/commands/command_factory/command_factory.dart +++ b/lib/core/commands/command_factory/command_factory.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/text_command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/dashed_path_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/line_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/path_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/shape/ellipse_shape_command.dart'; @@ -16,6 +17,12 @@ class CommandFactory { ) => PathCommand(path, paint); + DashedPathCommand createDashedPathCommand( + PathWithActionHistory path, + Paint paint, + ) => + DashedPathCommand(path, paint); + LineCommand createLineCommand( PathWithActionHistory path, Paint paint, diff --git a/lib/core/commands/command_implementation/graphic/dashed_path_command.dart b/lib/core/commands/command_implementation/graphic/dashed_path_command.dart new file mode 100644 index 00000000..226a506a --- /dev/null +++ b/lib/core/commands/command_implementation/graphic/dashed_path_command.dart @@ -0,0 +1,66 @@ +import 'dart:ui'; + +import 'package:flutter/widgets.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart'; +import 'package:paintroid/core/commands/path_with_action_history.dart'; +import 'package:paintroid/core/json_serialization/converter/path_with_action_history_converter.dart'; +import 'package:paintroid/core/json_serialization/converter/paint_converter.dart'; +import 'package:paintroid/core/json_serialization/versioning/serializer_version.dart'; +import 'package:paintroid/core/json_serialization/versioning/version_strategy.dart'; +import 'package:path_drawing/path_drawing.dart'; + +part 'dashed_path_command.g.dart'; + +@JsonSerializable() +class DashedPathCommand extends GraphicCommand { + @JsonKey(includeToJson: true, includeFromJson: true) + final String type; + @JsonKey(includeToJson: true, includeFromJson: true) + final int version; + + DashedPathCommand( + this.path, + super.paint, { + this.type = SerializerType.DASHED_PATH_COMMAND, + int? version, + }) : version = version ?? + VersionStrategyManager.strategy.getDashedPathCommandVersion(); + + @PathWithActionHistoryConverter() + final PathWithActionHistory path; + + @override + void call(Canvas canvas) { + final dashLength = paint.strokeWidth * 4; + final dashGap = paint.strokeWidth * 2; + + final dashedPath = dashPath( + path.path, + dashArray: CircularIntervalList([dashLength, dashGap]), + ); + + canvas.drawPath(dashedPath, paint); + } + + @override + List get props => [paint, path, type, version]; + + factory DashedPathCommand.fromJson(Map json) { + int version = json['version'] as int; + + switch (version) { + case Version.v1: + return _$DashedPathCommandFromJson(json); + case Version.v2: + // For different versions of DashedPathCommand the deserialization + // has to be implemented manually. + // Autogenerated code can only be used for one version + default: + return _$DashedPathCommandFromJson(json); + } + } + + @override + Map toJson() => _$DashedPathCommandToJson(this); +} diff --git a/lib/core/commands/command_implementation/graphic/dashed_path_command.g.dart b/lib/core/commands/command_implementation/graphic/dashed_path_command.g.dart new file mode 100644 index 00000000..4545e144 --- /dev/null +++ b/lib/core/commands/command_implementation/graphic/dashed_path_command.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'dashed_path_command.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DashedPathCommand _$DashedPathCommandFromJson(Map json) => + DashedPathCommand( + const PathWithActionHistoryConverter() + .fromJson(json['path'] as Map), + const PaintConverter().fromJson(json['paint'] as Map), + type: json['type'] as String? ?? SerializerType.DASHED_PATH_COMMAND, + version: (json['version'] as num?)?.toInt(), + ); + +Map _$DashedPathCommandToJson(DashedPathCommand instance) => + { + 'paint': const PaintConverter().toJson(instance.paint), + 'type': instance.type, + 'version': instance.version, + 'path': const PathWithActionHistoryConverter().toJson(instance.path), + }; diff --git a/lib/core/json_serialization/versioning/serializer_version.dart b/lib/core/json_serialization/versioning/serializer_version.dart index 117b2763..9092db4c 100644 --- a/lib/core/json_serialization/versioning/serializer_version.dart +++ b/lib/core/json_serialization/versioning/serializer_version.dart @@ -7,6 +7,7 @@ class SerializerVersion { static const int ELLIPSE_SHAPE_COMMAND_VERSION = Version.v1; static const int TEXT_COMMAND_VERSION = Version.v1; static const int SPRAY_COMMAND_VERSION = Version.v1; + static const int DASHED_PATH_COMMAND_VERSION = Version.v1; } class Version { @@ -25,4 +26,5 @@ class SerializerType { static const String ELLIPSE_SHAPE_COMMAND = 'EllipseShapeCommand'; static const String TEXT_COMMAND = 'TextCommand'; static const String SPRAY_COMMAND = 'SprayCommand'; + static const String DASHED_PATH_COMMAND = 'DashedPathCommand'; } diff --git a/lib/core/json_serialization/versioning/version_strategy.dart b/lib/core/json_serialization/versioning/version_strategy.dart index 0ba6c120..bdc10f36 100644 --- a/lib/core/json_serialization/versioning/version_strategy.dart +++ b/lib/core/json_serialization/versioning/version_strategy.dart @@ -14,6 +14,8 @@ abstract class IVersionStrategy { int getTextCommandVersion(); int getSprayCommandVersion(); + + int getDashedPathCommandVersion(); } class ProductionVersionStrategy implements IVersionStrategy { @@ -39,6 +41,10 @@ class ProductionVersionStrategy implements IVersionStrategy { @override int getSprayCommandVersion() => SerializerVersion.SPRAY_COMMAND_VERSION; + + @override + int getDashedPathCommandVersion() => + SerializerVersion.DASHED_PATH_COMMAND_VERSION; } class VersionStrategyManager { diff --git a/lib/core/providers/object/tools/clipping_tool_provider.dart b/lib/core/providers/object/tools/clipping_tool_provider.dart new file mode 100644 index 00000000..6036f5cf --- /dev/null +++ b/lib/core/providers/object/tools/clipping_tool_provider.dart @@ -0,0 +1,22 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import 'package:paintroid/core/commands/command_factory/command_factory_provider.dart'; +import 'package:paintroid/core/commands/command_manager/command_manager_provider.dart'; +import 'package:paintroid/core/commands/graphic_factory/graphic_factory_provider.dart'; +import 'package:paintroid/core/enums/tool_types.dart'; +import 'package:paintroid/core/tools/implementation/clipping_tool.dart'; + +part 'clipping_tool_provider.g.dart'; + +@riverpod +class ClippingToolProvider extends _$ClippingToolProvider { + @override + ClippingTool build() { + return ClippingTool( + commandManager: ref.watch(commandManagerProvider), + commandFactory: ref.watch(commandFactoryProvider), + graphicFactory: ref.watch(graphicFactoryProvider), + type: ToolType.CLIPPING, // Ensure ToolType.CLIPPING is defined + ); + } +} diff --git a/lib/core/providers/object/tools/clipping_tool_provider.g.dart b/lib/core/providers/object/tools/clipping_tool_provider.g.dart new file mode 100644 index 00000000..03e57fe9 --- /dev/null +++ b/lib/core/providers/object/tools/clipping_tool_provider.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'clipping_tool_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$clippingToolProviderHash() => + r'7ebab6a5f8f8c8bd87712118c7df41a89f3022d1'; + +/// See also [ClippingToolProvider]. +@ProviderFor(ClippingToolProvider) +final clippingToolProvider = + AutoDisposeNotifierProvider.internal( + ClippingToolProvider.new, + name: r'clippingToolProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$clippingToolProviderHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ClippingToolProvider = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/core/providers/state/toolbox_state_provider.dart b/lib/core/providers/state/toolbox_state_provider.dart index 1f194d10..30569e0c 100644 --- a/lib/core/providers/state/toolbox_state_provider.dart +++ b/lib/core/providers/state/toolbox_state_provider.dart @@ -4,6 +4,7 @@ import 'package:paintroid/core/commands/command_manager/command_manager_provider import 'package:paintroid/core/enums/tool_types.dart'; import 'package:paintroid/core/providers/object/canvas_painter_provider.dart'; import 'package:paintroid/core/providers/object/tools/brush_tool_provider.dart'; +import 'package:paintroid/core/providers/object/tools/clipping_tool_provider.dart'; import 'package:paintroid/core/providers/object/tools/eraser_tool_provider.dart'; import 'package:paintroid/core/providers/object/tools/hand_tool_provider.dart'; import 'package:paintroid/core/providers/object/tools/line_tool_provider.dart'; @@ -81,6 +82,9 @@ class ToolBoxStateProvider extends _$ToolBoxStateProvider { (state.currentTool as SprayTool).updateSprayRadius(currentStrokeWidth); ref.read(paintProvider.notifier).updateStrokeWidth(SPRAY_TOOL_RADIUS); break; + case ToolType.CLIPPING: + state = state.copyWith(currentTool: ref.read(clippingToolProvider)); + break; default: state = state.copyWith(currentTool: ref.read(brushToolProvider)); break; diff --git a/lib/core/providers/state/toolbox_state_provider.g.dart b/lib/core/providers/state/toolbox_state_provider.g.dart index 5d63e956..65683a67 100644 --- a/lib/core/providers/state/toolbox_state_provider.g.dart +++ b/lib/core/providers/state/toolbox_state_provider.g.dart @@ -7,7 +7,7 @@ part of 'toolbox_state_provider.dart'; // ************************************************************************** String _$toolBoxStateProviderHash() => - r'3866c79e5faeb54cf1a0da8cafa580a9d3971a4b'; + r'f04056392e57363e564e98f4ee92e024d7c52174'; /// See also [ToolBoxStateProvider]. @ProviderFor(ToolBoxStateProvider) diff --git a/lib/core/tools/implementation/clipping_tool.dart b/lib/core/tools/implementation/clipping_tool.dart new file mode 100644 index 00000000..1567fd83 --- /dev/null +++ b/lib/core/tools/implementation/clipping_tool.dart @@ -0,0 +1,110 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:paintroid/core/commands/graphic_factory/graphic_factory.dart'; +import 'package:paintroid/core/commands/path_with_action_history.dart'; +import 'package:paintroid/core/tools/tool.dart'; +import 'package:paintroid/core/enums/tool_types.dart'; + +class ClippingTool extends Tool { + final GraphicFactory graphicFactory; + + @visibleForTesting + late PathWithActionHistory pathToDraw; + Offset? _startPoint; + + ClippingTool({ + required super.commandFactory, + required super.commandManager, + required this.graphicFactory, + super.type = ToolType.CLIPPING, + super.hasAddFunctionality = false, + super.hasFinalizeFunctionality = true, + }); + + @override + void onDown(Offset point, Paint paint) { + _startPoint = point; + pathToDraw = graphicFactory.createPathWithActionHistory() + ..moveTo(point.dx, point.dy); + + final command = commandFactory.createDashedPathCommand(pathToDraw, paint); + commandManager.addGraphicCommand(command); + } + + @override + void onDrag(Offset point, Paint paint) { + pathToDraw.lineTo(point.dx, point.dy); + } + + @override + void onUp(Offset point, Paint paint) { + bool pointAddedInUp = false; + if (pathToDraw.actions.isNotEmpty) { + final lastAction = pathToDraw.actions.last; + bool isSameAsLastPoint = false; + if (lastAction is LineToAction) { + isSameAsLastPoint = + (lastAction.x == point.dx && lastAction.y == point.dy); + } else if (lastAction is MoveToAction && pathToDraw.actions.length == 1) { + isSameAsLastPoint = + (lastAction.x == point.dx && lastAction.y == point.dy); + } + if (!isSameAsLastPoint) { + pathToDraw.lineTo(point.dx, point.dy); + pointAddedInUp = true; + } + } else { + pathToDraw.moveTo(point.dx, point.dy); + pathToDraw.lineTo(point.dx, point.dy); + pointAddedInUp = true; + } + + if (_startPoint != null && pathToDraw.actions.isNotEmpty) { + Offset currentEndPoint = point; + if (pathToDraw.actions.last is LineToAction) { + final lastLineTo = pathToDraw.actions.last as LineToAction; + currentEndPoint = Offset(lastLineTo.x, lastLineTo.y); + } else if (pathToDraw.actions.last is MoveToAction) { + final lastMoveTo = pathToDraw.actions.last as MoveToAction; + currentEndPoint = Offset(lastMoveTo.x, lastMoveTo.y); + } + + if (!(currentEndPoint.dx == _startPoint!.dx && + currentEndPoint.dy == _startPoint!.dy)) { + bool isEffectivelySingleTap = + pathToDraw.actions.length <= (pointAddedInUp ? 2 : 1) && + (currentEndPoint.dx == _startPoint!.dx && + currentEndPoint.dy == _startPoint!.dy); + if (!isEffectivelySingleTap) { + pathToDraw.lineTo(_startPoint!.dx, _startPoint!.dy); + } + } + } + + pathToDraw.close(); + _startPoint = null; + } + + @override + void onCancel() { + commandManager.discardLastCommand(); + _startPoint = null; + } + + @override + void onCheckmark(Paint paint) {} + + @override + void onPlus() {} + + @override + void onRedo() { + commandManager.redo(); + } + + @override + void onUndo() { + commandManager.undo(); + } +} diff --git a/lib/ui/pages/workspace_page/components/bottom_bar/tool_options/tool_options.dart b/lib/ui/pages/workspace_page/components/bottom_bar/tool_options/tool_options.dart index ccb478b2..f71c7348 100644 --- a/lib/ui/pages/workspace_page/components/bottom_bar/tool_options/tool_options.dart +++ b/lib/ui/pages/workspace_page/components/bottom_bar/tool_options/tool_options.dart @@ -36,6 +36,7 @@ class ToolOptions extends ConsumerWidget { ToolType.SHAPES => const ShapesToolOptions(), ToolType.SPRAY => const SprayToolOptions(), ToolType.TEXT => const TextToolOptions(), + ToolType.CLIPPING => const StrokeToolOptions(), _ => Container(), }, ), diff --git a/pubspec.lock b/pubspec.lock index 9f3bd8d3..51655368 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -809,7 +809,7 @@ packages: source: hosted version: "1.9.1" path_drawing: - dependency: transitive + dependency: "direct main" description: name: path_drawing sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 diff --git a/pubspec.yaml b/pubspec.yaml index b8d6015f..05ad32db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: file_picker: ^8.3.5 floor: ^1.2.0 sqflite: ^2.3.0 + path_drawing: ^1.0.1 colorpicker: path: packages/colorpicker diff --git a/test/unit/serialization/utils/dummy_version_strategy.dart b/test/unit/serialization/utils/dummy_version_strategy.dart index a87478cc..d7c34608 100644 --- a/test/unit/serialization/utils/dummy_version_strategy.dart +++ b/test/unit/serialization/utils/dummy_version_strategy.dart @@ -9,6 +9,7 @@ class DummyVersionStrategy implements IVersionStrategy { final int ellipseShapeCommandVersion; final int sprayCommandVersion; final int textCommandVersion; + final int dashedPathCommandVersion; DummyVersionStrategy({ this.pathCommandVersion = SerializerVersion.PATH_COMMAND_VERSION, @@ -20,6 +21,8 @@ class DummyVersionStrategy implements IVersionStrategy { SerializerVersion.ELLIPSE_SHAPE_COMMAND_VERSION, this.sprayCommandVersion = SerializerVersion.SPRAY_COMMAND_VERSION, this.textCommandVersion = SerializerVersion.TEXT_COMMAND_VERSION, + this.dashedPathCommandVersion = + SerializerVersion.DASHED_PATH_COMMAND_VERSION, }); @override @@ -28,6 +31,9 @@ class DummyVersionStrategy implements IVersionStrategy { @override int getPathCommandVersion() => pathCommandVersion; + @override + int getDashedPathCommandVersion() => dashedPathCommandVersion; + @override int getLineCommandVersion() => lineCommandVersion; From 0479b9c60ac6fb96ced250e56f90ed5354a11ab0 Mon Sep 17 00:00:00 2001 From: Amit-Matth Date: Fri, 15 Aug 2025 19:23:44 +0530 Subject: [PATCH 2/4] PAINTROID-796 auto close path --- .../command_factory/command_factory.dart | 17 ++++-- ...th_command.dart => clip_path_command.dart} | 53 +++++++++++----- .../graphic/clip_path_command.g.dart | 44 ++++++++++++++ .../graphic/dashed_path_command.g.dart | 24 -------- .../versioning/serializer_version.dart | 4 +- .../versioning/version_strategy.dart | 6 +- .../tools/implementation/clipping_tool.dart | 60 +++++++++++++------ .../utils/dummy_version_strategy.dart | 4 +- 8 files changed, 144 insertions(+), 68 deletions(-) rename lib/core/commands/command_implementation/graphic/{dashed_path_command.dart => clip_path_command.dart} (54%) create mode 100644 lib/core/commands/command_implementation/graphic/clip_path_command.g.dart delete mode 100644 lib/core/commands/command_implementation/graphic/dashed_path_command.g.dart diff --git a/lib/core/commands/command_factory/command_factory.dart b/lib/core/commands/command_factory/command_factory.dart index 46cd96b1..c569161c 100644 --- a/lib/core/commands/command_factory/command_factory.dart +++ b/lib/core/commands/command_factory/command_factory.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/text_command.dart'; -import 'package:paintroid/core/commands/command_implementation/graphic/dashed_path_command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/line_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/path_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/shape/ellipse_shape_command.dart'; @@ -17,11 +17,18 @@ class CommandFactory { ) => PathCommand(path, paint); - DashedPathCommand createDashedPathCommand( + ClipPathCommand createClipPathCommand( PathWithActionHistory path, - Paint paint, - ) => - DashedPathCommand(path, paint); + Paint paint, { + Offset? startPoint, + Offset? endPoint, + }) => + ClipPathCommand( + path, + paint, + startPoint: startPoint, + endPoint: endPoint, + ); LineCommand createLineCommand( PathWithActionHistory path, diff --git a/lib/core/commands/command_implementation/graphic/dashed_path_command.dart b/lib/core/commands/command_implementation/graphic/clip_path_command.dart similarity index 54% rename from lib/core/commands/command_implementation/graphic/dashed_path_command.dart rename to lib/core/commands/command_implementation/graphic/clip_path_command.dart index 226a506a..5fa3248b 100644 --- a/lib/core/commands/command_implementation/graphic/dashed_path_command.dart +++ b/lib/core/commands/command_implementation/graphic/clip_path_command.dart @@ -4,31 +4,39 @@ import 'package:flutter/widgets.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart'; import 'package:paintroid/core/commands/path_with_action_history.dart'; +import 'package:paintroid/core/json_serialization/converter/offset_converter.dart'; import 'package:paintroid/core/json_serialization/converter/path_with_action_history_converter.dart'; import 'package:paintroid/core/json_serialization/converter/paint_converter.dart'; import 'package:paintroid/core/json_serialization/versioning/serializer_version.dart'; import 'package:paintroid/core/json_serialization/versioning/version_strategy.dart'; import 'package:path_drawing/path_drawing.dart'; -part 'dashed_path_command.g.dart'; +part 'clip_path_command.g.dart'; @JsonSerializable() -class DashedPathCommand extends GraphicCommand { +class ClipPathCommand extends GraphicCommand { @JsonKey(includeToJson: true, includeFromJson: true) final String type; @JsonKey(includeToJson: true, includeFromJson: true) final int version; - DashedPathCommand( + @PathWithActionHistoryConverter() + final PathWithActionHistory path; + + @OffsetConverter() + final Offset? startPoint; + @OffsetConverter() + final Offset? endPoint; + + ClipPathCommand( this.path, super.paint, { - this.type = SerializerType.DASHED_PATH_COMMAND, + this.startPoint, + this.endPoint, + this.type = SerializerType.CLIP_PATH_COMMAND, int? version, }) : version = version ?? - VersionStrategyManager.strategy.getDashedPathCommandVersion(); - - @PathWithActionHistoryConverter() - final PathWithActionHistory path; + VersionStrategyManager.strategy.getClipPathCommandVersion(); @override void call(Canvas canvas) { @@ -39,28 +47,43 @@ class DashedPathCommand extends GraphicCommand { path.path, dashArray: CircularIntervalList([dashLength, dashGap]), ); - canvas.drawPath(dashedPath, paint); + + if (startPoint != null && + endPoint != null && + (startPoint!.dx != endPoint!.dx || startPoint!.dy != endPoint!.dy)) { + final solidPaint = Paint() + ..color = paint.color + ..style = PaintingStyle.stroke + ..strokeWidth = paint.strokeWidth + ..isAntiAlias = true; + + final solidPath = Path() + ..moveTo(startPoint!.dx, startPoint!.dy) + ..lineTo(endPoint!.dx, endPoint!.dy); + + canvas.drawPath(solidPath, solidPaint); + } } @override - List get props => [paint, path, type, version]; + List get props => [paint, path, startPoint, endPoint, type, version]; - factory DashedPathCommand.fromJson(Map json) { + factory ClipPathCommand.fromJson(Map json) { int version = json['version'] as int; switch (version) { case Version.v1: - return _$DashedPathCommandFromJson(json); + return _$ClipPathCommandFromJson(json); case Version.v2: - // For different versions of DashedPathCommand the deserialization + // For different versions of ClipPathCommand the deserialization // has to be implemented manually. // Autogenerated code can only be used for one version default: - return _$DashedPathCommandFromJson(json); + return _$ClipPathCommandFromJson(json); } } @override - Map toJson() => _$DashedPathCommandToJson(this); + Map toJson() => _$ClipPathCommandToJson(this); } diff --git a/lib/core/commands/command_implementation/graphic/clip_path_command.g.dart b/lib/core/commands/command_implementation/graphic/clip_path_command.g.dart new file mode 100644 index 00000000..3f88e3d5 --- /dev/null +++ b/lib/core/commands/command_implementation/graphic/clip_path_command.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'clip_path_command.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ClipPathCommand _$ClipPathCommandFromJson(Map json) => + ClipPathCommand( + const PathWithActionHistoryConverter() + .fromJson(json['path'] as Map), + const PaintConverter().fromJson(json['paint'] as Map), + startPoint: _$JsonConverterFromJson, Offset>( + json['startPoint'], const OffsetConverter().fromJson), + endPoint: _$JsonConverterFromJson, Offset>( + json['endPoint'], const OffsetConverter().fromJson), + type: json['type'] as String? ?? SerializerType.CLIP_PATH_COMMAND, + version: (json['version'] as num?)?.toInt(), + ); + +Map _$ClipPathCommandToJson(ClipPathCommand instance) => + { + 'paint': const PaintConverter().toJson(instance.paint), + 'type': instance.type, + 'version': instance.version, + 'path': const PathWithActionHistoryConverter().toJson(instance.path), + 'startPoint': _$JsonConverterToJson, Offset>( + instance.startPoint, const OffsetConverter().toJson), + 'endPoint': _$JsonConverterToJson, Offset>( + instance.endPoint, const OffsetConverter().toJson), + }; + +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); diff --git a/lib/core/commands/command_implementation/graphic/dashed_path_command.g.dart b/lib/core/commands/command_implementation/graphic/dashed_path_command.g.dart deleted file mode 100644 index 4545e144..00000000 --- a/lib/core/commands/command_implementation/graphic/dashed_path_command.g.dart +++ /dev/null @@ -1,24 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'dashed_path_command.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -DashedPathCommand _$DashedPathCommandFromJson(Map json) => - DashedPathCommand( - const PathWithActionHistoryConverter() - .fromJson(json['path'] as Map), - const PaintConverter().fromJson(json['paint'] as Map), - type: json['type'] as String? ?? SerializerType.DASHED_PATH_COMMAND, - version: (json['version'] as num?)?.toInt(), - ); - -Map _$DashedPathCommandToJson(DashedPathCommand instance) => - { - 'paint': const PaintConverter().toJson(instance.paint), - 'type': instance.type, - 'version': instance.version, - 'path': const PathWithActionHistoryConverter().toJson(instance.path), - }; diff --git a/lib/core/json_serialization/versioning/serializer_version.dart b/lib/core/json_serialization/versioning/serializer_version.dart index 9092db4c..359822de 100644 --- a/lib/core/json_serialization/versioning/serializer_version.dart +++ b/lib/core/json_serialization/versioning/serializer_version.dart @@ -7,7 +7,7 @@ class SerializerVersion { static const int ELLIPSE_SHAPE_COMMAND_VERSION = Version.v1; static const int TEXT_COMMAND_VERSION = Version.v1; static const int SPRAY_COMMAND_VERSION = Version.v1; - static const int DASHED_PATH_COMMAND_VERSION = Version.v1; + static const int CLIP_PATH_COMMAND_VERSION = Version.v1; } class Version { @@ -26,5 +26,5 @@ class SerializerType { static const String ELLIPSE_SHAPE_COMMAND = 'EllipseShapeCommand'; static const String TEXT_COMMAND = 'TextCommand'; static const String SPRAY_COMMAND = 'SprayCommand'; - static const String DASHED_PATH_COMMAND = 'DashedPathCommand'; + static const String CLIP_PATH_COMMAND = 'DashedPathCommand'; } diff --git a/lib/core/json_serialization/versioning/version_strategy.dart b/lib/core/json_serialization/versioning/version_strategy.dart index bdc10f36..7a59fb40 100644 --- a/lib/core/json_serialization/versioning/version_strategy.dart +++ b/lib/core/json_serialization/versioning/version_strategy.dart @@ -15,7 +15,7 @@ abstract class IVersionStrategy { int getSprayCommandVersion(); - int getDashedPathCommandVersion(); + int getClipPathCommandVersion(); } class ProductionVersionStrategy implements IVersionStrategy { @@ -43,8 +43,8 @@ class ProductionVersionStrategy implements IVersionStrategy { int getSprayCommandVersion() => SerializerVersion.SPRAY_COMMAND_VERSION; @override - int getDashedPathCommandVersion() => - SerializerVersion.DASHED_PATH_COMMAND_VERSION; + int getClipPathCommandVersion() => + SerializerVersion.CLIP_PATH_COMMAND_VERSION; } class VersionStrategyManager { diff --git a/lib/core/tools/implementation/clipping_tool.dart b/lib/core/tools/implementation/clipping_tool.dart index 1567fd83..895a17e3 100644 --- a/lib/core/tools/implementation/clipping_tool.dart +++ b/lib/core/tools/implementation/clipping_tool.dart @@ -28,8 +28,9 @@ class ClippingTool extends Tool { pathToDraw = graphicFactory.createPathWithActionHistory() ..moveTo(point.dx, point.dy); - final command = commandFactory.createDashedPathCommand(pathToDraw, paint); - commandManager.addGraphicCommand(command); + final dashedCommand = + commandFactory.createClipPathCommand(pathToDraw, paint); + commandManager.addGraphicCommand(dashedCommand); } @override @@ -39,17 +40,21 @@ class ClippingTool extends Tool { @override void onUp(Offset point, Paint paint) { + commandManager.discardLastCommand(); + bool pointAddedInUp = false; if (pathToDraw.actions.isNotEmpty) { final lastAction = pathToDraw.actions.last; bool isSameAsLastPoint = false; + if (lastAction is LineToAction) { isSameAsLastPoint = - (lastAction.x == point.dx && lastAction.y == point.dy); + (lastAction.x == point.dx && lastAction.y == point.dy); } else if (lastAction is MoveToAction && pathToDraw.actions.length == 1) { isSameAsLastPoint = - (lastAction.x == point.dx && lastAction.y == point.dy); + (lastAction.x == point.dx && lastAction.y == point.dy); } + if (!isSameAsLastPoint) { pathToDraw.lineTo(point.dx, point.dy); pointAddedInUp = true; @@ -60,8 +65,8 @@ class ClippingTool extends Tool { pointAddedInUp = true; } - if (_startPoint != null && pathToDraw.actions.isNotEmpty) { - Offset currentEndPoint = point; + Offset currentEndPoint = point; + if (pathToDraw.actions.isNotEmpty) { if (pathToDraw.actions.last is LineToAction) { final lastLineTo = pathToDraw.actions.last as LineToAction; currentEndPoint = Offset(lastLineTo.x, lastLineTo.y); @@ -69,20 +74,41 @@ class ClippingTool extends Tool { final lastMoveTo = pathToDraw.actions.last as MoveToAction; currentEndPoint = Offset(lastMoveTo.x, lastMoveTo.y); } + } - if (!(currentEndPoint.dx == _startPoint!.dx && - currentEndPoint.dy == _startPoint!.dy)) { - bool isEffectivelySingleTap = - pathToDraw.actions.length <= (pointAddedInUp ? 2 : 1) && - (currentEndPoint.dx == _startPoint!.dx && - currentEndPoint.dy == _startPoint!.dy); - if (!isEffectivelySingleTap) { - pathToDraw.lineTo(_startPoint!.dx, _startPoint!.dy); - } + if (_startPoint != null && pathToDraw.actions.isNotEmpty) { + bool needsSolidClosingLine = !(currentEndPoint.dx == _startPoint!.dx && + currentEndPoint.dy == _startPoint!.dy); + + bool isEffectivelySingleTap = + pathToDraw.actions.length <= (pointAddedInUp ? 2 : 1) && + (currentEndPoint.dx == _startPoint!.dx && + currentEndPoint.dy == _startPoint!.dy); + + if (needsSolidClosingLine && !isEffectivelySingleTap) { + final finalDashedCommand = commandFactory.createClipPathCommand( + pathToDraw, + paint, + startPoint: currentEndPoint, + endPoint: _startPoint!, + ); + commandManager.addGraphicCommand(finalDashedCommand); + } else { + pathToDraw.close(); + final finalDashedCommand = commandFactory.createClipPathCommand( + pathToDraw, + paint, + ); + commandManager.addGraphicCommand(finalDashedCommand); } + } else { + pathToDraw.close(); + final finalDashedCommand = commandFactory.createClipPathCommand( + pathToDraw, + paint, + ); + commandManager.addGraphicCommand(finalDashedCommand); } - - pathToDraw.close(); _startPoint = null; } diff --git a/test/unit/serialization/utils/dummy_version_strategy.dart b/test/unit/serialization/utils/dummy_version_strategy.dart index d7c34608..5f1e6c7a 100644 --- a/test/unit/serialization/utils/dummy_version_strategy.dart +++ b/test/unit/serialization/utils/dummy_version_strategy.dart @@ -22,7 +22,7 @@ class DummyVersionStrategy implements IVersionStrategy { this.sprayCommandVersion = SerializerVersion.SPRAY_COMMAND_VERSION, this.textCommandVersion = SerializerVersion.TEXT_COMMAND_VERSION, this.dashedPathCommandVersion = - SerializerVersion.DASHED_PATH_COMMAND_VERSION, + SerializerVersion.CLIP_PATH_COMMAND_VERSION, }); @override @@ -32,7 +32,7 @@ class DummyVersionStrategy implements IVersionStrategy { int getPathCommandVersion() => pathCommandVersion; @override - int getDashedPathCommandVersion() => dashedPathCommandVersion; + int getClipPathCommandVersion() => dashedPathCommandVersion; @override int getLineCommandVersion() => lineCommandVersion; From 73fd9cc2a062b1b6b64ff8d31ce0e9146b0f6634 Mon Sep 17 00:00:00 2001 From: Amit-Matth Date: Sun, 17 Aug 2025 20:06:48 +0530 Subject: [PATCH 3/4] PAINTROID-796 add clip area command --- .../command_factory/command_factory.dart | 7 ++ .../graphic/clip_area_command.dart | 69 +++++++++++++++ .../graphic/clip_area_command.g.dart | 25 ++++++ .../command_manager/command_manager.dart | 12 ++- .../versioning/serializer_version.dart | 2 + .../versioning/version_strategy.dart | 6 ++ .../object/tools/clipping_tool_provider.dart | 4 +- .../tools/clipping_tool_provider.g.dart | 2 +- .../tools/clipping_tool_state_provider.dart | 21 +++++ .../tools/clipping_tool_state_provider.g.dart | 26 ++++++ .../state/canvas_state_provider.dart | 34 +++++++- .../tools/implementation/clipping_tool.dart | 85 ++++++++++++++++--- .../components/top_bar/top_app_bar.dart | 2 +- .../utils/dummy_version_strategy.dart | 9 +- .../render_image_for_export_test.mocks.dart | 9 ++ 15 files changed, 290 insertions(+), 23 deletions(-) create mode 100644 lib/core/commands/command_implementation/graphic/clip_area_command.dart create mode 100644 lib/core/commands/command_implementation/graphic/clip_area_command.g.dart create mode 100644 lib/core/providers/object/tools/clipping_tool_state_provider.dart create mode 100644 lib/core/providers/object/tools/clipping_tool_state_provider.g.dart diff --git a/lib/core/commands/command_factory/command_factory.dart b/lib/core/commands/command_factory/command_factory.dart index c569161c..50370826 100644 --- a/lib/core/commands/command_factory/command_factory.dart +++ b/lib/core/commands/command_factory/command_factory.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/text_command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_area_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/line_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/path_command.dart'; @@ -86,4 +87,10 @@ class CommandFactory { SprayCommand createSprayCommand(List points, Paint paint) { return SprayCommand(points, paint); } + + ClipAreaCommand createClipAreaCommand( + PathWithActionHistory path, + Paint paint, + ) => + ClipAreaCommand(path, paint); } diff --git a/lib/core/commands/command_implementation/graphic/clip_area_command.dart b/lib/core/commands/command_implementation/graphic/clip_area_command.dart new file mode 100644 index 00000000..607934d4 --- /dev/null +++ b/lib/core/commands/command_implementation/graphic/clip_area_command.dart @@ -0,0 +1,69 @@ +import 'dart:ui'; + +import 'package:json_annotation/json_annotation.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart'; +import 'package:paintroid/core/commands/path_with_action_history.dart'; +import 'package:paintroid/core/json_serialization/converter/paint_converter.dart'; +import 'package:paintroid/core/json_serialization/converter/path_with_action_history_converter.dart'; +import 'package:paintroid/core/json_serialization/versioning/serializer_version.dart'; +import 'package:paintroid/core/json_serialization/versioning/version_strategy.dart'; + +part 'clip_area_command.g.dart'; + +@JsonSerializable() +class ClipAreaCommand extends GraphicCommand { + @JsonKey(includeToJson: true, includeFromJson: true) + final String type; + @JsonKey(includeToJson: true, includeFromJson: true) + final int version; + + @PathWithActionHistoryConverter() + final PathWithActionHistory clipPathData; + + ClipAreaCommand( + this.clipPathData, + Paint paint, { + this.type = SerializerType.CLIP_AREA_COMMAND, + int? version, + }) : version = version ?? + VersionStrategyManager.strategy.getClipAreaCommandVersion(), + super(paint); + + @override + void call(Canvas canvas) { + final Rect canvasBounds = canvas.getLocalClipBounds(); + + Path areaToClear = Path.combine( + PathOperation.difference, + Path()..addRect(canvasBounds), + clipPathData.path, + ); + + canvas.drawPath( + areaToClear, + Paint() + ..blendMode = BlendMode.clear + ..style = PaintingStyle.fill); + } + + @override + List get props => [paint, clipPathData, type, version]; + + factory ClipAreaCommand.fromJson(Map json) { + int version = json['version'] as int; + + switch (version) { + case Version.v1: + return _$ClipAreaCommandFromJson(json); + case Version.v2: + // For different versions of ClipAreaCommand the deserialization + // has to be implemented manually. + // Autogenerated code can only be used for one version + default: + return _$ClipAreaCommandFromJson(json); + } + } + + @override + Map toJson() => _$ClipAreaCommandToJson(this); +} diff --git a/lib/core/commands/command_implementation/graphic/clip_area_command.g.dart b/lib/core/commands/command_implementation/graphic/clip_area_command.g.dart new file mode 100644 index 00000000..c8c75133 --- /dev/null +++ b/lib/core/commands/command_implementation/graphic/clip_area_command.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'clip_area_command.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ClipAreaCommand _$ClipAreaCommandFromJson(Map json) => + ClipAreaCommand( + const PathWithActionHistoryConverter() + .fromJson(json['clipPathData'] as Map), + const PaintConverter().fromJson(json['paint'] as Map), + type: json['type'] as String? ?? SerializerType.CLIP_AREA_COMMAND, + version: (json['version'] as num?)?.toInt(), + ); + +Map _$ClipAreaCommandToJson(ClipAreaCommand instance) => + { + 'paint': const PaintConverter().toJson(instance.paint), + 'type': instance.type, + 'version': instance.version, + 'clipPathData': + const PathWithActionHistoryConverter().toJson(instance.clipPathData), + }; diff --git a/lib/core/commands/command_manager/command_manager.dart b/lib/core/commands/command_manager/command_manager.dart index d102a8fd..45d8e115 100644 --- a/lib/core/commands/command_manager/command_manager.dart +++ b/lib/core/commands/command_manager/command_manager.dart @@ -23,13 +23,19 @@ class CommandManager { _undoStack.add(command); } + void removeCommand(Command commandToRemove) { + _undoStack.remove(commandToRemove); + } + void setUndoStack(List commands) { _undoStack.clear(); _undoStack.addAll(commands); } void executeLastCommand(Canvas canvas) { - if (_undoStack.isEmpty) return; + if (_undoStack.isEmpty) { + return; + } final lastCommand = _undoStack.last; if (lastCommand is GraphicCommand) { lastCommand.call(canvas); @@ -45,7 +51,9 @@ class CommandManager { } void discardLastCommand() { - if (_undoStack.isNotEmpty) _undoStack.removeLast(); + if (_undoStack.isNotEmpty) { + _undoStack.removeLast(); + } } void clearUndoStack({Iterable? newCommands}) { diff --git a/lib/core/json_serialization/versioning/serializer_version.dart b/lib/core/json_serialization/versioning/serializer_version.dart index 359822de..7e48dde8 100644 --- a/lib/core/json_serialization/versioning/serializer_version.dart +++ b/lib/core/json_serialization/versioning/serializer_version.dart @@ -8,6 +8,7 @@ class SerializerVersion { static const int TEXT_COMMAND_VERSION = Version.v1; static const int SPRAY_COMMAND_VERSION = Version.v1; static const int CLIP_PATH_COMMAND_VERSION = Version.v1; + static const int CLIP_AREA_COMMAND_VERSION = Version.v1; } class Version { @@ -27,4 +28,5 @@ class SerializerType { static const String TEXT_COMMAND = 'TextCommand'; static const String SPRAY_COMMAND = 'SprayCommand'; static const String CLIP_PATH_COMMAND = 'DashedPathCommand'; + static const String CLIP_AREA_COMMAND = 'ClipAreaCommand'; } diff --git a/lib/core/json_serialization/versioning/version_strategy.dart b/lib/core/json_serialization/versioning/version_strategy.dart index 7a59fb40..01f83284 100644 --- a/lib/core/json_serialization/versioning/version_strategy.dart +++ b/lib/core/json_serialization/versioning/version_strategy.dart @@ -16,6 +16,8 @@ abstract class IVersionStrategy { int getSprayCommandVersion(); int getClipPathCommandVersion(); + + int getClipAreaCommandVersion(); } class ProductionVersionStrategy implements IVersionStrategy { @@ -45,6 +47,10 @@ class ProductionVersionStrategy implements IVersionStrategy { @override int getClipPathCommandVersion() => SerializerVersion.CLIP_PATH_COMMAND_VERSION; + + @override + int getClipAreaCommandVersion() => + SerializerVersion.CLIP_AREA_COMMAND_VERSION; } class VersionStrategyManager { diff --git a/lib/core/providers/object/tools/clipping_tool_provider.dart b/lib/core/providers/object/tools/clipping_tool_provider.dart index 6036f5cf..041db427 100644 --- a/lib/core/providers/object/tools/clipping_tool_provider.dart +++ b/lib/core/providers/object/tools/clipping_tool_provider.dart @@ -5,6 +5,7 @@ import 'package:paintroid/core/commands/command_manager/command_manager_provider import 'package:paintroid/core/commands/graphic_factory/graphic_factory_provider.dart'; import 'package:paintroid/core/enums/tool_types.dart'; import 'package:paintroid/core/tools/implementation/clipping_tool.dart'; +import 'package:paintroid/core/providers/object/tools/clipping_tool_state_provider.dart'; part 'clipping_tool_provider.g.dart'; @@ -16,7 +17,8 @@ class ClippingToolProvider extends _$ClippingToolProvider { commandManager: ref.watch(commandManagerProvider), commandFactory: ref.watch(commandFactoryProvider), graphicFactory: ref.watch(graphicFactoryProvider), - type: ToolType.CLIPPING, // Ensure ToolType.CLIPPING is defined + clippingToolState: ref.watch(clippingToolState.notifier), + type: ToolType.CLIPPING, ); } } diff --git a/lib/core/providers/object/tools/clipping_tool_provider.g.dart b/lib/core/providers/object/tools/clipping_tool_provider.g.dart index 03e57fe9..4ebe3fb2 100644 --- a/lib/core/providers/object/tools/clipping_tool_provider.g.dart +++ b/lib/core/providers/object/tools/clipping_tool_provider.g.dart @@ -7,7 +7,7 @@ part of 'clipping_tool_provider.dart'; // ************************************************************************** String _$clippingToolProviderHash() => - r'7ebab6a5f8f8c8bd87712118c7df41a89f3022d1'; + r'a7b34cc0817d2b94f4fd348d61a115622f72e259'; /// See also [ClippingToolProvider]. @ProviderFor(ClippingToolProvider) diff --git a/lib/core/providers/object/tools/clipping_tool_state_provider.dart b/lib/core/providers/object/tools/clipping_tool_state_provider.dart new file mode 100644 index 00000000..203d4381 --- /dev/null +++ b/lib/core/providers/object/tools/clipping_tool_state_provider.dart @@ -0,0 +1,21 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'clipping_tool_state_provider.g.dart'; + +@riverpod +class ClippingToolState extends _$ClippingToolState { + @override + bool build() { + return false; + } + + void setHasActiveClipPath(bool hasActive) { + state = hasActive; + } + + void clearClipPath() { + state = false; + } + + bool get hasActiveClipPath => state; +} diff --git a/lib/core/providers/object/tools/clipping_tool_state_provider.g.dart b/lib/core/providers/object/tools/clipping_tool_state_provider.g.dart new file mode 100644 index 00000000..8cf879e7 --- /dev/null +++ b/lib/core/providers/object/tools/clipping_tool_state_provider.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'clipping_tool_state_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$clippingToolStateHash() => r'd6a9d9e023dc616a6a7eb99c55b38590d14b2311'; + +/// See also [ClippingToolState]. +@ProviderFor(ClippingToolState) +final clippingToolState = + AutoDisposeNotifierProvider.internal( + ClippingToolState.new, + name: r'clippingToolState', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$clippingToolStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ClippingToolState = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/core/providers/state/canvas_state_provider.dart b/lib/core/providers/state/canvas_state_provider.dart index 272cbe1f..4735e4c7 100644 --- a/lib/core/providers/state/canvas_state_provider.dart +++ b/lib/core/providers/state/canvas_state_provider.dart @@ -3,6 +3,7 @@ import 'dart:ui'; import 'package:flutter/painting.dart'; import 'package:flutter/widgets.dart' as widgets; import 'package:paintroid/core/commands/command_implementation/command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart'; import 'package:paintroid/core/commands/command_manager/command_manager_provider.dart'; import 'package:paintroid/core/commands/graphic_factory/graphic_factory_provider.dart'; import 'package:paintroid/core/providers/object/device_service.dart'; @@ -45,7 +46,16 @@ class CanvasStateProvider extends _$CanvasStateProvider { final canvas = state.graphicFactory.createCanvasWithRecorder(recorder); final size = state.size; final bounds = Rect.fromLTWH(0, 0, size.width, size.height); - if (state.cachedImage != null) { + + bool isDrawingClipPathPreview = false; + if (state.commandManager.undoStack.isNotEmpty) { + final lastCommand = state.commandManager.undoStack.last; + if (lastCommand is ClipPathCommand) { + isDrawingClipPathPreview = true; + } + } + + if (!isDrawingClipPathPreview && state.cachedImage != null) { paintImage( canvas: canvas, rect: bounds, @@ -53,6 +63,14 @@ class CanvasStateProvider extends _$CanvasStateProvider { fit: BoxFit.fill, filterQuality: FilterQuality.none, ); + } else if (state.backgroundImage != null) { + paintImage( + canvas: canvas, + rect: bounds, + image: state.backgroundImage!, + fit: BoxFit.fill, + filterQuality: FilterQuality.none, + ); } canvas.clipRect(bounds); state.commandManager.executeLastCommand(canvas); @@ -79,7 +97,19 @@ class CanvasStateProvider extends _$CanvasStateProvider { final recorder = state.graphicFactory.createPictureRecorder(); final canvas = state.graphicFactory.createCanvasWithRecorder(recorder); final size = state.size; - canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height)); + final bounds = Rect.fromLTWH(0, 0, size.width, size.height); + + if (state.backgroundImage != null) { + paintImage( + canvas: canvas, + rect: bounds, + image: state.backgroundImage!, + fit: BoxFit.fill, + filterQuality: FilterQuality.none, + ); + } + + canvas.clipRect(bounds); state.commandManager.executeAllCommands(canvas); final picture = recorder.endRecording(); final img = await picture.toImage(size.width.toInt(), size.height.toInt()); diff --git a/lib/core/tools/implementation/clipping_tool.dart b/lib/core/tools/implementation/clipping_tool.dart index 895a17e3..fda81412 100644 --- a/lib/core/tools/implementation/clipping_tool.dart +++ b/lib/core/tools/implementation/clipping_tool.dart @@ -1,22 +1,28 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart'; import 'package:paintroid/core/commands/graphic_factory/graphic_factory.dart'; import 'package:paintroid/core/commands/path_with_action_history.dart'; import 'package:paintroid/core/tools/tool.dart'; import 'package:paintroid/core/enums/tool_types.dart'; +import 'package:paintroid/core/providers/object/tools/clipping_tool_state_provider.dart'; class ClippingTool extends Tool { final GraphicFactory graphicFactory; + final ClippingToolState clippingToolState; @visibleForTesting late PathWithActionHistory pathToDraw; Offset? _startPoint; + GraphicCommand? _activePreviewCommand; + GraphicCommand? _liveDrawingCommand; ClippingTool({ required super.commandFactory, required super.commandManager, required this.graphicFactory, + required this.clippingToolState, super.type = ToolType.CLIPPING, super.hasAddFunctionality = false, super.hasFinalizeFunctionality = true, @@ -24,13 +30,27 @@ class ClippingTool extends Tool { @override void onDown(Offset point, Paint paint) { + if (clippingToolState.hasActiveClipPath) { + if (_activePreviewCommand != null) { + commandManager.removeCommand(_activePreviewCommand!); + _activePreviewCommand = null; + } + clippingToolState.clearClipPath(); + } + + if (_liveDrawingCommand != null) { + commandManager.removeCommand(_liveDrawingCommand!); + _liveDrawingCommand = null; + } + _startPoint = point; pathToDraw = graphicFactory.createPathWithActionHistory() ..moveTo(point.dx, point.dy); - final dashedCommand = - commandFactory.createClipPathCommand(pathToDraw, paint); - commandManager.addGraphicCommand(dashedCommand); + final newLiveDrawingCommand = + commandFactory.createClipPathCommand(pathToDraw, paint); + commandManager.addGraphicCommand(newLiveDrawingCommand); + _liveDrawingCommand = newLiveDrawingCommand; } @override @@ -40,7 +60,10 @@ class ClippingTool extends Tool { @override void onUp(Offset point, Paint paint) { - commandManager.discardLastCommand(); + if (_liveDrawingCommand != null) { + commandManager.removeCommand(_liveDrawingCommand!); + _liveDrawingCommand = null; + } bool pointAddedInUp = false; if (pathToDraw.actions.isNotEmpty) { @@ -49,10 +72,10 @@ class ClippingTool extends Tool { if (lastAction is LineToAction) { isSameAsLastPoint = - (lastAction.x == point.dx && lastAction.y == point.dy); + (lastAction.x == point.dx && lastAction.y == point.dy); } else if (lastAction is MoveToAction && pathToDraw.actions.length == 1) { isSameAsLastPoint = - (lastAction.x == point.dx && lastAction.y == point.dy); + (lastAction.x == point.dx && lastAction.y == point.dy); } if (!isSameAsLastPoint) { @@ -76,6 +99,7 @@ class ClippingTool extends Tool { } } + GraphicCommand newPreviewCommand; if (_startPoint != null && pathToDraw.actions.isNotEmpty) { bool needsSolidClosingLine = !(currentEndPoint.dx == _startPoint!.dx && currentEndPoint.dy == _startPoint!.dy); @@ -86,40 +110,73 @@ class ClippingTool extends Tool { currentEndPoint.dy == _startPoint!.dy); if (needsSolidClosingLine && !isEffectivelySingleTap) { - final finalDashedCommand = commandFactory.createClipPathCommand( + newPreviewCommand = commandFactory.createClipPathCommand( pathToDraw, paint, startPoint: currentEndPoint, endPoint: _startPoint!, ); - commandManager.addGraphicCommand(finalDashedCommand); } else { pathToDraw.close(); - final finalDashedCommand = commandFactory.createClipPathCommand( + newPreviewCommand = commandFactory.createClipPathCommand( pathToDraw, paint, ); - commandManager.addGraphicCommand(finalDashedCommand); } } else { pathToDraw.close(); - final finalDashedCommand = commandFactory.createClipPathCommand( + newPreviewCommand = commandFactory.createClipPathCommand( pathToDraw, paint, ); - commandManager.addGraphicCommand(finalDashedCommand); } + commandManager.addGraphicCommand(newPreviewCommand); + _activePreviewCommand = newPreviewCommand; + clippingToolState.setHasActiveClipPath(true); _startPoint = null; } @override void onCancel() { - commandManager.discardLastCommand(); + if (_liveDrawingCommand != null) { + commandManager.removeCommand(_liveDrawingCommand!); + _liveDrawingCommand = null; + } + + if (clippingToolState.hasActiveClipPath) { + if (_activePreviewCommand != null) { + commandManager.removeCommand(_activePreviewCommand!); + _activePreviewCommand = null; + } + clippingToolState.clearClipPath(); + } _startPoint = null; } @override - void onCheckmark(Paint paint) {} + void onCheckmark(Paint paint) { + if (clippingToolState.hasActiveClipPath) { + if (_activePreviewCommand != null) { + commandManager.removeCommand(_activePreviewCommand!); + _activePreviewCommand = null; + } + clippingToolState.clearClipPath(); + } + + if (_liveDrawingCommand != null) { + commandManager.removeCommand(_liveDrawingCommand!); + _liveDrawingCommand = null; + } + + if (pathToDraw.actions.isNotEmpty) { + pathToDraw.close(); + + final cropCommand = + commandFactory.createClipAreaCommand(pathToDraw, paint); + commandManager.addGraphicCommand(cropCommand); + } + _startPoint = null; + } @override void onPlus() {} diff --git a/lib/ui/pages/workspace_page/components/top_bar/top_app_bar.dart b/lib/ui/pages/workspace_page/components/top_bar/top_app_bar.dart index 319a2f39..e0039ff7 100644 --- a/lib/ui/pages/workspace_page/components/top_bar/top_app_bar.dart +++ b/lib/ui/pages/workspace_page/components/top_bar/top_app_bar.dart @@ -74,7 +74,7 @@ class TopAppBar extends ConsumerWidget implements PreferredSizeWidget { currentTool is LineTool && currentTool.vertexStack.isNotEmpty; final isShapeTool = currentTool.type == ToolType.SHAPES; final isTextTool = currentTool is TextTool; - if (isLineTool || isShapeTool || isTextTool) { + if (isLineTool || isShapeTool || isTextTool || currentTool.type == ToolType.CLIPPING) { return () { currentTool.onCheckmark(ref.read(paintProvider)); ref.read(appBarProvider.notifier).update(); diff --git a/test/unit/serialization/utils/dummy_version_strategy.dart b/test/unit/serialization/utils/dummy_version_strategy.dart index 5f1e6c7a..db172483 100644 --- a/test/unit/serialization/utils/dummy_version_strategy.dart +++ b/test/unit/serialization/utils/dummy_version_strategy.dart @@ -10,6 +10,7 @@ class DummyVersionStrategy implements IVersionStrategy { final int sprayCommandVersion; final int textCommandVersion; final int dashedPathCommandVersion; + final int clipAreaCommandVersion; DummyVersionStrategy({ this.pathCommandVersion = SerializerVersion.PATH_COMMAND_VERSION, @@ -21,8 +22,9 @@ class DummyVersionStrategy implements IVersionStrategy { SerializerVersion.ELLIPSE_SHAPE_COMMAND_VERSION, this.sprayCommandVersion = SerializerVersion.SPRAY_COMMAND_VERSION, this.textCommandVersion = SerializerVersion.TEXT_COMMAND_VERSION, - this.dashedPathCommandVersion = - SerializerVersion.CLIP_PATH_COMMAND_VERSION, + this.dashedPathCommandVersion = SerializerVersion.CLIP_PATH_COMMAND_VERSION, + this.clipAreaCommandVersion = + SerializerVersion.CLIP_AREA_COMMAND_VERSION, }); @override @@ -48,4 +50,7 @@ class DummyVersionStrategy implements IVersionStrategy { @override int getTextCommandVersion() => textCommandVersion; + + @override + int getClipAreaCommandVersion() => clipAreaCommandVersion; } diff --git a/test/unit/workspace/render_image_for_export_test.mocks.dart b/test/unit/workspace/render_image_for_export_test.mocks.dart index b8276da8..45d34a79 100644 --- a/test/unit/workspace/render_image_for_export_test.mocks.dart +++ b/test/unit/workspace/render_image_for_export_test.mocks.dart @@ -677,6 +677,15 @@ class MockCommandManager extends _i1.Mock implements _i6.CommandManager { returnValueForMissingStub: null, ); + @override + void removeCommand(_i3.Command? commandToRemove) => super.noSuchMethod( + Invocation.method( + #removeCommand, + [commandToRemove], + ), + returnValueForMissingStub: null, + ); + @override void setUndoStack(List<_i3.Command>? commands) => super.noSuchMethod( Invocation.method( From 9679dc0140c8f1413c3dabafdcf181702be25e66 Mon Sep 17 00:00:00 2001 From: Amit-Matth Date: Wed, 27 Aug 2025 20:45:17 +0530 Subject: [PATCH 4/4] PAINTROID-796 fix bugs --- .../command_implementation/command.dart | 6 + .../command_manager/command_manager.dart | 5 + .../versioning/serializer_version.dart | 2 +- .../object/tools/clipping_tool_provider.dart | 2 + .../tools/clipping_tool_provider.g.dart | 2 +- .../state/canvas_state_provider.dart | 34 +- .../state/toolbox_state_provider.g.dart | 2 +- .../tools/implementation/clipping_tool.dart | 34 + test/integration/clipping_tool_test.dart | 140 ++ .../utils/dummy_version_strategy.dart | 13 +- test/unit/tools/clipping_tool_test.dart | 206 +++ test/unit/tools/clipping_tool_test.mocks.dart | 1284 +++++++++++++++++ test/unit/tools/text_tool_test.mocks.dart | 622 ++++---- .../workspace_page/clipping_tool_test.dart | 45 + 14 files changed, 2092 insertions(+), 305 deletions(-) create mode 100644 test/integration/clipping_tool_test.dart create mode 100644 test/unit/tools/clipping_tool_test.dart create mode 100644 test/unit/tools/clipping_tool_test.mocks.dart create mode 100644 test/widget/workspace_page/clipping_tool_test.dart diff --git a/lib/core/commands/command_implementation/command.dart b/lib/core/commands/command_implementation/command.dart index ed2c8efb..0fef2d31 100644 --- a/lib/core/commands/command_implementation/command.dart +++ b/lib/core/commands/command_implementation/command.dart @@ -1,4 +1,6 @@ import 'package:equatable/equatable.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_area_command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/line_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/path_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/shape/ellipse_shape_command.dart'; @@ -24,6 +26,10 @@ abstract class Command with EquatableMixin { return EllipseShapeCommand.fromJson(json); case SerializerType.TEXT_COMMAND: return TextCommand.fromJson(json); + case SerializerType.CLIP_PATH_COMMAND: + return ClipPathCommand.fromJson(json); + case SerializerType.CLIP_AREA_COMMAND: + return ClipAreaCommand.fromJson(json); default: return PathCommand.fromJson(json); } diff --git a/lib/core/commands/command_manager/command_manager.dart b/lib/core/commands/command_manager/command_manager.dart index 45d8e115..3ea8db75 100644 --- a/lib/core/commands/command_manager/command_manager.dart +++ b/lib/core/commands/command_manager/command_manager.dart @@ -1,6 +1,8 @@ import 'dart:ui'; import 'package:paintroid/core/commands/command_implementation/command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_area_command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/text_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/line_command.dart'; @@ -123,6 +125,9 @@ class CommandManager { return ToolData.TEXT; } else if (command.runtimeType == SprayCommand) { return ToolData.SPRAY; + } else if (command.runtimeType == ClipAreaCommand || + command.runtimeType == ClipPathCommand) { + return ToolData.CLIPPING; } else { return ToolData.BRUSH; } diff --git a/lib/core/json_serialization/versioning/serializer_version.dart b/lib/core/json_serialization/versioning/serializer_version.dart index 7e48dde8..5eed1749 100644 --- a/lib/core/json_serialization/versioning/serializer_version.dart +++ b/lib/core/json_serialization/versioning/serializer_version.dart @@ -27,6 +27,6 @@ class SerializerType { static const String ELLIPSE_SHAPE_COMMAND = 'EllipseShapeCommand'; static const String TEXT_COMMAND = 'TextCommand'; static const String SPRAY_COMMAND = 'SprayCommand'; - static const String CLIP_PATH_COMMAND = 'DashedPathCommand'; + static const String CLIP_PATH_COMMAND = 'ClipPathCommand'; static const String CLIP_AREA_COMMAND = 'ClipAreaCommand'; } diff --git a/lib/core/providers/object/tools/clipping_tool_provider.dart b/lib/core/providers/object/tools/clipping_tool_provider.dart index 041db427..402b5faa 100644 --- a/lib/core/providers/object/tools/clipping_tool_provider.dart +++ b/lib/core/providers/object/tools/clipping_tool_provider.dart @@ -6,6 +6,7 @@ import 'package:paintroid/core/commands/graphic_factory/graphic_factory_provider import 'package:paintroid/core/enums/tool_types.dart'; import 'package:paintroid/core/tools/implementation/clipping_tool.dart'; import 'package:paintroid/core/providers/object/tools/clipping_tool_state_provider.dart'; +import 'package:paintroid/core/providers/state/canvas_state_provider.dart'; part 'clipping_tool_provider.g.dart'; @@ -18,6 +19,7 @@ class ClippingToolProvider extends _$ClippingToolProvider { commandFactory: ref.watch(commandFactoryProvider), graphicFactory: ref.watch(graphicFactoryProvider), clippingToolState: ref.watch(clippingToolState.notifier), + canvasStateProvider: ref.watch(canvasStateProvider.notifier), type: ToolType.CLIPPING, ); } diff --git a/lib/core/providers/object/tools/clipping_tool_provider.g.dart b/lib/core/providers/object/tools/clipping_tool_provider.g.dart index 4ebe3fb2..f46fddd5 100644 --- a/lib/core/providers/object/tools/clipping_tool_provider.g.dart +++ b/lib/core/providers/object/tools/clipping_tool_provider.g.dart @@ -7,7 +7,7 @@ part of 'clipping_tool_provider.dart'; // ************************************************************************** String _$clippingToolProviderHash() => - r'a7b34cc0817d2b94f4fd348d61a115622f72e259'; + r'10381b6a787aef98e726b246d488001814bcb1e5'; /// See also [ClippingToolProvider]. @ProviderFor(ClippingToolProvider) diff --git a/lib/core/providers/state/canvas_state_provider.dart b/lib/core/providers/state/canvas_state_provider.dart index 4735e4c7..272cbe1f 100644 --- a/lib/core/providers/state/canvas_state_provider.dart +++ b/lib/core/providers/state/canvas_state_provider.dart @@ -3,7 +3,6 @@ import 'dart:ui'; import 'package:flutter/painting.dart'; import 'package:flutter/widgets.dart' as widgets; import 'package:paintroid/core/commands/command_implementation/command.dart'; -import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart'; import 'package:paintroid/core/commands/command_manager/command_manager_provider.dart'; import 'package:paintroid/core/commands/graphic_factory/graphic_factory_provider.dart'; import 'package:paintroid/core/providers/object/device_service.dart'; @@ -46,16 +45,7 @@ class CanvasStateProvider extends _$CanvasStateProvider { final canvas = state.graphicFactory.createCanvasWithRecorder(recorder); final size = state.size; final bounds = Rect.fromLTWH(0, 0, size.width, size.height); - - bool isDrawingClipPathPreview = false; - if (state.commandManager.undoStack.isNotEmpty) { - final lastCommand = state.commandManager.undoStack.last; - if (lastCommand is ClipPathCommand) { - isDrawingClipPathPreview = true; - } - } - - if (!isDrawingClipPathPreview && state.cachedImage != null) { + if (state.cachedImage != null) { paintImage( canvas: canvas, rect: bounds, @@ -63,14 +53,6 @@ class CanvasStateProvider extends _$CanvasStateProvider { fit: BoxFit.fill, filterQuality: FilterQuality.none, ); - } else if (state.backgroundImage != null) { - paintImage( - canvas: canvas, - rect: bounds, - image: state.backgroundImage!, - fit: BoxFit.fill, - filterQuality: FilterQuality.none, - ); } canvas.clipRect(bounds); state.commandManager.executeLastCommand(canvas); @@ -97,19 +79,7 @@ class CanvasStateProvider extends _$CanvasStateProvider { final recorder = state.graphicFactory.createPictureRecorder(); final canvas = state.graphicFactory.createCanvasWithRecorder(recorder); final size = state.size; - final bounds = Rect.fromLTWH(0, 0, size.width, size.height); - - if (state.backgroundImage != null) { - paintImage( - canvas: canvas, - rect: bounds, - image: state.backgroundImage!, - fit: BoxFit.fill, - filterQuality: FilterQuality.none, - ); - } - - canvas.clipRect(bounds); + canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height)); state.commandManager.executeAllCommands(canvas); final picture = recorder.endRecording(); final img = await picture.toImage(size.width.toInt(), size.height.toInt()); diff --git a/lib/core/providers/state/toolbox_state_provider.g.dart b/lib/core/providers/state/toolbox_state_provider.g.dart index 65683a67..fd09ff3e 100644 --- a/lib/core/providers/state/toolbox_state_provider.g.dart +++ b/lib/core/providers/state/toolbox_state_provider.g.dart @@ -7,7 +7,7 @@ part of 'toolbox_state_provider.dart'; // ************************************************************************** String _$toolBoxStateProviderHash() => - r'f04056392e57363e564e98f4ee92e024d7c52174'; + r'ae8601a5972b8df4a204b0bb04ed5fc3bf371cc5'; /// See also [ToolBoxStateProvider]. @ProviderFor(ToolBoxStateProvider) diff --git a/lib/core/tools/implementation/clipping_tool.dart b/lib/core/tools/implementation/clipping_tool.dart index fda81412..f7187ace 100644 --- a/lib/core/tools/implementation/clipping_tool.dart +++ b/lib/core/tools/implementation/clipping_tool.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart'; import 'package:paintroid/core/commands/graphic_factory/graphic_factory.dart'; import 'package:paintroid/core/commands/path_with_action_history.dart'; +import 'package:paintroid/core/providers/state/canvas_state_provider.dart'; import 'package:paintroid/core/tools/tool.dart'; import 'package:paintroid/core/enums/tool_types.dart'; import 'package:paintroid/core/providers/object/tools/clipping_tool_state_provider.dart'; @@ -11,6 +12,7 @@ import 'package:paintroid/core/providers/object/tools/clipping_tool_state_provid class ClippingTool extends Tool { final GraphicFactory graphicFactory; final ClippingToolState clippingToolState; + final CanvasStateProvider canvasStateProvider; @visibleForTesting late PathWithActionHistory pathToDraw; @@ -23,6 +25,7 @@ class ClippingTool extends Tool { required super.commandManager, required this.graphicFactory, required this.clippingToolState, + required this.canvasStateProvider, super.type = ToolType.CLIPPING, super.hasAddFunctionality = false, super.hasFinalizeFunctionality = true, @@ -30,10 +33,12 @@ class ClippingTool extends Tool { @override void onDown(Offset point, Paint paint) { + bool requiresRefresh = false; if (clippingToolState.hasActiveClipPath) { if (_activePreviewCommand != null) { commandManager.removeCommand(_activePreviewCommand!); _activePreviewCommand = null; + requiresRefresh = true; } clippingToolState.clearClipPath(); } @@ -41,6 +46,11 @@ class ClippingTool extends Tool { if (_liveDrawingCommand != null) { commandManager.removeCommand(_liveDrawingCommand!); _liveDrawingCommand = null; + requiresRefresh = true; + } + + if (requiresRefresh) { + canvasStateProvider.resetCanvasWithExistingCommands(); } _startPoint = point; @@ -60,9 +70,11 @@ class ClippingTool extends Tool { @override void onUp(Offset point, Paint paint) { + bool requiresRefresh = false; if (_liveDrawingCommand != null) { commandManager.removeCommand(_liveDrawingCommand!); _liveDrawingCommand = null; + requiresRefresh = true; } bool pointAddedInUp = false; @@ -134,31 +146,45 @@ class ClippingTool extends Tool { _activePreviewCommand = newPreviewCommand; clippingToolState.setHasActiveClipPath(true); _startPoint = null; + + if (requiresRefresh) { + canvasStateProvider.resetCanvasWithExistingCommands(); + } + canvasStateProvider.updateCachedImage(); } @override void onCancel() { + bool requiresRefresh = false; if (_liveDrawingCommand != null) { commandManager.removeCommand(_liveDrawingCommand!); _liveDrawingCommand = null; + requiresRefresh = true; } if (clippingToolState.hasActiveClipPath) { if (_activePreviewCommand != null) { commandManager.removeCommand(_activePreviewCommand!); _activePreviewCommand = null; + requiresRefresh = true; } clippingToolState.clearClipPath(); } + + if (requiresRefresh) { + canvasStateProvider.resetCanvasWithExistingCommands(); + } _startPoint = null; } @override void onCheckmark(Paint paint) { + bool requiresRefresh = false; if (clippingToolState.hasActiveClipPath) { if (_activePreviewCommand != null) { commandManager.removeCommand(_activePreviewCommand!); _activePreviewCommand = null; + requiresRefresh = true; } clippingToolState.clearClipPath(); } @@ -166,6 +192,7 @@ class ClippingTool extends Tool { if (_liveDrawingCommand != null) { commandManager.removeCommand(_liveDrawingCommand!); _liveDrawingCommand = null; + requiresRefresh = true; } if (pathToDraw.actions.isNotEmpty) { @@ -174,6 +201,11 @@ class ClippingTool extends Tool { final cropCommand = commandFactory.createClipAreaCommand(pathToDraw, paint); commandManager.addGraphicCommand(cropCommand); + requiresRefresh = true; + } + + if (requiresRefresh) { + canvasStateProvider.resetCanvasWithExistingCommands(); } _startPoint = null; } @@ -184,10 +216,12 @@ class ClippingTool extends Tool { @override void onRedo() { commandManager.redo(); + canvasStateProvider.resetCanvasWithExistingCommands(); } @override void onUndo() { commandManager.undo(); + canvasStateProvider.resetCanvasWithExistingCommands(); } } diff --git a/test/integration/clipping_tool_test.dart b/test/integration/clipping_tool_test.dart new file mode 100644 index 00000000..ad9310b5 --- /dev/null +++ b/test/integration/clipping_tool_test.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:paintroid/app.dart'; +import 'package:paintroid/core/tools/tool_data.dart'; +import 'package:paintroid/core/utils/color_utils.dart'; + +import '../utils/canvas_positions.dart'; +import '../utils/ui_interaction.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + const String testIDStr = String.fromEnvironment('id', defaultValue: '-1'); + final testID = int.tryParse(testIDStr) ?? testIDStr; + + late Widget sut; + + setUp(() async { + sut = ProviderScope( + child: App( + showOnboardingPage: false, + ), + ); + }); + + if (testID == -1 || testID == 0) { + testWidgets('[CLIPPING_TOOL]: clip a part of the image', + (WidgetTester tester) async { + UIInteraction.initialize(tester); + await tester.pumpWidget(sut); + await UIInteraction.createNewImage(); + UIInteraction.setColor(Colors.black); + await UIInteraction.selectTool(ToolData.BRUSH.name); + + await UIInteraction.dragFromTo( + CanvasPosition.topLeft, + CanvasPosition.bottomRight, + ); + + await UIInteraction.selectTool(ToolData.CLIPPING.name); + + final Offset center = CanvasPosition.center; + final Offset p1 = center + const Offset(-50, -50); + final Offset p2 = center + const Offset(50, -50); + final Offset p3 = center + const Offset(50, 50); + final Offset p4 = center + const Offset(-50, 50); + + final TestGesture gesture = await tester.startGesture(p1); + await tester.pumpAndSettle(); + await gesture.moveTo(p2); + await tester.pumpAndSettle(); + await gesture.moveTo(p3); + await tester.pumpAndSettle(); + await gesture.moveTo(p4); + await tester.pumpAndSettle(); + await gesture.moveTo(p1); + await tester.pumpAndSettle(); + await gesture.up(); + await tester.pumpAndSettle(); + + await UIInteraction.clickCheckmark(); + + var centerColor = await UIInteraction.getPixelColor( + CanvasPosition.centerX, + CanvasPosition.centerY, + ); + expect(centerColor.toValue(), Colors.black.toValue()); + + var topLeftColor = await UIInteraction.getPixelColor( + CanvasPosition.left, + CanvasPosition.top, + ); + expect(topLeftColor.toValue(), Colors.transparent.toValue()); + + var bottomRightColor = await UIInteraction.getPixelColor( + CanvasPosition.right, + CanvasPosition.bottom, + ); + expect(bottomRightColor.toValue(), Colors.transparent.toValue()); + }); + } + + if (testID == -1 || testID == 1) { + testWidgets('[CLIPPING_TOOL]: cancel clipping', + (WidgetTester tester) async { + UIInteraction.initialize(tester); + await tester.pumpWidget(sut); + await UIInteraction.createNewImage(); + UIInteraction.setColor(Colors.black); + await UIInteraction.selectTool(ToolData.BRUSH.name); + + await UIInteraction.dragFromTo( + CanvasPosition.topLeft, + CanvasPosition.bottomRight, + ); + + await UIInteraction.selectTool(ToolData.CLIPPING.name); + + final Offset center = CanvasPosition.center; + final Offset p1 = center + const Offset(-50, -50); + final Offset p2 = center + const Offset(50, -50); + final Offset p3 = center + const Offset(50, 50); + final Offset p4 = center + const Offset(-50, 50); + + final TestGesture gesture = await tester.startGesture(p1); + await tester.pumpAndSettle(); + await gesture.moveTo(p2); + await tester.pumpAndSettle(); + await gesture.moveTo(p3); + await tester.pumpAndSettle(); + await gesture.moveTo(p4); + await tester.pumpAndSettle(); + await gesture.moveTo(p1); + await tester.pumpAndSettle(); + await gesture.up(); + await tester.pumpAndSettle(); + await UIInteraction.clickBackButton(); + + var centerColor = await UIInteraction.getPixelColor( + CanvasPosition.centerX, + CanvasPosition.centerY, + ); + expect(centerColor.toValue(), Colors.black.toValue()); + + var topLeftColor = await UIInteraction.getPixelColor( + CanvasPosition.left, + CanvasPosition.top, + ); + expect(topLeftColor.toValue(), Colors.black.toValue()); + + var bottomRightColor = await UIInteraction.getPixelColor( + CanvasPosition.right, + CanvasPosition.bottom, + ); + expect(bottomRightColor.toValue(), Colors.black.toValue()); + }); + } +} diff --git a/test/unit/serialization/utils/dummy_version_strategy.dart b/test/unit/serialization/utils/dummy_version_strategy.dart index db172483..0c6fd323 100644 --- a/test/unit/serialization/utils/dummy_version_strategy.dart +++ b/test/unit/serialization/utils/dummy_version_strategy.dart @@ -9,7 +9,7 @@ class DummyVersionStrategy implements IVersionStrategy { final int ellipseShapeCommandVersion; final int sprayCommandVersion; final int textCommandVersion; - final int dashedPathCommandVersion; + final int clipPathCommandVersion; final int clipAreaCommandVersion; DummyVersionStrategy({ @@ -22,9 +22,8 @@ class DummyVersionStrategy implements IVersionStrategy { SerializerVersion.ELLIPSE_SHAPE_COMMAND_VERSION, this.sprayCommandVersion = SerializerVersion.SPRAY_COMMAND_VERSION, this.textCommandVersion = SerializerVersion.TEXT_COMMAND_VERSION, - this.dashedPathCommandVersion = SerializerVersion.CLIP_PATH_COMMAND_VERSION, - this.clipAreaCommandVersion = - SerializerVersion.CLIP_AREA_COMMAND_VERSION, + this.clipPathCommandVersion = SerializerVersion.CLIP_PATH_COMMAND_VERSION, + this.clipAreaCommandVersion = SerializerVersion.CLIP_AREA_COMMAND_VERSION, }); @override @@ -33,9 +32,6 @@ class DummyVersionStrategy implements IVersionStrategy { @override int getPathCommandVersion() => pathCommandVersion; - @override - int getClipPathCommandVersion() => dashedPathCommandVersion; - @override int getLineCommandVersion() => lineCommandVersion; @@ -51,6 +47,9 @@ class DummyVersionStrategy implements IVersionStrategy { @override int getTextCommandVersion() => textCommandVersion; + @override + int getClipPathCommandVersion() => clipPathCommandVersion; + @override int getClipAreaCommandVersion() => clipAreaCommandVersion; } diff --git a/test/unit/tools/clipping_tool_test.dart b/test/unit/tools/clipping_tool_test.dart new file mode 100644 index 00000000..4fc4c46f --- /dev/null +++ b/test/unit/tools/clipping_tool_test.dart @@ -0,0 +1,206 @@ +import 'dart:ui'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:paintroid/core/commands/command_factory/command_factory.dart'; +import 'package:mockito/annotations.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_area_command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart'; +import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart'; +import 'package:paintroid/core/commands/graphic_factory/graphic_factory.dart'; +import 'package:paintroid/core/commands/path_with_action_history.dart'; +import 'package:paintroid/core/providers/state/canvas_state_provider.dart'; +import 'package:paintroid/core/tools/implementation/clipping_tool.dart'; +import 'package:paintroid/core/providers/object/tools/clipping_tool_state_provider.dart'; +import 'package:paintroid/core/commands/command_manager/command_manager.dart'; + +import 'clipping_tool_test.mocks.dart'; + +@GenerateMocks([ + CommandManager, + CommandFactory, + GraphicFactory, + GraphicCommand, + CanvasStateProvider, + ClippingToolState, + PathWithActionHistory, + ClipPathCommand, + ClipAreaCommand, +]) +void main() { + late MockCommandManager commandManager; + late MockCommandFactory commandFactory; + late MockGraphicFactory graphicFactory; + late MockCanvasStateProvider canvasStateProvider; + late MockClippingToolState clippingToolState; + late MockPathWithActionHistory pathMock; + late ClippingTool clippingTool; + late Paint paint; + + setUp(() { + commandManager = MockCommandManager(); + commandFactory = MockCommandFactory(); + graphicFactory = MockGraphicFactory(); + canvasStateProvider = MockCanvasStateProvider(); + clippingToolState = MockClippingToolState(); + pathMock = MockPathWithActionHistory(); + paint = Paint(); + + when(graphicFactory.createPathWithActionHistory()).thenReturn(pathMock); + + when(commandFactory.createClipPathCommand(any, any, + startPoint: anyNamed('startPoint'), endPoint: anyNamed('endPoint'))) + .thenAnswer((_) => MockClipPathCommand()); + when(commandFactory.createClipPathCommand(any, any)) + .thenAnswer((_) => MockClipPathCommand()); + when(commandFactory.createClipAreaCommand(any, any)) + .thenAnswer((_) => MockClipAreaCommand()); + + when(clippingToolState.hasActiveClipPath).thenReturn(false); + when(pathMock.actions).thenReturn([MoveToAction(0, 0)]); + + clippingTool = ClippingTool( + commandFactory: commandFactory, + commandManager: commandManager, + graphicFactory: graphicFactory, + clippingToolState: clippingToolState, + canvasStateProvider: canvasStateProvider, + ); + }); + + group('[CLIPPING_TOOL]: onDown', () { + test( + '[CLIPPING_TOOL]: creates a live drawing command when no active clip path', + () { + when(clippingToolState.hasActiveClipPath).thenReturn(false); + clippingTool.onDown(const Offset(10, 20), paint); + + verify(graphicFactory.createPathWithActionHistory()).called(1); + verify(commandFactory.createClipPathCommand(pathMock, paint)).called(1); + final captured = + verify(commandManager.addGraphicCommand(captureAny)).captured; + expect(captured.single, isA()); + + verifyNever(commandManager.removeCommand(any)); + verifyNever(canvasStateProvider.resetCanvasWithExistingCommands()); + }); + + test( + '[CLIPPING_TOOL]: clears existing preview and creates live drawing command when active clip path exists', + () { + clippingTool.onDown(const Offset(5, 5), paint); + clippingTool.onUp(const Offset(10, 10), paint); + + clearInteractions(commandManager); + clearInteractions(commandFactory); + clearInteractions(graphicFactory); + clearInteractions(canvasStateProvider); + clearInteractions(clippingToolState); + + when(graphicFactory.createPathWithActionHistory()).thenReturn(pathMock); + when(commandFactory.createClipPathCommand(any, any)) + .thenAnswer((_) => MockClipPathCommand()); + when(clippingToolState.hasActiveClipPath).thenReturn(true); + + clippingTool.onDown(const Offset(30, 40), paint); + + final capturedRemove = + verify(commandManager.removeCommand(captureAny)).captured; + expect(capturedRemove.single, isA()); + + verify(clippingToolState.clearClipPath()).called(1); + verify(canvasStateProvider.resetCanvasWithExistingCommands()).called(1); + + verify(graphicFactory.createPathWithActionHistory()).called(1); + verify(commandFactory.createClipPathCommand(pathMock, paint)).called(1); + + final capturedAdd = + verify(commandManager.addGraphicCommand(captureAny)).captured; + expect(capturedAdd.single, isA()); + }); + }); + + test( + '[CLIPPING_TOOL]: onUp removes live command, creates and adds preview command', + () { + when(pathMock.actions).thenReturn([MoveToAction(5, 5)]); + + clippingTool.onDown(const Offset(5, 5), paint); + + clearInteractions(commandManager); + clearInteractions(clippingToolState); + clearInteractions(canvasStateProvider); + clearInteractions(commandFactory); + + when(commandFactory.createClipPathCommand(any, any, + startPoint: anyNamed('startPoint'), endPoint: anyNamed('endPoint'))) + .thenAnswer((_) => MockClipPathCommand()); + + clippingTool.onUp(const Offset(15, 15), paint); + + final capturedRemove = + verify(commandManager.removeCommand(captureAny)).captured; + expect(capturedRemove.single, isA()); + + verify(commandFactory.createClipPathCommand(pathMock, paint, + startPoint: anyNamed('startPoint'), endPoint: anyNamed('endPoint'))) + .called(1); + final capturedAdd = + verify(commandManager.addGraphicCommand(captureAny)).captured; + expect(capturedAdd.single, isA()); + + verify(clippingToolState.setHasActiveClipPath(true)).called(1); + verify(canvasStateProvider.resetCanvasWithExistingCommands()).called(1); + verify(canvasStateProvider.updateCachedImage()).called(1); + }); + + test('[CLIPPING_TOOL]: onCancel clears live and preview commands', () { + clippingTool.onDown(const Offset(1, 1), paint); + clippingTool.onUp(const Offset(2, 2), paint); + + clearInteractions(commandManager); + clearInteractions(clippingToolState); + clearInteractions(canvasStateProvider); + + when(clippingToolState.hasActiveClipPath).thenReturn(true); + + clippingTool.onCancel(); + + final capturedRemove = + verify(commandManager.removeCommand(captureAny)).captured; + expect(capturedRemove.single, isA()); + + verify(clippingToolState.clearClipPath()).called(1); + verify(canvasStateProvider.resetCanvasWithExistingCommands()).called(1); + }); + + test('[CLIPPING_TOOL]: onCheckmark finalizes the clip area', () { + when(pathMock.actions).thenReturn([MoveToAction(0, 0), LineToAction(1, 1)]); + + clippingTool.onDown(const Offset(0, 0), paint); + clippingTool.onUp(const Offset(1, 1), paint); + + clearInteractions(commandManager); + clearInteractions(commandFactory); + clearInteractions(clippingToolState); + clearInteractions(canvasStateProvider); + + when(commandFactory.createClipAreaCommand(any, any)) + .thenAnswer((_) => MockClipAreaCommand()); + when(clippingToolState.hasActiveClipPath).thenReturn(true); + + clippingTool.onCheckmark(paint); + + final capturedRemove = + verify(commandManager.removeCommand(captureAny)).captured; + expect(capturedRemove.single, isA()); + + verify(clippingToolState.clearClipPath()).called(1); + verify(commandFactory.createClipAreaCommand(pathMock, paint)).called(1); + + final capturedAdd = + verify(commandManager.addGraphicCommand(captureAny)).captured; + expect(capturedAdd.single, isA()); + + verify(canvasStateProvider.resetCanvasWithExistingCommands()).called(1); + }); +} diff --git a/test/unit/tools/clipping_tool_test.mocks.dart b/test/unit/tools/clipping_tool_test.mocks.dart new file mode 100644 index 00000000..3c7d33d4 --- /dev/null +++ b/test/unit/tools/clipping_tool_test.mocks.dart @@ -0,0 +1,1284 @@ +// Mocks generated by Mockito 5.4.5 from annotations +// in paintroid/test/unit/tools/clipping_tool_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i24; +import 'dart:ui' as _i13; + +import 'package:flutter/material.dart' as _i20; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i22; +import 'package:paintroid/core/commands/command_factory/command_factory.dart' + as _i19; +import 'package:paintroid/core/commands/command_implementation/command.dart' + as _i2; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_area_command.dart' + as _i11; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart' + as _i5; +import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart' + as _i17; +import 'package:paintroid/core/commands/command_implementation/graphic/line_command.dart' + as _i6; +import 'package:paintroid/core/commands/command_implementation/graphic/path_command.dart' + as _i4; +import 'package:paintroid/core/commands/command_implementation/graphic/shape/ellipse_shape_command.dart' + as _i8; +import 'package:paintroid/core/commands/command_implementation/graphic/shape/square_shape_command.dart' + as _i7; +import 'package:paintroid/core/commands/command_implementation/graphic/spray_command.dart' + as _i10; +import 'package:paintroid/core/commands/command_implementation/graphic/text_command.dart' + as _i9; +import 'package:paintroid/core/commands/command_manager/command_manager.dart' + as _i16; +import 'package:paintroid/core/commands/graphic_factory/graphic_factory.dart' + as _i21; +import 'package:paintroid/core/commands/path_with_action_history.dart' as _i12; +import 'package:paintroid/core/providers/object/tools/clipping_tool_state_provider.dart' + as _i25; +import 'package:paintroid/core/providers/state/canvas_state_data.dart' as _i15; +import 'package:paintroid/core/providers/state/canvas_state_provider.dart' + as _i23; +import 'package:paintroid/core/tools/line_tool/vertex_stack.dart' as _i18; +import 'package:paintroid/core/tools/tool_data.dart' as _i3; +import 'package:riverpod_annotation/riverpod_annotation.dart' as _i14; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeCommand_0 extends _i1.SmartFake implements _i2.Command { + _FakeCommand_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeToolData_1 extends _i1.SmartFake implements _i3.ToolData { + _FakeToolData_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePathCommand_2 extends _i1.SmartFake implements _i4.PathCommand { + _FakePathCommand_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeClipPathCommand_3 extends _i1.SmartFake + implements _i5.ClipPathCommand { + _FakeClipPathCommand_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeLineCommand_4 extends _i1.SmartFake implements _i6.LineCommand { + _FakeLineCommand_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSquareShapeCommand_5 extends _i1.SmartFake + implements _i7.SquareShapeCommand { + _FakeSquareShapeCommand_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeEllipseShapeCommand_6 extends _i1.SmartFake + implements _i8.EllipseShapeCommand { + _FakeEllipseShapeCommand_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeTextCommand_7 extends _i1.SmartFake implements _i9.TextCommand { + _FakeTextCommand_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSprayCommand_8 extends _i1.SmartFake implements _i10.SprayCommand { + _FakeSprayCommand_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeClipAreaCommand_9 extends _i1.SmartFake + implements _i11.ClipAreaCommand { + _FakeClipAreaCommand_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePathWithActionHistory_10 extends _i1.SmartFake + implements _i12.PathWithActionHistory { + _FakePathWithActionHistory_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePictureRecorder_11 extends _i1.SmartFake + implements _i13.PictureRecorder { + _FakePictureRecorder_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeCanvas_12 extends _i1.SmartFake implements _i13.Canvas { + _FakeCanvas_12( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSize_13 extends _i1.SmartFake implements _i13.Size { + _FakeSize_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeNotifierProviderRef_14 extends _i1.SmartFake + implements _i14.NotifierProviderRef { + _FakeNotifierProviderRef_14( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeCanvasStateData_15 extends _i1.SmartFake + implements _i15.CanvasStateData { + _FakeCanvasStateData_15( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeAutoDisposeNotifierProviderRef_16 extends _i1.SmartFake + implements _i14.AutoDisposeNotifierProviderRef { + _FakeAutoDisposeNotifierProviderRef_16( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePath_17 extends _i1.SmartFake implements _i13.Path { + _FakePath_17( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [CommandManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommandManager extends _i1.Mock implements _i16.CommandManager { + MockCommandManager() { + _i1.throwOnMissingStub(this); + } + + @override + List<_i2.Command> get redoStack => (super.noSuchMethod( + Invocation.getter(#redoStack), + returnValue: <_i2.Command>[], + ) as List<_i2.Command>); + + @override + List<_i2.Command> get undoStack => (super.noSuchMethod( + Invocation.getter(#undoStack), + returnValue: <_i2.Command>[], + ) as List<_i2.Command>); + + @override + void addGraphicCommand(_i17.GraphicCommand? command) => super.noSuchMethod( + Invocation.method( + #addGraphicCommand, + [command], + ), + returnValueForMissingStub: null, + ); + + @override + void removeCommand(_i2.Command? commandToRemove) => super.noSuchMethod( + Invocation.method( + #removeCommand, + [commandToRemove], + ), + returnValueForMissingStub: null, + ); + + @override + void setUndoStack(List<_i2.Command>? commands) => super.noSuchMethod( + Invocation.method( + #setUndoStack, + [commands], + ), + returnValueForMissingStub: null, + ); + + @override + void executeLastCommand(_i13.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #executeLastCommand, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + void executeAllCommands(_i13.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #executeAllCommands, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + void discardLastCommand() => super.noSuchMethod( + Invocation.method( + #discardLastCommand, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void clearUndoStack({Iterable<_i2.Command>? newCommands}) => + super.noSuchMethod( + Invocation.method( + #clearUndoStack, + [], + {#newCommands: newCommands}, + ), + returnValueForMissingStub: null, + ); + + @override + void clearRedoStack() => super.noSuchMethod( + Invocation.method( + #clearRedoStack, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void drawLineToolGhostPaths( + _i13.Canvas? canvas, + _i6.LineCommand? ingoingGhostPathCommand, + _i6.LineCommand? outgoingGhostPathCommand, + ) => + super.noSuchMethod( + Invocation.method( + #drawLineToolGhostPaths, + [ + canvas, + ingoingGhostPathCommand, + outgoingGhostPathCommand, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawLineToolVertices( + _i13.Canvas? canvas, + _i18.VertexStack? vertexStack, + ) => + super.noSuchMethod( + Invocation.method( + #drawLineToolVertices, + [ + canvas, + vertexStack, + ], + ), + returnValueForMissingStub: null, + ); + + @override + _i2.Command redo() => (super.noSuchMethod( + Invocation.method( + #redo, + [], + ), + returnValue: _FakeCommand_0( + this, + Invocation.method( + #redo, + [], + ), + ), + ) as _i2.Command); + + @override + void undo() => super.noSuchMethod( + Invocation.method( + #undo, + [], + ), + returnValueForMissingStub: null, + ); + + @override + _i3.ToolData getNextTool(_i16.ActionType? actionType) => (super.noSuchMethod( + Invocation.method( + #getNextTool, + [actionType], + ), + returnValue: _FakeToolData_1( + this, + Invocation.method( + #getNextTool, + [actionType], + ), + ), + ) as _i3.ToolData); + + @override + List<_i6.LineCommand> getTopLineCommandSequence() => (super.noSuchMethod( + Invocation.method( + #getTopLineCommandSequence, + [], + ), + returnValue: <_i6.LineCommand>[], + ) as List<_i6.LineCommand>); +} + +/// A class which mocks [CommandFactory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommandFactory extends _i1.Mock implements _i19.CommandFactory { + MockCommandFactory() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.PathCommand createPathCommand( + _i12.PathWithActionHistory? path, + _i13.Paint? paint, + ) => + (super.noSuchMethod( + Invocation.method( + #createPathCommand, + [ + path, + paint, + ], + ), + returnValue: _FakePathCommand_2( + this, + Invocation.method( + #createPathCommand, + [ + path, + paint, + ], + ), + ), + ) as _i4.PathCommand); + + @override + _i5.ClipPathCommand createClipPathCommand( + _i12.PathWithActionHistory? path, + _i13.Paint? paint, { + _i13.Offset? startPoint, + _i13.Offset? endPoint, + }) => + (super.noSuchMethod( + Invocation.method( + #createClipPathCommand, + [ + path, + paint, + ], + { + #startPoint: startPoint, + #endPoint: endPoint, + }, + ), + returnValue: _FakeClipPathCommand_3( + this, + Invocation.method( + #createClipPathCommand, + [ + path, + paint, + ], + { + #startPoint: startPoint, + #endPoint: endPoint, + }, + ), + ), + ) as _i5.ClipPathCommand); + + @override + _i6.LineCommand createLineCommand( + _i12.PathWithActionHistory? path, + _i13.Paint? paint, + _i13.Offset? startPoint, + _i13.Offset? endPoint, + ) => + (super.noSuchMethod( + Invocation.method( + #createLineCommand, + [ + path, + paint, + startPoint, + endPoint, + ], + ), + returnValue: _FakeLineCommand_4( + this, + Invocation.method( + #createLineCommand, + [ + path, + paint, + startPoint, + endPoint, + ], + ), + ), + ) as _i6.LineCommand); + + @override + _i7.SquareShapeCommand createSquareShapeCommand( + _i13.Paint? paint, + _i13.Offset? topLeft, + _i13.Offset? topRight, + _i13.Offset? bottomLeft, + _i13.Offset? bottomRight, + ) => + (super.noSuchMethod( + Invocation.method( + #createSquareShapeCommand, + [ + paint, + topLeft, + topRight, + bottomLeft, + bottomRight, + ], + ), + returnValue: _FakeSquareShapeCommand_5( + this, + Invocation.method( + #createSquareShapeCommand, + [ + paint, + topLeft, + topRight, + bottomLeft, + bottomRight, + ], + ), + ), + ) as _i7.SquareShapeCommand); + + @override + _i8.EllipseShapeCommand createEllipseShapeCommand( + _i13.Paint? paint, + double? radiusX, + double? radiusY, + _i13.Offset? center, + double? angle, + ) => + (super.noSuchMethod( + Invocation.method( + #createEllipseShapeCommand, + [ + paint, + radiusX, + radiusY, + center, + angle, + ], + ), + returnValue: _FakeEllipseShapeCommand_6( + this, + Invocation.method( + #createEllipseShapeCommand, + [ + paint, + radiusX, + radiusY, + center, + angle, + ], + ), + ), + ) as _i8.EllipseShapeCommand); + + @override + _i9.TextCommand createTextCommand( + _i13.Offset? point, + String? text, + _i20.TextStyle? style, + double? fontSize, + _i13.Paint? paint, + double? rotationAngle, { + double? scaleX = 1.0, + double? scaleY = 1.0, + }) => + (super.noSuchMethod( + Invocation.method( + #createTextCommand, + [ + point, + text, + style, + fontSize, + paint, + rotationAngle, + ], + { + #scaleX: scaleX, + #scaleY: scaleY, + }, + ), + returnValue: _FakeTextCommand_7( + this, + Invocation.method( + #createTextCommand, + [ + point, + text, + style, + fontSize, + paint, + rotationAngle, + ], + { + #scaleX: scaleX, + #scaleY: scaleY, + }, + ), + ), + ) as _i9.TextCommand); + + @override + _i10.SprayCommand createSprayCommand( + List<_i13.Offset>? points, + _i13.Paint? paint, + ) => + (super.noSuchMethod( + Invocation.method( + #createSprayCommand, + [ + points, + paint, + ], + ), + returnValue: _FakeSprayCommand_8( + this, + Invocation.method( + #createSprayCommand, + [ + points, + paint, + ], + ), + ), + ) as _i10.SprayCommand); + + @override + _i11.ClipAreaCommand createClipAreaCommand( + _i12.PathWithActionHistory? path, + _i13.Paint? paint, + ) => + (super.noSuchMethod( + Invocation.method( + #createClipAreaCommand, + [ + path, + paint, + ], + ), + returnValue: _FakeClipAreaCommand_9( + this, + Invocation.method( + #createClipAreaCommand, + [ + path, + paint, + ], + ), + ), + ) as _i11.ClipAreaCommand); +} + +/// A class which mocks [GraphicFactory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockGraphicFactory extends _i1.Mock implements _i21.GraphicFactory { + MockGraphicFactory() { + _i1.throwOnMissingStub(this); + } + + @override + _i13.Paint createPaint() => (super.noSuchMethod( + Invocation.method( + #createPaint, + [], + ), + returnValue: _i22.dummyValue<_i13.Paint>( + this, + Invocation.method( + #createPaint, + [], + ), + ), + ) as _i13.Paint); + + @override + _i12.PathWithActionHistory createPathWithActionHistory() => + (super.noSuchMethod( + Invocation.method( + #createPathWithActionHistory, + [], + ), + returnValue: _FakePathWithActionHistory_10( + this, + Invocation.method( + #createPathWithActionHistory, + [], + ), + ), + ) as _i12.PathWithActionHistory); + + @override + _i13.PictureRecorder createPictureRecorder() => (super.noSuchMethod( + Invocation.method( + #createPictureRecorder, + [], + ), + returnValue: _FakePictureRecorder_11( + this, + Invocation.method( + #createPictureRecorder, + [], + ), + ), + ) as _i13.PictureRecorder); + + @override + _i13.Canvas createCanvasWithRecorder(_i13.PictureRecorder? recorder) => + (super.noSuchMethod( + Invocation.method( + #createCanvasWithRecorder, + [recorder], + ), + returnValue: _FakeCanvas_12( + this, + Invocation.method( + #createCanvasWithRecorder, + [recorder], + ), + ), + ) as _i13.Canvas); + + @override + _i13.Paint copyPaint(_i13.Paint? original) => (super.noSuchMethod( + Invocation.method( + #copyPaint, + [original], + ), + returnValue: _i22.dummyValue<_i13.Paint>( + this, + Invocation.method( + #copyPaint, + [original], + ), + ), + ) as _i13.Paint); +} + +/// A class which mocks [GraphicCommand]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockGraphicCommand extends _i1.Mock implements _i17.GraphicCommand { + MockGraphicCommand() { + _i1.throwOnMissingStub(this); + } + + @override + _i13.Paint get paint => (super.noSuchMethod( + Invocation.getter(#paint), + returnValue: _i22.dummyValue<_i13.Paint>( + this, + Invocation.getter(#paint), + ), + ) as _i13.Paint); + + @override + List get props => (super.noSuchMethod( + Invocation.getter(#props), + returnValue: [], + ) as List); + + @override + void call(_i13.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #call, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + Map toJson() => (super.noSuchMethod( + Invocation.method( + #toJson, + [], + ), + returnValue: {}, + ) as Map); +} + +/// A class which mocks [CanvasStateProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCanvasStateProvider extends _i1.Mock + implements _i23.CanvasStateProvider { + MockCanvasStateProvider() { + _i1.throwOnMissingStub(this); + } + + @override + _i13.Size get initialCanvasSize => (super.noSuchMethod( + Invocation.getter(#initialCanvasSize), + returnValue: _FakeSize_13( + this, + Invocation.getter(#initialCanvasSize), + ), + ) as _i13.Size); + + @override + set initialCanvasSize(_i13.Size? _initialCanvasSize) => super.noSuchMethod( + Invocation.setter( + #initialCanvasSize, + _initialCanvasSize, + ), + returnValueForMissingStub: null, + ); + + @override + _i14.NotifierProviderRef<_i15.CanvasStateData> get ref => (super.noSuchMethod( + Invocation.getter(#ref), + returnValue: _FakeNotifierProviderRef_14<_i15.CanvasStateData>( + this, + Invocation.getter(#ref), + ), + ) as _i14.NotifierProviderRef<_i15.CanvasStateData>); + + @override + _i15.CanvasStateData get state => (super.noSuchMethod( + Invocation.getter(#state), + returnValue: _FakeCanvasStateData_15( + this, + Invocation.getter(#state), + ), + ) as _i15.CanvasStateData); + + @override + set state(_i15.CanvasStateData? value) => super.noSuchMethod( + Invocation.setter( + #state, + value, + ), + returnValueForMissingStub: null, + ); + + @override + _i15.CanvasStateData build() => (super.noSuchMethod( + Invocation.method( + #build, + [], + ), + returnValue: _FakeCanvasStateData_15( + this, + Invocation.method( + #build, + [], + ), + ), + ) as _i15.CanvasStateData); + + @override + void setBackgroundImage(_i13.Image? image) => super.noSuchMethod( + Invocation.method( + #setBackgroundImage, + [image], + ), + returnValueForMissingStub: null, + ); + + @override + void clearBackgroundImageAndResetDimensions() => super.noSuchMethod( + Invocation.method( + #clearBackgroundImageAndResetDimensions, + [], + ), + returnValueForMissingStub: null, + ); + + @override + _i24.Future updateCachedImage() => (super.noSuchMethod( + Invocation.method( + #updateCachedImage, + [], + ), + returnValue: _i24.Future.value(), + returnValueForMissingStub: _i24.Future.value(), + ) as _i24.Future); + + @override + _i24.Future resetCanvasWithNewCommands( + Iterable<_i2.Command>? commands) => + (super.noSuchMethod( + Invocation.method( + #resetCanvasWithNewCommands, + [commands], + ), + returnValue: _i24.Future.value(), + returnValueForMissingStub: _i24.Future.value(), + ) as _i24.Future); + + @override + _i24.Future resetCanvasWithExistingCommands() => (super.noSuchMethod( + Invocation.method( + #resetCanvasWithExistingCommands, + [], + ), + returnValue: _i24.Future.value(), + returnValueForMissingStub: _i24.Future.value(), + ) as _i24.Future); + + @override + void listenSelf( + void Function( + _i15.CanvasStateData?, + _i15.CanvasStateData, + )? listener, { + void Function( + Object, + StackTrace, + )? onError, + }) => + super.noSuchMethod( + Invocation.method( + #listenSelf, + [listener], + {#onError: onError}, + ), + returnValueForMissingStub: null, + ); + + @override + bool updateShouldNotify( + _i15.CanvasStateData? previous, + _i15.CanvasStateData? next, + ) => + (super.noSuchMethod( + Invocation.method( + #updateShouldNotify, + [ + previous, + next, + ], + ), + returnValue: false, + ) as bool); +} + +/// A class which mocks [ClippingToolState]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockClippingToolState extends _i1.Mock implements _i25.ClippingToolState { + MockClippingToolState() { + _i1.throwOnMissingStub(this); + } + + @override + bool get hasActiveClipPath => (super.noSuchMethod( + Invocation.getter(#hasActiveClipPath), + returnValue: false, + ) as bool); + + @override + _i14.AutoDisposeNotifierProviderRef get ref => (super.noSuchMethod( + Invocation.getter(#ref), + returnValue: _FakeAutoDisposeNotifierProviderRef_16( + this, + Invocation.getter(#ref), + ), + ) as _i14.AutoDisposeNotifierProviderRef); + + @override + bool get state => (super.noSuchMethod( + Invocation.getter(#state), + returnValue: false, + ) as bool); + + @override + set state(bool? value) => super.noSuchMethod( + Invocation.setter( + #state, + value, + ), + returnValueForMissingStub: null, + ); + + @override + bool build() => (super.noSuchMethod( + Invocation.method( + #build, + [], + ), + returnValue: false, + ) as bool); + + @override + void setHasActiveClipPath(bool? hasActive) => super.noSuchMethod( + Invocation.method( + #setHasActiveClipPath, + [hasActive], + ), + returnValueForMissingStub: null, + ); + + @override + void clearClipPath() => super.noSuchMethod( + Invocation.method( + #clearClipPath, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void listenSelf( + void Function( + bool?, + bool, + )? listener, { + void Function( + Object, + StackTrace, + )? onError, + }) => + super.noSuchMethod( + Invocation.method( + #listenSelf, + [listener], + {#onError: onError}, + ), + returnValueForMissingStub: null, + ); + + @override + bool updateShouldNotify( + bool? previous, + bool? next, + ) => + (super.noSuchMethod( + Invocation.method( + #updateShouldNotify, + [ + previous, + next, + ], + ), + returnValue: false, + ) as bool); +} + +/// A class which mocks [PathWithActionHistory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPathWithActionHistory extends _i1.Mock + implements _i12.PathWithActionHistory { + MockPathWithActionHistory() { + _i1.throwOnMissingStub(this); + } + + @override + _i13.Path get path => (super.noSuchMethod( + Invocation.getter(#path), + returnValue: _FakePath_17( + this, + Invocation.getter(#path), + ), + ) as _i13.Path); + + @override + List<_i12.PathAction> get actions => (super.noSuchMethod( + Invocation.getter(#actions), + returnValue: <_i12.PathAction>[], + ) as List<_i12.PathAction>); + + @override + void moveTo( + double? x, + double? y, + ) => + super.noSuchMethod( + Invocation.method( + #moveTo, + [ + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void lineTo( + double? x, + double? y, + ) => + super.noSuchMethod( + Invocation.method( + #lineTo, + [ + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void close() => super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValueForMissingStub: null, + ); + + @override + Map toJson() => (super.noSuchMethod( + Invocation.method( + #toJson, + [], + ), + returnValue: {}, + ) as Map); +} + +/// A class which mocks [ClipPathCommand]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockClipPathCommand extends _i1.Mock implements _i5.ClipPathCommand { + MockClipPathCommand() { + _i1.throwOnMissingStub(this); + } + + @override + String get type => (super.noSuchMethod( + Invocation.getter(#type), + returnValue: _i22.dummyValue( + this, + Invocation.getter(#type), + ), + ) as String); + + @override + int get version => (super.noSuchMethod( + Invocation.getter(#version), + returnValue: 0, + ) as int); + + @override + _i12.PathWithActionHistory get path => (super.noSuchMethod( + Invocation.getter(#path), + returnValue: _FakePathWithActionHistory_10( + this, + Invocation.getter(#path), + ), + ) as _i12.PathWithActionHistory); + + @override + List get props => (super.noSuchMethod( + Invocation.getter(#props), + returnValue: [], + ) as List); + + @override + _i13.Paint get paint => (super.noSuchMethod( + Invocation.getter(#paint), + returnValue: _i22.dummyValue<_i13.Paint>( + this, + Invocation.getter(#paint), + ), + ) as _i13.Paint); + + @override + void call(_i13.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #call, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + Map toJson() => (super.noSuchMethod( + Invocation.method( + #toJson, + [], + ), + returnValue: {}, + ) as Map); +} + +/// A class which mocks [ClipAreaCommand]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockClipAreaCommand extends _i1.Mock implements _i11.ClipAreaCommand { + MockClipAreaCommand() { + _i1.throwOnMissingStub(this); + } + + @override + String get type => (super.noSuchMethod( + Invocation.getter(#type), + returnValue: _i22.dummyValue( + this, + Invocation.getter(#type), + ), + ) as String); + + @override + int get version => (super.noSuchMethod( + Invocation.getter(#version), + returnValue: 0, + ) as int); + + @override + _i12.PathWithActionHistory get clipPathData => (super.noSuchMethod( + Invocation.getter(#clipPathData), + returnValue: _FakePathWithActionHistory_10( + this, + Invocation.getter(#clipPathData), + ), + ) as _i12.PathWithActionHistory); + + @override + List get props => (super.noSuchMethod( + Invocation.getter(#props), + returnValue: [], + ) as List); + + @override + _i13.Paint get paint => (super.noSuchMethod( + Invocation.getter(#paint), + returnValue: _i22.dummyValue<_i13.Paint>( + this, + Invocation.getter(#paint), + ), + ) as _i13.Paint); + + @override + void call(_i13.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #call, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + Map toJson() => (super.noSuchMethod( + Invocation.method( + #toJson, + [], + ), + returnValue: {}, + ) as Map); +} diff --git a/test/unit/tools/text_tool_test.mocks.dart b/test/unit/tools/text_tool_test.mocks.dart index c650d49e..2992615c 100644 --- a/test/unit/tools/text_tool_test.mocks.dart +++ b/test/unit/tools/text_tool_test.mocks.dart @@ -3,41 +3,45 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:typed_data' as _i19; -import 'dart:ui' as _i11; +import 'dart:typed_data' as _i21; +import 'dart:ui' as _i13; -import 'package:flutter/material.dart' as _i12; +import 'package:flutter/material.dart' as _i14; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i18; +import 'package:mockito/src/dummies.dart' as _i20; import 'package:paintroid/core/commands/command_factory/command_factory.dart' - as _i17; + as _i19; import 'package:paintroid/core/commands/command_implementation/command.dart' as _i2; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_area_command.dart' + as _i11; +import 'package:paintroid/core/commands/command_implementation/graphic/clip_path_command.dart' + as _i5; import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart' - as _i15; + as _i17; import 'package:paintroid/core/commands/command_implementation/graphic/line_command.dart' - as _i5; + as _i6; import 'package:paintroid/core/commands/command_implementation/graphic/path_command.dart' as _i4; import 'package:paintroid/core/commands/command_implementation/graphic/shape/ellipse_shape_command.dart' - as _i7; + as _i8; import 'package:paintroid/core/commands/command_implementation/graphic/shape/square_shape_command.dart' - as _i6; + as _i7; import 'package:paintroid/core/commands/command_implementation/graphic/spray_command.dart' - as _i9; + as _i10; import 'package:paintroid/core/commands/command_implementation/graphic/text_command.dart' - as _i8; + as _i9; import 'package:paintroid/core/commands/command_manager/command_manager.dart' - as _i14; + as _i16; import 'package:paintroid/core/commands/graphic_factory/graphic_factory.dart' - as _i20; -import 'package:paintroid/core/commands/path_with_action_history.dart' as _i13; -import 'package:paintroid/core/enums/bounding_box_action.dart' as _i22; -import 'package:paintroid/core/enums/bounding_box_resize_action.dart' as _i23; -import 'package:paintroid/core/tools/bounding_box.dart' as _i21; -import 'package:paintroid/core/tools/line_tool/vertex_stack.dart' as _i16; + as _i22; +import 'package:paintroid/core/commands/path_with_action_history.dart' as _i15; +import 'package:paintroid/core/enums/bounding_box_action.dart' as _i24; +import 'package:paintroid/core/enums/bounding_box_resize_action.dart' as _i25; +import 'package:paintroid/core/tools/bounding_box.dart' as _i23; +import 'package:paintroid/core/tools/line_tool/vertex_stack.dart' as _i18; import 'package:paintroid/core/tools/tool_data.dart' as _i3; -import 'package:riverpod/src/internals.dart' as _i10; +import 'package:riverpod/src/internals.dart' as _i12; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -83,8 +87,9 @@ class _FakePathCommand_2 extends _i1.SmartFake implements _i4.PathCommand { ); } -class _FakeLineCommand_3 extends _i1.SmartFake implements _i5.LineCommand { - _FakeLineCommand_3( +class _FakeClipPathCommand_3 extends _i1.SmartFake + implements _i5.ClipPathCommand { + _FakeClipPathCommand_3( Object parent, Invocation parentInvocation, ) : super( @@ -93,9 +98,8 @@ class _FakeLineCommand_3 extends _i1.SmartFake implements _i5.LineCommand { ); } -class _FakeSquareShapeCommand_4 extends _i1.SmartFake - implements _i6.SquareShapeCommand { - _FakeSquareShapeCommand_4( +class _FakeLineCommand_4 extends _i1.SmartFake implements _i6.LineCommand { + _FakeLineCommand_4( Object parent, Invocation parentInvocation, ) : super( @@ -104,9 +108,9 @@ class _FakeSquareShapeCommand_4 extends _i1.SmartFake ); } -class _FakeEllipseShapeCommand_5 extends _i1.SmartFake - implements _i7.EllipseShapeCommand { - _FakeEllipseShapeCommand_5( +class _FakeSquareShapeCommand_5 extends _i1.SmartFake + implements _i7.SquareShapeCommand { + _FakeSquareShapeCommand_5( Object parent, Invocation parentInvocation, ) : super( @@ -115,8 +119,9 @@ class _FakeEllipseShapeCommand_5 extends _i1.SmartFake ); } -class _FakeTextCommand_6 extends _i1.SmartFake implements _i8.TextCommand { - _FakeTextCommand_6( +class _FakeEllipseShapeCommand_6 extends _i1.SmartFake + implements _i8.EllipseShapeCommand { + _FakeEllipseShapeCommand_6( Object parent, Invocation parentInvocation, ) : super( @@ -125,8 +130,8 @@ class _FakeTextCommand_6 extends _i1.SmartFake implements _i8.TextCommand { ); } -class _FakeSprayCommand_7 extends _i1.SmartFake implements _i9.SprayCommand { - _FakeSprayCommand_7( +class _FakeTextCommand_7 extends _i1.SmartFake implements _i9.TextCommand { + _FakeTextCommand_7( Object parent, Invocation parentInvocation, ) : super( @@ -135,9 +140,8 @@ class _FakeSprayCommand_7 extends _i1.SmartFake implements _i9.SprayCommand { ); } -class _FakeProviderContainer_8 extends _i1.SmartFake - implements _i10.ProviderContainer { - _FakeProviderContainer_8( +class _FakeSprayCommand_8 extends _i1.SmartFake implements _i10.SprayCommand { + _FakeSprayCommand_8( Object parent, Invocation parentInvocation, ) : super( @@ -146,8 +150,9 @@ class _FakeProviderContainer_8 extends _i1.SmartFake ); } -class _FakeKeepAliveLink_9 extends _i1.SmartFake implements _i10.KeepAliveLink { - _FakeKeepAliveLink_9( +class _FakeClipAreaCommand_9 extends _i1.SmartFake + implements _i11.ClipAreaCommand { + _FakeClipAreaCommand_9( Object parent, Invocation parentInvocation, ) : super( @@ -156,9 +161,9 @@ class _FakeKeepAliveLink_9 extends _i1.SmartFake implements _i10.KeepAliveLink { ); } -class _FakeProviderSubscription_10 extends _i1.SmartFake - implements _i10.ProviderSubscription { - _FakeProviderSubscription_10( +class _FakeProviderContainer_10 extends _i1.SmartFake + implements _i12.ProviderContainer { + _FakeProviderContainer_10( Object parent, Invocation parentInvocation, ) : super( @@ -167,8 +172,9 @@ class _FakeProviderSubscription_10 extends _i1.SmartFake ); } -class _FakeRect_11 extends _i1.SmartFake implements _i11.Rect { - _FakeRect_11( +class _FakeKeepAliveLink_11 extends _i1.SmartFake + implements _i12.KeepAliveLink { + _FakeKeepAliveLink_11( Object parent, Invocation parentInvocation, ) : super( @@ -177,8 +183,9 @@ class _FakeRect_11 extends _i1.SmartFake implements _i11.Rect { ); } -class _FakeOffset_12 extends _i1.SmartFake implements _i11.Offset { - _FakeOffset_12( +class _FakeProviderSubscription_12 extends _i1.SmartFake + implements _i12.ProviderSubscription { + _FakeProviderSubscription_12( Object parent, Invocation parentInvocation, ) : super( @@ -187,8 +194,28 @@ class _FakeOffset_12 extends _i1.SmartFake implements _i11.Offset { ); } -class _FakeTextStyle_13 extends _i1.SmartFake implements _i12.TextStyle { - _FakeTextStyle_13( +class _FakeRect_13 extends _i1.SmartFake implements _i13.Rect { + _FakeRect_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeOffset_14 extends _i1.SmartFake implements _i13.Offset { + _FakeOffset_14( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeTextStyle_15 extends _i1.SmartFake implements _i14.TextStyle { + _FakeTextStyle_15( Object parent, Invocation parentInvocation, ) : super( @@ -198,13 +225,13 @@ class _FakeTextStyle_13 extends _i1.SmartFake implements _i12.TextStyle { @override String toString( - {_i12.DiagnosticLevel? minLevel = _i12.DiagnosticLevel.info}) => + {_i14.DiagnosticLevel? minLevel = _i14.DiagnosticLevel.info}) => super.toString(); } -class _FakePathWithActionHistory_14 extends _i1.SmartFake - implements _i13.PathWithActionHistory { - _FakePathWithActionHistory_14( +class _FakePathWithActionHistory_16 extends _i1.SmartFake + implements _i15.PathWithActionHistory { + _FakePathWithActionHistory_16( Object parent, Invocation parentInvocation, ) : super( @@ -213,9 +240,9 @@ class _FakePathWithActionHistory_14 extends _i1.SmartFake ); } -class _FakePictureRecorder_15 extends _i1.SmartFake - implements _i11.PictureRecorder { - _FakePictureRecorder_15( +class _FakePictureRecorder_17 extends _i1.SmartFake + implements _i13.PictureRecorder { + _FakePictureRecorder_17( Object parent, Invocation parentInvocation, ) : super( @@ -224,8 +251,8 @@ class _FakePictureRecorder_15 extends _i1.SmartFake ); } -class _FakeCanvas_16 extends _i1.SmartFake implements _i11.Canvas { - _FakeCanvas_16( +class _FakeCanvas_18 extends _i1.SmartFake implements _i13.Canvas { + _FakeCanvas_18( Object parent, Invocation parentInvocation, ) : super( @@ -237,7 +264,7 @@ class _FakeCanvas_16 extends _i1.SmartFake implements _i11.Canvas { /// A class which mocks [CommandManager]. /// /// See the documentation for Mockito's code generation for more information. -class MockCommandManager extends _i1.Mock implements _i14.CommandManager { +class MockCommandManager extends _i1.Mock implements _i16.CommandManager { MockCommandManager() { _i1.throwOnMissingStub(this); } @@ -255,7 +282,7 @@ class MockCommandManager extends _i1.Mock implements _i14.CommandManager { ) as List<_i2.Command>); @override - void addGraphicCommand(_i15.GraphicCommand? command) => super.noSuchMethod( + void addGraphicCommand(_i17.GraphicCommand? command) => super.noSuchMethod( Invocation.method( #addGraphicCommand, [command], @@ -263,6 +290,15 @@ class MockCommandManager extends _i1.Mock implements _i14.CommandManager { returnValueForMissingStub: null, ); + @override + void removeCommand(_i2.Command? commandToRemove) => super.noSuchMethod( + Invocation.method( + #removeCommand, + [commandToRemove], + ), + returnValueForMissingStub: null, + ); + @override void setUndoStack(List<_i2.Command>? commands) => super.noSuchMethod( Invocation.method( @@ -273,7 +309,7 @@ class MockCommandManager extends _i1.Mock implements _i14.CommandManager { ); @override - void executeLastCommand(_i11.Canvas? canvas) => super.noSuchMethod( + void executeLastCommand(_i13.Canvas? canvas) => super.noSuchMethod( Invocation.method( #executeLastCommand, [canvas], @@ -282,7 +318,7 @@ class MockCommandManager extends _i1.Mock implements _i14.CommandManager { ); @override - void executeAllCommands(_i11.Canvas? canvas) => super.noSuchMethod( + void executeAllCommands(_i13.Canvas? canvas) => super.noSuchMethod( Invocation.method( #executeAllCommands, [canvas], @@ -321,9 +357,9 @@ class MockCommandManager extends _i1.Mock implements _i14.CommandManager { @override void drawLineToolGhostPaths( - _i11.Canvas? canvas, - _i5.LineCommand? ingoingGhostPathCommand, - _i5.LineCommand? outgoingGhostPathCommand, + _i13.Canvas? canvas, + _i6.LineCommand? ingoingGhostPathCommand, + _i6.LineCommand? outgoingGhostPathCommand, ) => super.noSuchMethod( Invocation.method( @@ -339,8 +375,8 @@ class MockCommandManager extends _i1.Mock implements _i14.CommandManager { @override void drawLineToolVertices( - _i11.Canvas? canvas, - _i16.VertexStack? vertexStack, + _i13.Canvas? canvas, + _i18.VertexStack? vertexStack, ) => super.noSuchMethod( Invocation.method( @@ -378,7 +414,7 @@ class MockCommandManager extends _i1.Mock implements _i14.CommandManager { ); @override - _i3.ToolData getNextTool(_i14.ActionType? actionType) => (super.noSuchMethod( + _i3.ToolData getNextTool(_i16.ActionType? actionType) => (super.noSuchMethod( Invocation.method( #getNextTool, [actionType], @@ -393,27 +429,27 @@ class MockCommandManager extends _i1.Mock implements _i14.CommandManager { ) as _i3.ToolData); @override - List<_i5.LineCommand> getTopLineCommandSequence() => (super.noSuchMethod( + List<_i6.LineCommand> getTopLineCommandSequence() => (super.noSuchMethod( Invocation.method( #getTopLineCommandSequence, [], ), - returnValue: <_i5.LineCommand>[], - ) as List<_i5.LineCommand>); + returnValue: <_i6.LineCommand>[], + ) as List<_i6.LineCommand>); } /// A class which mocks [CommandFactory]. /// /// See the documentation for Mockito's code generation for more information. -class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { +class MockCommandFactory extends _i1.Mock implements _i19.CommandFactory { MockCommandFactory() { _i1.throwOnMissingStub(this); } @override _i4.PathCommand createPathCommand( - _i13.PathWithActionHistory? path, - _i11.Paint? paint, + _i15.PathWithActionHistory? path, + _i13.Paint? paint, ) => (super.noSuchMethod( Invocation.method( @@ -436,11 +472,46 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { ) as _i4.PathCommand); @override - _i5.LineCommand createLineCommand( - _i13.PathWithActionHistory? path, - _i11.Paint? paint, - _i11.Offset? startPoint, - _i11.Offset? endPoint, + _i5.ClipPathCommand createClipPathCommand( + _i15.PathWithActionHistory? path, + _i13.Paint? paint, { + _i13.Offset? startPoint, + _i13.Offset? endPoint, + }) => + (super.noSuchMethod( + Invocation.method( + #createClipPathCommand, + [ + path, + paint, + ], + { + #startPoint: startPoint, + #endPoint: endPoint, + }, + ), + returnValue: _FakeClipPathCommand_3( + this, + Invocation.method( + #createClipPathCommand, + [ + path, + paint, + ], + { + #startPoint: startPoint, + #endPoint: endPoint, + }, + ), + ), + ) as _i5.ClipPathCommand); + + @override + _i6.LineCommand createLineCommand( + _i15.PathWithActionHistory? path, + _i13.Paint? paint, + _i13.Offset? startPoint, + _i13.Offset? endPoint, ) => (super.noSuchMethod( Invocation.method( @@ -452,7 +523,7 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { endPoint, ], ), - returnValue: _FakeLineCommand_3( + returnValue: _FakeLineCommand_4( this, Invocation.method( #createLineCommand, @@ -464,15 +535,15 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { ], ), ), - ) as _i5.LineCommand); + ) as _i6.LineCommand); @override - _i6.SquareShapeCommand createSquareShapeCommand( - _i11.Paint? paint, - _i11.Offset? topLeft, - _i11.Offset? topRight, - _i11.Offset? bottomLeft, - _i11.Offset? bottomRight, + _i7.SquareShapeCommand createSquareShapeCommand( + _i13.Paint? paint, + _i13.Offset? topLeft, + _i13.Offset? topRight, + _i13.Offset? bottomLeft, + _i13.Offset? bottomRight, ) => (super.noSuchMethod( Invocation.method( @@ -485,7 +556,7 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { bottomRight, ], ), - returnValue: _FakeSquareShapeCommand_4( + returnValue: _FakeSquareShapeCommand_5( this, Invocation.method( #createSquareShapeCommand, @@ -498,14 +569,14 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { ], ), ), - ) as _i6.SquareShapeCommand); + ) as _i7.SquareShapeCommand); @override - _i7.EllipseShapeCommand createEllipseShapeCommand( - _i11.Paint? paint, + _i8.EllipseShapeCommand createEllipseShapeCommand( + _i13.Paint? paint, double? radiusX, double? radiusY, - _i11.Offset? center, + _i13.Offset? center, double? angle, ) => (super.noSuchMethod( @@ -519,7 +590,7 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { angle, ], ), - returnValue: _FakeEllipseShapeCommand_5( + returnValue: _FakeEllipseShapeCommand_6( this, Invocation.method( #createEllipseShapeCommand, @@ -532,15 +603,15 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { ], ), ), - ) as _i7.EllipseShapeCommand); + ) as _i8.EllipseShapeCommand); @override - _i8.TextCommand createTextCommand( - _i11.Offset? point, + _i9.TextCommand createTextCommand( + _i13.Offset? point, String? text, - _i12.TextStyle? style, + _i14.TextStyle? style, double? fontSize, - _i11.Paint? paint, + _i13.Paint? paint, double? rotationAngle, { double? scaleX = 1.0, double? scaleY = 1.0, @@ -561,7 +632,7 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { #scaleY: scaleY, }, ), - returnValue: _FakeTextCommand_6( + returnValue: _FakeTextCommand_7( this, Invocation.method( #createTextCommand, @@ -579,12 +650,12 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { }, ), ), - ) as _i8.TextCommand); + ) as _i9.TextCommand); @override - _i9.SprayCommand createSprayCommand( - List<_i11.Offset>? points, - _i11.Paint? paint, + _i10.SprayCommand createSprayCommand( + List<_i13.Offset>? points, + _i13.Paint? paint, ) => (super.noSuchMethod( Invocation.method( @@ -594,7 +665,7 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { paint, ], ), - returnValue: _FakeSprayCommand_7( + returnValue: _FakeSprayCommand_8( this, Invocation.method( #createSprayCommand, @@ -604,34 +675,59 @@ class MockCommandFactory extends _i1.Mock implements _i17.CommandFactory { ], ), ), - ) as _i9.SprayCommand); + ) as _i10.SprayCommand); + + @override + _i11.ClipAreaCommand createClipAreaCommand( + _i15.PathWithActionHistory? path, + _i13.Paint? paint, + ) => + (super.noSuchMethod( + Invocation.method( + #createClipAreaCommand, + [ + path, + paint, + ], + ), + returnValue: _FakeClipAreaCommand_9( + this, + Invocation.method( + #createClipAreaCommand, + [ + path, + paint, + ], + ), + ), + ) as _i11.ClipAreaCommand); } /// A class which mocks [Ref]. /// /// See the documentation for Mockito's code generation for more information. class MockRef extends _i1.Mock - implements _i10.Ref { + implements _i12.Ref { MockRef() { _i1.throwOnMissingStub(this); } @override - _i10.ProviderContainer get container => (super.noSuchMethod( + _i12.ProviderContainer get container => (super.noSuchMethod( Invocation.getter(#container), - returnValue: _FakeProviderContainer_8( + returnValue: _FakeProviderContainer_10( this, Invocation.getter(#container), ), - ) as _i10.ProviderContainer); + ) as _i12.ProviderContainer); @override - T refresh(_i10.Refreshable? provider) => (super.noSuchMethod( + T refresh(_i12.Refreshable? provider) => (super.noSuchMethod( Invocation.method( #refresh, [provider], ), - returnValue: _i18.dummyValue( + returnValue: _i20.dummyValue( this, Invocation.method( #refresh, @@ -641,7 +737,7 @@ class MockRef extends _i1.Mock ) as T); @override - void invalidate(_i10.ProviderOrFamily? provider) => super.noSuchMethod( + void invalidate(_i12.ProviderOrFamily? provider) => super.noSuchMethod( Invocation.method( #invalidate, [provider], @@ -733,12 +829,12 @@ class MockRef extends _i1.Mock ); @override - T read(_i10.ProviderListenable? provider) => (super.noSuchMethod( + T read(_i12.ProviderListenable? provider) => (super.noSuchMethod( Invocation.method( #read, [provider], ), - returnValue: _i18.dummyValue( + returnValue: _i20.dummyValue( this, Invocation.method( #read, @@ -748,7 +844,7 @@ class MockRef extends _i1.Mock ) as T); @override - bool exists(_i10.ProviderBase? provider) => (super.noSuchMethod( + bool exists(_i12.ProviderBase? provider) => (super.noSuchMethod( Invocation.method( #exists, [provider], @@ -757,12 +853,12 @@ class MockRef extends _i1.Mock ) as bool); @override - T watch(_i10.ProviderListenable? provider) => (super.noSuchMethod( + T watch(_i12.ProviderListenable? provider) => (super.noSuchMethod( Invocation.method( #watch, [provider], ), - returnValue: _i18.dummyValue( + returnValue: _i20.dummyValue( this, Invocation.method( #watch, @@ -772,23 +868,23 @@ class MockRef extends _i1.Mock ) as T); @override - _i10.KeepAliveLink keepAlive() => (super.noSuchMethod( + _i12.KeepAliveLink keepAlive() => (super.noSuchMethod( Invocation.method( #keepAlive, [], ), - returnValue: _FakeKeepAliveLink_9( + returnValue: _FakeKeepAliveLink_11( this, Invocation.method( #keepAlive, [], ), ), - ) as _i10.KeepAliveLink); + ) as _i12.KeepAliveLink); @override - _i10.ProviderSubscription listen( - _i10.ProviderListenable? provider, + _i12.ProviderSubscription listen( + _i12.ProviderListenable? provider, void Function( T?, T, @@ -811,7 +907,7 @@ class MockRef extends _i1.Mock #fireImmediately: fireImmediately, }, ), - returnValue: _FakeProviderSubscription_10( + returnValue: _FakeProviderSubscription_12( this, Invocation.method( #listen, @@ -825,13 +921,13 @@ class MockRef extends _i1.Mock }, ), ), - ) as _i10.ProviderSubscription); + ) as _i12.ProviderSubscription); } /// A class which mocks [Canvas]. /// /// See the documentation for Mockito's code generation for more information. -class MockCanvas extends _i1.Mock implements _i11.Canvas { +class MockCanvas extends _i1.Mock implements _i13.Canvas { MockCanvas() { _i1.throwOnMissingStub(this); } @@ -847,8 +943,8 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void saveLayer( - _i11.Rect? bounds, - _i11.Paint? paint, + _i13.Rect? bounds, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -946,7 +1042,7 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { ); @override - void transform(_i19.Float64List? matrix4) => super.noSuchMethod( + void transform(_i21.Float64List? matrix4) => super.noSuchMethod( Invocation.method( #transform, [matrix4], @@ -955,18 +1051,18 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { ); @override - _i19.Float64List getTransform() => (super.noSuchMethod( + _i21.Float64List getTransform() => (super.noSuchMethod( Invocation.method( #getTransform, [], ), - returnValue: _i19.Float64List(0), - ) as _i19.Float64List); + returnValue: _i21.Float64List(0), + ) as _i21.Float64List); @override void clipRect( - _i11.Rect? rect, { - _i11.ClipOp? clipOp = _i11.ClipOp.intersect, + _i13.Rect? rect, { + _i13.ClipOp? clipOp = _i13.ClipOp.intersect, bool? doAntiAlias = true, }) => super.noSuchMethod( @@ -983,7 +1079,7 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void clipRRect( - _i11.RRect? rrect, { + _i13.RRect? rrect, { bool? doAntiAlias = true, }) => super.noSuchMethod( @@ -997,7 +1093,7 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void clipPath( - _i11.Path? path, { + _i13.Path? path, { bool? doAntiAlias = true, }) => super.noSuchMethod( @@ -1010,39 +1106,39 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { ); @override - _i11.Rect getLocalClipBounds() => (super.noSuchMethod( + _i13.Rect getLocalClipBounds() => (super.noSuchMethod( Invocation.method( #getLocalClipBounds, [], ), - returnValue: _FakeRect_11( + returnValue: _FakeRect_13( this, Invocation.method( #getLocalClipBounds, [], ), ), - ) as _i11.Rect); + ) as _i13.Rect); @override - _i11.Rect getDestinationClipBounds() => (super.noSuchMethod( + _i13.Rect getDestinationClipBounds() => (super.noSuchMethod( Invocation.method( #getDestinationClipBounds, [], ), - returnValue: _FakeRect_11( + returnValue: _FakeRect_13( this, Invocation.method( #getDestinationClipBounds, [], ), ), - ) as _i11.Rect); + ) as _i13.Rect); @override void drawColor( - _i11.Color? color, - _i11.BlendMode? blendMode, + _i13.Color? color, + _i13.BlendMode? blendMode, ) => super.noSuchMethod( Invocation.method( @@ -1057,9 +1153,9 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawLine( - _i11.Offset? p1, - _i11.Offset? p2, - _i11.Paint? paint, + _i13.Offset? p1, + _i13.Offset? p2, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1074,7 +1170,7 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { ); @override - void drawPaint(_i11.Paint? paint) => super.noSuchMethod( + void drawPaint(_i13.Paint? paint) => super.noSuchMethod( Invocation.method( #drawPaint, [paint], @@ -1084,8 +1180,8 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawRect( - _i11.Rect? rect, - _i11.Paint? paint, + _i13.Rect? rect, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1100,8 +1196,8 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawRRect( - _i11.RRect? rrect, - _i11.Paint? paint, + _i13.RRect? rrect, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1116,9 +1212,9 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawDRRect( - _i11.RRect? outer, - _i11.RRect? inner, - _i11.Paint? paint, + _i13.RRect? outer, + _i13.RRect? inner, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1134,8 +1230,8 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawOval( - _i11.Rect? rect, - _i11.Paint? paint, + _i13.Rect? rect, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1150,9 +1246,9 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawCircle( - _i11.Offset? c, + _i13.Offset? c, double? radius, - _i11.Paint? paint, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1168,11 +1264,11 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawArc( - _i11.Rect? rect, + _i13.Rect? rect, double? startAngle, double? sweepAngle, bool? useCenter, - _i11.Paint? paint, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1190,8 +1286,8 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawPath( - _i11.Path? path, - _i11.Paint? paint, + _i13.Path? path, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1206,9 +1302,9 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawImage( - _i11.Image? image, - _i11.Offset? offset, - _i11.Paint? paint, + _i13.Image? image, + _i13.Offset? offset, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1224,10 +1320,10 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawImageRect( - _i11.Image? image, - _i11.Rect? src, - _i11.Rect? dst, - _i11.Paint? paint, + _i13.Image? image, + _i13.Rect? src, + _i13.Rect? dst, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1244,10 +1340,10 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawImageNine( - _i11.Image? image, - _i11.Rect? center, - _i11.Rect? dst, - _i11.Paint? paint, + _i13.Image? image, + _i13.Rect? center, + _i13.Rect? dst, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1263,7 +1359,7 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { ); @override - void drawPicture(_i11.Picture? picture) => super.noSuchMethod( + void drawPicture(_i13.Picture? picture) => super.noSuchMethod( Invocation.method( #drawPicture, [picture], @@ -1273,8 +1369,8 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawParagraph( - _i11.Paragraph? paragraph, - _i11.Offset? offset, + _i13.Paragraph? paragraph, + _i13.Offset? offset, ) => super.noSuchMethod( Invocation.method( @@ -1289,9 +1385,9 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawPoints( - _i11.PointMode? pointMode, - List<_i11.Offset>? points, - _i11.Paint? paint, + _i13.PointMode? pointMode, + List<_i13.Offset>? points, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1307,9 +1403,9 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawRawPoints( - _i11.PointMode? pointMode, - _i19.Float32List? points, - _i11.Paint? paint, + _i13.PointMode? pointMode, + _i21.Float32List? points, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1325,9 +1421,9 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawVertices( - _i11.Vertices? vertices, - _i11.BlendMode? blendMode, - _i11.Paint? paint, + _i13.Vertices? vertices, + _i13.BlendMode? blendMode, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1343,13 +1439,13 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawAtlas( - _i11.Image? atlas, - List<_i11.RSTransform>? transforms, - List<_i11.Rect>? rects, - List<_i11.Color>? colors, - _i11.BlendMode? blendMode, - _i11.Rect? cullRect, - _i11.Paint? paint, + _i13.Image? atlas, + List<_i13.RSTransform>? transforms, + List<_i13.Rect>? rects, + List<_i13.Color>? colors, + _i13.BlendMode? blendMode, + _i13.Rect? cullRect, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1369,13 +1465,13 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawRawAtlas( - _i11.Image? atlas, - _i19.Float32List? rstTransforms, - _i19.Float32List? rects, - _i19.Int32List? colors, - _i11.BlendMode? blendMode, - _i11.Rect? cullRect, - _i11.Paint? paint, + _i13.Image? atlas, + _i21.Float32List? rstTransforms, + _i21.Float32List? rects, + _i21.Int32List? colors, + _i13.BlendMode? blendMode, + _i13.Rect? cullRect, + _i13.Paint? paint, ) => super.noSuchMethod( Invocation.method( @@ -1395,8 +1491,8 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { @override void drawShadow( - _i11.Path? path, - _i11.Color? color, + _i13.Path? path, + _i13.Color? color, double? elevation, bool? transparentOccluder, ) => @@ -1417,22 +1513,22 @@ class MockCanvas extends _i1.Mock implements _i11.Canvas { /// A class which mocks [TextCommand]. /// /// See the documentation for Mockito's code generation for more information. -class MockTextCommand extends _i1.Mock implements _i8.TextCommand { +class MockTextCommand extends _i1.Mock implements _i9.TextCommand { MockTextCommand() { _i1.throwOnMissingStub(this); } @override - _i11.Offset get point => (super.noSuchMethod( + _i13.Offset get point => (super.noSuchMethod( Invocation.getter(#point), - returnValue: _FakeOffset_12( + returnValue: _FakeOffset_14( this, Invocation.getter(#point), ), - ) as _i11.Offset); + ) as _i13.Offset); @override - set point(_i11.Offset? _point) => super.noSuchMethod( + set point(_i13.Offset? _point) => super.noSuchMethod( Invocation.setter( #point, _point, @@ -1441,18 +1537,18 @@ class MockTextCommand extends _i1.Mock implements _i8.TextCommand { ); @override - _i12.TextStyle get style => (super.noSuchMethod( + _i14.TextStyle get style => (super.noSuchMethod( Invocation.getter(#style), - returnValue: _FakeTextStyle_13( + returnValue: _FakeTextStyle_15( this, Invocation.getter(#style), ), - ) as _i12.TextStyle); + ) as _i14.TextStyle); @override String get text => (super.noSuchMethod( Invocation.getter(#text), - returnValue: _i18.dummyValue( + returnValue: _i20.dummyValue( this, Invocation.getter(#text), ), @@ -1473,7 +1569,7 @@ class MockTextCommand extends _i1.Mock implements _i8.TextCommand { @override String get type => (super.noSuchMethod( Invocation.getter(#type), - returnValue: _i18.dummyValue( + returnValue: _i20.dummyValue( this, Invocation.getter(#type), ), @@ -1510,16 +1606,16 @@ class MockTextCommand extends _i1.Mock implements _i8.TextCommand { ) as bool); @override - _i11.Paint get paint => (super.noSuchMethod( + _i13.Paint get paint => (super.noSuchMethod( Invocation.getter(#paint), - returnValue: _i18.dummyValue<_i11.Paint>( + returnValue: _i20.dummyValue<_i13.Paint>( this, Invocation.getter(#paint), ), - ) as _i11.Paint); + ) as _i13.Paint); @override - void call(_i11.Canvas? canvas) => super.noSuchMethod( + void call(_i13.Canvas? canvas) => super.noSuchMethod( Invocation.method( #call, [canvas], @@ -1540,108 +1636,108 @@ class MockTextCommand extends _i1.Mock implements _i8.TextCommand { /// A class which mocks [GraphicFactory]. /// /// See the documentation for Mockito's code generation for more information. -class MockGraphicFactory extends _i1.Mock implements _i20.GraphicFactory { +class MockGraphicFactory extends _i1.Mock implements _i22.GraphicFactory { MockGraphicFactory() { _i1.throwOnMissingStub(this); } @override - _i11.Paint createPaint() => (super.noSuchMethod( + _i13.Paint createPaint() => (super.noSuchMethod( Invocation.method( #createPaint, [], ), - returnValue: _i18.dummyValue<_i11.Paint>( + returnValue: _i20.dummyValue<_i13.Paint>( this, Invocation.method( #createPaint, [], ), ), - ) as _i11.Paint); + ) as _i13.Paint); @override - _i13.PathWithActionHistory createPathWithActionHistory() => + _i15.PathWithActionHistory createPathWithActionHistory() => (super.noSuchMethod( Invocation.method( #createPathWithActionHistory, [], ), - returnValue: _FakePathWithActionHistory_14( + returnValue: _FakePathWithActionHistory_16( this, Invocation.method( #createPathWithActionHistory, [], ), ), - ) as _i13.PathWithActionHistory); + ) as _i15.PathWithActionHistory); @override - _i11.PictureRecorder createPictureRecorder() => (super.noSuchMethod( + _i13.PictureRecorder createPictureRecorder() => (super.noSuchMethod( Invocation.method( #createPictureRecorder, [], ), - returnValue: _FakePictureRecorder_15( + returnValue: _FakePictureRecorder_17( this, Invocation.method( #createPictureRecorder, [], ), ), - ) as _i11.PictureRecorder); + ) as _i13.PictureRecorder); @override - _i11.Canvas createCanvasWithRecorder(_i11.PictureRecorder? recorder) => + _i13.Canvas createCanvasWithRecorder(_i13.PictureRecorder? recorder) => (super.noSuchMethod( Invocation.method( #createCanvasWithRecorder, [recorder], ), - returnValue: _FakeCanvas_16( + returnValue: _FakeCanvas_18( this, Invocation.method( #createCanvasWithRecorder, [recorder], ), ), - ) as _i11.Canvas); + ) as _i13.Canvas); @override - _i11.Paint copyPaint(_i11.Paint? original) => (super.noSuchMethod( + _i13.Paint copyPaint(_i13.Paint? original) => (super.noSuchMethod( Invocation.method( #copyPaint, [original], ), - returnValue: _i18.dummyValue<_i11.Paint>( + returnValue: _i20.dummyValue<_i13.Paint>( this, Invocation.method( #copyPaint, [original], ), ), - ) as _i11.Paint); + ) as _i13.Paint); } /// A class which mocks [BoundingBox]. /// /// See the documentation for Mockito's code generation for more information. -class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { +class MockBoundingBox extends _i1.Mock implements _i23.BoundingBox { MockBoundingBox() { _i1.throwOnMissingStub(this); } @override - _i11.Offset get center => (super.noSuchMethod( + _i13.Offset get center => (super.noSuchMethod( Invocation.getter(#center), - returnValue: _FakeOffset_12( + returnValue: _FakeOffset_14( this, Invocation.getter(#center), ), - ) as _i11.Offset); + ) as _i13.Offset); @override - set center(_i11.Offset? _center) => super.noSuchMethod( + set center(_i13.Offset? _center) => super.noSuchMethod( Invocation.setter( #center, _center, @@ -1695,13 +1791,13 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - _i22.BoundingBoxAction get currentAction => (super.noSuchMethod( + _i24.BoundingBoxAction get currentAction => (super.noSuchMethod( Invocation.getter(#currentAction), - returnValue: _i22.BoundingBoxAction.none, - ) as _i22.BoundingBoxAction); + returnValue: _i24.BoundingBoxAction.none, + ) as _i24.BoundingBoxAction); @override - set currentAction(_i22.BoundingBoxAction? _currentAction) => + set currentAction(_i24.BoundingBoxAction? _currentAction) => super.noSuchMethod( Invocation.setter( #currentAction, @@ -1711,15 +1807,15 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - _i23.BoundingBoxResizeAction get currentBoundingBoxResizeAction => + _i25.BoundingBoxResizeAction get currentBoundingBoxResizeAction => (super.noSuchMethod( Invocation.getter(#currentBoundingBoxResizeAction), - returnValue: _i23.BoundingBoxResizeAction.none, - ) as _i23.BoundingBoxResizeAction); + returnValue: _i25.BoundingBoxResizeAction.none, + ) as _i25.BoundingBoxResizeAction); @override set currentBoundingBoxResizeAction( - _i23.BoundingBoxResizeAction? _currentBoundingBoxResizeAction) => + _i25.BoundingBoxResizeAction? _currentBoundingBoxResizeAction) => super.noSuchMethod( Invocation.setter( #currentBoundingBoxResizeAction, @@ -1729,7 +1825,7 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - set lastDragGlobalPosition(_i11.Offset? _lastDragGlobalPosition) => + set lastDragGlobalPosition(_i13.Offset? _lastDragGlobalPosition) => super.noSuchMethod( Invocation.setter( #lastDragGlobalPosition, @@ -1739,7 +1835,7 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - set dragStartLocalPosition(_i11.Offset? _dragStartLocalPosition) => + set dragStartLocalPosition(_i13.Offset? _dragStartLocalPosition) => super.noSuchMethod( Invocation.setter( #dragStartLocalPosition, @@ -1765,16 +1861,16 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - _i11.Paint get boxPaint => (super.noSuchMethod( + _i13.Paint get boxPaint => (super.noSuchMethod( Invocation.getter(#boxPaint), - returnValue: _i18.dummyValue<_i11.Paint>( + returnValue: _i20.dummyValue<_i13.Paint>( this, Invocation.getter(#boxPaint), ), - ) as _i11.Paint); + ) as _i13.Paint); @override - set boxPaint(_i11.Paint? _boxPaint) => super.noSuchMethod( + set boxPaint(_i13.Paint? _boxPaint) => super.noSuchMethod( Invocation.setter( #boxPaint, _boxPaint, @@ -1783,16 +1879,16 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - _i11.Paint get handlePaint => (super.noSuchMethod( + _i13.Paint get handlePaint => (super.noSuchMethod( Invocation.getter(#handlePaint), - returnValue: _i18.dummyValue<_i11.Paint>( + returnValue: _i20.dummyValue<_i13.Paint>( this, Invocation.getter(#handlePaint), ), - ) as _i11.Paint); + ) as _i13.Paint); @override - set handlePaint(_i11.Paint? _handlePaint) => super.noSuchMethod( + set handlePaint(_i13.Paint? _handlePaint) => super.noSuchMethod( Invocation.setter( #handlePaint, _handlePaint, @@ -1801,16 +1897,16 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - _i11.Paint get rotationHandlePaint => (super.noSuchMethod( + _i13.Paint get rotationHandlePaint => (super.noSuchMethod( Invocation.getter(#rotationHandlePaint), - returnValue: _i18.dummyValue<_i11.Paint>( + returnValue: _i20.dummyValue<_i13.Paint>( this, Invocation.getter(#rotationHandlePaint), ), - ) as _i11.Paint); + ) as _i13.Paint); @override - set rotationHandlePaint(_i11.Paint? _rotationHandlePaint) => + set rotationHandlePaint(_i13.Paint? _rotationHandlePaint) => super.noSuchMethod( Invocation.setter( #rotationHandlePaint, @@ -1835,25 +1931,25 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - _i11.Rect get rect => (super.noSuchMethod( + _i13.Rect get rect => (super.noSuchMethod( Invocation.getter(#rect), - returnValue: _FakeRect_11( + returnValue: _FakeRect_13( this, Invocation.getter(#rect), ), - ) as _i11.Rect); + ) as _i13.Rect); @override - List<_i11.Offset> getCorners() => (super.noSuchMethod( + List<_i13.Offset> getCorners() => (super.noSuchMethod( Invocation.method( #getCorners, [], ), - returnValue: <_i11.Offset>[], - ) as List<_i11.Offset>); + returnValue: <_i13.Offset>[], + ) as List<_i13.Offset>); @override - void determineAction(_i11.Offset? globalPoint) => super.noSuchMethod( + void determineAction(_i13.Offset? globalPoint) => super.noSuchMethod( Invocation.method( #determineAction, [globalPoint], @@ -1862,7 +1958,7 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - void updateDrag(_i11.Offset? globalPoint) => super.noSuchMethod( + void updateDrag(_i13.Offset? globalPoint) => super.noSuchMethod( Invocation.method( #updateDrag, [globalPoint], @@ -1880,7 +1976,7 @@ class MockBoundingBox extends _i1.Mock implements _i21.BoundingBox { ); @override - void drawGuides(_i11.Canvas? canvas) => super.noSuchMethod( + void drawGuides(_i13.Canvas? canvas) => super.noSuchMethod( Invocation.method( #drawGuides, [canvas], diff --git a/test/widget/workspace_page/clipping_tool_test.dart b/test/widget/workspace_page/clipping_tool_test.dart new file mode 100644 index 00000000..40845228 --- /dev/null +++ b/test/widget/workspace_page/clipping_tool_test.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:paintroid/app.dart'; +import 'package:paintroid/core/tools/tool_data.dart'; +import '../../utils/test_utils.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + late Widget sut; + + setUp(() async => sut = ProviderScope(child: App(showOnboardingPage: false))); + + testWidgets('[CLIPPING_TOOL]: selecting clipping tool shows checkmark', + (WidgetTester tester) async { + UIInteraction.initialize(tester); + await tester.pumpWidget(sut); + await UIInteraction.createNewImage(); + + expect(WidgetFinder.checkMark, findsNothing); + + await UIInteraction.selectTool(ToolData.CLIPPING.name); + + expect(WidgetFinder.checkMark, findsOneWidget); + }); + + testWidgets('[CLIPPING_TOOL]: selecting other tools hide the checkmark', + (WidgetTester tester) async { + UIInteraction.initialize(tester); + await tester.pumpWidget(sut); + await UIInteraction.createNewImage(); + + expect(WidgetFinder.checkMark, findsNothing); + + await UIInteraction.selectTool(ToolData.CLIPPING.name); + + expect(WidgetFinder.checkMark, findsOneWidget); + + await UIInteraction.selectTool(ToolData.BRUSH.name); + + expect(WidgetFinder.checkMark, findsNothing); + }); +}