Skip to content

Commit b127c34

Browse files
committed
feat: better rendering
1 parent 1db1a6f commit b127c34

2 files changed

Lines changed: 67 additions & 142 deletions

File tree

lib/features/models/canvas_models/canvas_painter.dart

Lines changed: 61 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,45 @@ class CanvasPainter extends CustomPainter {
3232
required this.interactionMode,
3333
});
3434

35-
// Helper method to parse color from string
3635
Color _parseColor(String? colorString) {
3736
if (colorString == null) return Colors.black;
3837
try {
3938
final hex = colorString.replaceAll('#', '');
40-
if (hex.length == 6) {
41-
return Color(int.parse('FF$hex', radix: 16));
42-
}
43-
return Color(int.parse(hex, radix: 16));
39+
return Color(int.parse('FF$hex', radix: 16));
4440
} catch (e) {
4541
return Colors.black;
4642
}
4743
}
4844

45+
// CHANGE: More robust style parsing
46+
TextStyle _getTextStyle(Map<String, dynamic>? attributes) {
47+
if (attributes == null) return const TextStyle(fontSize: 14.0, color: Colors.black);
48+
49+
double fontSize = 14.0;
50+
if (attributes['header'] == 1) {
51+
fontSize = 28.0;
52+
} else if (attributes['header'] == 2) {
53+
fontSize = 22.0;
54+
} else if (attributes.containsKey('size')) {
55+
// You can add more specific size handling if needed
56+
fontSize = 14.0;
57+
}
58+
59+
final isCode = attributes['code-block'] == true;
60+
61+
return TextStyle(
62+
fontWeight: attributes['bold'] == true ? FontWeight.bold : FontWeight.normal,
63+
fontStyle: attributes['italic'] == true ? FontStyle.italic : FontStyle.normal,
64+
color: _parseColor(attributes['color'] as String?),
65+
fontSize: fontSize,
66+
fontFamily: isCode ? 'monospace' : (attributes['font'] as String?),
67+
decoration: attributes['underline'] == true ? TextDecoration.underline : TextDecoration.none,
68+
backgroundColor: attributes['background'] != null
69+
? _parseColor(attributes['background'] as String?)
70+
: null,
71+
);
72+
}
73+
4974
@override
5075
void paint(Canvas canvas, Size size) {
5176
for (final canvasObject in canvasObjects.values) {
@@ -54,8 +79,7 @@ class CanvasPainter extends CustomPainter {
5479

5580
if (canvasObject is Circle) {
5681
canvas.drawCircle(canvasObject.center, canvasObject.radius, paint);
57-
rect = Rect.fromCircle(
58-
center: canvasObject.center, radius: canvasObject.radius);
82+
rect = Rect.fromCircle(center: canvasObject.center, radius: canvasObject.radius);
5983
} else if (canvasObject is TextBoxObject) {
6084
rect = canvasObject.getBounds();
6185
if (canvasObject.color != Colors.transparent) {
@@ -68,10 +92,7 @@ class CanvasPainter extends CustomPainter {
6892
} else if (canvasObject is Square) {
6993
canvas.drawRect(rect, paint);
7094
} else if (canvasObject is RoundedSquare) {
71-
canvas.drawRRect(
72-
RRect.fromRectAndRadius(
73-
rect, Radius.circular(canvasObject.cornerRadius)),
74-
paint);
95+
canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(canvasObject.cornerRadius)), paint);
7596
} else if (canvasObject is Diamond) {
7697
final path = Path()
7798
..moveTo(rect.center.dx, rect.top)
@@ -105,134 +126,46 @@ class CanvasPainter extends CustomPainter {
105126
canvas.drawPath(path, paint);
106127
} else if (canvasObject is Cylinder) {
107128
final ellipseHeight = min(rect.height * 0.3, 40.0);
108-
final bodyRect = Rect.fromLTRB(rect.left,
109-
rect.top + ellipseHeight / 2, rect.right, rect.bottom - ellipseHeight / 2);
129+
final bodyRect = Rect.fromLTRB(rect.left, rect.top + ellipseHeight / 2, rect.right, rect.bottom - ellipseHeight / 2);
110130
canvas.drawRect(bodyRect, paint);
111-
canvas.drawOval(
112-
Rect.fromCenter(
113-
center: bodyRect.topCenter,
114-
width: rect.width,
115-
height: ellipseHeight),
116-
paint);
117-
canvas.drawOval(
118-
Rect.fromCenter(
119-
center: bodyRect.bottomCenter,
120-
width: rect.width,
121-
height: ellipseHeight),
122-
paint);
131+
canvas.drawOval(Rect.fromCenter(center: bodyRect.topCenter, width: rect.width, height: ellipseHeight), paint);
132+
canvas.drawOval(Rect.fromCenter(center: bodyRect.bottomCenter, width: rect.width, height: ellipseHeight), paint);
123133
}
124134
}
125135

126-
final bool isEditingText = interactionMode == InteractionMode.editingText &&
127-
currentlySelectedObjectId == canvasObject.id;
136+
final bool isEditingText = interactionMode == InteractionMode.editingText && currentlySelectedObjectId == canvasObject.id;
128137

129-
// CHANGE: Rewrote text rendering logic for proper styling.
130-
if (canvasObject.textDelta != null &&
131-
canvasObject.textDelta!.isNotEmpty &&
132-
!isEditingText) {
138+
// CHANGE: Rewrote text rendering logic to be more robust.
139+
if (canvasObject.textDelta != null && canvasObject.textDelta!.isNotEmpty && !isEditingText) {
133140
try {
134-
final List<dynamic> deltaJson = jsonDecode(canvasObject.textDelta!);
135-
final List<TextSpan> textSpans = [];
136-
int listCounter = 1;
137-
138-
for (final op in deltaJson) {
139-
if (op is Map && op.containsKey('insert')) {
140-
String text = op['insert'];
141-
final Map<String, dynamic>? attributes =
142-
op['attributes'] as Map<String, dynamic>?;
143-
144-
double fontSize = 14.0;
145-
FontWeight fontWeight = FontWeight.normal;
146-
FontStyle fontStyle = FontStyle.normal;
147-
TextDecoration textDecoration = TextDecoration.none;
148-
Color color = Colors.black;
149-
Color? backgroundColor;
150-
String? listType;
151-
152-
if (attributes != null) {
153-
fontWeight = attributes['bold'] == true
154-
? FontWeight.bold
155-
: FontWeight.normal;
156-
fontStyle = attributes['italic'] == true
157-
? FontStyle.italic
158-
: FontStyle.normal;
159-
textDecoration = attributes['underline'] == true
160-
? TextDecoration.underline
161-
: TextDecoration.none;
162-
color = _parseColor(attributes['color'] as String?);
163-
backgroundColor =
164-
_parseColor(attributes['background'] as String?);
165-
if (attributes['header'] == 1) fontSize = 24.0;
166-
if (attributes['header'] == 2) fontSize = 20.0;
167-
if (attributes['list'] != null) {
168-
listType = attributes['list'];
169-
}
170-
}
171-
172-
if (text.endsWith('\n') && listType != null) {
173-
if (listType == 'bullet') {
174-
text = '• ${text.substring(0, text.length -1)}\n';
175-
} else if (listType == 'ordered') {
176-
text = '$listCounter. ${text.substring(0, text.length -1)}\n';
177-
listCounter++;
178-
}
179-
} else if (listType == null) {
180-
listCounter = 1; // Reset counter when not in a list
181-
}
182-
183-
textSpans.add(
184-
TextSpan(
185-
text: text,
186-
style: TextStyle(
187-
fontSize: fontSize,
188-
fontWeight: fontWeight,
189-
fontStyle: fontStyle,
190-
decoration: textDecoration,
191-
color: color,
192-
backgroundColor: backgroundColor,
193-
),
194-
),
195-
);
196-
}
141+
final List<dynamic> delta = jsonDecode(canvasObject.textDelta!);
142+
143+
final List<InlineSpan> textSpans = [];
144+
for (final op in delta) {
145+
textSpans.add(TextSpan(
146+
text: op['insert'],
147+
style: _getTextStyle(op['attributes'] as Map<String, dynamic>?),
148+
));
197149
}
198-
199150
final richText = TextSpan(children: textSpans);
151+
200152
final textPainter = TextPainter(
201153
text: richText,
202154
textDirection: TextDirection.ltr,
203-
textAlign: TextAlign.start,
204155
);
205-
206-
double textPadding = 5.0;
207-
double availableWidth = rect.width - 2 * textPadding;
208-
209-
if (availableWidth <= 0) continue;
210-
211-
textPainter.layout(maxWidth: availableWidth);
212156

213-
final textOffset = Offset(
214-
rect.left + textPadding,
215-
rect.top + textPadding,
216-
);
217-
218-
canvas.save();
219-
canvas.clipRect(rect);
220-
textPainter.paint(canvas, textOffset);
221-
canvas.restore();
157+
final double textPadding = 8.0;
158+
final double availableWidth = rect.width - (2 * textPadding);
159+
if (availableWidth > 0) {
160+
textPainter.layout(maxWidth: availableWidth);
161+
canvas.save();
162+
canvas.clipRect(rect);
163+
textPainter.paint(canvas, rect.topLeft.translate(textPadding, textPadding));
164+
canvas.restore();
165+
}
222166

223167
} catch (e) {
224-
print(
225-
"Warning: Could not parse Quill Delta, rendering as plain text: $e");
226-
// Fallback for plain text
227-
final textPainter = TextPainter(
228-
text: TextSpan(
229-
text: canvasObject.textDelta,
230-
style: const TextStyle(color: Colors.black, fontSize: 14.0),
231-
),
232-
textDirection: TextDirection.ltr,
233-
);
234-
textPainter.layout(maxWidth: rect.width);
235-
textPainter.paint(canvas, rect.topLeft);
168+
print("Error painting text: $e");
236169
}
237170
}
238171

@@ -251,11 +184,8 @@ class CanvasPainter extends CustomPainter {
251184
..style = PaintingStyle.stroke
252185
..strokeWidth = 2.0;
253186

254-
if (canvasObject is TextBoxObject &&
255-
canvasObject.color == Colors.transparent) {
256-
// Draw dashed border for transparent text box
257-
final path = Path()
258-
..addRect(rect);
187+
if (canvasObject is TextBoxObject && canvasObject.color == Colors.transparent) {
188+
final path = Path()..addRect(rect);
259189
canvas.drawPath(
260190
dashPath(path, dashArray: CircularIntervalList<double>([5.0, 3.0])),
261191
borderPaint,
@@ -289,8 +219,7 @@ class CanvasPainter extends CustomPainter {
289219
_hasCanvasObjectsChanged(oldPainter.canvasObjects, canvasObjects);
290220
}
291221

292-
bool _hasCanvasObjectsChanged(
293-
Map<String, CanvasObject> oldObjects, Map<String, CanvasObject> newObjects) {
222+
bool _hasCanvasObjectsChanged(Map<String, CanvasObject> oldObjects, Map<String, CanvasObject> newObjects) {
294223
if (oldObjects.length != newObjects.length) return true;
295224
for (final id in newObjects.keys) {
296225
final newObj = newObjects[id];
@@ -303,11 +232,7 @@ class CanvasPainter extends CustomPainter {
303232
}
304233
}
305234

306-
// Copied from path_drawing package to avoid adding a dependency
307-
Path dashPath(
308-
Path source, {
309-
required CircularIntervalList<double> dashArray,
310-
}) {
235+
Path dashPath(Path source, {required CircularIntervalList<double> dashArray}) {
311236
final Path dest = Path();
312237
for (final metric in source.computeMetrics()) {
313238
double distance = 0.0;

lib/features/workspace/widgets/object_text_editor.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,13 @@ class _ObjectTextEditorState extends State<ObjectTextEditor> {
110110
controller: quillController,
111111
config: const QuillSimpleToolbarConfig(
112112
showBackgroundColorButton: true,
113-
showFontFamily: false,
114-
showLink: false,
113+
showFontFamily: true,
114+
showLink: true,
115115
showSearchButton: false,
116-
showInlineCode: false,
117-
showListCheck: false,
118-
showQuote: false,
119-
showCodeBlock: false,
116+
showInlineCode: true,
117+
showListCheck: true,
118+
showQuote: true,
119+
showCodeBlock: true,
120120
showListBullets: true,
121121
showListNumbers: true,
122122
showClearFormat: true,

0 commit comments

Comments
 (0)