Skip to content

Commit 9593038

Browse files
committed
feat: pan tool
1 parent 47b8af8 commit 9593038

File tree

5 files changed

+46
-28
lines changed

5 files changed

+46
-28
lines changed

lib/core/utils/enums.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum ShapeType {
3434

3535
enum DrawMode {
3636
pointer(iconData: Icons.pan_tool_alt),
37+
hand(iconData: PhosphorIconsRegular.hand), // NEW: Add hand tool for panning
3738
circle(iconData: Icons.circle_outlined),
3839
rectangle(iconData: Icons.rectangle_outlined),
3940
square(iconData: Icons.square_outlined),
@@ -45,7 +46,6 @@ enum DrawMode {
4546
invertedTriangle(iconData: Icons.warning_amber_rounded), // Placeholder
4647
textBox(iconData: PhosphorIconsRegular.textT),
4748
stickyNote(iconData: PhosphorIconsRegular.notePencil);
48-
// connector(iconData: PhosphorIconsRegular.lineSegment);
4949

5050
const DrawMode({required this.iconData});
5151
final IconData iconData;
@@ -62,4 +62,4 @@ enum InteractionMode {
6262
drawingConnector,
6363
}
6464

65-
enum ConnectorAnchor { top, bottom, left, right }
65+
enum ConnectorAnchor { top, bottom, left, right }

lib/features/workspace/pages/canvas_page.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class CanvasPage extends StatelessWidget {
1414
Widget build(BuildContext context) {
1515
return Consumer2<WorkspaceProvider, CanvasProvider>(
1616
builder: (context, workspaceProvider, canvasProvider, child) {
17+
final isHandToolActive = workspaceProvider.currentMode == DrawMode.hand;
18+
1719
return Scaffold(
1820
backgroundColor: workspaceProvider.currentWorkspaceColor,
1921
body: Listener(
@@ -23,6 +25,8 @@ class CanvasPage extends StatelessWidget {
2325
}
2426
},
2527
child: MouseRegion(
28+
// Change cursor based on the active tool
29+
cursor: isHandToolActive ? SystemMouseCursors.grab : SystemMouseCursors.basic,
2630
onHover: (event) {
2731
final Matrix4 transform = canvasProvider.transformationController.value;
2832
final Matrix4? inverseTransform = Matrix4.tryInvert(transform);
@@ -43,21 +47,23 @@ class CanvasPage extends StatelessWidget {
4347
maxScale: 4.0,
4448
boundaryMargin: const EdgeInsets.all(double.infinity),
4549
constrained: false,
46-
panEnabled: workspaceProvider.interactionMode != InteractionMode.editingText && workspaceProvider.interactionMode != InteractionMode.drawingConnector,
50+
// Enable panning only when Hand Tool is active
51+
panEnabled: isHandToolActive,
4752
scaleEnabled: workspaceProvider.interactionMode != InteractionMode.editingText,
4853
child: Container(
4954
color: workspaceProvider.currentWorkspaceColor,
5055
child: GestureDetector(
51-
onPanDown: (details) {
56+
// Disable GestureDetector's pan events when Hand Tool is active
57+
onPanDown: isHandToolActive ? null : (details) {
5258
workspaceProvider.onPanDown(DragDownDetails(globalPosition: details.localPosition));
5359
},
54-
onPanUpdate: (details) {
60+
onPanUpdate: isHandToolActive ? null : (details) {
5561
workspaceProvider.onPanUpdate(DragUpdateDetails(
5662
globalPosition: details.localPosition,
5763
delta: details.delta,
5864
));
5965
},
60-
onPanEnd: workspaceProvider.onPanEnd,
66+
onPanEnd: isHandToolActive ? null : workspaceProvider.onPanEnd,
6167
child: CustomPaint(
6268
size: const Size(20000, 20000),
6369
painter: CanvasPainter(
@@ -66,7 +72,6 @@ class CanvasPage extends StatelessWidget {
6672
currentlySelectedObjectId: workspaceProvider.currentlySelectedObjectId,
6773
handleRadius: workspaceProvider.handleRadius,
6874
interactionMode: workspaceProvider.interactionMode,
69-
// NEW: Pass connector-related state to the painter
7075
connectionPointRadius: workspaceProvider.connectionPointRadius,
7176
connectorSourceId: workspaceProvider.connectorSourceId,
7277
connectorSourceAlignment: workspaceProvider.connectorSourceAlignment,

lib/features/workspace/pages/desktop/workspace_desktop.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class WorkspaceDesktop extends StatelessWidget {
3838
// Undo/Redo Controls Container
3939
Positioned(top: 0,left: 0.21.sw,child: UndoRedoButton()),
4040
// Export project button
41-
Positioned(top: 0,right: 0.02.sw,child: ExportProjectButton()),
41+
Positioned(top: 0,right: 0.001.sw,child: ExportProjectButton()),
4242

4343
Positioned(right: 0,top: 0.15.sh,child: ToolBar()),
4444

lib/features/workspace/providers/workspace_provider.dart

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ class WorkspaceProvider extends StateHandler {
7474

7575
Color _nextObjectColor = Colors.yellow;
7676

77-
// NEW: State for drawing a connector
7877
String? _connectorSourceId;
7978
Alignment? _connectorSourceAlignment;
8079
Offset? _connectorDragPosition;
@@ -99,7 +98,6 @@ class WorkspaceProvider extends StateHandler {
9998
TextEditingController get workspaceNameController => _workspaceNameController;
10099
SupabaseService get supabaseService => _supabaseService;
101100

102-
// NEW: Getter for temporary connector state for the painter
103101
String? get connectorSourceId => _connectorSourceId;
104102
Alignment? get connectorSourceAlignment => _connectorSourceAlignment;
105103
Offset? get connectorDragPosition => _connectorDragPosition;
@@ -117,7 +115,6 @@ class WorkspaceProvider extends StateHandler {
117115
notifyListeners();
118116
}
119117

120-
// ... (rest of the provider is the same until getIconForObjectType)
121118
void _onQuillContentChanged() {
122119
if (_currentlySelectedObjectId != null &&
123120
_interactionMode == InteractionMode.editingText) {
@@ -296,7 +293,9 @@ class WorkspaceProvider extends StateHandler {
296293
}
297294

298295
void onPanEnd(DragEndDetails details) async {
299-
// NEW: Logic for completing a connector
296+
// If we are in hand mode, do nothing.
297+
if (_currentMode == DrawMode.hand) return;
298+
300299
if (_interactionMode == InteractionMode.drawingConnector &&
301300
_connectorSourceId != null) {
302301
final target = _findConnectionTarget(_cursorPosition);
@@ -309,7 +308,7 @@ class WorkspaceProvider extends StateHandler {
309308
);
310309
_canvasObjects[newConnector.id] = newConnector;
311310
await _saveCanvasObjectToDb(newConnector.id);
312-
syncCanvasObject(_cursorPosition); // Sync the new connector
311+
syncCanvasObject(_cursorPosition);
313312
}
314313
} else if (_currentlySelectedObjectId != null &&
315314
_interactionMode != InteractionMode.editingText) {
@@ -358,7 +357,6 @@ class WorkspaceProvider extends StateHandler {
358357
} else {
359358
final selectedObject = _canvasObjects[id];
360359
if (selectedObject is ConnectorObject) {
361-
// Don't load anything for connectors
362360
_tempQuillController.clear();
363361
} else if (selectedObject?.textDelta != null) {
364362
try {
@@ -509,6 +507,7 @@ class WorkspaceProvider extends StateHandler {
509507
);
510508
break;
511509
case DrawMode.pointer:
510+
case DrawMode.hand: // Do nothing for pointer or hand mode
512511
break;
513512
}
514513

@@ -527,10 +526,9 @@ class WorkspaceProvider extends StateHandler {
527526
}
528527
}
529528

530-
// Helper method to find if a point is over a connection point
531529
Map<String, dynamic>? _findConnectionTarget(Offset point) {
532530
for (final object in _canvasObjects.values) {
533-
if (object is ConnectorObject) continue; // Cannot connect to a connector
531+
if (object is ConnectorObject) continue;
534532

535533
const alignments = [
536534
Alignment.topCenter,
@@ -549,6 +547,11 @@ class WorkspaceProvider extends StateHandler {
549547
}
550548

551549
void onPanDown(DragDownDetails details) {
550+
// If in hand mode, do nothing and let the InteractiveViewer handle panning.
551+
if (_currentMode == DrawMode.hand) {
552+
return;
553+
}
554+
552555
_cursorPosition = details.globalPosition;
553556
_panStartPoint = details.globalPosition;
554557

@@ -563,7 +566,6 @@ class WorkspaceProvider extends StateHandler {
563566
}
564567

565568
if (_currentMode == DrawMode.pointer) {
566-
// NEW: Check for connection point interaction first
567569
final connectionTarget = _findConnectionTarget(details.globalPosition);
568570
if (connectionTarget != null) {
569571
_interactionMode = InteractionMode.drawingConnector;
@@ -574,7 +576,6 @@ class WorkspaceProvider extends StateHandler {
574576
return;
575577
}
576578

577-
// Check for resize handle interaction
578579
if (_currentlySelectedObjectId != null) {
579580
final selectedObject = _canvasObjects[_currentlySelectedObjectId!];
580581
if (selectedObject != null && selectedObject is! ConnectorObject) {
@@ -645,9 +646,13 @@ class WorkspaceProvider extends StateHandler {
645646
}
646647

647648
void onPanUpdate(DragUpdateDetails details) {
649+
// If in hand mode, do nothing and let the InteractiveViewer handle panning.
650+
if (_currentMode == DrawMode.hand) {
651+
return;
652+
}
653+
648654
_cursorPosition = details.globalPosition;
649655

650-
// NEW: Update the connector drag position
651656
if (_interactionMode == InteractionMode.drawingConnector) {
652657
_connectorDragPosition = details.globalPosition;
653658
notifyListeners();
@@ -699,7 +704,7 @@ class WorkspaceProvider extends StateHandler {
699704
break;
700705
case InteractionMode.none:
701706
case InteractionMode.editingText:
702-
case InteractionMode.drawingConnector: // Already handled
707+
case InteractionMode.drawingConnector:
703708
break;
704709
}
705710

lib/features/workspace/widgets/toolbar.dart

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,32 +41,40 @@ class ToolBar extends StatelessWidget {
4141
_horizontalDivider(),
4242
_toolIcon(
4343
PhosphorIconsRegular.circlesThreePlus,
44-
iconColor: provider.currentMode != DrawMode.pointer && provider.currentMode != DrawMode.textBox ? Colors.blue : Colors.black,
4544
'Add new node',
4645
device,
4746
onPressed: () {
48-
// Call the new method to show the NodePicker dialog
4947
_showNodePicker(context);
5048
print('Circles three plus pressed');
5149
},
5250
),
5351
_horizontalDivider(),
5452
_toolIcon(
55-
PhosphorIconsRegular.handGrabbing,
56-
iconColor: provider.currentMode == DrawMode.pointer ? Colors.blue : Colors.black,
53+
PhosphorIconsRegular.cursor,
5754
'Pointer',
5855
device,
56+
iconColor: provider.currentMode == DrawMode.pointer ? Colors.blue : Colors.black,
5957
onPressed: () {
6058
provider.changeDrawMode(DrawMode.pointer);
61-
print('Hand grabbing pressed');
59+
print('Pointer tool selected');
60+
},
61+
),
62+
// NEW: Hand tool for panning
63+
_toolIcon(
64+
PhosphorIconsRegular.handGrabbing,
65+
'Pan',
66+
device,
67+
iconColor: provider.currentMode == DrawMode.hand ? Colors.blue : Colors.black,
68+
onPressed: () {
69+
provider.changeDrawMode(DrawMode.hand);
70+
print('Hand tool selected');
6271
},
6372
),
6473
_toolIcon(
6574
PhosphorIconsRegular.textT,
66-
iconColor: provider.currentMode == DrawMode.textBox ? Colors.blue : Colors.black,
6775
'Text box',
6876
device,
69-
// MODIFIED: Changed onPressed to set DrawMode.textBox
77+
iconColor: provider.currentMode == DrawMode.textBox ? Colors.blue : Colors.black,
7078
onPressed: () {
7179
provider.changeDrawMode(DrawMode.textBox);
7280
print('Text box pressed, mode changed to textBox');
@@ -85,7 +93,7 @@ class ToolBar extends StatelessWidget {
8593
PhosphorIconsFill.noteBlank,
8694
'Add new sticky note',
8795
device,
88-
iconColor: tertiaryColors[6],
96+
iconColor: provider.currentMode == DrawMode.stickyNote ? Colors.blue : tertiaryColors[6],
8997
onPressed: () => _showStickyNote(context),
9098
),
9199
],

0 commit comments

Comments
 (0)