input, render: keep fast touch typing whole, fire pattern nodes once, and make the software image path cheap
CI / build + test (push) Has been cancelled
CI / cargo audit (push) Has been cancelled

Two ways a tap was lost on a phone. The gesture machine serves one finger: a second finger landing while the first was still down went to the auxiliary slot and never reached it, so with two thumbs every overlapping tap on a keypad vanished. A finger landing on a primary that is still a plain press (GestureState::is_plain_press: no drag, scroll, swipe or slider in flight) now commits that press as a tap at its last position and takes the primary slot; the earlier finger's release reports through on_touch_up, and a second finger during a pattern stroke or a slider drag stays out as before. The touch-up body moves to release_primary so both paths share it. The other loss needed one finger: the 8 px travel that turns a press into a scroll, swipe or drag, and the 6 px stray that cancels a touch long-press, were compared against physical coordinates, so at buffer scale 2 they were 4 and 3 logical px and an ordinary thumb tap became a vertical drag whose release skipped the button. SwipeConfig carries the surface's scale and the machine reads the thresholds through slop() and stray(), which are logical.
Pattern grids fired on_node on every motion event within a node's snap radius, and the Slider move outcome requested a repaint each time, so drawing a pattern rebuilt and re-rastered the surface at the touchscreen's motion rate. The machine remembers the node under the finger (last_pattern_node) and emits only on change; the handlers split pattern_node_at from pattern_msg, with pattern_node_msg kept on top of them; a move outcome without a message no longer requests a redraw.
SoftwareCanvas::draw_image_data premultiplied the whole source in floating point on every call, so a full-surface backdrop paid a pass over every pixel per frame before the blit. Premultiplied copies now live in a 32 MiB least-recently-drawn cache keyed by size, content fingerprint and alpha, the key the GLES texture cache already used; fingerprint_rgba moves to render::helpers and both backends share it. The entry records whether the source was fully opaque, and an opaque image drawn 1:1 with no clip and full alpha is copied row by row into the canvas instead of going through tiny-skia's bilinear pipeline.
docs/architecture.md describes the primary-slot hand-over, docs/widgets.md the once-per-node contract, and the CHANGELOG lists the five fixes.
This commit is contained in:
2026-09-12 10:43:51 +02:00
parent 0a4afd8f81
commit 75be800390
13 changed files with 270 additions and 94 deletions
+4 -4
View File
@@ -362,10 +362,10 @@ pattern_grid( self.path.clone() )
# }}
```
`on_node( f )` fires with a node index whenever a press or a drag reaches
a node, including nodes already in the path — appending, skipping a
repeat, or taking in the node a straight stroke crosses is the app's
rule, not the widget's. `on_release( msg )` fires when the finger lifts.
`on_node( f )` fires with a node index when a press or a drag enters a
node, once per entry rather than once per motion event, including nodes
already in the path — appending, skipping a repeat, or taking in the node
a straight stroke crosses is the app's rule, not the widget's. `on_release( msg )` fires when the finger lifts.
`hide_stroke( true )` draws only the dots, against shoulder surfing;
`error( true )` paints the current path in the palette's `danger` colour.
`size( len )` sets the side of the square grid (any `Length`); without it the