Skip to content

Commit

Permalink
Merge branch 'color_picker' of https://github.com/bhav-khurana/Paintr…
Browse files Browse the repository at this point in the history
…oid-Flutter into color_picker
  • Loading branch information
bhav-khurana committed Jan 16, 2024
2 parents a6cafb0 + 5aaaaaa commit 372b63a
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 12 deletions.
47 changes: 47 additions & 0 deletions lib/tool/src/hand_tool/hand_tool.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'dart:ui';

import 'package:equatable/equatable.dart';
import 'package:paintroid/command/src/command_factory.dart';
import 'package:paintroid/command/src/command_manager.dart';
import 'package:paintroid/core/graphic_factory.dart';
import 'package:paintroid/tool/src/tool.dart';
import 'package:paintroid/tool/src/tool_types.dart';

class HandTool extends Tool with EquatableMixin {
HandTool({
required super.paint,
required super.commandFactory,
required super.commandManager,
required super.type,
});

@override
void onDown(Offset point) {}

@override
void onDrag(Offset point) {}

@override
void onUp(Offset? point) {}

@override
void onCancel() {}

@override
List<Object?> get props => [commandManager, commandFactory];

HandTool copyWith({
Paint? paint,
CommandFactory? commandFactory,
CommandManager? commandManager,
GraphicFactory? graphicFactory,
ToolType? type,
}) {
return HandTool(
paint: paint ?? this.paint,
commandFactory: commandFactory ?? this.commandFactory,
commandManager: commandManager ?? this.commandManager,
type: type ?? this.type,
);
}
}
18 changes: 18 additions & 0 deletions lib/tool/src/hand_tool/hand_tool_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:paintroid/command/src/command_factory_provider.dart';
import 'package:paintroid/command/src/command_manager_provider.dart';
import 'package:paintroid/tool/src/brush_tool/brush_tool_state_provider.dart';
import 'package:paintroid/tool/src/hand_tool/hand_tool.dart';
import 'package:paintroid/tool/src/tool_types.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'hand_tool_provider.g.dart';

@riverpod
HandTool handTool(HandToolRef ref) {
return HandTool(
paint: ref.watch(brushToolStateProvider.select((state) => state.paint)),
type: ToolType.HAND,
commandManager: ref.watch(commandManagerProvider),
commandFactory: ref.watch(commandFactoryProvider),
);
}
7 changes: 7 additions & 0 deletions lib/tool/src/toolbox/toolbox_state_provider.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:ui';

import 'package:paintroid/tool/src/hand_tool/hand_tool_provider.dart';
import 'package:paintroid/tool/tool.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:toast/toast.dart';
Expand Down Expand Up @@ -38,6 +39,12 @@ class ToolBoxState extends _$ToolBoxState {
currentToolType: ToolType.BRUSH,
);

break;
case ToolType.HAND:
state = state.copyWith(
currentTool: ref.read(handToolProvider),
currentToolType: ToolType.HAND,
);
break;
case ToolType.ERASER:
ref
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/drawing_space/bottom_brush_tool_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ class _BottomBrushToolOptionsState
spacing: 8,
children: [
CustomActionChip(
hint: 'Round stroke',
chipIcon: const Icon(Icons.circle),
onPressed: () =>
_changeActionChipBackgroundColor(StrokeCap.round),
chipBackgroundColor: _roundChipBackgroundColor,
),
CustomActionChip(
hint: 'Square stroke',
chipIcon: const Icon(Icons.square),
onPressed: () =>
_changeActionChipBackgroundColor(StrokeCap.square),
Expand Down
3 changes: 3 additions & 0 deletions lib/ui/landing_page/custom_action_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ class CustomActionButton extends StatelessWidget {
final String heroTag;
final IconData icon;
final VoidCallback onPressed;
final String hint;

const CustomActionButton({
Key? key,
required this.heroTag,
required this.icon,
required this.onPressed,
required this.hint,
}) : super(key: key);

@override
Expand All @@ -18,6 +20,7 @@ class CustomActionButton extends StatelessWidget {
heroTag: heroTag,
backgroundColor: const Color(0xFFFFAB08),
foregroundColor: const Color(0xFFFFFFFF),
tooltip: hint,
child: Icon(icon),
onPressed: () async => onPressed(),
);
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/landing_page/landing_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class _LandingPageState extends ConsumerState<LandingPage> {
CustomActionButton(
heroTag: 'import_image',
icon: Icons.file_download,
hint: 'Load image',
onPressed: () async {
final bool imageLoaded =
await ioHandler.loadImage(context, this, false);
Expand All @@ -175,6 +176,7 @@ class _LandingPageState extends ConsumerState<LandingPage> {
CustomActionButton(
heroTag: 'new_image',
icon: Icons.add,
hint: 'New image',
onPressed: () async {
_clearCanvas();
_navigateToPocketPaint();
Expand Down
3 changes: 3 additions & 0 deletions lib/ui/shared/custom_action_chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ class CustomActionChip extends StatelessWidget {
final Color chipBackgroundColor;
final EdgeInsetsGeometry? padding;
final MaterialTapTargetSize? materialTapTargetSize;
final String hint;

const CustomActionChip({
super.key,
required this.chipIcon,
required this.onPressed,
required this.chipBackgroundColor,
required this.hint,
this.shape,
this.padding,
this.materialTapTargetSize,
Expand All @@ -21,6 +23,7 @@ class CustomActionChip extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ActionChip(
tooltip: hint,
label: chipIcon,
onPressed: onPressed,
shape: shape ??
Expand Down
14 changes: 5 additions & 9 deletions lib/workspace/src/ui/drawing_canvas.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:paintroid/service/device_service.dart';
import 'package:paintroid/tool/src/tool_types.dart';
import 'package:paintroid/tool/src/toolbox/toolbox_state_provider.dart';
import 'package:paintroid/workspace/src/state/canvas/canvas_state_provider.dart';
import 'package:paintroid/workspace/src/state/canvas_dirty_state.dart';
Expand Down Expand Up @@ -114,23 +115,18 @@ class _DrawingCanvasState extends ConsumerState<DrawingCanvas> {
_resetCanvasScale(fitToScreen: isFullscreen);
},
);
final canvasSize = ref.watch(
canvasStateProvider.select((state) => state.size),
);
final panningMargin = (canvasSize - const Offset(5, 5)) as Size;
return Listener(
onPointerDown: _onPointerDown,
onPointerUp: _onPointerUp,
child: InteractiveViewer(
clipBehavior: Clip.none,
transformationController: _transformationController,
boundaryMargin: EdgeInsets.symmetric(
horizontal: panningMargin.width,
vertical: panningMargin.height,
),
minScale: 0.2,
maxScale: 100,
panEnabled: false,
boundaryMargin: const EdgeInsets.all(double.infinity),
interactionEndFrictionCoefficient: double.minPositive,
panEnabled:
ref.watch(toolBoxStateProvider).currentTool.type == ToolType.HAND,
onInteractionStart: _onInteractionStart,
onInteractionUpdate: _onInteractionUpdate,
onInteractionEnd: _onInteractionEnd,
Expand Down
72 changes: 72 additions & 0 deletions test/unit/tool/hand_tool_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import 'dart:ui';

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:paintroid/command/src/command_factory.dart';
import 'package:paintroid/command/src/command_manager.dart';
import 'package:paintroid/tool/src/hand_tool/hand_tool.dart';
import 'package:paintroid/tool/src/tool_types.dart';

import 'hand_tool_test.mocks.dart';

@GenerateMocks([
Paint,
CommandManager,
CommandFactory,
])
void main() {
late MockPaint mockPaint;
late MockCommandFactory mockCommandFactory;
late MockCommandManager mockCommandManager;

late HandTool sut;
const Offset offset = Offset(10, 10);

setUp(() {
mockPaint = MockPaint();
mockCommandFactory = MockCommandFactory();
mockCommandManager = MockCommandManager();

sut = HandTool(
paint: mockPaint,
commandFactory: mockCommandFactory,
commandManager: mockCommandManager,
type: ToolType.HAND,
);
});

group('HandTool Tests', () {
test('onDown should not interact with any dependencies', () {
sut.onDown(offset);

verifyNoMoreInteractions(mockPaint);
verifyNoMoreInteractions(mockCommandFactory);
verifyNoMoreInteractions(mockCommandManager);
});

test('onDrag should not interact with any dependencies', () {
sut.onDrag(offset);

verifyNoMoreInteractions(mockPaint);
verifyNoMoreInteractions(mockCommandFactory);
verifyNoMoreInteractions(mockCommandManager);
});

test('onUp should not interact with any dependencies', () {
sut.onUp(offset);

verifyNoMoreInteractions(mockPaint);
verifyNoMoreInteractions(mockCommandFactory);
verifyNoMoreInteractions(mockCommandManager);
});

test('onCancel should not interact with any dependencies', () {
sut.onCancel();

verifyNoMoreInteractions(mockPaint);
verifyNoMoreInteractions(mockCommandFactory);
verifyNoMoreInteractions(mockCommandManager);
});
});
}
4 changes: 2 additions & 2 deletions test/widget/tool/eraser_tool_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ void main() {
const testOffset = Offset(10, 10);

final pixelColorBeforeErase = await canvasInteractions
.drawLineFromCenter(testOffset)
.dragFromCenter(testOffset)
.then((_) => canvasInteractions.getPixelColor(300, 300));
expect(pixelColorBeforeErase, isNot(equals(0)));

await bottomNavBarInteractions.selectTool(ToolData.ERASER);

final pixelColorAfterErase = await canvasInteractions
.drawLineFromCenter(testOffset)
.dragFromCenter(testOffset)
.then((_) => canvasInteractions.getPixelColor(300, 300));
expect(pixelColorAfterErase, equals(0));
},
Expand Down
68 changes: 68 additions & 0 deletions test/widget/tool/hand_tool_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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/service/device_service.dart';
import 'package:paintroid/tool/tool.dart';
import 'package:paintroid/ui/pocket_paint.dart';

import '../utils/utils.dart';
import '../utils/interactions/interactive_viewer_interactions.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

late Widget sut;

setUp(() {
sut = ProviderScope(
overrides: [
IDeviceService.sizeProvider
.overrideWith((ref) => Future.value(const Size(600, 600)))
],
child: const MaterialApp(
home: PocketPaint(),
),
);
});

testWidgets('Pan bottom left', (WidgetTester tester) async {
await tester.pumpWidget(sut);
await tester.pumpAndSettle();

final bottomNavBarInteractions = BottomNavBarInteractions(tester);
final interactiveViewerInteractions = InterActiveViewerInteractions(tester);
await bottomNavBarInteractions.selectTool(ToolData.HAND);
await interactiveViewerInteractions.panAndVerify(const Offset(-50, 50));
});

testWidgets('Pan bottom right', (WidgetTester tester) async {
await tester.pumpWidget(sut);
await tester.pumpAndSettle();

final bottomNavBarInteractions = BottomNavBarInteractions(tester);
final interactiveViewerInteractions = InterActiveViewerInteractions(tester);
await bottomNavBarInteractions.selectTool(ToolData.HAND);
await interactiveViewerInteractions.panAndVerify(const Offset(50, 50));
});

testWidgets('Pan top left', (WidgetTester tester) async {
await tester.pumpWidget(sut);
await tester.pumpAndSettle();

final bottomNavBarInteractions = BottomNavBarInteractions(tester);
final interactiveViewerInteractions = InterActiveViewerInteractions(tester);
await bottomNavBarInteractions.selectTool(ToolData.HAND);
await interactiveViewerInteractions.panAndVerify(const Offset(-50, -50));
});

testWidgets('Pan top right', (WidgetTester tester) async {
await tester.pumpWidget(sut);
await tester.pumpAndSettle();

final bottomNavBarInteractions = BottomNavBarInteractions(tester);
final interactiveViewerInteractions = InterActiveViewerInteractions(tester);
await bottomNavBarInteractions.selectTool(ToolData.HAND);
await interactiveViewerInteractions.panAndVerify(const Offset(50, -50));
});
}
2 changes: 1 addition & 1 deletion test/widget/utils/interactions/canvas_interactions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class CanvasInteractions {

List<Offset> lastOffsets = [];

Future<CanvasInteractions> drawLineFromCenter(Offset offset) async {
Future<CanvasInteractions> dragFromCenter(Offset offset) async {
final drawingCanvasFinder = find.byType(CanvasPainter);
expect(drawingCanvasFinder, findsOneWidget);
await _tester.drag(drawingCanvasFinder, offset);
Expand Down
Loading

0 comments on commit 372b63a

Please sign in to comment.