Skip to content

Commit

Permalink
fix: the composing text did not show an underline during IME conversi…
Browse files Browse the repository at this point in the history
…on (#2242)
  • Loading branch information
agata committed Sep 18, 2024
1 parent 8c644e4 commit eea56e9
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 5 deletions.
16 changes: 15 additions & 1 deletion lib/src/editor/raw_editor/raw_editor_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ class QuillRawEditorState extends EditorState
widget.configurations.customRecognizerBuilder,
customStyleBuilder: widget.configurations.customStyleBuilder,
customLinkPrefixes: widget.configurations.customLinkPrefixes,
composingRange: composingRange.value,
);
result.add(
Directionality(
Expand Down Expand Up @@ -720,7 +721,9 @@ class QuillRawEditorState extends EditorState
_hasFocus,
MediaQuery.devicePixelRatioOf(context),
_cursorCont,
_styles!.inlineCode!);
_styles!.inlineCode!,
composingRange.value,
_styles!.paragraph!.style.color!);
return editableTextLine;
}

Expand Down Expand Up @@ -850,6 +853,9 @@ class QuillRawEditorState extends EditorState
_floatingCursorResetController = AnimationController(vsync: this);
_floatingCursorResetController.addListener(onFloatingCursorResetTick);

// listen to composing range changes
composingRange.addListener(_onComposingRangeChanged);

if (isKeyboardOS) {
_keyboardVisible = true;
} else if (!kIsWeb && isFlutterTest) {
Expand Down Expand Up @@ -987,6 +993,7 @@ class QuillRawEditorState extends EditorState
controller.removeListener(_didChangeTextEditingValueListener);
widget.configurations.focusNode.removeListener(_handleFocusChanged);
_cursorCont.dispose();
composingRange.removeListener(_onComposingRangeChanged);
if (_clipboardStatus != null) {
_clipboardStatus!
..removeListener(_onChangedClipboardStatus)
Expand All @@ -999,6 +1006,13 @@ class QuillRawEditorState extends EditorState
_selectionOverlay?.updateForScroll();
}

void _onComposingRangeChanged() {
if (!mounted) {
return;
}
_markNeedsBuild();
}

/// Marks the editor as dirty and trigger a rebuild.
///
/// When the editor is dirty methods that depend on the editor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:ui' show lerpDouble;

import 'package:flutter/animation.dart' show Curves;
import 'package:flutter/cupertino.dart' show CupertinoTheme;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/foundation.dart' show ValueNotifier, kIsWeb;
import 'package:flutter/material.dart' show Theme;
import 'package:flutter/scheduler.dart' show SchedulerBinding;
import 'package:flutter/services.dart';
Expand All @@ -15,7 +15,22 @@ import 'raw_editor.dart';
mixin RawEditorStateTextInputClientMixin on EditorState
implements TextInputClient {
TextInputConnection? _textInputConnection;
TextEditingValue? _lastKnownRemoteTextEditingValue;
TextEditingValue? __lastKnownRemoteTextEditingValue;

set _lastKnownRemoteTextEditingValue(TextEditingValue? value) {
__lastKnownRemoteTextEditingValue = value;
if (composingRange.value != value?.composing) {
composingRange.value = value?.composing ?? TextRange.empty;
}
}

TextEditingValue? get _lastKnownRemoteTextEditingValue =>
__lastKnownRemoteTextEditingValue;

/// The range of text that is currently being composed.
final ValueNotifier<TextRange> composingRange = ValueNotifier<TextRange>(
TextRange.empty,
);

/// Whether to create an input connection with the platform for text editing
/// or not.
Expand Down
4 changes: 4 additions & 0 deletions lib/src/editor/widgets/text/text_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class EditableTextBlock extends StatelessWidget {
required this.onCheckboxTap,
required this.readOnly,
required this.customRecognizerBuilder,
required this.composingRange,
this.checkBoxReadOnly,
this.onLaunchUrl,
this.customStyleBuilder,
Expand Down Expand Up @@ -111,6 +112,7 @@ class EditableTextBlock extends StatelessWidget {
final bool readOnly;
final bool? checkBoxReadOnly;
final List<String> customLinkPrefixes;
final TextRange composingRange;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -204,6 +206,8 @@ class EditableTextBlock extends StatelessWidget {
MediaQuery.devicePixelRatioOf(context),
cursorCont,
styles!.inlineCode!,
composingRange,
styles!.paragraph!.style.color!,
);
final nodeTextDirection = getDirectionOfNode(line, textDirection);
children.add(
Expand Down
54 changes: 52 additions & 2 deletions lib/src/editor/widgets/text/text_line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,8 @@ class EditableTextLine extends RenderObjectWidget {
this.devicePixelRatio,
this.cursorCont,
this.inlineCodeStyle,
this.composingRange,
this.composingColor,
{super.key});

final Line line;
Expand All @@ -658,6 +660,8 @@ class EditableTextLine extends RenderObjectWidget {
final double devicePixelRatio;
final CursorCont cursorCont;
final InlineCodeStyle inlineCodeStyle;
final TextRange composingRange;
final Color composingColor;

@override
RenderObjectElement createElement() {
Expand All @@ -676,7 +680,9 @@ class EditableTextLine extends RenderObjectWidget {
_getPadding(),
color,
cursorCont,
inlineCodeStyle);
inlineCodeStyle,
composingRange,
composingColor);
}

@override
Expand All @@ -692,7 +698,8 @@ class EditableTextLine extends RenderObjectWidget {
..hasFocus = hasFocus
..setDevicePixelRatio(devicePixelRatio)
..setCursorCont(cursorCont)
..setInlineCodeStyle(inlineCodeStyle);
..setInlineCodeStyle(inlineCodeStyle)
..setComposingRange(composingRange);
}

EdgeInsetsGeometry _getPadding() {
Expand All @@ -719,6 +726,8 @@ class RenderEditableTextLine extends RenderEditableBox {
this.color,
this.cursorCont,
this.inlineCodeStyle,
this.composingRange,
this.composingColor,
);

RenderBox? _leading;
Expand All @@ -737,6 +746,8 @@ class RenderEditableTextLine extends RenderEditableBox {
List<TextBox>? _selectedRects;
late Rect _caretPrototype;
InlineCodeStyle inlineCodeStyle;
TextRange composingRange;
Color composingColor;
final Map<TextLineSlot, RenderBox> children = <TextLineSlot, RenderBox>{};

Iterable<RenderBox> get _children sync* {
Expand Down Expand Up @@ -852,6 +863,12 @@ class RenderEditableTextLine extends RenderEditableBox {
markNeedsLayout();
}

void setComposingRange(TextRange newComposingRange) {
if (composingRange == newComposingRange) return;
composingRange = newComposingRange;
markNeedsLayout();
}

// Start selection implementation

bool containsTextSelection() {
Expand Down Expand Up @@ -1334,6 +1351,11 @@ class RenderEditableTextLine extends RenderEditableBox {

_paintSelection(context, effectiveOffset);
}

// Paints an underline to indicate the text being composed by the IME.
if (composingRange.isValid) {
_paintComposing(context);
}
}
}

Expand Down Expand Up @@ -1365,6 +1387,34 @@ class RenderEditableTextLine extends RenderEditableBox {
);
}

// Paints a line below the composing text.
void _paintComposing(PaintingContext context) {
assert(composingRange.isValid);
final composingStart = composingRange.start - line.documentOffset;
final composingEnd = composingRange.end - line.documentOffset;
if (composingStart < 0 || composingEnd < 0) {
return;
}
final composingRects = _body!.getBoxesForSelection(
TextSelection(
baseOffset: composingStart,
extentOffset: composingEnd,
),
);
final paint = Paint()
..color = composingColor
..style = PaintingStyle.stroke
..strokeWidth = 1;
for (final box in composingRects) {
final rect = box.toRect();
context.canvas.drawLine(
rect.bottomLeft.translate(0, -5),
rect.bottomRight.translate(0, -5),
paint,
);
}
}

@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
if (_leading != null) {
Expand Down

0 comments on commit eea56e9

Please sign in to comment.