Skip to content

Commit b4ae815

Browse files
authored
Refine IME composing range styling by applying underline as text style (singerdmx#2244)
1 parent 9d87315 commit b4ae815

File tree

3 files changed

+67
-61
lines changed

3 files changed

+67
-61
lines changed

lib/src/editor/raw_editor/raw_editor_state.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ class QuillRawEditorState extends EditorState
707707
linkActionPicker: _linkActionPicker,
708708
onLaunchUrl: widget.configurations.onLaunchUrl,
709709
customLinkPrefixes: widget.configurations.customLinkPrefixes,
710+
composingRange: composingRange.value,
710711
);
711712
final editableTextLine = EditableTextLine(
712713
node,
@@ -721,9 +722,7 @@ class QuillRawEditorState extends EditorState
721722
_hasFocus,
722723
MediaQuery.devicePixelRatioOf(context),
723724
_cursorCont,
724-
_styles!.inlineCode!,
725-
composingRange.value,
726-
_styles!.paragraph!.style.color!);
725+
_styles!.inlineCode!);
727726
return editableTextLine;
728727
}
729728

lib/src/editor/widgets/text/text_block.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class EditableTextBlock extends StatelessWidget {
195195
onLaunchUrl: onLaunchUrl,
196196
customLinkPrefixes: customLinkPrefixes,
197197
customRecognizerBuilder: customRecognizerBuilder,
198+
composingRange: composingRange,
198199
),
199200
indentWidthBuilder(block, context, count, numberPointWidthBuilder),
200201
_getSpacingForLine(line, index, count, defaultStyles),
@@ -206,8 +207,6 @@ class EditableTextBlock extends StatelessWidget {
206207
MediaQuery.devicePixelRatioOf(context),
207208
cursorCont,
208209
styles!.inlineCode!,
209-
composingRange,
210-
styles!.paragraph!.style.color!,
211210
);
212211
final nodeTextDirection = getDirectionOfNode(line, textDirection);
213212
children.add(

lib/src/editor/widgets/text/text_line.dart

Lines changed: 64 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class TextLine extends StatefulWidget {
3131
required this.controller,
3232
required this.onLaunchUrl,
3333
required this.linkActionPicker,
34+
required this.composingRange,
3435
this.textDirection,
3536
this.customStyleBuilder,
3637
this.customRecognizerBuilder,
@@ -49,6 +50,7 @@ class TextLine extends StatefulWidget {
4950
final ValueChanged<String>? onLaunchUrl;
5051
final LinkActionPicker linkActionPicker;
5152
final List<String> customLinkPrefixes;
53+
final TextRange composingRange;
5254

5355
@override
5456
State<TextLine> createState() => _TextLineState();
@@ -267,14 +269,70 @@ class _TextLineState extends State<TextLine> {
267269
if (nodes.isEmpty && kIsWeb) {
268270
nodes = LinkedList<Node>()..add(leaf.QuillText('\u{200B}'));
269271
}
270-
final children = nodes
271-
.map((node) =>
272-
_getTextSpanFromNode(defaultStyles, node, widget.line.style))
273-
.toList(growable: false);
272+
273+
final isComposingRangeOutOfLine = !widget.composingRange.isValid ||
274+
widget.composingRange.isCollapsed ||
275+
(widget.composingRange.start < widget.line.documentOffset ||
276+
widget.composingRange.end >
277+
widget.line.documentOffset + widget.line.length);
278+
279+
if (isComposingRangeOutOfLine) {
280+
final children = nodes
281+
.map((node) =>
282+
_getTextSpanFromNode(defaultStyles, node, widget.line.style))
283+
.toList(growable: false);
284+
return TextSpan(children: children, style: lineStyle);
285+
}
286+
287+
final children = nodes.expand((node) {
288+
final child =
289+
_getTextSpanFromNode(defaultStyles, node, widget.line.style);
290+
final isNodeInComposingRange =
291+
node.documentOffset <= widget.composingRange.start &&
292+
widget.composingRange.end <= node.documentOffset + node.length;
293+
if (isNodeInComposingRange) {
294+
return _splitAndApplyComposingStyle(node, child);
295+
} else {
296+
return [child];
297+
}
298+
}).toList(growable: false);
274299

275300
return TextSpan(children: children, style: lineStyle);
276301
}
277302

303+
// split the text nodes into composing and non-composing nodes
304+
// and apply the composing style to the composing nodes
305+
List<InlineSpan> _splitAndApplyComposingStyle(Node node, InlineSpan child) {
306+
assert(widget.composingRange.isValid && !widget.composingRange.isCollapsed);
307+
308+
final composingStart = widget.composingRange.start - node.documentOffset;
309+
final composingEnd = widget.composingRange.end - node.documentOffset;
310+
final text = child.toPlainText();
311+
312+
final textBefore = text.substring(0, composingStart);
313+
final textComposing = text.substring(composingStart, composingEnd);
314+
final textAfter = text.substring(composingEnd);
315+
316+
final composingStyle = child.style
317+
?.merge(const TextStyle(decoration: TextDecoration.underline)) ??
318+
const TextStyle(decoration: TextDecoration.underline);
319+
320+
return [
321+
TextSpan(
322+
text: textBefore,
323+
style: child.style,
324+
),
325+
TextSpan(
326+
text: textComposing,
327+
style: composingStyle,
328+
),
329+
TextSpan(
330+
text: textAfter,
331+
style: child.style,
332+
),
333+
];
334+
}
335+
278336
TextStyle _getLineStyle(DefaultStyles defaultStyles) {
279337
var textStyle = const TextStyle();
280338

@@ -643,8 +701,6 @@ class EditableTextLine extends RenderObjectWidget {
643701
this.devicePixelRatio,
644702
this.cursorCont,
645703
this.inlineCodeStyle,
646-
this.composingRange,
647-
this.composingColor,
648704
{super.key});
649705

650706
final Line line;
@@ -660,8 +716,6 @@ class EditableTextLine extends RenderObjectWidget {
660716
final double devicePixelRatio;
661717
final CursorCont cursorCont;
662718
final InlineCodeStyle inlineCodeStyle;
663-
final TextRange composingRange;
664-
final Color composingColor;
665719

666720
@override
667721
RenderObjectElement createElement() {
@@ -680,9 +734,7 @@ class EditableTextLine extends RenderObjectWidget {
680734
_getPadding(),
681735
color,
682736
cursorCont,
683-
inlineCodeStyle,
684-
composingRange,
685-
composingColor);
737+
inlineCodeStyle);
686738
}
687739

688740
@override
@@ -698,8 +750,7 @@ class EditableTextLine extends RenderObjectWidget {
698750
..hasFocus = hasFocus
699751
..setDevicePixelRatio(devicePixelRatio)
700752
..setCursorCont(cursorCont)
701-
..setInlineCodeStyle(inlineCodeStyle)
702-
..setComposingRange(composingRange);
753+
..setInlineCodeStyle(inlineCodeStyle);
703754
}
704755

705756
EdgeInsetsGeometry _getPadding() {
@@ -726,8 +777,6 @@ class RenderEditableTextLine extends RenderEditableBox {
726777
this.color,
727778
this.cursorCont,
728779
this.inlineCodeStyle,
729-
this.composingRange,
730-
this.composingColor,
731780
);
732781

733782
RenderBox? _leading;
@@ -746,8 +795,6 @@ class RenderEditableTextLine extends RenderEditableBox {
746795
List<TextBox>? _selectedRects;
747796
late Rect _caretPrototype;
748797
InlineCodeStyle inlineCodeStyle;
749-
TextRange composingRange;
750-
Color composingColor;
751798
final Map<TextLineSlot, RenderBox> children = <TextLineSlot, RenderBox>{};
752799

753800
Iterable<RenderBox> get _children sync* {
@@ -863,12 +910,6 @@ class RenderEditableTextLine extends RenderEditableBox {
863910
markNeedsLayout();
864911
}
865912

866-
void setComposingRange(TextRange newComposingRange) {
867-
if (composingRange == newComposingRange) return;
868-
composingRange = newComposingRange;
869-
markNeedsLayout();
870-
}
871-
872913
// Start selection implementation
873914

874915
bool containsTextSelection() {
@@ -1351,11 +1392,6 @@ class RenderEditableTextLine extends RenderEditableBox {
13511392

13521393
_paintSelection(context, effectiveOffset);
13531394
}
1354-
1355-
// Paints an underline to indicate the text being composed by the IME.
1356-
if (composingRange.isValid) {
1357-
_paintComposing(context);
1358-
}
13591395
}
13601396
}
13611397

@@ -1387,34 +1423,6 @@ class RenderEditableTextLine extends RenderEditableBox {
13871423
);
13881424
}
13891425

1390-
// Paints a line below the composing text.
1391-
void _paintComposing(PaintingContext context) {
1392-
assert(composingRange.isValid);
1393-
final composingStart = composingRange.start - line.documentOffset;
1394-
final composingEnd = composingRange.end - line.documentOffset;
1395-
if (composingStart < 0 || composingEnd < 0) {
1396-
return;
1397-
}
1398-
final composingRects = _body!.getBoxesForSelection(
1399-
TextSelection(
1400-
baseOffset: composingStart,
1401-
extentOffset: composingEnd,
1402-
),
1403-
);
1404-
final paint = Paint()
1405-
..color = composingColor
1406-
..style = PaintingStyle.stroke
1407-
..strokeWidth = 1;
1408-
for (final box in composingRects) {
1409-
final rect = box.toRect();
1410-
context.canvas.drawLine(
1411-
rect.bottomLeft.translate(0, -5),
1412-
rect.bottomRight.translate(0, -5),
1413-
paint,
1414-
);
1415-
}
1416-
}
1417-
14181426
@override
14191427
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
14201428
if (_leading != null) {

0 commit comments

Comments
 (0)