diff --git a/src/event_loop/handlers.rs b/src/event_loop/handlers.rs index 28de1b4..b7c03b5 100644 --- a/src/event_loop/handlers.rs +++ b/src/event_loop/handlers.rs @@ -92,6 +92,10 @@ impl CompositorHandler for AppData ss.request_redraw(); ( pw, ph ) }; + // Anchors and baked overlay geometry are in the old scale's + // physical pixels — drop the tooltip and rebuild the specs. + self.cancel_tooltip(); + self.overlays_dirty = true; // Notify the app of the new physical dimensions. The previous // `on_resize` it received was scaled with the OLD factor, so any // app-side state keyed off those pixels is now stale. Only fire diff --git a/src/event_loop/tooltip.rs b/src/event_loop/tooltip.rs index e9fe83d..7f2c86e 100644 --- a/src/event_loop/tooltip.rs +++ b/src/event_loop/tooltip.rs @@ -88,12 +88,27 @@ impl AppData pub( crate ) fn tooltip_overlay( &self ) -> Option> { let v = self.tooltip_visible.as_ref()?; + + use std::hash::{ Hash, Hasher }; + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + "ltk-tooltip".hash( &mut hasher ); + let id = crate::app::OverlayId( hasher.finish() as u32 ); + + // Layout runs in the overlay's physical buffer space, so every + // coordinate below is physical. The overlay's own scale wins once + // the surface exists; before that, main's is the best predictor. + let sf = self.overlays.get( &id ) + .map( |ss| ss.scale_factor ) + .unwrap_or( self.main.scale_factor ) + .max( 1 ) as f32; + let ts = crate::types::text_scale(); + let ( ox, oy ) = self.surface_offset_for( v.focus ); - let scale = self.try_surface( v.focus )?.scale_factor as f32; - let anchor_x = ox + v.anchor.x / scale; - let anchor_y = oy + v.anchor.y / scale; - let anchor_w = v.anchor.width / scale; - let anchor_h = v.anchor.height / scale; + let src_scale = self.try_surface( v.focus )?.scale_factor.max( 1 ) as f32; + let anchor_x = ( ox + v.anchor.x / src_scale ) * sf; + let anchor_y = ( oy + v.anchor.y / src_scale ) * sf; + let anchor_w = v.anchor.width / src_scale * sf; + let anchor_h = v.anchor.height / src_scale * sf; let palette = crate::theme::palette(); let bg_col = crate::types::Color::rgba( palette.text_primary.r, palette.text_primary.g, palette.text_primary.b, 0.95 ); @@ -103,30 +118,28 @@ impl AppData .color( palette.bg ) ) .background( bg_col ) - .padding_h( 12.0 ) - .padding_v( 6.0 ) - .radius( 8.0 ) + .padding_h( 12.0 * sf ) + .padding_v( 6.0 * sf ) + .radius( 8.0 * sf ) .into(); - let estimated_w = ( v.text.chars().count() as f32 * 7.5 + 24.0 ).clamp( 40.0, 320.0 ); - let estimated_h = 28.0_f32; + let estimated_w = ( v.text.chars().count() as f32 * 7.5 * ts * sf + 24.0 * sf ) + .clamp( 40.0 * sf, 320.0 * sf ); + let estimated_h = 16.0 * ts * sf + 12.0 * sf; + let gap = 6.0 * sf; + let margin = 4.0 * sf; let mut x = anchor_x + ( anchor_w - estimated_w ) / 2.0; - let mut y = anchor_y - estimated_h - 6.0; - let sw = self.main.width as f32; - let sh = self.main.height as f32; - x = x.clamp( 4.0, ( sw - estimated_w - 4.0 ).max( 4.0 ) ); - if y < 4.0 { y = anchor_y + anchor_h + 6.0; } - if y + estimated_h > sh - 4.0 { y = ( sh - estimated_h - 4.0 ).max( 4.0 ); } + let mut y = anchor_y - estimated_h - gap; + let sw = self.main.width as f32 * sf; + let sh = self.main.height as f32 * sf; + x = x.clamp( margin, ( sw - estimated_w - margin ).max( margin ) ); + if y < margin { y = anchor_y + anchor_h + gap; } + if y + estimated_h > sh - margin { y = ( sh - estimated_h - margin ).max( margin ); } let view: crate::widget::Element = crate::layout::stack::stack() .push_translated( pill, crate::layout::stack::HAlign::Start, crate::layout::stack::VAlign::Top, x, y ) .into(); - use std::hash::{ Hash, Hasher }; - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - "ltk-tooltip".hash( &mut hasher ); - let id = crate::app::OverlayId( hasher.finish() as u32 ); - Some( crate::app::OverlaySpec { id,