@@ -42,32 +42,44 @@ class CanvasPainter extends CustomPainter {
4242 }
4343 }
4444
45- // CHANGE: More robust style parsing
45+ // FIX: Added robust font size parsing
46+ double _getFontSize (dynamic size) {
47+ if (size == null ) return 14.0 ;
48+ if (size is double ) return size;
49+ if (size is int ) return size.toDouble ();
50+ if (size is String ) {
51+ switch (size) {
52+ case 'small' :
53+ return 10.0 ;
54+ case 'large' :
55+ return 18.0 ;
56+ case 'huge' :
57+ return 22.0 ;
58+ default :
59+ return double .tryParse (size) ?? 14.0 ;
60+ }
61+ }
62+ return 14.0 ;
63+ }
64+
4665 TextStyle _getTextStyle (Map <String , dynamic >? attributes) {
4766 if (attributes == null ) return const TextStyle (fontSize: 14.0 , color: Colors .black);
4867
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 ;
68+ final isLink = attributes['link' ] != null ;
69+ final isCode = attributes['code' ] == true || attributes['code-block' ] == true ;
6070
6171 return TextStyle (
6272 fontWeight: attributes['bold' ] == true ? FontWeight .bold : FontWeight .normal,
6373 fontStyle: attributes['italic' ] == true ? FontStyle .italic : FontStyle .normal,
64- color: _parseColor (attributes['color' ] as String ? ),
65- fontSize: fontSize,
74+ // FIX: Handle links correctly
75+ color: isLink ? Colors .blue : _parseColor (attributes['color' ] as String ? ),
76+ fontSize: _getFontSize (attributes['size' ] ?? (attributes['header' ] == 1 ? 'huge' : (attributes['header' ] == 2 ? 'large' : null ))),
6677 fontFamily: isCode ? 'monospace' : (attributes['font' ] as String ? ),
67- decoration: attributes['underline' ] == true ? TextDecoration .underline : TextDecoration .none,
78+ // FIX: Handle links and inline code underline
79+ decoration: attributes['underline' ] == true || isLink ? TextDecoration .underline : TextDecoration .none,
6880 backgroundColor: attributes['background' ] != null
6981 ? _parseColor (attributes['background' ] as String ? )
70- : null ,
82+ : (isCode && attributes[ 'code-block' ] != true ? Colors .grey.shade300 : null ) ,
7183 );
7284 }
7385
@@ -95,34 +107,17 @@ class CanvasPainter extends CustomPainter {
95107 canvas.drawRRect (RRect .fromRectAndRadius (rect, Radius .circular (canvasObject.cornerRadius)), paint);
96108 } else if (canvasObject is Diamond ) {
97109 final path = Path ()
98- ..moveTo (rect.center.dx, rect.top)
99- ..lineTo (rect.right, rect.center.dy)
100- ..lineTo (rect.center.dx, rect.bottom)
101- ..lineTo (rect.left, rect.center.dy)
102- ..close ();
110+ ..moveTo (rect.center.dx, rect.top)..lineTo (rect.right, rect.center.dy)..lineTo (rect.center.dx, rect.bottom)..lineTo (rect.left, rect.center.dy)..close ();
103111 canvas.drawPath (path, paint);
104112 } else if (canvasObject is Triangle ) {
105- final path = Path ()
106- ..moveTo (rect.center.dx, rect.top)
107- ..lineTo (rect.right, rect.bottom)
108- ..lineTo (rect.left, rect.bottom)
109- ..close ();
113+ final path = Path ()..moveTo (rect.center.dx, rect.top)..lineTo (rect.right, rect.bottom)..lineTo (rect.left, rect.bottom)..close ();
110114 canvas.drawPath (path, paint);
111115 } else if (canvasObject is InvertedTriangle ) {
112- final path = Path ()
113- ..moveTo (rect.left, rect.top)
114- ..lineTo (rect.right, rect.top)
115- ..lineTo (rect.center.dx, rect.bottom)
116- ..close ();
116+ final path = Path ()..moveTo (rect.left, rect.top)..lineTo (rect.right, rect.top)..lineTo (rect.center.dx, rect.bottom)..close ();
117117 canvas.drawPath (path, paint);
118118 } else if (canvasObject is Parallelogram ) {
119119 final skew = rect.width * 0.25 ;
120- final path = Path ()
121- ..moveTo (rect.left + skew, rect.top)
122- ..lineTo (rect.right, rect.top)
123- ..lineTo (rect.right - skew, rect.bottom)
124- ..lineTo (rect.left, rect.bottom)
125- ..close ();
120+ final path = Path ()..moveTo (rect.left + skew, rect.top)..lineTo (rect.right, rect.top)..lineTo (rect.right - skew, rect.bottom)..lineTo (rect.left, rect.bottom)..close ();
126121 canvas.drawPath (path, paint);
127122 } else if (canvasObject is Cylinder ) {
128123 final ellipseHeight = min (rect.height * 0.3 , 40.0 );
@@ -135,61 +130,99 @@ class CanvasPainter extends CustomPainter {
135130
136131 final bool isEditingText = interactionMode == InteractionMode .editingText && currentlySelectedObjectId == canvasObject.id;
137132
138- // CHANGE: Rewrote text rendering logic to be more robust .
133+ // FIX: Complete rewrite of the text rendering logic .
139134 if (canvasObject.textDelta != null && canvasObject.textDelta! .isNotEmpty && ! isEditingText) {
140135 try {
141136 final List <dynamic > delta = jsonDecode (canvasObject.textDelta! );
137+ final double textPadding = 8.0 ;
138+ double yOffset = rect.top + textPadding;
142139
143- final List <InlineSpan > textSpans = [];
140+ List <Map < String , dynamic >> currentLineOps = [];
144141 for (final op in delta) {
145- textSpans.add (TextSpan (
146- text: op['insert' ],
147- style: _getTextStyle (op['attributes' ] as Map <String , dynamic >? ),
148- ));
142+ final String text = op['insert' ];
143+ final Map <String , dynamic >? attributes = op['attributes' ] as Map <String , dynamic >? ;
144+
145+ if (text.contains ('\n ' )) {
146+ final lines = text.split ('\n ' );
147+ for (int i = 0 ; i < lines.length; i++ ) {
148+ if (lines[i].isNotEmpty) {
149+ currentLineOps.add ({'insert' : lines[i], 'attributes' : attributes});
150+ }
151+
152+ if (i < lines.length - 1 ) { // This is a line break
153+ final lineSpans = currentLineOps.map ((o) => TextSpan (text: o['insert' ], style: _getTextStyle (o['attributes' ]))).toList ();
154+
155+ // Check for block attributes on the line break
156+ final blockAttributes = attributes ?? {};
157+ String prefix = '' ;
158+ double indent = 0 ;
159+ if (blockAttributes['list' ] == 'bullet' ) {
160+ prefix = '• ' ;
161+ indent = 10.0 ;
162+ } else if (blockAttributes['list' ] == 'ordered' ) {
163+ prefix = '1. ' ; // This is simplified, a proper implementation needs a counter
164+ indent = 10.0 ;
165+ } else if (blockAttributes['blockquote' ] == true ) {
166+ indent = 20.0 ;
167+ }
168+
169+ if (blockAttributes['code-block' ] == true ) {
170+ final blockPaint = Paint ()..color = Colors .grey.shade200;
171+ // This is a simplified block drawing, would need to calculate total block height for a perfect rect
172+ // For now, it draws a rect behind each line of the code block.
173+ canvas.drawRect (Rect .fromLTWH (rect.left, yOffset, rect.width, 20 ), blockPaint); // Approximate height
174+ }
175+
176+ final textPainter = TextPainter (
177+ text: TextSpan (children: [TextSpan (text: prefix), ...lineSpans]),
178+ textDirection: TextDirection .ltr,
179+ );
180+
181+ final availableWidth = rect.width - (2 * textPadding) - indent;
182+ if (availableWidth > 0 ) {
183+ textPainter.layout (maxWidth: availableWidth);
184+ textPainter.paint (canvas, Offset (rect.left + textPadding + indent, yOffset));
185+ yOffset += textPainter.height;
186+ }
187+ currentLineOps = [];
188+ }
189+ }
190+ } else {
191+ currentLineOps.add (op);
192+ }
149193 }
150- final richText = TextSpan (children: textSpans);
151-
152- final textPainter = TextPainter (
153- text: richText,
154- textDirection: TextDirection .ltr,
155- );
156-
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 ();
194+
195+ // Paint any remaining text that didn't end with a newline
196+ if (currentLineOps.isNotEmpty) {
197+ final lineSpans = currentLineOps.map ((o) => TextSpan (text: o['insert' ], style: _getTextStyle (o['attributes' ]))).toList ();
198+ final textPainter = TextPainter (
199+ text: TextSpan (children: lineSpans),
200+ textDirection: TextDirection .ltr,
201+ );
202+ final availableWidth = rect.width - (2 * textPadding);
203+ if (availableWidth > 0 ) {
204+ textPainter.layout (maxWidth: availableWidth);
205+ textPainter.paint (canvas, Offset (rect.left + textPadding, yOffset));
206+ }
165207 }
166208
167209 } catch (e) {
168210 print ("Error painting text: $e " );
169211 }
170212 }
171213
172- if (canvasObject.id == currentlySelectedObjectId && ! isEditingText) {
173- final handlePaint = Paint ()
174- ..color = Colors .blue
175- ..style = PaintingStyle .fill;
176214
215+ if (canvasObject.id == currentlySelectedObjectId && ! isEditingText) {
216+ final handlePaint = Paint ()..color = Colors .blue..style = PaintingStyle .fill;
177217 canvas.drawCircle (rect.topLeft, handleRadius, handlePaint);
178218 canvas.drawCircle (rect.topRight, handleRadius, handlePaint);
179219 canvas.drawCircle (rect.bottomLeft, handleRadius, handlePaint);
180220 canvas.drawCircle (rect.bottomRight, handleRadius, handlePaint);
181221
182- final borderPaint = Paint ()
183- ..color = Colors .blue
184- ..style = PaintingStyle .stroke
185- ..strokeWidth = 2.0 ;
186-
222+ final borderPaint = Paint ()..color = Colors .blue..style = PaintingStyle .stroke..strokeWidth = 2.0 ;
187223 if (canvasObject is TextBoxObject && canvasObject.color == Colors .transparent) {
188224 final path = Path ()..addRect (rect);
189- canvas.drawPath (
190- dashPath (path, dashArray: CircularIntervalList <double >([5.0 , 3.0 ])),
191- borderPaint,
192- );
225+ canvas.drawPath (dashPath (path, dashArray: CircularIntervalList <double >([5.0 , 3.0 ])), borderPaint);
193226 } else {
194227 canvas.drawRect (rect, borderPaint);
195228 }
@@ -198,15 +231,10 @@ class CanvasPainter extends CustomPainter {
198231
199232 for (final userCursor in userCursors.values) {
200233 final position = userCursor.position;
201- final paint = Paint ()..color = userCursor.color;
234+ final paint = Paint ()..color = userCursor.color..strokeWidth = 2 ;
202235 final path = Path ()
203- ..moveTo (position.dx, position.dy)
204- ..lineTo (position.dx, position.dy + 20 )
205- ..lineTo (position.dx + 5 , position.dy + 15 )
206- ..moveTo (position.dx, position.dy + 20 )
207- ..lineTo (position.dx + 10 , position.dy + 20 )
208- ..close ();
209- canvas.drawPath (path, paint..strokeWidth = 2 );
236+ ..moveTo (position.dx, position.dy)..lineTo (position.dx, position.dy + 20 )..lineTo (position.dx + 5 , position.dy + 15 )..moveTo (position.dx, position.dy + 20 )..lineTo (position.dx + 10 , position.dy + 20 )..close ();
237+ canvas.drawPath (path, paint);
210238 }
211239 }
212240
0 commit comments