mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support IME in macOS
This commit is contained in:
parent
70853b918e
commit
ec47aa51a1
@ -55,4 +55,18 @@ class Selection {
|
|||||||
"end": end.toJson(),
|
"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> {
|
class _FlowyEditorState extends State<FlowyEditor> {
|
||||||
late ScrollController _scrollController;
|
|
||||||
|
|
||||||
EditorState get editorState => widget.editorState;
|
EditorState get editorState => widget.editorState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -74,13 +72,6 @@ class _FlowyEditorState extends State<FlowyEditor> {
|
|||||||
editorState.service.renderPluginService = _createRenderPlugin();
|
editorState.service.renderPluginService = _createRenderPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_scrollController.dispose();
|
|
||||||
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant FlowyEditor oldWidget) {
|
void didUpdateWidget(covariant FlowyEditor oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
|
@ -2,14 +2,15 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:flowy_editor/document/node.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/position.dart';
|
||||||
import 'package:flowy_editor/document/selection.dart';
|
import 'package:flowy_editor/document/selection.dart';
|
||||||
import 'package:flowy_editor/editor_state.dart';
|
import 'package:flowy_editor/editor_state.dart';
|
||||||
|
import 'package:flowy_editor/extensions/node_extensions.dart';
|
||||||
import 'package:flowy_editor/operation/transaction_builder.dart';
|
import 'package:flowy_editor/operation/transaction_builder.dart';
|
||||||
|
|
||||||
mixin FlowyInputService {
|
mixin FlowyInputService {
|
||||||
void attach(TextEditingValue textEditingValue);
|
void attach(TextEditingValue textEditingValue);
|
||||||
void setTextEditingValue(TextEditingValue textEditingValue);
|
|
||||||
void apply(List<TextEditingDelta> deltas);
|
void apply(List<TextEditingDelta> deltas);
|
||||||
void close();
|
void close();
|
||||||
}
|
}
|
||||||
@ -33,6 +34,7 @@ class _FlowyInputState extends State<FlowyInput>
|
|||||||
with FlowyInputService
|
with FlowyInputService
|
||||||
implements DeltaTextInputClient {
|
implements DeltaTextInputClient {
|
||||||
TextInputConnection? _textInputConnection;
|
TextInputConnection? _textInputConnection;
|
||||||
|
TextRange? _composingTextRange;
|
||||||
|
|
||||||
EditorState get _editorState => widget.editorState;
|
EditorState get _editorState => widget.editorState;
|
||||||
|
|
||||||
@ -46,6 +48,7 @@ class _FlowyInputState extends State<FlowyInput>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
close();
|
||||||
_editorState.service.selectionService.currentSelectedNodes
|
_editorState.service.selectionService.currentSelectedNodes
|
||||||
.removeListener(_onSelectedNodesChange);
|
.removeListener(_onSelectedNodesChange);
|
||||||
|
|
||||||
@ -61,11 +64,7 @@ class _FlowyInputState extends State<FlowyInput>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void attach(TextEditingValue textEditingValue) {
|
void attach(TextEditingValue textEditingValue) {
|
||||||
if (_textInputConnection != null) {
|
_textInputConnection ??= TextInput.attach(
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_textInputConnection = TextInput.attach(
|
|
||||||
this,
|
this,
|
||||||
const TextInputConfiguration(
|
const TextInputConfiguration(
|
||||||
// TODO: customize
|
// TODO: customize
|
||||||
@ -75,18 +74,9 @@ class _FlowyInputState extends State<FlowyInput>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
_textInputConnection
|
_textInputConnection!
|
||||||
?..show()
|
..setEditingState(textEditingValue)
|
||||||
..setEditingState(textEditingValue);
|
..show();
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void setTextEditingValue(TextEditingValue textEditingValue) {
|
|
||||||
assert(_textInputConnection != null,
|
|
||||||
'Must call `attach` before set textEditingValue');
|
|
||||||
if (_textInputConnection != null) {
|
|
||||||
_textInputConnection?.setEditingState(textEditingValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -94,13 +84,21 @@ class _FlowyInputState extends State<FlowyInput>
|
|||||||
// TODO: implement the detail
|
// TODO: implement the detail
|
||||||
for (final delta in deltas) {
|
for (final delta in deltas) {
|
||||||
if (delta is TextEditingDeltaInsertion) {
|
if (delta is TextEditingDeltaInsertion) {
|
||||||
|
if (_composingTextRange != null) {
|
||||||
|
_composingTextRange = TextRange(
|
||||||
|
start: _composingTextRange!.start,
|
||||||
|
end: delta.composing.end,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_composingTextRange = delta.composing;
|
||||||
|
}
|
||||||
|
|
||||||
_applyInsert(delta);
|
_applyInsert(delta);
|
||||||
} else if (delta is TextEditingDeltaDeletion) {
|
} else if (delta is TextEditingDeltaDeletion) {
|
||||||
} else if (delta is TextEditingDeltaReplacement) {
|
} else if (delta is TextEditingDeltaReplacement) {
|
||||||
_applyReplacement(delta);
|
_applyReplacement(delta);
|
||||||
} else if (delta is TextEditingDeltaNonTextUpdate) {
|
} else if (delta is TextEditingDeltaNonTextUpdate) {
|
||||||
// We don't need to care the [TextEditingDeltaNonTextUpdate].
|
_composingTextRange = null;
|
||||||
// Do nothing.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,12 +210,12 @@ class _FlowyInputState extends State<FlowyInput>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onSelectedNodesChange() {
|
void _onSelectedNodesChange() {
|
||||||
final nodes =
|
final textNodes = _editorState
|
||||||
_editorState.service.selectionService.currentSelectedNodes.value;
|
.service.selectionService.currentSelectedNodes.value
|
||||||
|
.whereType<TextNode>();
|
||||||
final selection = _editorState.service.selectionService.currentSelection;
|
final selection = _editorState.service.selectionService.currentSelection;
|
||||||
// FIXME: upward.
|
// FIXME: upward and selection update.
|
||||||
if (nodes.isNotEmpty && selection != null) {
|
if (textNodes.isNotEmpty && selection != null) {
|
||||||
final textNodes = nodes.whereType<TextNode>();
|
|
||||||
final text = textNodes.fold<String>(
|
final text = textNodes.fold<String>(
|
||||||
'', (sum, textNode) => '$sum${textNode.toRawString()}\n');
|
'', (sum, textNode) => '$sum${textNode.toRawString()}\n');
|
||||||
attach(
|
attach(
|
||||||
@ -227,10 +225,32 @@ class _FlowyInputState extends State<FlowyInput>
|
|||||||
baseOffset: selection.start.offset,
|
baseOffset: selection.start.offset,
|
||||||
extentOffset: selection.end.offset,
|
extentOffset: selection.end.offset,
|
||||||
),
|
),
|
||||||
|
composing: _composingTextRange ?? const TextRange.collapsed(-1),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
if (textNodes.length == 1) {
|
||||||
|
_updateCaretPosition(textNodes.first, selection);
|
||||||
|
}
|
||||||
} else {
|
} 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…
x
Reference in New Issue
Block a user