Expand description
Native-grade text selection primitives for BasicTextField.
This module holds the pure, unit-tested building blocks the text field uses to offer Android/iOS-style selection: tap-count classification, word and line/paragraph boundary detection, and the geometry of the draggable teardrop selection handles (their shapes, their hit regions, and the selection math that a handle drag produces).
Keeping these as free functions makes the touch behavior testable without a
renderer and keeps TextFieldModifierNode focused on wiring.
Structs§
- Handle
Grab Offset - Finger-to-handle relationship for one drag. The first phase preserves the captured offset exactly, the second shifts the handle above the finger, and the third preserves that final offset exactly. Progress is based on the furthest displacement from the grab, so event cadence and small reversals cannot change the result.
Enums§
- Handle
Kind - Which selection handle a lollipop represents.
- Line
Affinity - Which visual line a caret/handle at a soft-wrap boundary belongs to. At a shared boundary byte (the end of one wrapped visual line IS the start of the next — mid-word wraps produce these) the offset alone is ambiguous:
- Selection
Granularity - The unit of text a tap gesture selects, growing with the tap count the way
mature text editors do (Android
TextView, iOSUITextView, VS Code):
Constants§
- GRAB_
BIAS_ VIEW_ CLEARANCE - Extra clearance (dp) below the handle dot once fully visible above the finger.
- GRAB_
DIRECT_ FOLLOW_ DISTANCE - Downward travel that follows with the original finger-to-handle offset before the visibility drift starts.
- GRAB_
VISIBILITY_ DRIFT_ DISTANCE - Additional downward travel over which the handle moves into full view.
- HANDLE_
DOT_ LINE_ OVERLAP - How far the dot dips INTO the line box (dp): the reference start dot’s bottom sits ~5 px (1.7 dp) below the line-box top, the end dot’s top ~6 px above the line-box bottom, so dot and stem read as one continuous shape.
- HANDLE_
GRAB_ SLOP - Finger-sized grab slop (px) added around a handle’s drawn teardrop to enlarge
its touch target, matching Android’s generous handle hit area. A bare
teardrop (~2·
HANDLE_RADIUSacross) is far smaller than a fingertip, so a touch-DOWN aimed at a handle routinely lands a few px off it; without this slop the press falls through to the field below and places a caret, which collapses the selection. The slop is applied to the sides and BELOW the tip (where the bulb and the grabbing finger sit) but never ABOVE the tip — seecrate::widgets::selection_handle, which keeps the box off the glyph line so a double-tap still reaches the field to escalate into a word selection. - HANDLE_
RADIUS - Radius of a selection/cursor handle dot in dp (the reference dot is 16.2 physical px at 3x ≈ a 16 dp circle).
- HANDLE_
STEM_ WIDTH - Width of the handle stem in dp (measured 6 px at 3x = 2 dp — the same weight as the caret).
- MULTI_
TAP_ SLOP_ PX - Maximum distance (px) between consecutive taps that still counts as a
multi-tap. A tap that lands far from the previous one starts a fresh
single tap even if it arrives quickly, matching Android’s
ViewConfigurationdouble-tap slop behavior. - MULTI_
TAP_ TIMEOUT_ MS - Maximum time between taps that still counts as a multi-tap, in milliseconds.
Functions§
- caret_
visual_ line - Given the source byte ranges of the visual (wrapped) lines and a caret
byte
offset, returns the(visual_line_index, line_start_byte)the caret sits on. - classify_
tap_ count - Classifies a press into a 1-based tap count from the previous tap’s count, the time since it, and the distance from it.
- find_
line_ boundaries - Returns the byte range
[start, end)of the line containingpos, delimited by\n(the newline itself is excluded from the range). - find_
paragraph_ boundaries - Returns the byte range
[start, end)of the paragraph containingpos. - grab_
bias_ full_ view - The drift target: bias placing the finger just below the handle dot (tip + dot + clearance), so the whole lollipop stays visible above it.
- handle_
path_ data - SVG path data for a handle lollipop at a text edge.
- resolve_
selection_ tap_ count - Resolves the effective tap count for a press, folding in the “tap inside an
existing selection” gesture so it drives the same word → line → paragraph
granularity ladder (
tap_selection_granularity) as a rapid multi-tap. - selection_
after_ handle_ drag - Computes the selection
(min, max)that results from dragging one handle to a new textoffset, keeping the opposite (fixed) edge anchored. - tap_
selection_ granularity - Maps a 1-based tap count to the granularity it selects.