Skip to content

Commit

Permalink
[SuperEditor] - Handle automatic refocus selection when the doc conte…
Browse files Browse the repository at this point in the history
…nt has changed (Resolves #2386) (#2389)
  • Loading branch information
matthew-carroll committed Oct 31, 2024
1 parent 6f307f7 commit 55bc88c
Show file tree
Hide file tree
Showing 5 changed files with 664 additions and 1 deletion.
5 changes: 5 additions & 0 deletions super_editor/lib/src/core/document.dart
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,11 @@ abstract class DocumentNode implements ChangeNotifier {
/// For example, a [ParagraphNode] would return [TextNodePosition(offset: text.length)].
NodePosition get endPosition;

/// Returns `true` if this [DocumentNode] contains the given [position], or `false`
/// if the [position] doesn't sit within this node, or if the [position] type doesn't
/// apply to this [DocumentNode].
bool containsPosition(Object position);

/// Inspects [position1] and [position2] and returns the one that's
/// positioned further upstream in this [DocumentNode].
///
Expand Down
3 changes: 3 additions & 0 deletions super_editor/lib/src/default_editor/box_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ abstract class BlockNode extends DocumentNode {
@override
UpstreamDownstreamNodePosition get endPosition => const UpstreamDownstreamNodePosition.downstream();

@override
bool containsPosition(Object position) => position is UpstreamDownstreamNodePosition;

@override
UpstreamDownstreamNodePosition selectUpstreamPosition(NodePosition position1, NodePosition position2) {
if (position1 is! UpstreamDownstreamNodePosition) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,62 @@ class _EditorSelectionAndFocusPolicyState extends State<EditorSelectionAndFocusP
return;
}

if (_previousSelection == null) {
// There's no selection to restore.
return;
}

// Restore the previous selection.
editorPoliciesLog
.info("[${widget.runtimeType}] - restoring previous editor selection because the editor re-gained focus");
final previousSelection = _previousSelection!;
late final DocumentSelection restoredSelection;
final baseNode = widget.editor.context.document.getNodeById(previousSelection.base.nodeId);
final extentNode = widget.editor.context.document.getNodeById(previousSelection.extent.nodeId);
if (baseNode == null && extentNode == null) {
// The node(s) where the selection was previously are gone. Possibly deleted.
// Therefore, we can't restore the previous selection. Fizzle.
return;
}

if (baseNode != null && extentNode != null) {
if (!baseNode.containsPosition(previousSelection.base.nodePosition)) {
// Either the base node content changed and the selection no longer fits, or the
// type of content in the node changed. Either way, we can't restore this selection.
return;
}
if (!extentNode.containsPosition(previousSelection.extent.nodePosition)) {
// Either the extent node content changed and the selection no longer fits, or the
// type of content in the node changed. Either way, we can't restore this selection.
return;
}

// The base and extent nodes both still exist. Use the previous selection
// without modification.
restoredSelection = previousSelection;
} else if (baseNode == null) {
// The base node disappeared, but the extent node remains.
if (!extentNode!.containsPosition(previousSelection.extent.nodePosition)) {
// Either the extent node content changed and the selection no longer fits, or the
// type of content in the node changed. Either way, we can't restore this selection.
return;
}

restoredSelection = DocumentSelection.collapsed(position: previousSelection.extent);
} else if (extentNode == null) {
// The extent node disappeared, but the base node remains.
if (!baseNode.containsPosition(previousSelection.base.nodePosition)) {
// Either the base node content changed and the selection no longer fits, or the
// type of content in the node changed. Either way, we can't restore this selection.
return;
}

restoredSelection = DocumentSelection.collapsed(position: previousSelection.base);
}

widget.editor.execute([
ChangeSelectionRequest(
_previousSelection,
restoredSelection,
SelectionChangeType.placeCaret,
SelectionReason.contentChange,
),
Expand Down Expand Up @@ -177,6 +227,7 @@ class _EditorSelectionAndFocusPolicyState extends State<EditorSelectionAndFocusP
// (Maybe) remove the editor's selection when it loses focus.
if (!widget.focusNode.hasFocus && widget.clearSelectionWhenEditorLosesFocus) {
editorPoliciesLog.info("[${widget.runtimeType}] - clearing editor selection because the editor lost all focus");

widget.editor.execute([
const ClearSelectionRequest(),
]);
Expand Down
13 changes: 13 additions & 0 deletions super_editor/lib/src/default_editor/text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ class TextNode extends DocumentNode with ChangeNotifier {
@override
TextNodePosition get endPosition => TextNodePosition(offset: text.length);

@override
bool containsPosition(Object position) {
if (position is! TextNodePosition) {
return false;
}

if (position.offset < 0 || position.offset > text.length) {
return false;
}

return true;
}

@override
NodePosition selectUpstreamPosition(NodePosition position1, NodePosition position2) {
if (position1 is! TextNodePosition) {
Expand Down
Loading

0 comments on commit 55bc88c

Please sign in to comment.