Skip to content

Commit 0cb4957

Browse files
committed
feat: connector customizer
1 parent ef00b4b commit 0cb4957

File tree

3 files changed

+134
-73
lines changed

3 files changed

+134
-73
lines changed

lib/features/models/canvas_models/objects/connector_object.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// lib/features/models/canvas_models/objects/connector_object.dart
12
import 'dart:ui';
23
import 'package:cookethflow/core/utils/enums.dart';
34
import 'package:cookethflow/features/models/canvas_models/canvas_object.dart';
@@ -132,4 +133,4 @@ class ConnectorObject extends CanvasObject {
132133
// This logic is best handled in the WorkspaceProvider's onPanDown, where all objects are available.
133134
return false;
134135
}
135-
}
136+
}

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

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
// lib/features/workspace/pages/workspace_desktop.dart (Fully Modified)
1+
// lib/features/workspace/pages/workspace_desktop.dart
22
import 'package:cookethflow/core/helpers/responsive_layout.helper.dart' as rh;
33
import 'package:cookethflow/core/providers/supabase_provider.dart';
44
import 'package:cookethflow/core/utils/enums.dart';
5+
import 'package:cookethflow/features/models/canvas_models/objects/connector_object.dart';
56
import 'package:cookethflow/features/workspace/pages/canvas_page.dart';
67
import 'package:cookethflow/features/workspace/providers/canvas_provider.dart';
78
import 'package:cookethflow/features/workspace/providers/workspace_provider.dart';
@@ -33,20 +34,17 @@ class _WorkspaceDesktopState extends State<WorkspaceDesktop> {
3334
@override
3435
void initState() {
3536
super.initState();
36-
// Request focus when the widget is first built
3737
_focusNode.requestFocus();
3838
}
3939

4040
@override
4141
void dispose() {
42-
// Clean up the focus node when the widget is disposed
4342
_focusNode.dispose();
4443
super.dispose();
4544
}
4645

4746
@override
4847
Widget build(BuildContext context) {
49-
// Define actions
5048
final Map<Type, Action<Intent>> actions = {
5149
PointerIntent: CallbackAction<PointerIntent>(
5250
onInvoke: (intent) {
@@ -136,7 +134,6 @@ class _WorkspaceDesktopState extends State<WorkspaceDesktop> {
136134
backgroundColor: provider.currentWorkspaceColor,
137135
body: GestureDetector(
138136
onTap: () {
139-
// This ensures focus is regained when the user taps on the canvas
140137
_focusNode.requestFocus();
141138
},
142139
child: Padding(
@@ -149,12 +146,6 @@ class _WorkspaceDesktopState extends State<WorkspaceDesktop> {
149146
children: [
150147
const CanvasPage(),
151148
const WorkspaceDrawer(),
152-
// SizedBox(width: 20.w),
153-
// Positioned(
154-
// top: 0,
155-
// left: 0.21.sw,
156-
// child: UndoRedoButton(su: suprovider),
157-
// ),
158149
Positioned(
159150
top: 0,
160151
right: 0.001.sw,
@@ -185,23 +176,54 @@ class _WorkspaceDesktopState extends State<WorkspaceDesktop> {
185176
workspaceProvider
186177
.canvasObjects[workspaceProvider
187178
.currentlySelectedObjectId!]!;
188-
final objectBounds =
189-
selectedObject.getBounds();
179+
180+
Offset objectPosition;
181+
if (selectedObject is ConnectorObject) {
182+
// For connectors, calculate the midpoint for positioning
183+
final sourceObject =
184+
workspaceProvider
185+
.canvasObjects[selectedObject
186+
.sourceId];
187+
final targetObject =
188+
workspaceProvider
189+
.canvasObjects[selectedObject
190+
.targetId];
191+
if (sourceObject != null &&
192+
targetObject != null) {
193+
final startPoint = sourceObject
194+
.getConnectionPoint(
195+
selectedObject.sourceAlignment,
196+
);
197+
final endPoint = targetObject
198+
.getConnectionPoint(
199+
selectedObject.targetAlignment,
200+
);
201+
objectPosition = Offset(
202+
(startPoint.dx + endPoint.dx) / 2,
203+
(startPoint.dy + endPoint.dy) / 2,
204+
);
205+
} else {
206+
return const SizedBox.shrink();
207+
}
208+
} else {
209+
objectPosition =
210+
selectedObject.getBounds().topCenter;
211+
}
212+
190213
final matrix =
191214
canvasProvider
192215
.transformationController
193216
.value;
194-
final transformedTopCenter = matrix
195-
.transform3(
196-
vector_math.Vector3(
197-
objectBounds.topCenter.dx,
198-
objectBounds.topCenter.dy,
199-
0,
200-
),
201-
);
217+
final transformedPosition = matrix.transform3(
218+
vector_math.Vector3(
219+
objectPosition.dx,
220+
objectPosition.dy,
221+
0,
222+
),
223+
);
202224
final screenPosition = Offset(
203-
transformedTopCenter.x,
204-
transformedTopCenter.y,
225+
transformedPosition.x,
226+
transformedPosition.y,
205227
);
206228
const double toolboxWidth = 240;
207229
const double toolboxHeight = 48;
Lines changed: 87 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// lib/features/workspace/widgets/node_editing_toolbox.dart
12
import 'package:cookethflow/core/providers/supabase_provider.dart';
23
import 'package:cookethflow/core/utils/enums.dart';
34
import 'package:cookethflow/features/models/canvas_models/objects/connector_object.dart';
@@ -34,7 +35,7 @@ class NodeEditingToolbox extends StatelessWidget {
3435
return IconButton(
3536
icon: Icon(icon, size: 22),
3637
onPressed: onPressed,
37-
color: color ??(su.isDark?Colors.white: Colors.black87),
38+
color: color ?? (su.isDark ? Colors.white : Colors.black87),
3839
splashRadius: 20,
3940
tooltip: tooltip,
4041
padding: const EdgeInsets.all(8),
@@ -45,33 +46,30 @@ class NodeEditingToolbox extends StatelessWidget {
4546
@override
4647
Widget build(BuildContext context) {
4748
final provider = context.read<WorkspaceProvider>();
48-
final provider2 = context.read<SupabaseService>();
49+
final su = context.read<SupabaseService>();
4950
final selectedObjectId = provider.currentlySelectedObjectId;
5051

5152
if (selectedObjectId == null) {
5253
return const SizedBox.shrink();
5354
}
54-
55+
5556
final object = provider.canvasObjects[selectedObjectId];
5657
if (object == null) {
5758
return const SizedBox.shrink();
5859
}
5960

60-
final isConnector = object is ConnectorObject;
61-
final showShapeChanger =
62-
object is! TextBoxObject &&
63-
object is! ConnectorObject &&
64-
object is! StickyNoteObject;
65-
final showColorChanger =
66-
object is! TextBoxObject;
67-
6861
final buttons = <Widget>[];
6962

63+
final isConnector = object is ConnectorObject;
64+
final isShape =
65+
object is! TextBoxObject && object is! ConnectorObject && object is! StickyNoteObject;
66+
final canChangeColor = object is! TextBoxObject && object is! ConnectorObject;
67+
7068
if (isConnector) {
7169
buttons.add(
7270
_buildIconButton(
7371
context,
74-
icon: PhosphorIcons.database(),
72+
icon: PhosphorIcons.lineSegment(),
7573
onPressed: () {
7674
showDialog(
7775
context: context,
@@ -82,69 +80,110 @@ class NodeEditingToolbox extends StatelessWidget {
8280
onStyleSelected: (color, type, thickness) {
8381
provider.changeConnectorStyle(color, type, thickness);
8482
},
85-
su: provider2,
83+
su: su,
8684
),
8785
);
8886
},
8987
tooltip: 'Change Connector Style',
90-
su: provider2,
88+
su: su,
9189
),
9290
);
93-
buttons.add(_buildDivider());
94-
}
95-
96-
if (showShapeChanger) {
91+
if (canChangeColor) {
92+
buttons.add(_buildDivider());
93+
buttons.add(
94+
_buildIconButton(
95+
context,
96+
icon: PhosphorIcons.paintBucket(),
97+
onPressed: () {
98+
showDialog(
99+
context: context,
100+
builder: (context) => NodeColourPicker(
101+
initialColor: object.color,
102+
onColorSelected: (color) {
103+
provider.changeConnectorStyle(
104+
color,
105+
object.connectionType,
106+
object.thickness,
107+
);
108+
Navigator.of(context).pop();
109+
},
110+
),
111+
);
112+
},
113+
tooltip: 'Change Color',
114+
su: su,
115+
),
116+
);
117+
}
118+
} else if (isShape) {
97119
buttons.add(
98120
_buildIconButton(
99121
context,
100122
icon: PhosphorIcons.shapes(),
101123
onPressed: () {
102124
showDialog(
103125
context: context,
104-
builder:
105-
(context) => NodePicker(
106-
onShapeSelected: (shapeType) {
107-
provider.changeObjectShape(shapeType);
108-
},
109-
su: provider2,
110-
),
126+
builder: (context) => NodePicker(
127+
onShapeSelected: (shapeType) {
128+
provider.changeObjectShape(shapeType);
129+
Navigator.of(context).pop();
130+
},
131+
su: su,
132+
),
111133
);
112134
},
113135
tooltip: 'Change Shape',
114-
su: provider2
136+
su: su,
115137
),
116138
);
117-
buttons.add(_buildDivider());
118-
}
119-
120-
if (showColorChanger) {
139+
if (canChangeColor) {
140+
buttons.add(_buildDivider());
141+
buttons.add(
142+
_buildIconButton(
143+
context,
144+
icon: PhosphorIcons.paintBucket(),
145+
onPressed: () {
146+
showDialog(
147+
context: context,
148+
builder: (context) => NodeColourPicker(
149+
initialColor: object.color,
150+
onColorSelected: (color) {
151+
provider.changeObjectColor(color);
152+
Navigator.of(context).pop();
153+
},
154+
),
155+
);
156+
},
157+
tooltip: 'Change Color',
158+
su: su,
159+
),
160+
);
161+
}
162+
} else if (canChangeColor) {
121163
buttons.add(
122164
_buildIconButton(
123165
context,
124166
icon: PhosphorIcons.paintBucket(),
125167
onPressed: () {
126-
final selectedObject =
127-
provider.canvasObjects[selectedObjectId];
128168
showDialog(
129169
context: context,
130-
builder:
131-
(context) => NodeColourPicker(
132-
initialColor: selectedObject?.color,
133-
onColorSelected: (color) {
134-
if (isConnector) {
135-
provider.changeConnectorStyle(color, (object as ConnectorObject).connectionType, (object as ConnectorObject).thickness);
136-
} else {
137-
provider.changeObjectColor(color);
138-
}
139-
Navigator.of(context).pop();
140-
},
141-
),
170+
builder: (context) => NodeColourPicker(
171+
initialColor: object.color,
172+
onColorSelected: (color) {
173+
provider.changeObjectColor(color);
174+
Navigator.of(context).pop();
175+
},
176+
),
142177
);
143178
},
144179
tooltip: 'Change Color',
145-
su: provider2
180+
su: su,
146181
),
147182
);
183+
}
184+
185+
// Always add the delete button if there's an object selected
186+
if (buttons.isNotEmpty) {
148187
buttons.add(_buildDivider());
149188
}
150189

@@ -157,7 +196,7 @@ class NodeEditingToolbox extends StatelessWidget {
157196
},
158197
tooltip: 'Delete Object',
159198
color: Colors.redAccent,
160-
su: provider2
199+
su: su,
161200
),
162201
);
163202

@@ -166,8 +205,7 @@ class NodeEditingToolbox extends StatelessWidget {
166205
child: Container(
167206
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
168207
decoration: BoxDecoration(
169-
color:
170-
provider2.isDark ? const Color.fromRGBO(48, 48, 48, 1) : Colors.white,
208+
color: su.isDark ? const Color.fromRGBO(48, 48, 48, 1) : Colors.white,
171209
borderRadius: BorderRadius.circular(10),
172210
border: Border.all(color: Colors.grey.shade300),
173211
boxShadow: [
@@ -182,4 +220,4 @@ class NodeEditingToolbox extends StatelessWidget {
182220
),
183221
);
184222
}
185-
}
223+
}

0 commit comments

Comments
 (0)