event_loop, widget, input: pointer-dwell tooltips, global drag coords, foreign-toplevel name cascade
`Button::tooltip( text )` registers a hint string that fires after a 600 ms pointer dwell. `LaidOutWidget` gains a `tooltip: Option<String>` field, `Element::tooltip()` exposes it to the input layer, and the existing pointer-hover path now calls `arm_tooltip` on hover-enter and `cancel_tooltip` on hover-leave / touch. The deadline is polled alongside `next_long_press_wakeup` in `try_run` so an idle pointer still gets a wake-up at the firing instant; on fire, `tooltip_overlay()` synthesises an `OverlaySpec` — a rounded `text_primary @ 95%` pill drawn with `bg` text — anchored above the hovered widget, flipping below or clamping inside the screen if it would clip, and pushed alongside the app's own overlays both in the redraw path and in `reconcile_overlays` so the layer surface is created the same frame the tooltip becomes visible. Pointer-only by design: touch events explicitly cancel because a tap-and-release should never linger into a hint. The `showcase` example wires `.tooltip(..)` on the three button variants as a smoke test. The drag pipeline now reports positions in main-surface (global) coordinates instead of per-surface. `surface_offset_for( focus )` derives the top-left of an overlay surface from `SurfaceState::layer_anchor` — newly stored at `reconcile_overlays` time from `OverlaySpec::anchor` — combined with the main surface's dimensions; `on_drag_move`, `on_drop`, the synthetic move emitted on drag-promotion in `pointer.rs`, and the `pending_drag_inits` push site in `gesture::start_drag` all translate before handing coordinates to the app. The motion and release paths additionally `request_redraw()` every overlay so a dock-style drop target painted on an `Anchor::Bottom` layer surface gets repainted as the drag moves — without that, the visible drop indicator only updates when the cursor re-enters the main surface. Drops still target whichever surface fired the release; only the coordinates are unified. `ForeignToplevelListHandler` previously read `app_id` directly via `ForeignToplevelList::info()`. Clients that never set `app_id` (some winit-windowed compositors, simple test clients) were silently invisible to crustace-style docks because the empty string fell through `unwrap_or_default()` and the dock then keyed entries off `""`. `toplevel_display_id()` cascades: prefer `app_id` for desktop-entry matching, fall back to `title` for human-readable identification, and finally to the protocol-issued `identifier` which is always present and unique per handle. Applied to both `new_toplevel` and `update_toplevel`. `theme::system_fontdb()` lazily loads the system font database once via `OnceLock` and reuses the `Arc` for every `decode_svg_bytes` call. resvg's default `Options::fontdb` is empty, so any SVG containing `<text>` rendered with the built-in fallback font or no font at all; with the system DB attached, icons and decorative SVGs with embedded labels now resolve glyphs correctly. Cached because `load_system_fonts()` walks every font path on the system and is comfortably tens of milliseconds on a cold cache — not something to repeat per icon decode. `themes/default/theme.json` tweaks one variant's slot palette: `surface-alt` from `@indigo/D9` to `@white/D9` and `text-primary` from `@white` to `@navy`, plus a cosmetic re-alignment of the `"value"` columns in the same slots block.
This commit is contained in:
@@ -304,6 +304,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
||||
debug_layout,
|
||||
pending_msgs: Vec::new(),
|
||||
pending_drag_inits: Vec::new(),
|
||||
tooltip_pending: None,
|
||||
tooltip_visible: None,
|
||||
qh: qh.clone(),
|
||||
last_pointer_serial: 0,
|
||||
last_input_serial: 0,
|
||||
@@ -374,7 +376,11 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
||||
// at a fixed rate. The one exception is a pending long-press:
|
||||
// cap the wait at its deadline so a perfectly still press still
|
||||
// fires on time.
|
||||
let timeout = data.next_long_press_wakeup();
|
||||
let timeout = match ( data.next_long_press_wakeup(), data.next_tooltip_wakeup() )
|
||||
{
|
||||
( Some( a ), Some( b ) ) => Some( a.min( b ) ),
|
||||
( a, b ) => a.or( b ),
|
||||
};
|
||||
match event_loop.dispatch( timeout, &mut data )
|
||||
{
|
||||
Ok( () ) => {},
|
||||
@@ -404,6 +410,7 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
||||
// emits its stored message and flips into drag mode for the rest
|
||||
// of the gesture.
|
||||
data.check_long_press_deadlines();
|
||||
data.check_tooltip_deadline();
|
||||
|
||||
// Poll external messages (immediate async results, e.g. PAM auth channel)
|
||||
let ext: Vec<_> = data.app.poll_external();
|
||||
@@ -491,7 +498,9 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
||||
}
|
||||
if data.overlays_dirty
|
||||
{
|
||||
data.cached_overlays = Some( data.app.overlays() );
|
||||
let mut specs = data.app.overlays();
|
||||
if let Some( ts ) = data.tooltip_overlay() { specs.push( ts ); }
|
||||
data.cached_overlays = Some( specs );
|
||||
data.overlays_dirty = false;
|
||||
}
|
||||
draw_frame( &mut data );
|
||||
@@ -615,7 +624,8 @@ pub fn diff_overlay_ids(
|
||||
/// [`SurfaceKind::Pending`] and the `new_output` handler will bring them up.
|
||||
fn reconcile_overlays<A: App>( data: &mut AppData<A> )
|
||||
{
|
||||
let specs = data.app.overlays();
|
||||
let mut specs = data.app.overlays();
|
||||
if let Some( ts ) = data.tooltip_overlay() { specs.push( ts ); }
|
||||
let next_ids: Vec<_> = specs.iter().map( |s| s.id ).collect();
|
||||
let wanted: HashSet<crate::app::OverlayId> = next_ids.iter().copied().collect();
|
||||
|
||||
@@ -863,6 +873,7 @@ fn reconcile_overlays<A: App>( data: &mut AppData<A> )
|
||||
}
|
||||
let mut ss = SurfaceState::<A::Message>::new( surface, 0.0, String::new() );
|
||||
ss.last_requested_size = spec.size;
|
||||
ss.layer_anchor = Some( spec.anchor );
|
||||
overlays_m.insert( spec.id, ss );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user