feat: support IME in macOS

This commit is contained in:
Lucas.Xu 2022-08-05 20:05:36 +08:00
parent 70853b918e
commit ec47aa51a1
3 changed files with 60 additions and 35 deletions

View File

@ -55,4 +55,18 @@ class Selection {
"end": end.toJson(),
};
}
@override
bool operator ==(Object other) {
if (other is! Selection) {
return false;
}
if (identical(this, other)) {
return true;
}
return start == other.start && end == other.end;
}
@override
int get hashCode => Object.hash(start, end);
}

View File

@ -63,8 +63,6 @@ class FlowyEditor extends StatefulWidget {
}
class _FlowyEditorState extends State<FlowyEditor> {
late ScrollController _scrollController;
EditorState get editorState => widget.editorState;
@override
@ -74,13 +72,6 @@ class _FlowyEditorState extends State<FlowyEditor> {
editorState.service.renderPluginService = _createRenderPlugin();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
void didUpdateWidget(covariant FlowyEditor oldWidget) {
super.didUpdateWidget(oldWidget);

View File

@ -2,14 +2,15 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flowy_editor/document/node.dart';
import 'package:flowy_editor/document/path.dart';
import 'package:flowy_editor/document/position.dart';
import 'package:flowy_editor/document/selection.dart';
import 'package:flowy_editor/editor_state.dart';
import 'package:flowy_editor/extensions/node_extensions.dart';
import 'package:flowy_editor/operation/transaction_builder.dart';
mixin FlowyInputService {
void attach(TextEditingValue textEditingValue);
void setTextEditingValue(TextEditingValue textEditingValue);
void apply(List<TextEditingDelta> deltas);
void close();
}
@ -33,6 +34,7 @@ class _FlowyInputState extends State<FlowyInput>
with FlowyInputService
implements DeltaTextInputClient {
TextInputConnection? _textInputConnection;
TextRange? _composingTextRange;
EditorState get _editorState => widget.editorState;
@ -46,6 +48,7 @@ class _FlowyInputState extends State<FlowyInput>
@override
void dispose() {
close();
_editorState.service.selectionService.currentSelectedNodes
.removeListener(_onSelectedNodesChange);
@ -61,11 +64,7 @@ class _FlowyInputState extends State<FlowyInput>
@override
void attach(TextEditingValue textEditingValue) {
if (_textInputConnection != null) {
return;
}
_textInputConnection = TextInput.attach(
_textInputConnection ??= TextInput.attach(
this,
const TextInputConfiguration(
// TODO: customize
@ -75,18 +74,9 @@ class _FlowyInputState extends State<FlowyInput>
),
);
_textInputConnection
?..show()
..setEditingState(textEditingValue);
}
@override
void setTextEditingValue(TextEditingValue textEditingValue) {
assert(_textInputConnection != null,
'Must call `attach` before set textEditingValue');
if (_textInputConnection != null) {
_textInputConnection?.setEditingState(textEditingValue);
}
_textInputConnection!
..setEditingState(textEditingValue)
..show();
}
@override
@ -94,13 +84,21 @@ class _FlowyInputState extends State<FlowyInput>
// TODO: implement the detail
for (final delta in deltas) {
if (delta is TextEditingDeltaInsertion) {
if (_composingTextRange != null) {
_composingTextRange = TextRange(
start: _composingTextRange!.start,
end: delta.composing.end,
);
} else {
_composingTextRange = delta.composing;
}
_applyInsert(delta);
} else if (delta is TextEditingDeltaDeletion) {
} else if (delta is TextEditingDeltaReplacement) {
_applyReplacement(delta);
} else if (delta is TextEditingDeltaNonTextUpdate) {
// We don't need to care the [TextEditingDeltaNonTextUpdate].
// Do nothing.
_composingTextRange = null;
}
}
}
@ -212,12 +210,12 @@ class _FlowyInputState extends State<FlowyInput>
}
void _onSelectedNodesChange() {
final nodes =
_editorState.service.selectionService.currentSelectedNodes.value;
final textNodes = _editorState
.service.selectionService.currentSelectedNodes.value
.whereType<TextNode>();
final selection = _editorState.service.selectionService.currentSelection;
// FIXME: upward.
if (nodes.isNotEmpty && selection != null) {
final textNodes = nodes.whereType<TextNode>();
// FIXME: upward and selection update.
if (textNodes.isNotEmpty && selection != null) {
final text = textNodes.fold<String>(
'', (sum, textNode) => '$sum${textNode.toRawString()}\n');
attach(
@ -227,10 +225,32 @@ class _FlowyInputState extends State<FlowyInput>
baseOffset: selection.start.offset,
extentOffset: selection.end.offset,
),
composing: _composingTextRange ?? const TextRange.collapsed(-1),
),
);
if (textNodes.length == 1) {
_updateCaretPosition(textNodes.first, selection);
}
} else {
close();
// close();
}
}
// TODO: support IME in linux / windows / ios / android
// Only support macOS now.
void _updateCaretPosition(TextNode textNode, Selection selection) {
if (!selection.isCollapsed) {
return;
}
final renderBox = textNode.renderBox;
final selectable = textNode.selectable;
if (renderBox != null && selectable != null) {
final size = renderBox.size;
final transform = renderBox.getTransformTo(null);
final rect = selectable.getCursorRectInPosition(selection.end);
_textInputConnection
?..setEditableSizeAndTransform(size, transform)
..setCaretRect(rect);
}
}
}