From c4c10cd7b41a6bdabf591a3646c5f7162ad0c178 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Sun, 17 Dec 2023 21:59:01 +0100 Subject: [PATCH] Refactor text logic to enable multi input characters --- api/lib/src/models/text.dart | 10 ++++-- app/lib/handlers/label.dart | 64 ++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/api/lib/src/models/text.dart b/api/lib/src/models/text.dart index c27d7b2736ee..ca2080b38f04 100644 --- a/api/lib/src/models/text.dart +++ b/api/lib/src/models/text.dart @@ -240,10 +240,14 @@ sealed class TextParagraph with _$TextParagraph { return copyWith(textSpans: subSpans); } - TextParagraph replaceText(String text, [int start = 0, int length = 0]) { + TextParagraph replaceText(String text, + [int start = 0, int length = 0, bool replaceWholeSpan = false]) { + final indexed = getIndexedSpan(start); return replace( - getSpan(start)?.copyWith(text: text) ?? TextSpan.text(text: text), - start, + indexed != null + ? indexed.model.copyWith(text: text) + : TextSpan.text(text: text), + (replaceWholeSpan ? indexed?.index : null) ?? start, length); } diff --git a/app/lib/handlers/label.dart b/app/lib/handlers/label.dart index fa2cf4967493..f494cbfdcc40 100644 --- a/app/lib/handlers/label.dart +++ b/app/lib/handlers/label.dart @@ -188,7 +188,7 @@ class LabelHandler extends Handler enableSuggestions: false, enableInteractiveSelection: true, )) - ..setEditingState(currentTextEditingValue ?? const TextEditingValue()) + ..setEditingState(currentTextEditingValue) ..setStyle( fontFamily: style.fontFamily, fontSize: style.fontSize! * pixelRatio, @@ -354,20 +354,35 @@ class LabelHandler extends Handler AutofillScope? get currentAutofillScope => null; @override - TextEditingValue? get currentTextEditingValue => _context?.element == null - ? null - : TextEditingValue( - text: _context!.text!, - selection: _context!.selection, - composing: TextRange(start: 0, end: _context!.text!.length), - ); + TextEditingValue get currentTextEditingValue { + final context = _context; + if (context == null) return const TextEditingValue(); + final element = context.element; + final text = context.text; + if (element == null || text == null) return const TextEditingValue(); + var (indexed, length) = element.maybeMap( + text: (e) { + final indexed = + e.area.paragraph.getIndexedSpan(context.selection.start); + if (indexed == null) return (0, text.length); + return (indexed.index, indexed.model.length); + }, + orElse: () => (0, text.length), + ); + + return TextEditingValue( + text: text, + selection: context.selection, + composing: TextRange(start: indexed, end: length), + ); + } @override void performAction(TextInputAction action) { switch (action) { case TextInputAction.newline: case TextInputAction.done: - _updateText('\n'); + _updateText('\n', false); break; default: } @@ -385,22 +400,27 @@ class LabelHandler extends Handler _updateText(value.text); } - void _updateText(String value) { + void _updateText(String value, [bool replace = true]) { TextElement element; final state = _bloc?.state; if (state is! DocumentLoadSuccess || _context == null) return; final data = state.data; var newIndex = value.length; - final selection = _context!.selection; - final start = selection.start; - final length = selection.end - start; - newIndex += selection.start; + final lastValue = currentTextEditingValue; + final start = + replace ? lastValue.composing.start : lastValue.selection.start; + final length = replace + ? lastValue.composing.end - start + : lastValue.selection.end - start; + newIndex += lastValue.selection.start; _context = _context?.map(text: (e) { final old = e.element; if (old != null) { + final currentProperty = e.getSpanProperty(data); final newSpan = - e.getDefinedForcedSpanProperty(data) != e.getSpanProperty(data); + e.getDefinedForcedSpanProperty(data) != currentProperty && + currentProperty != null; final paragraph = newSpan ? old.area.paragraph.replace( text.TextSpan.text( @@ -410,7 +430,7 @@ class LabelHandler extends Handler ), start, length) - : old.area.paragraph.replaceText(value, start, length); + : old.area.paragraph.replaceText(value, start, length, replace); final area = old.area.copyWith( paragraph: paragraph, ); @@ -432,8 +452,8 @@ class LabelHandler extends Handler ); }, markdown: (e) { var text = e.text ?? ''; - text = - text.replaceRange(start, selection.end.clamp(0, text.length), value); + text = text.replaceRange( + start, lastValue.selection.end.clamp(0, text.length), value); return e.copyWith( element: e.element?.copyWith( text: text, @@ -441,9 +461,7 @@ class LabelHandler extends Handler selection: TextSelection.collapsed(offset: newIndex), ); }); - _connection?.setEditingState(const TextEditingValue( - text: '', - )); + _connection?.setEditingState(currentTextEditingValue); _bloc?.refresh(); if (_bloc != null) _refreshToolbar(_bloc!); } @@ -532,6 +550,7 @@ class LabelHandler extends Handler }); bloc.refresh(); _refreshToolbar(bloc); + _connection?.setEditingState(currentTextEditingValue); return null; }, ), @@ -549,6 +568,7 @@ class LabelHandler extends Handler ), ); bloc.refresh(); + _connection?.setEditingState(currentTextEditingValue); return null; }, ), @@ -588,6 +608,7 @@ class LabelHandler extends Handler ); bloc.refresh(); _refreshToolbar(bloc); + _connection?.setEditingState(currentTextEditingValue); return null; }, ), @@ -651,6 +672,7 @@ class LabelHandler extends Handler bloc.refresh(); _refreshToolbar(bloc); + _connection?.setEditingState(currentTextEditingValue); return null; }, ),