Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SuperEditor][SuperTextField][iOS] Allow caret to slide over characters (Resolves #2103) #2245

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

angelosilvestre
Copy link
Collaborator

[SuperEditor][SuperTextField][iOS] Allow caret to slide over characters. Resolves #2103

Currently, as the user drags the caret on iOS, SuperEditor and SuperTextField snap the caret to the nearest legal caret position.

On native iOS, dragging the caret moves the caret by the exact drag amount in the current line of text, regardless of whether that x-offset is a legal caret position or not. When the user releases, the caret animates to the nearest legal position. Also, when the user is dragging the caret far from the text, a gray caret is displayed at the nearest legal position.

This PR implements this behavior.

Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2024-08-14.at.22.27.06.mp4

Implementation details:

  • Our iOS caret is displayed in IosHandlesDocumentLayer. Currently, this widget doesn't have access to the position where the caret is being dragged. I added a ValueListenable so the widget can listen be notified when the user drags the caret.

  • I added a ghostCaret (for the lack of a better name) in DocumentSelectionLayout to hold the position for the gray caret. DocumentSelectionLayout isn't specific to iOS, so this isn't ideal.

@angelosilvestre
Copy link
Collaborator Author

@matthew-carroll Could you take a look to see if I'm on the right direction here? So far I implemented this for SuperEditor only and I didn't add tests yet.

return DocumentSelectionLayout(
caret: caretRect,
ghostCaret: ghostCaretRect,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you describe why we need to provide the ghost caret to the DocumentSelectionLayout? Maybe the Dart Doc for DocumentSelectionLayout isn't quite accurate, but it says "Visual layout bounds related to a user selection..." - that sounds like its just tracking geometry so that other things can also track that geometry. If so, I'm curious what other parts of Super Editor needs to track the ghost caret inside of DocumentSelectionLayout?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DocumentSelectionLayout is how we expose the information of where the caret should be displayed. The IosControlsDocumentLayerState uses it to size and position the caret. Other parts of the editor won't need this.

Maybe we should create an IosDocumentSelectionLayout to expose this additional property that only iOS uses?

_docLayout.getRectForPosition(docPositionToMagnify!)!.center,
);

_magnifierFocalPointInDocumentSpace.value = centerOfContentAtOffset;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you mention why we're making changes to magnifier stuff in a couple places in this PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magnifier focal point is aligned with the caret. Since now the caret isn't snapped to character edges, the magnifier should follow the same policies.

floatingCursorController: SuperEditorIosControlsScope.rootOf(context).floatingCursorController,
shouldCaretBlink: controlsScope.shouldCaretBlink,
floatingCursorController: controlsScope.floatingCursorController,
caretDragOffset: controlsScope.caretDragOffset,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we de-dup this with floating cursor? I know you mentioned sharing some code over on Discord, but I wasn't sure if you already did that you wanted, or if that task still remains.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into de-duping that, but I've found it would only make things more complicated, and we wouldn't be de-duping that much code.

@@ -621,13 +642,35 @@ class IosControlsDocumentLayerState extends DocumentLayoutLayerState<IosHandlesD
@visibleForTesting
bool get isDownstreamHandleDisplayed => layoutData?.downstream != null;

Offset? _latestCaretDragOfffset;

/// Controls the animation of the caret moving from the position where
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clarify the difference between "the position where the user released" and "the selected position" - how are these different? what are they, literally?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[SuperEditor][SuperTextField][iOS] - Caret movement on drag should slide over characters
2 participants