mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #773 from LucasXu0/feat/macos_ime_support
feat: support IME in macOS
This commit is contained in:
commit
4d6227c502
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user