|
|
|
@ -86,8 +86,7 @@ class RawEditor extends StatefulWidget { |
|
|
|
|
this.contentInsertionConfiguration, |
|
|
|
|
}) : assert(maxHeight == null || maxHeight > 0, 'maxHeight cannot be null'), |
|
|
|
|
assert(minHeight == null || minHeight >= 0, 'minHeight cannot be null'), |
|
|
|
|
assert(maxHeight == null || minHeight == null || maxHeight >= minHeight, |
|
|
|
|
'maxHeight cannot be null'), |
|
|
|
|
assert(maxHeight == null || minHeight == null || maxHeight >= minHeight, 'maxHeight cannot be null'), |
|
|
|
|
showCursor = showCursor ?? true, |
|
|
|
|
super(key: key); |
|
|
|
|
|
|
|
|
@ -337,9 +336,7 @@ class RawEditorState extends EditorState |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void insertContent(KeyboardInsertedContent content) { |
|
|
|
|
assert(widget.contentInsertionConfiguration?.allowedMimeTypes |
|
|
|
|
.contains(content.mimeType) ?? |
|
|
|
|
false); |
|
|
|
|
assert(widget.contentInsertionConfiguration?.allowedMimeTypes.contains(content.mimeType) ?? false); |
|
|
|
|
widget.contentInsertionConfiguration?.onContentInserted.call(content); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -350,17 +347,10 @@ class RawEditorState extends EditorState |
|
|
|
|
List<ContextMenuButtonItem> get contextMenuButtonItems { |
|
|
|
|
return EditableText.getEditableButtonItems( |
|
|
|
|
clipboardStatus: _clipboardStatus.value, |
|
|
|
|
onLiveTextInput: null, |
|
|
|
|
onCopy: copyEnabled |
|
|
|
|
? () => copySelection(SelectionChangedCause.toolbar) |
|
|
|
|
: null, |
|
|
|
|
onCut: |
|
|
|
|
cutEnabled ? () => cutSelection(SelectionChangedCause.toolbar) : null, |
|
|
|
|
onPaste: |
|
|
|
|
pasteEnabled ? () => pasteText(SelectionChangedCause.toolbar) : null, |
|
|
|
|
onSelectAll: selectAllEnabled |
|
|
|
|
? () => selectAll(SelectionChangedCause.toolbar) |
|
|
|
|
: null, |
|
|
|
|
onCopy: copyEnabled ? () => copySelection(SelectionChangedCause.toolbar) : null, |
|
|
|
|
onCut: cutEnabled ? () => cutSelection(SelectionChangedCause.toolbar) : null, |
|
|
|
|
onPaste: pasteEnabled ? () => pasteText(SelectionChangedCause.toolbar) : null, |
|
|
|
|
onSelectAll: selectAllEnabled ? () => selectAll(SelectionChangedCause.toolbar) : null, |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -402,10 +392,8 @@ class RawEditorState extends EditorState |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final startCharacterRect = |
|
|
|
|
renderEditor.getLocalRectForCaret(selection.base); |
|
|
|
|
final endCharacterRect = |
|
|
|
|
renderEditor.getLocalRectForCaret(selection.extent); |
|
|
|
|
final startCharacterRect = renderEditor.getLocalRectForCaret(selection.base); |
|
|
|
|
final endCharacterRect = renderEditor.getLocalRectForCaret(selection.extent); |
|
|
|
|
return _GlyphHeights( |
|
|
|
|
startCharacterRect.height, |
|
|
|
|
endCharacterRect.height, |
|
|
|
@ -434,8 +422,7 @@ class RawEditorState extends EditorState |
|
|
|
|
widget.focusNode.unfocus(); |
|
|
|
|
break; |
|
|
|
|
case ui.PointerDeviceKind.trackpad: |
|
|
|
|
throw UnimplementedError( |
|
|
|
|
'Unexpected pointer down event for trackpad'); |
|
|
|
|
throw UnimplementedError('Unexpected pointer down event for trackpad'); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case TargetPlatform.linux: |
|
|
|
@ -454,8 +441,7 @@ class RawEditorState extends EditorState |
|
|
|
|
var _doc = controller.document; |
|
|
|
|
if (_doc.isEmpty() && widget.placeholder != null) { |
|
|
|
|
final raw = widget.placeholder?.replaceAll(r'"', '\\"'); |
|
|
|
|
_doc = Document.fromJson(jsonDecode( |
|
|
|
|
'[{"attributes":{"placeholder":true},"insert":"$raw\\n"}]')); |
|
|
|
|
_doc = Document.fromJson(jsonDecode('[{"attributes":{"placeholder":true},"insert":"$raw\\n"}]')); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Widget child = CompositedTransformTarget( |
|
|
|
@ -492,8 +478,7 @@ class RawEditorState extends EditorState |
|
|
|
|
/// the scroll view with [BaselineProxy] which mimics the editor's |
|
|
|
|
/// baseline. |
|
|
|
|
// This implies that the first line has no styles applied to it. |
|
|
|
|
final baselinePadding = |
|
|
|
|
EdgeInsets.only(top: _styles!.paragraph!.verticalSpacing.top); |
|
|
|
|
final baselinePadding = EdgeInsets.only(top: _styles!.paragraph!.verticalSpacing.top); |
|
|
|
|
child = BaselineProxy( |
|
|
|
|
textStyle: _styles!.paragraph!.style, |
|
|
|
|
padding: baselinePadding, |
|
|
|
@ -531,9 +516,7 @@ class RawEditorState extends EditorState |
|
|
|
|
|
|
|
|
|
final constraints = widget.expands |
|
|
|
|
? const BoxConstraints.expand() |
|
|
|
|
: BoxConstraints( |
|
|
|
|
minHeight: widget.minHeight ?? 0.0, |
|
|
|
|
maxHeight: widget.maxHeight ?? double.infinity); |
|
|
|
|
: BoxConstraints(minHeight: widget.minHeight ?? 0.0, maxHeight: widget.maxHeight ?? double.infinity); |
|
|
|
|
|
|
|
|
|
final isMacOS = Theme.of(context).platform == TargetPlatform.macOS; |
|
|
|
|
|
|
|
|
@ -722,8 +705,7 @@ class RawEditorState extends EditorState |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
KeyEventResult _handleSpaceKey(RawKeyEvent event) { |
|
|
|
|
final child = |
|
|
|
|
controller.document.queryChild(controller.selection.baseOffset); |
|
|
|
|
final child = controller.document.queryChild(controller.selection.baseOffset); |
|
|
|
|
if (child.node == null) { |
|
|
|
|
return KeyEventResult.ignored; |
|
|
|
|
} |
|
|
|
@ -753,8 +735,7 @@ class RawEditorState extends EditorState |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
KeyEventResult _handleTabKey(RawKeyEvent event) { |
|
|
|
|
final child = |
|
|
|
|
controller.document.queryChild(controller.selection.baseOffset); |
|
|
|
|
final child = controller.document.queryChild(controller.selection.baseOffset); |
|
|
|
|
|
|
|
|
|
KeyEventResult insertTabCharacter() { |
|
|
|
|
controller.replaceText(controller.selection.baseOffset, 0, '\t', null); |
|
|
|
@ -813,15 +794,13 @@ class RawEditorState extends EditorState |
|
|
|
|
void _moveCursor(int chars) { |
|
|
|
|
final selection = controller.selection; |
|
|
|
|
controller.updateSelection( |
|
|
|
|
controller.selection.copyWith( |
|
|
|
|
baseOffset: selection.baseOffset + chars, |
|
|
|
|
extentOffset: selection.baseOffset + chars), |
|
|
|
|
controller.selection |
|
|
|
|
.copyWith(baseOffset: selection.baseOffset + chars, extentOffset: selection.baseOffset + chars), |
|
|
|
|
ChangeSource.LOCAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void _updateSelectionForKeyPhrase(String phrase, Attribute attribute) { |
|
|
|
|
controller.replaceText(controller.selection.baseOffset - phrase.length, |
|
|
|
|
phrase.length, '\n', null); |
|
|
|
|
controller.replaceText(controller.selection.baseOffset - phrase.length, phrase.length, '\n', null); |
|
|
|
|
_moveCursor(-phrase.length); |
|
|
|
|
controller |
|
|
|
|
..formatSelection(attribute) |
|
|
|
@ -829,8 +808,7 @@ class RawEditorState extends EditorState |
|
|
|
|
..replaceText(controller.selection.baseOffset + 1, 1, '', null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void _handleSelectionChanged( |
|
|
|
|
TextSelection selection, SelectionChangedCause cause) { |
|
|
|
|
void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) { |
|
|
|
|
final oldSelection = controller.selection; |
|
|
|
|
controller.updateSelection(selection, ChangeSource.LOCAL); |
|
|
|
|
|
|
|
|
@ -872,10 +850,7 @@ class RawEditorState extends EditorState |
|
|
|
|
|
|
|
|
|
// Checkbox tapping causes controller.selection to go to offset 0 |
|
|
|
|
// Stop toggling those two toolbar buttons |
|
|
|
|
..toolbarButtonToggler = { |
|
|
|
|
Attribute.list.key: attribute, |
|
|
|
|
Attribute.header.key: Attribute.header |
|
|
|
|
}; |
|
|
|
|
..toolbarButtonToggler = {Attribute.list.key: attribute, Attribute.header.key: Attribute.header}; |
|
|
|
|
|
|
|
|
|
// Go back from offset 0 to current selection |
|
|
|
|
SchedulerBinding.instance.addPostFrameCallback((_) { |
|
|
|
@ -908,8 +883,7 @@ class RawEditorState extends EditorState |
|
|
|
|
|
|
|
|
|
if (node is Line) { |
|
|
|
|
final editableTextLine = _getEditableTextLineFromNode(node, context); |
|
|
|
|
result.add(Directionality( |
|
|
|
|
textDirection: getDirectionOfNode(node), child: editableTextLine)); |
|
|
|
|
result.add(Directionality(textDirection: getDirectionOfNode(node), child: editableTextLine)); |
|
|
|
|
} else if (node is Block) { |
|
|
|
|
final editableTextBlock = EditableTextBlock( |
|
|
|
|
block: node, |
|
|
|
@ -922,9 +896,7 @@ class RawEditorState extends EditorState |
|
|
|
|
styles: _styles, |
|
|
|
|
enableInteractiveSelection: widget.enableInteractiveSelection, |
|
|
|
|
hasFocus: _hasFocus, |
|
|
|
|
contentPadding: attrs.containsKey(Attribute.codeBlock.key) |
|
|
|
|
? const EdgeInsets.all(16) |
|
|
|
|
: null, |
|
|
|
|
contentPadding: attrs.containsKey(Attribute.codeBlock.key) ? const EdgeInsets.all(16) : null, |
|
|
|
|
embedBuilder: widget.embedBuilder, |
|
|
|
|
linkActionPicker: _linkActionPicker, |
|
|
|
|
onLaunchUrl: widget.onLaunchUrl, |
|
|
|
@ -935,8 +907,7 @@ class RawEditorState extends EditorState |
|
|
|
|
readOnly: widget.readOnly, |
|
|
|
|
customStyleBuilder: widget.customStyleBuilder, |
|
|
|
|
customLinkPrefixes: widget.customLinkPrefixes); |
|
|
|
|
result.add(Directionality( |
|
|
|
|
textDirection: getDirectionOfNode(node), child: editableTextBlock)); |
|
|
|
|
result.add(Directionality(textDirection: getDirectionOfNode(node), child: editableTextBlock)); |
|
|
|
|
|
|
|
|
|
clearIndents = false; |
|
|
|
|
} else { |
|
|
|
@ -948,8 +919,7 @@ class RawEditorState extends EditorState |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EditableTextLine _getEditableTextLineFromNode( |
|
|
|
|
Line node, BuildContext context) { |
|
|
|
|
EditableTextLine _getEditableTextLineFromNode(Line node, BuildContext context) { |
|
|
|
|
final textLine = TextLine( |
|
|
|
|
line: node, |
|
|
|
|
textDirection: _textDirection, |
|
|
|
@ -979,8 +949,7 @@ class RawEditorState extends EditorState |
|
|
|
|
return editableTextLine; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VerticalSpacing _getVerticalSpacingForLine( |
|
|
|
|
Line line, DefaultStyles? defaultStyles) { |
|
|
|
|
VerticalSpacing _getVerticalSpacingForLine(Line line, DefaultStyles? defaultStyles) { |
|
|
|
|
final attrs = line.style.attributes; |
|
|
|
|
if (attrs.containsKey(Attribute.header.key)) { |
|
|
|
|
int level; |
|
|
|
@ -1004,8 +973,7 @@ class RawEditorState extends EditorState |
|
|
|
|
return defaultStyles!.paragraph!.verticalSpacing; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VerticalSpacing _getVerticalSpacingForBlock( |
|
|
|
|
Block node, DefaultStyles? defaultStyles) { |
|
|
|
|
VerticalSpacing _getVerticalSpacingForBlock(Block node, DefaultStyles? defaultStyles) { |
|
|
|
|
final attrs = node.style.attributes; |
|
|
|
|
if (attrs.containsKey(Attribute.blockQuote.key)) { |
|
|
|
|
return defaultStyles!.quote!.verticalSpacing; |
|
|
|
@ -1057,8 +1025,7 @@ class RawEditorState extends EditorState |
|
|
|
|
} else { |
|
|
|
|
_keyboardVisibilityController = KeyboardVisibilityController(); |
|
|
|
|
_keyboardVisible = _keyboardVisibilityController!.isVisible; |
|
|
|
|
_keyboardVisibilitySubscription = |
|
|
|
|
_keyboardVisibilityController?.onChange.listen((visible) { |
|
|
|
|
_keyboardVisibilitySubscription = _keyboardVisibilityController?.onChange.listen((visible) { |
|
|
|
|
_keyboardVisible = visible; |
|
|
|
|
if (visible) { |
|
|
|
|
_onChangeTextEditingValue(!_hasFocus); |
|
|
|
@ -1100,9 +1067,7 @@ class RawEditorState extends EditorState |
|
|
|
|
super.didChangeDependencies(); |
|
|
|
|
final parentStyles = QuillStyles.getStyles(context, true); |
|
|
|
|
final defaultStyles = DefaultStyles.getInstance(context); |
|
|
|
|
_styles = (parentStyles != null) |
|
|
|
|
? defaultStyles.merge(parentStyles) |
|
|
|
|
: defaultStyles; |
|
|
|
|
_styles = (parentStyles != null) ? defaultStyles.merge(parentStyles) : defaultStyles; |
|
|
|
|
|
|
|
|
|
if (widget.customStyles != null) { |
|
|
|
|
_styles = _styles!.merge(widget.customStyles!); |
|
|
|
@ -1270,9 +1235,8 @@ class RawEditorState extends EditorState |
|
|
|
|
selectionCtrls: widget.selectionCtrls, |
|
|
|
|
selectionDelegate: this, |
|
|
|
|
clipboardStatus: _clipboardStatus, |
|
|
|
|
contextMenuBuilder: widget.contextMenuBuilder == null |
|
|
|
|
? null |
|
|
|
|
: (context) => widget.contextMenuBuilder!(context, this), |
|
|
|
|
contextMenuBuilder: |
|
|
|
|
widget.contextMenuBuilder == null ? null : (context) => widget.contextMenuBuilder!(context, this), |
|
|
|
|
); |
|
|
|
|
_selectionOverlay!.handlesVisible = _shouldShowSelectionHandles(); |
|
|
|
|
_selectionOverlay!.showHandles(); |
|
|
|
@ -1281,8 +1245,7 @@ class RawEditorState extends EditorState |
|
|
|
|
|
|
|
|
|
void _handleFocusChanged() { |
|
|
|
|
if (dirty) { |
|
|
|
|
SchedulerBinding.instance |
|
|
|
|
.addPostFrameCallback((_) => _handleFocusChanged()); |
|
|
|
|
SchedulerBinding.instance.addPostFrameCallback((_) => _handleFocusChanged()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
openOrCloseConnection(); |
|
|
|
@ -1333,8 +1296,7 @@ class RawEditorState extends EditorState |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final viewport = RenderAbstractViewport.of(renderEditor); |
|
|
|
|
final editorOffset = |
|
|
|
|
renderEditor.localToGlobal(const Offset(0, 0), ancestor: viewport); |
|
|
|
|
final editorOffset = renderEditor.localToGlobal(const Offset(0, 0), ancestor: viewport); |
|
|
|
|
final offsetInViewport = _scrollController.offset + editorOffset.dy; |
|
|
|
|
|
|
|
|
|
final offset = renderEditor.getOffsetToRevealCursor( |
|
|
|
@ -1362,8 +1324,7 @@ class RawEditorState extends EditorState |
|
|
|
|
/// |
|
|
|
|
/// This property is typically used to notify the renderer of input gestures. |
|
|
|
|
@override |
|
|
|
|
RenderEditor get renderEditor => |
|
|
|
|
_editorKey.currentContext!.findRenderObject() as RenderEditor; |
|
|
|
|
RenderEditor get renderEditor => _editorKey.currentContext!.findRenderObject() as RenderEditor; |
|
|
|
|
|
|
|
|
|
/// Express interest in interacting with the keyboard. |
|
|
|
|
/// |
|
|
|
@ -1424,8 +1385,7 @@ class RawEditorState extends EditorState |
|
|
|
|
|
|
|
|
|
void _replaceText(ReplaceTextIntent intent) { |
|
|
|
|
userUpdateTextEditingValue( |
|
|
|
|
intent.currentTextEditingValue |
|
|
|
|
.replaced(intent.replacementRange, intent.replacementText), |
|
|
|
|
intent.currentTextEditingValue.replaced(intent.replacementRange, intent.replacementText), |
|
|
|
|
intent.cause, |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
@ -1451,8 +1411,7 @@ class RawEditorState extends EditorState |
|
|
|
|
userUpdateTextEditingValue( |
|
|
|
|
TextEditingValue( |
|
|
|
|
text: textEditingValue.text, |
|
|
|
|
selection: |
|
|
|
|
TextSelection.collapsed(offset: textEditingValue.selection.end), |
|
|
|
|
selection: TextSelection.collapsed(offset: textEditingValue.selection.end), |
|
|
|
|
), |
|
|
|
|
SelectionChangedCause.toolbar, |
|
|
|
|
); |
|
|
|
@ -1496,8 +1455,7 @@ class RawEditorState extends EditorState |
|
|
|
|
final copied = controller.copiedImageUrl!; |
|
|
|
|
controller.replaceText(index, length, BlockEmbed.image(copied.url), null); |
|
|
|
|
if (copied.styleString.isNotEmpty) { |
|
|
|
|
controller.formatText(getEmbedNode(controller, index + 1).offset, 1, |
|
|
|
|
StyleAttribute(copied.styleString)); |
|
|
|
|
controller.formatText(getEmbedNode(controller, index + 1).offset, 1, StyleAttribute(copied.styleString)); |
|
|
|
|
} |
|
|
|
|
controller.copiedImageUrl = null; |
|
|
|
|
await Clipboard.setData(const ClipboardData(text: '')); |
|
|
|
@ -1512,8 +1470,7 @@ class RawEditorState extends EditorState |
|
|
|
|
// See https://github.com/flutter/flutter/issues/11427 |
|
|
|
|
final text = await Clipboard.getData(Clipboard.kTextPlain); |
|
|
|
|
if (text != null) { |
|
|
|
|
_replaceText( |
|
|
|
|
ReplaceTextIntent(textEditingValue, text.text!, selection, cause)); |
|
|
|
|
_replaceText(ReplaceTextIntent(textEditingValue, text.text!, selection, cause)); |
|
|
|
|
|
|
|
|
|
bringIntoView(textEditingValue.selection.extent); |
|
|
|
|
|
|
|
|
@ -1521,8 +1478,7 @@ class RawEditorState extends EditorState |
|
|
|
|
userUpdateTextEditingValue( |
|
|
|
|
TextEditingValue( |
|
|
|
|
text: textEditingValue.text, |
|
|
|
|
selection: |
|
|
|
|
TextSelection.collapsed(offset: textEditingValue.selection.end), |
|
|
|
|
selection: TextSelection.collapsed(offset: textEditingValue.selection.end), |
|
|
|
|
), |
|
|
|
|
cause, |
|
|
|
|
); |
|
|
|
@ -1556,8 +1512,7 @@ class RawEditorState extends EditorState |
|
|
|
|
void selectAll(SelectionChangedCause cause) { |
|
|
|
|
userUpdateTextEditingValue( |
|
|
|
|
textEditingValue.copyWith( |
|
|
|
|
selection: TextSelection( |
|
|
|
|
baseOffset: 0, extentOffset: textEditingValue.text.length), |
|
|
|
|
selection: TextSelection(baseOffset: 0, extentOffset: textEditingValue.text.length), |
|
|
|
|
), |
|
|
|
|
cause, |
|
|
|
|
); |
|
|
|
@ -1571,16 +1526,14 @@ class RawEditorState extends EditorState |
|
|
|
|
bool get wantKeepAlive => widget.focusNode.hasFocus; |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
AnimationController get floatingCursorResetController => |
|
|
|
|
_floatingCursorResetController; |
|
|
|
|
AnimationController get floatingCursorResetController => _floatingCursorResetController; |
|
|
|
|
|
|
|
|
|
late AnimationController _floatingCursorResetController; |
|
|
|
|
|
|
|
|
|
// --------------------------- Text Editing Actions -------------------------- |
|
|
|
|
|
|
|
|
|
_TextBoundary _characterBoundary(DirectionalTextEditingIntent intent) { |
|
|
|
|
final _TextBoundary atomicTextBoundary = |
|
|
|
|
_CharacterBoundary(textEditingValue); |
|
|
|
|
final _TextBoundary atomicTextBoundary = _CharacterBoundary(textEditingValue); |
|
|
|
|
return _CollapsedSelectionBoundary(atomicTextBoundary, intent.forward); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1592,12 +1545,11 @@ class RawEditorState extends EditorState |
|
|
|
|
// _textEditingValueforTextLayoutMetrics; |
|
|
|
|
atomicTextBoundary = _CharacterBoundary(textEditingValue); |
|
|
|
|
// This isn't enough. Newline characters. |
|
|
|
|
boundary = _ExpandedTextBoundary(_WhitespaceBoundary(textEditingValue), |
|
|
|
|
_WordBoundary(renderEditor, textEditingValue)); |
|
|
|
|
boundary = |
|
|
|
|
_ExpandedTextBoundary(_WhitespaceBoundary(textEditingValue), _WordBoundary(renderEditor, textEditingValue)); |
|
|
|
|
|
|
|
|
|
final mixedBoundary = intent.forward |
|
|
|
|
? _MixedBoundary(atomicTextBoundary, boundary) |
|
|
|
|
: _MixedBoundary(boundary, atomicTextBoundary); |
|
|
|
|
final mixedBoundary = |
|
|
|
|
intent.forward ? _MixedBoundary(atomicTextBoundary, boundary) : _MixedBoundary(boundary, atomicTextBoundary); |
|
|
|
|
// Use a _MixedBoundary to make sure we don't leave invalid codepoints in |
|
|
|
|
// the field after deletion. |
|
|
|
|
return _CollapsedSelectionBoundary(mixedBoundary, intent.forward); |
|
|
|
@ -1618,22 +1570,17 @@ class RawEditorState extends EditorState |
|
|
|
|
// since the document boundary is unique and the linebreak boundary is |
|
|
|
|
// already caret-location based. |
|
|
|
|
return intent.forward |
|
|
|
|
? _MixedBoundary( |
|
|
|
|
_CollapsedSelectionBoundary(atomicTextBoundary, true), boundary) |
|
|
|
|
: _MixedBoundary( |
|
|
|
|
boundary, _CollapsedSelectionBoundary(atomicTextBoundary, false)); |
|
|
|
|
? _MixedBoundary(_CollapsedSelectionBoundary(atomicTextBoundary, true), boundary) |
|
|
|
|
: _MixedBoundary(boundary, _CollapsedSelectionBoundary(atomicTextBoundary, false)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_TextBoundary _documentBoundary(DirectionalTextEditingIntent intent) => |
|
|
|
|
_DocumentBoundary(textEditingValue); |
|
|
|
|
_TextBoundary _documentBoundary(DirectionalTextEditingIntent intent) => _DocumentBoundary(textEditingValue); |
|
|
|
|
|
|
|
|
|
Action<T> _makeOverridable<T extends Intent>(Action<T> defaultAction) { |
|
|
|
|
return Action<T>.overridable( |
|
|
|
|
context: context, defaultAction: defaultAction); |
|
|
|
|
return Action<T>.overridable(context: context, defaultAction: defaultAction); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
late final Action<ReplaceTextIntent> _replaceTextAction = |
|
|
|
|
CallbackAction<ReplaceTextIntent>(onInvoke: _replaceText); |
|
|
|
|
late final Action<ReplaceTextIntent> _replaceTextAction = CallbackAction<ReplaceTextIntent>(onInvoke: _replaceText); |
|
|
|
|
|
|
|
|
|
void _updateSelection(UpdateSelectionIntent intent) { |
|
|
|
|
userUpdateTextEditingValue( |
|
|
|
@ -1645,21 +1592,17 @@ class RawEditorState extends EditorState |
|
|
|
|
late final Action<UpdateSelectionIntent> _updateSelectionAction = |
|
|
|
|
CallbackAction<UpdateSelectionIntent>(onInvoke: _updateSelection); |
|
|
|
|
|
|
|
|
|
late final _UpdateTextSelectionToAdjacentLineAction< |
|
|
|
|
ExtendSelectionVerticallyToAdjacentLineIntent> _adjacentLineAction = |
|
|
|
|
_UpdateTextSelectionToAdjacentLineAction< |
|
|
|
|
ExtendSelectionVerticallyToAdjacentLineIntent>(this); |
|
|
|
|
late final _UpdateTextSelectionToAdjacentLineAction<ExtendSelectionVerticallyToAdjacentLineIntent> |
|
|
|
|
_adjacentLineAction = |
|
|
|
|
_UpdateTextSelectionToAdjacentLineAction<ExtendSelectionVerticallyToAdjacentLineIntent>(this); |
|
|
|
|
|
|
|
|
|
late final _ToggleTextStyleAction _formatSelectionAction = |
|
|
|
|
_ToggleTextStyleAction(this); |
|
|
|
|
late final _ToggleTextStyleAction _formatSelectionAction = _ToggleTextStyleAction(this); |
|
|
|
|
|
|
|
|
|
late final _IndentSelectionAction _indentSelectionAction = |
|
|
|
|
_IndentSelectionAction(this); |
|
|
|
|
late final _IndentSelectionAction _indentSelectionAction = _IndentSelectionAction(this); |
|
|
|
|
|
|
|
|
|
late final _OpenSearchAction _openSearchAction = _OpenSearchAction(this); |
|
|
|
|
late final _ApplyHeaderAction _applyHeaderAction = _ApplyHeaderAction(this); |
|
|
|
|
late final _ApplyCheckListAction _applyCheckListAction = |
|
|
|
|
_ApplyCheckListAction(this); |
|
|
|
|
late final _ApplyCheckListAction _applyCheckListAction = _ApplyCheckListAction(this); |
|
|
|
|
|
|
|
|
|
late final Map<Type, Action<Intent>> _actions = <Type, Action<Intent>>{ |
|
|
|
|
DoNothingAndStopPropagationTextIntent: DoNothingAction(consumesKey: false), |
|
|
|
@ -1668,43 +1611,33 @@ class RawEditorState extends EditorState |
|
|
|
|
DirectionalFocusIntent: DirectionalFocusAction.forTextField(), |
|
|
|
|
|
|
|
|
|
// Delete |
|
|
|
|
DeleteCharacterIntent: _makeOverridable( |
|
|
|
|
_DeleteTextAction<DeleteCharacterIntent>(this, _characterBoundary)), |
|
|
|
|
DeleteToNextWordBoundaryIntent: _makeOverridable( |
|
|
|
|
_DeleteTextAction<DeleteToNextWordBoundaryIntent>( |
|
|
|
|
this, _nextWordBoundary)), |
|
|
|
|
DeleteToLineBreakIntent: _makeOverridable( |
|
|
|
|
_DeleteTextAction<DeleteToLineBreakIntent>(this, _linebreak)), |
|
|
|
|
DeleteCharacterIntent: _makeOverridable(_DeleteTextAction<DeleteCharacterIntent>(this, _characterBoundary)), |
|
|
|
|
DeleteToNextWordBoundaryIntent: |
|
|
|
|
_makeOverridable(_DeleteTextAction<DeleteToNextWordBoundaryIntent>(this, _nextWordBoundary)), |
|
|
|
|
DeleteToLineBreakIntent: _makeOverridable(_DeleteTextAction<DeleteToLineBreakIntent>(this, _linebreak)), |
|
|
|
|
|
|
|
|
|
// Extend/Move Selection |
|
|
|
|
ExtendSelectionByCharacterIntent: _makeOverridable( |
|
|
|
|
_UpdateTextSelectionAction<ExtendSelectionByCharacterIntent>( |
|
|
|
|
ExtendSelectionByCharacterIntent: _makeOverridable(_UpdateTextSelectionAction<ExtendSelectionByCharacterIntent>( |
|
|
|
|
this, |
|
|
|
|
false, |
|
|
|
|
_characterBoundary, |
|
|
|
|
)), |
|
|
|
|
ExtendSelectionToNextWordBoundaryIntent: _makeOverridable( |
|
|
|
|
_UpdateTextSelectionAction<ExtendSelectionToNextWordBoundaryIntent>( |
|
|
|
|
this, true, _nextWordBoundary)), |
|
|
|
|
ExtendSelectionToLineBreakIntent: _makeOverridable( |
|
|
|
|
_UpdateTextSelectionAction<ExtendSelectionToLineBreakIntent>( |
|
|
|
|
this, true, _linebreak)), |
|
|
|
|
ExtendSelectionVerticallyToAdjacentLineIntent: |
|
|
|
|
_makeOverridable(_adjacentLineAction), |
|
|
|
|
_UpdateTextSelectionAction<ExtendSelectionToNextWordBoundaryIntent>(this, true, _nextWordBoundary)), |
|
|
|
|
ExtendSelectionToLineBreakIntent: |
|
|
|
|
_makeOverridable(_UpdateTextSelectionAction<ExtendSelectionToLineBreakIntent>(this, true, _linebreak)), |
|
|
|
|
ExtendSelectionVerticallyToAdjacentLineIntent: _makeOverridable(_adjacentLineAction), |
|
|
|
|
ExtendSelectionToDocumentBoundaryIntent: _makeOverridable( |
|
|
|
|
_UpdateTextSelectionAction<ExtendSelectionToDocumentBoundaryIntent>( |
|
|
|
|
this, true, _documentBoundary)), |
|
|
|
|
ExtendSelectionToNextWordBoundaryOrCaretLocationIntent: _makeOverridable( |
|
|
|
|
_ExtendSelectionOrCaretPositionAction(this, _nextWordBoundary)), |
|
|
|
|
_UpdateTextSelectionAction<ExtendSelectionToDocumentBoundaryIntent>(this, true, _documentBoundary)), |
|
|
|
|
ExtendSelectionToNextWordBoundaryOrCaretLocationIntent: |
|
|
|
|
_makeOverridable(_ExtendSelectionOrCaretPositionAction(this, _nextWordBoundary)), |
|
|
|
|
|
|
|
|
|
// Copy Paste |
|
|
|
|
SelectAllTextIntent: _makeOverridable(_SelectAllAction(this)), |
|
|
|
|
CopySelectionTextIntent: _makeOverridable(_CopySelectionAction(this)), |
|
|
|
|
PasteTextIntent: _makeOverridable(CallbackAction<PasteTextIntent>( |
|
|
|
|
onInvoke: (intent) => pasteText(intent.cause))), |
|
|
|
|
PasteTextIntent: _makeOverridable(CallbackAction<PasteTextIntent>(onInvoke: (intent) => pasteText(intent.cause))), |
|
|
|
|
|
|
|
|
|
HideSelectionToolbarIntent: |
|
|
|
|
_makeOverridable(_HideSelectionToolbarAction(this)), |
|
|
|
|
HideSelectionToolbarIntent: _makeOverridable(_HideSelectionToolbarAction(this)), |
|
|
|
|
UndoTextIntent: _makeOverridable(_UndoKeyboardAction(this)), |
|
|
|
|
RedoTextIntent: _makeOverridable(_RedoKeyboardAction(this)), |
|
|
|
|
|
|
|
|
@ -1731,8 +1664,7 @@ class RawEditorState extends EditorState |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void didChangeInputControl( |
|
|
|
|
TextInputControl? oldControl, TextInputControl? newControl) { |
|
|
|
|
void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) { |
|
|
|
|
// TODO: implement didChangeInputControl |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1811,8 +1743,7 @@ class _Editor extends MultiChildRenderObjectWidget { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void updateRenderObject( |
|
|
|
|
BuildContext context, covariant RenderEditor renderObject) { |
|
|
|
|
void updateRenderObject(BuildContext context, covariant RenderEditor renderObject) { |
|
|
|
|
renderObject |
|
|
|
|
..offset = offset |
|
|
|
|
..document = document |
|
|
|
@ -1881,8 +1812,7 @@ class _WhitespaceBoundary extends _TextBoundary { |
|
|
|
|
@override |
|
|
|
|
TextPosition getLeadingTextBoundaryAt(TextPosition position) { |
|
|
|
|
for (var index = position.offset; index >= 0; index -= 1) { |
|
|
|
|
if (!TextLayoutMetrics.isWhitespace( |
|
|
|
|
textEditingValue.text.codeUnitAt(index))) { |
|
|
|
|
if (!TextLayoutMetrics.isWhitespace(textEditingValue.text.codeUnitAt(index))) { |
|
|
|
|
return TextPosition(offset: index); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -1891,11 +1821,8 @@ class _WhitespaceBoundary extends _TextBoundary { |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
TextPosition getTrailingTextBoundaryAt(TextPosition position) { |
|
|
|
|
for (var index = position.offset; |
|
|
|
|
index < textEditingValue.text.length; |
|
|
|
|
index += 1) { |
|
|
|
|
if (!TextLayoutMetrics.isWhitespace( |
|
|
|
|
textEditingValue.text.codeUnitAt(index))) { |
|
|
|
|
for (var index = position.offset; index < textEditingValue.text.length; index += 1) { |
|
|
|
|
if (!TextLayoutMetrics.isWhitespace(textEditingValue.text.codeUnitAt(index))) { |
|
|
|
|
return TextPosition(offset: index + 1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -1914,21 +1841,16 @@ class _CharacterBoundary extends _TextBoundary { |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
TextPosition getLeadingTextBoundaryAt(TextPosition position) { |
|
|
|
|
final int endOffset = |
|
|
|
|
math.min(position.offset + 1, textEditingValue.text.length); |
|
|
|
|
final int endOffset = math.min(position.offset + 1, textEditingValue.text.length); |
|
|
|
|
return TextPosition( |
|
|
|
|
offset: |
|
|
|
|
CharacterRange.at(textEditingValue.text, position.offset, endOffset) |
|
|
|
|
.stringBeforeLength, |
|
|
|
|
offset: CharacterRange.at(textEditingValue.text, position.offset, endOffset).stringBeforeLength, |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
TextPosition getTrailingTextBoundaryAt(TextPosition position) { |
|
|
|
|
final int endOffset = |
|
|
|
|
math.min(position.offset + 1, textEditingValue.text.length); |
|
|
|
|
final range = |
|
|
|
|
CharacterRange.at(textEditingValue.text, position.offset, endOffset); |
|
|
|
|
final int endOffset = math.min(position.offset + 1, textEditingValue.text.length); |
|
|
|
|
final range = CharacterRange.at(textEditingValue.text, position.offset, endOffset); |
|
|
|
|
return TextPosition( |
|
|
|
|
offset: textEditingValue.text.length - range.stringAfterLength, |
|
|
|
|
); |
|
|
|
@ -1936,10 +1858,8 @@ class _CharacterBoundary extends _TextBoundary { |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
TextRange getTextBoundaryAt(TextPosition position) { |
|
|
|
|
final int endOffset = |
|
|
|
|
math.min(position.offset + 1, textEditingValue.text.length); |
|
|
|
|
final range = |
|
|
|
|
CharacterRange.at(textEditingValue.text, position.offset, endOffset); |
|
|
|
|
final int endOffset = math.min(position.offset + 1, textEditingValue.text.length); |
|
|
|
|
final range = CharacterRange.at(textEditingValue.text, position.offset, endOffset); |
|
|
|
|
return TextRange( |
|
|
|
|
start: range.stringBeforeLength, |
|
|
|
|
end: textEditingValue.text.length - range.stringAfterLength, |
|
|
|
@ -1961,8 +1881,7 @@ class _WordBoundary extends _TextBoundary { |
|
|
|
|
return TextPosition( |
|
|
|
|
offset: textLayout.getWordBoundary(position).start, |
|
|
|
|
// Word boundary seems to always report downstream on many platforms. |
|
|
|
|
affinity: |
|
|
|
|
TextAffinity.downstream, // ignore: avoid_redundant_argument_values |
|
|
|
|
affinity: TextAffinity.downstream, // ignore: avoid_redundant_argument_values |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1971,8 +1890,7 @@ class _WordBoundary extends _TextBoundary { |
|
|
|
|
return TextPosition( |
|
|
|
|
offset: textLayout.getWordBoundary(position).end, |
|
|
|
|
// Word boundary seems to always report downstream on many platforms. |
|
|
|
|
affinity: |
|
|
|
|
TextAffinity.downstream, // ignore: avoid_redundant_argument_values |
|
|
|
|
affinity: TextAffinity.downstream, // ignore: avoid_redundant_argument_values |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -2013,8 +1931,7 @@ class _DocumentBoundary extends _TextBoundary { |
|
|
|
|
final TextEditingValue textEditingValue; |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
TextPosition getLeadingTextBoundaryAt(TextPosition position) => |
|
|
|
|
const TextPosition(offset: 0); |
|
|
|
|
TextPosition getLeadingTextBoundaryAt(TextPosition position) => const TextPosition(offset: 0); |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
TextPosition getTrailingTextBoundaryAt(TextPosition position) { |
|
|
|
@ -2036,8 +1953,7 @@ class _ExpandedTextBoundary extends _TextBoundary { |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
TextEditingValue get textEditingValue { |
|
|
|
|
assert(innerTextBoundary.textEditingValue == |
|
|
|
|
outerTextBoundary.textEditingValue); |
|
|
|
|
assert(innerTextBoundary.textEditingValue == outerTextBoundary.textEditingValue); |
|
|
|
|
return innerTextBoundary.textEditingValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2076,8 +1992,7 @@ class _CollapsedSelectionBoundary extends _TextBoundary { |
|
|
|
|
? innerTextBoundary.getLeadingTextBoundaryAt(position) |
|
|
|
|
: position.offset <= 0 |
|
|
|
|
? const TextPosition(offset: 0) |
|
|
|
|
: innerTextBoundary.getLeadingTextBoundaryAt( |
|
|
|
|
TextPosition(offset: position.offset - 1)); |
|
|
|
|
: innerTextBoundary.getLeadingTextBoundaryAt(TextPosition(offset: position.offset - 1)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@ -2086,8 +2001,7 @@ class _CollapsedSelectionBoundary extends _TextBoundary { |
|
|
|
|
? innerTextBoundary.getTrailingTextBoundaryAt(position) |
|
|
|
|
: position.offset <= 0 |
|
|
|
|
? const TextPosition(offset: 0) |
|
|
|
|
: innerTextBoundary.getTrailingTextBoundaryAt( |
|
|
|
|
TextPosition(offset: position.offset - 1)); |
|
|
|
|
: innerTextBoundary.getTrailingTextBoundaryAt(TextPosition(offset: position.offset - 1)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2102,8 +2016,7 @@ class _MixedBoundary extends _TextBoundary { |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
TextEditingValue get textEditingValue { |
|
|
|
|
assert(leadingTextBoundary.textEditingValue == |
|
|
|
|
trailingTextBoundary.textEditingValue); |
|
|
|
|
assert(leadingTextBoundary.textEditingValue == trailingTextBoundary.textEditingValue); |
|
|
|
|
return leadingTextBoundary.textEditingValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2117,8 +2030,7 @@ class _MixedBoundary extends _TextBoundary { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ------------------------------- Text Actions ------------------------------- |
|
|
|
|
class _DeleteTextAction<T extends DirectionalTextEditingIntent> |
|
|
|
|
extends ContextAction<T> { |
|
|
|
|
class _DeleteTextAction<T extends DirectionalTextEditingIntent> extends ContextAction<T> { |
|
|
|
|
_DeleteTextAction(this.state, this.getTextBoundariesForIntent); |
|
|
|
|
|
|
|
|
|
final RawEditorState state; |
|
|
|
@ -2131,12 +2043,8 @@ class _DeleteTextAction<T extends DirectionalTextEditingIntent> |
|
|
|
|
final _TextBoundary atomicBoundary = _CharacterBoundary(value); |
|
|
|
|
|
|
|
|
|
return TextRange( |
|
|
|
|
start: atomicBoundary |
|
|
|
|
.getLeadingTextBoundaryAt(TextPosition(offset: selection.start)) |
|
|
|
|
.offset, |
|
|
|
|
end: atomicBoundary |
|
|
|
|
.getTrailingTextBoundaryAt(TextPosition(offset: selection.end - 1)) |
|
|
|
|
.offset, |
|
|
|
|
start: atomicBoundary.getLeadingTextBoundaryAt(TextPosition(offset: selection.start)).offset, |
|
|
|
|
end: atomicBoundary.getTrailingTextBoundaryAt(TextPosition(offset: selection.end - 1)).offset, |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2148,10 +2056,7 @@ class _DeleteTextAction<T extends DirectionalTextEditingIntent> |
|
|
|
|
if (!selection.isCollapsed) { |
|
|
|
|
return Actions.invoke( |
|
|
|
|
context!, |
|
|
|
|
ReplaceTextIntent( |
|
|
|
|
state.textEditingValue, |
|
|
|
|
'', |
|
|
|
|
_expandNonCollapsedRange(state.textEditingValue), |
|
|
|
|
ReplaceTextIntent(state.textEditingValue, '', _expandNonCollapsedRange(state.textEditingValue), |
|
|
|
|
SelectionChangedCause.keyboard), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
@ -2163,10 +2068,7 @@ class _DeleteTextAction<T extends DirectionalTextEditingIntent> |
|
|
|
|
if (!textBoundary.textEditingValue.selection.isCollapsed) { |
|
|
|
|
return Actions.invoke( |
|
|
|
|
context!, |
|
|
|
|
ReplaceTextIntent( |
|
|
|
|
state.textEditingValue, |
|
|
|
|
'', |
|
|
|
|
_expandNonCollapsedRange(textBoundary.textEditingValue), |
|
|
|
|
ReplaceTextIntent(state.textEditingValue, '', _expandNonCollapsedRange(textBoundary.textEditingValue), |
|
|
|
|
SelectionChangedCause.keyboard), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
@ -2176,22 +2078,18 @@ class _DeleteTextAction<T extends DirectionalTextEditingIntent> |
|
|
|
|
ReplaceTextIntent( |
|
|
|
|
textBoundary.textEditingValue, |
|
|
|
|
'', |
|
|
|
|
textBoundary |
|
|
|
|
.getTextBoundaryAt(textBoundary.textEditingValue.selection.base), |
|
|
|
|
textBoundary.getTextBoundaryAt(textBoundary.textEditingValue.selection.base), |
|
|
|
|
SelectionChangedCause.keyboard, |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
bool get isActionEnabled => |
|
|
|
|
!state.widget.readOnly && state.textEditingValue.selection.isValid; |
|
|
|
|
bool get isActionEnabled => !state.widget.readOnly && state.textEditingValue.selection.isValid; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class _UpdateTextSelectionAction<T extends DirectionalCaretMovementIntent> |
|
|
|
|
extends ContextAction<T> { |
|
|
|
|
_UpdateTextSelectionAction(this.state, this.ignoreNonCollapsedSelection, |
|
|
|
|
this.getTextBoundariesForIntent); |
|
|
|
|
class _UpdateTextSelectionAction<T extends DirectionalCaretMovementIntent> extends ContextAction<T> { |
|
|
|
|
_UpdateTextSelectionAction(this.state, this.ignoreNonCollapsedSelection, this.getTextBoundariesForIntent); |
|
|
|
|
|
|
|
|
|
final RawEditorState state; |
|
|
|
|
final bool ignoreNonCollapsedSelection; |
|
|
|
@ -2202,8 +2100,7 @@ class _UpdateTextSelectionAction<T extends DirectionalCaretMovementIntent> |
|
|
|
|
final selection = state.textEditingValue.selection; |
|
|
|
|
assert(selection.isValid); |
|
|
|
|
|
|
|
|
|
final collapseSelection = |
|
|
|
|
intent.collapseSelection || !state.widget.selectionEnabled; |
|
|
|
|
final collapseSelection = intent.collapseSelection || !state.widget.selectionEnabled; |
|
|
|
|
// Collapse to the logical start/end. |
|
|
|
|
TextSelection _collapse(TextSelection selection) { |
|
|
|
|
assert(selection.isValid); |
|
|
|
@ -2214,13 +2111,10 @@ class _UpdateTextSelectionAction<T extends DirectionalCaretMovementIntent> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!selection.isCollapsed && |
|
|
|
|
!ignoreNonCollapsedSelection && |
|
|
|
|
collapseSelection) { |
|
|
|
|
if (!selection.isCollapsed && !ignoreNonCollapsedSelection && collapseSelection) { |
|
|
|
|
return Actions.invoke( |
|
|
|
|
context!, |
|
|
|
|
UpdateSelectionIntent(state.textEditingValue, _collapse(selection), |
|
|
|
|
SelectionChangedCause.keyboard), |
|
|
|
|
UpdateSelectionIntent(state.textEditingValue, _collapse(selection), SelectionChangedCause.keyboard), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2229,30 +2123,24 @@ class _UpdateTextSelectionAction<T extends DirectionalCaretMovementIntent> |
|
|
|
|
if (!textBoundarySelection.isValid) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
if (!textBoundarySelection.isCollapsed && |
|
|
|
|
!ignoreNonCollapsedSelection && |
|
|
|
|
collapseSelection) { |
|
|
|
|
if (!textBoundarySelection.isCollapsed && !ignoreNonCollapsedSelection && collapseSelection) { |
|
|
|
|
return Actions.invoke( |
|
|
|
|
context!, |
|
|
|
|
UpdateSelectionIntent(state.textEditingValue, |
|
|
|
|
_collapse(textBoundarySelection), SelectionChangedCause.keyboard), |
|
|
|
|
UpdateSelectionIntent(state.textEditingValue, _collapse(textBoundarySelection), SelectionChangedCause.keyboard), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final extent = textBoundarySelection.extent; |
|
|
|
|
final newExtent = intent.forward |
|
|
|
|
? textBoundary.getTrailingTextBoundaryAt(extent) |
|
|
|
|
: textBoundary.getLeadingTextBoundaryAt(extent); |
|
|
|
|
final newExtent = |
|
|
|
|
intent.forward ? textBoundary.getTrailingTextBoundaryAt(extent) : textBoundary.getLeadingTextBoundaryAt(extent); |
|
|
|
|
|
|
|
|
|
final newSelection = collapseSelection |
|
|
|
|
? TextSelection.fromPosition(newExtent) |
|
|
|
|
: textBoundarySelection.extendTo(newExtent); |
|
|
|
|
final newSelection = |
|
|
|
|
collapseSelection ? TextSelection.fromPosition(newExtent) : textBoundarySelection.extendTo(newExtent); |
|
|
|
|
|
|
|
|
|
// If collapseAtReversal is true and would have an effect, collapse it. |
|
|
|
|
if (!selection.isCollapsed && |
|
|
|
|
intent.collapseAtReversal && |
|
|
|
|
(selection.baseOffset < selection.extentOffset != |
|
|
|
|
newSelection.baseOffset < newSelection.extentOffset)) { |
|
|
|
|
(selection.baseOffset < selection.extentOffset != newSelection.baseOffset < newSelection.extentOffset)) { |
|
|
|
|
return Actions.invoke( |
|
|
|
|
context!, |
|
|
|
|
UpdateSelectionIntent( |
|
|
|
@ -2265,8 +2153,7 @@ class _UpdateTextSelectionAction<T extends DirectionalCaretMovementIntent> |
|
|
|
|
|
|
|
|
|
return Actions.invoke( |
|
|
|
|
context!, |
|
|
|
|
UpdateSelectionIntent(textBoundary.textEditingValue, newSelection, |
|
|
|
|
SelectionChangedCause.keyboard), |
|
|
|
|
UpdateSelectionIntent(textBoundary.textEditingValue, newSelection, SelectionChangedCause.keyboard), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2274,19 +2161,16 @@ class _UpdateTextSelectionAction<T extends DirectionalCaretMovementIntent> |
|
|
|
|
bool get isActionEnabled => state.textEditingValue.selection.isValid; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class _ExtendSelectionOrCaretPositionAction extends ContextAction< |
|
|
|
|
ExtendSelectionToNextWordBoundaryOrCaretLocationIntent> { |
|
|
|
|
_ExtendSelectionOrCaretPositionAction( |
|
|
|
|
this.state, this.getTextBoundariesForIntent); |
|
|
|
|
class _ExtendSelectionOrCaretPositionAction |
|
|
|
|
extends ContextAction<ExtendSelectionToNextWordBoundaryOrCaretLocationIntent> { |
|
|
|
|
_ExtendSelectionOrCaretPositionAction(this.state, this.getTextBoundariesForIntent); |
|
|
|
|
|
|
|
|
|
final RawEditorState state; |
|
|
|
|
final _TextBoundary Function( |
|
|
|
|
ExtendSelectionToNextWordBoundaryOrCaretLocationIntent intent) |
|
|
|
|
final _TextBoundary Function(ExtendSelectionToNextWordBoundaryOrCaretLocationIntent intent) |
|
|
|
|
getTextBoundariesForIntent; |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
Object? invoke(ExtendSelectionToNextWordBoundaryOrCaretLocationIntent intent, |
|
|
|
|
[BuildContext? context]) { |
|
|
|
|
Object? invoke(ExtendSelectionToNextWordBoundaryOrCaretLocationIntent intent, [BuildContext? context]) { |
|
|
|
|
final selection = state.textEditingValue.selection; |
|
|
|
|
assert(selection.isValid); |
|
|
|
|
|
|
|
|
@ -2297,18 +2181,15 @@ class _ExtendSelectionOrCaretPositionAction extends ContextAction< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final extent = textBoundarySelection.extent; |
|
|
|
|
final newExtent = intent.forward |
|
|
|
|
? textBoundary.getTrailingTextBoundaryAt(extent) |
|
|
|
|
: textBoundary.getLeadingTextBoundaryAt(extent); |
|
|
|
|
final newExtent = |
|
|
|
|
intent.forward ? textBoundary.getTrailingTextBoundaryAt(extent) : textBoundary.getLeadingTextBoundaryAt(extent); |
|
|
|
|
|
|
|
|
|
final newSelection = (newExtent.offset - textBoundarySelection.baseOffset) * |
|
|
|
|
(textBoundarySelection.extentOffset - |
|
|
|
|
textBoundarySelection.baseOffset) < |
|
|
|
|
(textBoundarySelection.extentOffset - textBoundarySelection.baseOffset) < |
|
|
|
|
0 |
|
|
|
|
? textBoundarySelection.copyWith( |
|
|
|
|
extentOffset: textBoundarySelection.baseOffset, |
|
|
|
|
affinity: textBoundarySelection.extentOffset > |
|
|
|
|
textBoundarySelection.baseOffset |
|
|
|
|
affinity: textBoundarySelection.extentOffset > textBoundarySelection.baseOffset |
|
|
|
|
? TextAffinity.downstream |
|
|
|
|
: TextAffinity.upstream, |
|
|
|
|
) |
|
|
|
@ -2316,18 +2197,15 @@ class _ExtendSelectionOrCaretPositionAction extends ContextAction< |
|
|
|
|
|
|
|
|
|
return Actions.invoke( |
|
|
|
|
context!, |
|
|
|
|
UpdateSelectionIntent(textBoundary.textEditingValue, newSelection, |
|
|
|
|
SelectionChangedCause.keyboard), |
|
|
|
|
UpdateSelectionIntent(textBoundary.textEditingValue, newSelection, SelectionChangedCause.keyboard), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
bool get isActionEnabled => |
|
|
|
|
state.widget.selectionEnabled && state.textEditingValue.selection.isValid; |
|
|
|
|
bool get isActionEnabled => state.widget.selectionEnabled && state.textEditingValue.selection.isValid; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class _UpdateTextSelectionToAdjacentLineAction< |
|
|
|
|
T extends DirectionalCaretMovementIntent> extends ContextAction<T> { |
|
|
|
|
class _UpdateTextSelectionToAdjacentLineAction<T extends DirectionalCaretMovementIntent> extends ContextAction<T> { |
|
|
|
|
_UpdateTextSelectionToAdjacentLineAction(this.state); |
|
|
|
|
|
|
|
|
|
final RawEditorState state; |
|
|
|
@ -2357,32 +2235,25 @@ class _UpdateTextSelectionToAdjacentLineAction< |
|
|
|
|
void invoke(T intent, [BuildContext? context]) { |
|
|
|
|
assert(state.textEditingValue.selection.isValid); |
|
|
|
|
|
|
|
|
|
final collapseSelection = |
|
|
|
|
intent.collapseSelection || !state.widget.selectionEnabled; |
|
|
|
|
final collapseSelection = intent.collapseSelection || !state.widget.selectionEnabled; |
|
|
|
|
final value = state.textEditingValue; |
|
|
|
|
if (!value.selection.isValid) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final currentRun = _verticalMovementRun ?? |
|
|
|
|
state.renderEditor |
|
|
|
|
.startVerticalCaretMovement(state.renderEditor.selection.extent); |
|
|
|
|
final currentRun = |
|
|
|
|
_verticalMovementRun ?? state.renderEditor.startVerticalCaretMovement(state.renderEditor.selection.extent); |
|
|
|
|
|
|
|
|
|
final shouldMove = |
|
|
|
|
intent.forward ? currentRun.moveNext() : currentRun.movePrevious(); |
|
|
|
|
final shouldMove = intent.forward ? currentRun.moveNext() : currentRun.movePrevious(); |
|
|
|
|
final newExtent = shouldMove |
|
|
|
|
? currentRun.current |
|
|
|
|
: (intent.forward |
|
|
|
|
? TextPosition(offset: state.textEditingValue.text.length) |
|
|
|
|
: const TextPosition(offset: 0)); |
|
|
|
|
final newSelection = collapseSelection |
|
|
|
|
? TextSelection.fromPosition(newExtent) |
|
|
|
|
: value.selection.extendTo(newExtent); |
|
|
|
|
: (intent.forward ? TextPosition(offset: state.textEditingValue.text.length) : const TextPosition(offset: 0)); |
|
|
|
|
final newSelection = |
|
|
|
|
collapseSelection ? TextSelection.fromPosition(newExtent) : value.selection.extendTo(newExtent); |
|
|
|
|
|
|
|
|
|
Actions.invoke( |
|
|
|
|
context!, |
|
|
|
|
UpdateSelectionIntent( |
|
|
|
|
value, newSelection, SelectionChangedCause.keyboard), |
|
|
|
|
UpdateSelectionIntent(value, newSelection, SelectionChangedCause.keyboard), |
|
|
|
|
); |
|
|
|
|
if (state.textEditingValue.selection == newSelection) { |
|
|
|
|
_verticalMovementRun = currentRun; |
|
|
|
@ -2405,8 +2276,7 @@ class _SelectAllAction extends ContextAction<SelectAllTextIntent> { |
|
|
|
|
context!, |
|
|
|
|
UpdateSelectionIntent( |
|
|
|
|
state.textEditingValue, |
|
|
|
|
TextSelection( |
|
|
|
|
baseOffset: 0, extentOffset: state.textEditingValue.text.length), |
|
|
|
|
TextSelection(baseOffset: 0, extentOffset: state.textEditingValue.text.length), |
|
|
|
|
intent.cause, |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
@ -2431,9 +2301,7 @@ class _CopySelectionAction extends ContextAction<CopySelectionTextIntent> { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
bool get isActionEnabled => |
|
|
|
|
state.textEditingValue.selection.isValid && |
|
|
|
|
!state.textEditingValue.selection.isCollapsed; |
|
|
|
|
bool get isActionEnabled => state.textEditingValue.selection.isValid && !state.textEditingValue.selection.isCollapsed; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//Intent class for "escape" key to dismiss selection toolbar in Windows platform |
|
|
|
@ -2441,8 +2309,7 @@ class HideSelectionToolbarIntent extends Intent { |
|
|
|
|
const HideSelectionToolbarIntent(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class _HideSelectionToolbarAction |
|
|
|
|
extends ContextAction<HideSelectionToolbarIntent> { |
|
|
|
|
class _HideSelectionToolbarAction extends ContextAction<HideSelectionToolbarIntent> { |
|
|
|
|
_HideSelectionToolbarAction(this.state); |
|
|
|
|
|
|
|
|
|
final RawEditorState state; |
|
|
|
@ -2513,10 +2380,8 @@ class _ToggleTextStyleAction extends Action<ToggleTextStyleIntent> { |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void invoke(ToggleTextStyleIntent intent, [BuildContext? context]) { |
|
|
|
|
final isActive = _isStyleActive( |
|
|
|
|
intent.attribute, state.controller.getSelectionStyle().attributes); |
|
|
|
|
state.controller.formatSelection( |
|
|
|
|
isActive ? Attribute.clone(intent.attribute, null) : intent.attribute); |
|
|
|
|
final isActive = _isStyleActive(intent.attribute, state.controller.getSelectionStyle().attributes); |
|
|
|
|
state.controller.formatSelection(isActive ? Attribute.clone(intent.attribute, null) : intent.attribute); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@ -2579,16 +2444,12 @@ class _ApplyHeaderAction extends Action<ApplyHeaderIntent> { |
|
|
|
|
final RawEditorState state; |
|
|
|
|
|
|
|
|
|
Attribute<dynamic> _getHeaderValue() { |
|
|
|
|
return state.controller |
|
|
|
|
.getSelectionStyle() |
|
|
|
|
.attributes[Attribute.header.key] ?? |
|
|
|
|
Attribute.header; |
|
|
|
|
return state.controller.getSelectionStyle().attributes[Attribute.header.key] ?? Attribute.header; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void invoke(ApplyHeaderIntent intent, [BuildContext? context]) { |
|
|
|
|
final _attribute = |
|
|
|
|
_getHeaderValue() == intent.header ? Attribute.header : intent.header; |
|
|
|
|
final _attribute = _getHeaderValue() == intent.header ? Attribute.header : intent.header; |
|
|
|
|
state.controller.formatSelection(_attribute); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2620,15 +2481,13 @@ class _ApplyCheckListAction extends Action<ApplyCheckListIntent> { |
|
|
|
|
if (attribute == null) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return attribute.value == Attribute.unchecked.value || |
|
|
|
|
attribute.value == Attribute.checked.value; |
|
|
|
|
return attribute.value == Attribute.unchecked.value || attribute.value == Attribute.checked.value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void invoke(ApplyCheckListIntent intent, [BuildContext? context]) { |
|
|
|
|
state.controller.formatSelection(_getIsToggled() |
|
|
|
|
? Attribute.clone(Attribute.unchecked, null) |
|
|
|
|
: Attribute.unchecked); |
|
|
|
|
state.controller |
|
|
|
|
.formatSelection(_getIsToggled() ? Attribute.clone(Attribute.unchecked, null) : Attribute.unchecked); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|