feat: Keep controls and header bar visible when controls are hovered

Add a `controls_hovered` state tracked via new `ControlsHoverEnter`/
`ControlsHoverExit` messages. Previously the auto-hide timeout could
fire while the cursor was resting over the controls, since only mouse
movement/clicks reset the timer. This is now prevented whenever the
controls are hovered, i.e. `controls_hovered` is true.

Implementation comments:
- Controls popup is wrapped in a single `mouse_area` each to avoid
  duplicate enter/exit events at internal widget boundaries.
- Double trigger of enter/exit messages in `update()` is guarded
  against.

Resolves #304
This commit is contained in:
Lysander Treumann 2026-09-27 17:25:27 +02:00
parent 3b016ff126
commit 8ad21891b3

View file

@ -344,6 +344,8 @@ pub enum Message {
VideoAreaClick, VideoAreaClick,
PlaybackSpeed(f64), PlaybackSpeed(f64),
ShowControls, ShowControls,
ControlsHoverEnter,
ControlsHoverExit,
SystemThemeModeChange(cosmic_theme::ThemeMode), SystemThemeModeChange(cosmic_theme::ThemeMode),
WindowClose, WindowClose,
} }
@ -355,6 +357,7 @@ pub struct App {
album_art_opt: Option<tempfile::NamedTempFile>, album_art_opt: Option<tempfile::NamedTempFile>,
controls: bool, controls: bool,
controls_time: Instant, controls_time: Instant,
controls_hovered: bool,
dropdown_opt: Option<DropdownKind>, dropdown_opt: Option<DropdownKind>,
fullscreen: bool, fullscreen: bool,
key_binds: HashMap<KeyBind, Action>, key_binds: HashMap<KeyBind, Action>,
@ -652,6 +655,7 @@ impl App {
fn update_controls(&mut self, in_use: bool) { fn update_controls(&mut self, in_use: bool) {
if in_use if in_use
|| self.controls_hovered
|| !self || !self
.video_opt .video_opt
.as_ref() .as_ref()
@ -932,6 +936,7 @@ impl Application for App {
album_art_opt: None, album_art_opt: None,
controls: true, controls: true,
controls_time: Instant::now(), controls_time: Instant::now(),
controls_hovered: false,
dropdown_opt: None, dropdown_opt: None,
fullscreen: false, fullscreen: false,
key_binds: key_binds(), key_binds: key_binds(),
@ -1658,6 +1663,18 @@ impl Application for App {
Message::ShowControls => { Message::ShowControls => {
self.update_controls(true); self.update_controls(true);
} }
Message::ControlsHoverEnter => {
if !self.controls_hovered {
self.controls_hovered = true;
self.update_controls(true);
}
}
Message::ControlsHoverExit => {
if self.controls_hovered {
self.controls_hovered = false;
self.update_controls(false);
}
}
Message::SystemThemeModeChange(_theme_mode) => { Message::SystemThemeModeChange(_theme_mode) => {
return self.update_config(); return self.update_config();
} }
@ -2111,7 +2128,11 @@ impl Application for App {
} }
} }
if !popup_items.is_empty() { if !popup_items.is_empty() {
popover = popover.popup(widget::column::with_children(popup_items)); popover = popover.popup(
widget::mouse_area(widget::column::with_children(popup_items))
.on_enter(Message::ControlsHoverEnter)
.on_exit(Message::ControlsHoverExit),
);
} }
widget::container(popover) widget::container(popover)