diff --git a/cosmic-app-list/src/app.rs b/cosmic-app-list/src/app.rs index a6b967f7..7fd4337f 100755 --- a/cosmic-app-list/src/app.rs +++ b/cosmic-app-list/src/app.rs @@ -688,15 +688,62 @@ impl CosmicAppList { .collect::>(); } - /// Yoda: macOS-Tahoe-style hover magnification. Returns the icon - /// size multiplier for a given dock item — 1.3× when the pointer - /// is over it, 1.0× otherwise. Called from the view() icon builders. + /// Yoda: macOS-Tahoe fisheye-style magnification. Returns the + /// per-icon size multiplier based on the distance (in pixels) from + /// the currently hovered icon's center to this icon's center. + /// + /// Uses a gaussian bell curve so the hovered icon peaks at + /// 1.0 + PEAK, immediate neighbors still bulge noticeably, and icons + /// further away relax back to 1.0× — that's the smooth neighbour + /// deformation people associate with the macOS Dock. + /// + /// Falls back to binary 1.3×/1.0× when the rectangle tracker hasn't + /// populated yet (first render, or just after layout changes). fn icon_scale_for(&self, id: &DockItemId) -> f32 { - if self.hovered_dock_item.as_ref() == Some(id) { - 1.3 + const PEAK: f32 = 0.35; + // sigma expressed in multiples of the hovered icon's size — + // 1.4 means the ±1 neighbors sit ~0.7σ away and still bulge + // visibly, while ±3+ has collapsed to ~1.0× (fisheye footprint + // close to 5 icons wide, Tahoe-ish). + const SIGMA_FACTOR: f32 = 1.4; + + let Some(hovered_id) = self.hovered_dock_item.as_ref() else { + return 1.0; + }; + + // Without tracker data we can't compute distance — still peak on + // the hovered one so something visibly responds immediately. + let (Some(hovered_rect), Some(this_rect)) = + (self.rectangles.get(hovered_id), self.rectangles.get(id)) + else { + return if id == hovered_id { 1.0 + PEAK } else { 1.0 }; + }; + + let is_horizontal = matches!( + self.core.applet.anchor, + PanelAnchor::Top | PanelAnchor::Bottom + ); + let hovered_center = if is_horizontal { + hovered_rect.x + hovered_rect.width / 2.0 } else { - 1.0 + hovered_rect.y + hovered_rect.height / 2.0 + }; + let this_center = if is_horizontal { + this_rect.x + this_rect.width / 2.0 + } else { + this_rect.y + this_rect.height / 2.0 + }; + let distance = (this_center - hovered_center).abs(); + let icon_extent = if is_horizontal { + hovered_rect.width + } else { + hovered_rect.height } + .max(1.0); + let sigma = icon_extent * SIGMA_FACTOR; + // exp(-t²) bell curve, t = distance / sigma + let t = distance / sigma; + 1.0 + PEAK * (-t * t).exp() } fn is_on_current_monitor_and_workspace(&self, toplevel_info: &ToplevelInfo) -> bool {