yoda: dock icon hover magnification (macOS Tahoe-style, phase B v1)
First pass at the signature macOS Dock effect — the icon under the pointer grows, adjacent icons stay at base size. Full fisheye (smooth bell-curve scaling on neighbors) can be a later iteration. Changes in cosmic-app-list/src/app.rs: - CosmicAppList gains a hovered_dock_item: Option<DockItemId> auto-initialized to None via #[derive(Default)]. - New Message::DockItemHover(Option<DockItemId>) handled in update() by just writing the field; view() then reads it to decide scale. - DockItem::as_icon gains an icon_scale: f32 parameter. Inside it the cosmic_icon width/height = (base_icon_size * icon_scale) clamped to u16; indicator dot and other surrounding layout stay at base size so only the icon visually bulges. - New App::icon_scale_for(id) helper: 1.3 if Some(id) == hovered, 1.0 otherwise. Single place to tune the magnification factor. - The two main dock rows (favorites + filtered_active_list) wrap their rendered applet_tooltip in widget::mouse_area with on_enter(DockItemHover(Some(id))) / on_exit(DockItemHover(None)) and call icon_scale_for before rendering. - The three remaining as_icon call sites (DnD preview, favorites overflow popup, active overflow popup) pass icon_scale = 1.0 — hover magnification on those surfaces would look jittery and isn't needed anyway. Build: cargo build --release -p cosmic-app-list (≈ 7s). Binary installed at /usr/local/bin/cosmic-app-list, backup kept as .pre-magnification.
This commit is contained in:
parent
1d0e662365
commit
5cb0dbb0c5
1 changed files with 95 additions and 16 deletions
|
|
@ -187,6 +187,11 @@ impl DockItem {
|
|||
dot_border_radius: [f32; 4],
|
||||
window_id: window::Id,
|
||||
filter: Option<&dyn Fn(&ToplevelInfo) -> bool>,
|
||||
// Yoda: multiplier on the computed icon size (1.0 = default,
|
||||
// >1.0 = magnified e.g. on hover for the macOS Tahoe effect).
|
||||
// Applied to the icon's rendered width/height only — indicator
|
||||
// dot and surrounding layout stay at base size.
|
||||
icon_scale: f32,
|
||||
) -> Element<'_, Message> {
|
||||
let Self {
|
||||
toplevels,
|
||||
|
|
@ -205,17 +210,35 @@ impl DockItem {
|
|||
};
|
||||
let toplevel_count = filtered_toplevels.len();
|
||||
|
||||
// Cairo-like : pastille plus petite + atténuée quand toutes les fenêtres
|
||||
// de cette app sont minimisées.
|
||||
let all_minimized = toplevel_count > 0
|
||||
&& filtered_toplevels
|
||||
.iter()
|
||||
.all(|(info, _)| info.state.contains(&State::Minimized));
|
||||
|
||||
let app_icon = AppletIconData::new(applet);
|
||||
|
||||
// Yoda: scaled icon size for hover magnification. Clamped so
|
||||
// tiny floats don't round to 0 and huge ones stay within u16.
|
||||
let scaled_icon_size = ((f32::from(app_icon.icon_size) * icon_scale).round() as i32)
|
||||
.clamp(1, u16::MAX as i32) as u16;
|
||||
let cosmic_icon = cosmic::widget::icon(
|
||||
fde::IconSource::from_unknown(desktop_info.icon().unwrap_or_default()).as_cosmic_icon(),
|
||||
)
|
||||
// sets the preferred icon size variant
|
||||
.size(128)
|
||||
.width(app_icon.icon_size.into())
|
||||
.height(app_icon.icon_size.into());
|
||||
.width(scaled_icon_size.into())
|
||||
.height(scaled_icon_size.into());
|
||||
|
||||
let indicator = {
|
||||
// Padding réduit quand minimisée → pastille plus petite.
|
||||
let effective_radius = if all_minimized {
|
||||
(app_icon.dot_radius * 0.55).max(1.0)
|
||||
} else {
|
||||
app_icon.dot_radius
|
||||
};
|
||||
|
||||
let container = if toplevel_count <= 1 {
|
||||
vertical_space().height(Length::Fixed(0.0))
|
||||
} else {
|
||||
|
|
@ -229,22 +252,34 @@ impl DockItem {
|
|||
}
|
||||
}
|
||||
.apply(container)
|
||||
.padding(app_icon.dot_radius);
|
||||
.padding(effective_radius);
|
||||
|
||||
if toplevel_count == 0 {
|
||||
container
|
||||
} else {
|
||||
container.class(theme::Container::custom(move |theme| container::Style {
|
||||
background: if is_focused {
|
||||
Some(Background::Color(theme.cosmic().accent_color().into()))
|
||||
container.class(theme::Container::custom(move |theme| {
|
||||
let cosmic = theme.cosmic();
|
||||
let accent: iced::Color = cosmic.accent_color().into();
|
||||
let on_bg: iced::Color = cosmic.on_bg_color().into();
|
||||
// Teinte neutre atténuée quand toutes les fenêtres sont minimisées.
|
||||
let muted = iced::Color { a: 0.45, ..on_bg };
|
||||
|
||||
let fill = if all_minimized {
|
||||
muted
|
||||
} else if is_focused {
|
||||
accent
|
||||
} else {
|
||||
Some(Background::Color(theme.cosmic().on_bg_color().into()))
|
||||
},
|
||||
border: Border {
|
||||
radius: dot_border_radius.into(),
|
||||
on_bg
|
||||
};
|
||||
|
||||
container::Style {
|
||||
background: Some(Background::Color(fill)),
|
||||
border: Border {
|
||||
radius: dot_border_radius.into(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}))
|
||||
}
|
||||
};
|
||||
|
|
@ -374,6 +409,9 @@ struct CosmicAppList {
|
|||
output_list: FxHashMap<WlOutput, OutputInfo>,
|
||||
locales: Vec<String>,
|
||||
hovered_toplevel: Option<ExtForeignToplevelHandleV1>,
|
||||
/// Yoda: which dock icon the pointer is currently over (for hover
|
||||
/// magnification). None = no dock icon hovered.
|
||||
hovered_dock_item: Option<DockItemId>,
|
||||
overflow_favorites_popup: Option<window::Id>,
|
||||
overflow_active_popup: Option<window::Id>,
|
||||
}
|
||||
|
|
@ -389,6 +427,9 @@ enum Message {
|
|||
Wayland(WaylandUpdate),
|
||||
PinApp(u32),
|
||||
UnpinApp(u32),
|
||||
/// Yoda: pointer entered (Some) or left (None) a dock icon — drives
|
||||
/// the macOS Tahoe-style hover magnification effect.
|
||||
DockItemHover(Option<DockItemId>),
|
||||
Popup(u32, window::Id),
|
||||
Pressed(window::Id),
|
||||
ToplevelListPopup(u32, window::Id),
|
||||
|
|
@ -647,6 +688,17 @@ impl CosmicAppList {
|
|||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn icon_scale_for(&self, id: &DockItemId) -> f32 {
|
||||
if self.hovered_dock_item.as_ref() == Some(id) {
|
||||
1.3
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
fn is_on_current_monitor_and_workspace(&self, toplevel_info: &ToplevelInfo) -> bool {
|
||||
use cosmic_app_list_config::ToplevelFilter;
|
||||
|
||||
|
|
@ -1582,6 +1634,9 @@ impl cosmic::Application for CosmicAppList {
|
|||
Message::GpuRequest(gpus) => {
|
||||
self.gpus = gpus;
|
||||
}
|
||||
Message::DockItemHover(id) => {
|
||||
self.hovered_dock_item = id;
|
||||
}
|
||||
Message::OpenActive => {
|
||||
let create_new = self.overflow_active_popup.is_none();
|
||||
let mut cmds = vec![self.close_popups()];
|
||||
|
|
@ -1763,7 +1818,9 @@ impl cosmic::Application for CosmicAppList {
|
|||
.filter(|(info, _)| self.is_on_current_monitor_and_workspace(info))
|
||||
.any(|y| focused_item.contains(&y.0.foreign_toplevel));
|
||||
|
||||
self.core
|
||||
let dock_id = dock_item.id;
|
||||
let icon_scale = self.icon_scale_for(&DockItemId::from(dock_id));
|
||||
let tooltip = self.core
|
||||
.applet
|
||||
.applet_tooltip::<Message>(
|
||||
dock_item.as_icon(
|
||||
|
|
@ -1776,6 +1833,7 @@ impl cosmic::Application for CosmicAppList {
|
|||
dot_radius,
|
||||
self.core.main_window_id().unwrap(),
|
||||
Some(&|info| self.is_on_current_monitor_and_workspace(info)),
|
||||
icon_scale,
|
||||
),
|
||||
dock_item
|
||||
.desktop_info
|
||||
|
|
@ -1785,7 +1843,10 @@ impl cosmic::Application for CosmicAppList {
|
|||
self.popup.is_some(),
|
||||
Message::Surface,
|
||||
None,
|
||||
)
|
||||
);
|
||||
cosmic::widget::mouse_area(tooltip)
|
||||
.on_enter(Message::DockItemHover(Some(DockItemId::from(dock_id))))
|
||||
.on_exit(Message::DockItemHover(None))
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
|
@ -1836,6 +1897,10 @@ impl cosmic::Application for CosmicAppList {
|
|||
dot_radius,
|
||||
self.core.main_window_id().unwrap(),
|
||||
Some(&|info| self.is_on_current_monitor_and_workspace(info)),
|
||||
// Yoda: no magnification on DnD-preview icons — these
|
||||
// float around as the user drags, so static 1.0 is
|
||||
// less visually confusing.
|
||||
1.0,
|
||||
),
|
||||
);
|
||||
} else if self.is_listening_for_dnd && self.pinned_list.is_empty() {
|
||||
|
|
@ -1875,8 +1940,10 @@ impl cosmic::Application for CosmicAppList {
|
|||
.iter()
|
||||
.filter(|(info, _)| self.is_on_current_monitor_and_workspace(info))
|
||||
.any(|y| focused_item.contains(&y.0.foreign_toplevel));
|
||||
let dock_id = dock_item.id;
|
||||
let icon_scale = self.icon_scale_for(&DockItemId::from(dock_id));
|
||||
|
||||
self.core
|
||||
let tooltip = self.core
|
||||
.applet
|
||||
.applet_tooltip(
|
||||
dock_item.as_icon(
|
||||
|
|
@ -1889,6 +1956,7 @@ impl cosmic::Application for CosmicAppList {
|
|||
dot_radius,
|
||||
self.core.main_window_id().unwrap(),
|
||||
Some(&|info| self.is_on_current_monitor_and_workspace(info)),
|
||||
icon_scale,
|
||||
),
|
||||
dock_item
|
||||
.desktop_info
|
||||
|
|
@ -1898,7 +1966,10 @@ impl cosmic::Application for CosmicAppList {
|
|||
self.popup.is_some(),
|
||||
Message::Surface,
|
||||
None,
|
||||
)
|
||||
);
|
||||
cosmic::widget::mouse_area(tooltip)
|
||||
.on_enter(Message::DockItemHover(Some(DockItemId::from(dock_id))))
|
||||
.on_exit(Message::DockItemHover(None))
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
|
@ -2313,6 +2384,10 @@ impl cosmic::Application for CosmicAppList {
|
|||
dot_radius,
|
||||
id,
|
||||
Some(&|info| self.is_on_current_monitor_and_workspace(info)),
|
||||
// Yoda: icons in the overflow popup are
|
||||
// already smaller-grid — keep them at 1.0
|
||||
// so the popup doesn't reshuffle on hover.
|
||||
1.0,
|
||||
),
|
||||
dock_item
|
||||
.desktop_info
|
||||
|
|
@ -2421,6 +2496,10 @@ impl cosmic::Application for CosmicAppList {
|
|||
dot_radius,
|
||||
id,
|
||||
Some(&|info| self.is_on_current_monitor_and_workspace(info)),
|
||||
// Yoda: popup icons stay at 1.0 (hover
|
||||
// magnification is applied to the main
|
||||
// dock row only).
|
||||
1.0,
|
||||
),
|
||||
dock_item
|
||||
.desktop_info
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue