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.
44 lines
1.4 KiB
Rust
44 lines
1.4 KiB
Rust
// SPDX-License-Identifier: LGPL-2.1-only
|
|
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
|
|
|
use crate::app::App;
|
|
use crate::event_loop::{ AppData, SurfaceFocus };
|
|
use crate::tree::find_handlers;
|
|
|
|
use super::super::gesture::SwipeConfig;
|
|
|
|
/// Pointer-side helpers on `AppData`. Split out so touch can call the
|
|
/// same swipe-config factory; `apply_*` helpers live in `dispatch.rs`
|
|
/// because they are shared.
|
|
impl<A: App> AppData<A>
|
|
{
|
|
/// Snapshot the swipe thresholds + surface dimensions into a
|
|
/// [`SwipeConfig`] for the gesture machine. Called once per
|
|
/// motion / release event.
|
|
pub( crate ) fn swipe_config( &self, focus: SurfaceFocus ) -> SwipeConfig
|
|
{
|
|
let ss = self.surface( focus );
|
|
SwipeConfig
|
|
{
|
|
up_thresh: self.app.swipe_threshold(),
|
|
down_thresh: self.app.swipe_down_threshold(),
|
|
down_edge: self.app.swipe_down_edge(),
|
|
horizontal_thresh: self.app.swipe_horizontal_threshold(),
|
|
overscroll_thresh: self.app.overscroll_dismiss_threshold(),
|
|
surface_width: ss.physical_width(),
|
|
surface_height: ss.physical_height(),
|
|
scale: ss.scale_factor.max( 1 ) as f32,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub( crate ) fn hover_affects_paint<Msg: Clone>(
|
|
widget_rects: &[crate::widget::LaidOutWidget<Msg>],
|
|
idx: Option<usize>,
|
|
) -> bool
|
|
{
|
|
idx.and_then( |i| find_handlers( widget_rects, i ) )
|
|
.map( |h| !h.is_drag_target() )
|
|
.unwrap_or( false )
|
|
}
|