diff --git a/src/app/mod.rs b/src/app/mod.rs index cf794b80a..076f2a07b 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -3059,6 +3059,8 @@ pub enum Message { /// User clicked a command-line option button — feed its keyword to the /// active command as if typed (empty keyword = Enter). (#304) CommandOptionPick(String), + /// The pointer entered (true) or left the command line's overlay history. + CommandHistoryHover(bool), ToggleLayers, /// Open/focus (or close) the Reference Manager palette. ToggleXrefManager, diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 4c21f69b4..14443aa96 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -2857,6 +2857,11 @@ impl OpenCADStudio { self.focus_cmd_input() } + Message::CommandHistoryHover(hovered) => { + self.command_line.set_history_hover(hovered); + Task::none() + } + Message::CommandHistoryToggle => { self.command_line.toggle_history(); if !self.command_line.history_open { diff --git a/src/ui/command_line.rs b/src/ui/command_line.rs index 9a6596c1c..5995e5570 100644 --- a/src/ui/command_line.rs +++ b/src/ui/command_line.rs @@ -194,6 +194,9 @@ pub struct CommandLine { /// COMMANDLINEOPACITY: how opaque the command line's panels are, in /// percent (20–100, default 80), so the drawing shows through. opacity_percent: u8, + /// Since when the pointer rests on the overlay history: lines still + /// showing then stay until it leaves, and fade from that moment. + hover_since: Option, } impl Default for CommandLine { @@ -221,6 +224,7 @@ impl Default for CommandLine { cliprompt_lines: 3, fade_ms: DEFAULT_COMMANDLINE_FADE_MS, opacity_percent: 80, + hover_since: None, } } } @@ -527,7 +531,32 @@ impl CommandLine { } fn entry_visible(&self, e: &HistoryEntry) -> bool { - e.pinned || self.fade_ms == 0 || e.created_at.elapsed().as_secs_f32() < self.fade_secs() + let held = self.hover_since.is_some_and(|since| { + since.saturating_duration_since(e.created_at).as_secs_f32() < self.fade_secs() + }); + e.pinned + || self.fade_ms == 0 + || held + || e.created_at.elapsed().as_secs_f32() < self.fade_secs() + } + + /// The pointer entered or left the overlay history. Leaving restarts the + /// fade of every line it held. + pub fn set_history_hover(&mut self, hovered: bool) { + if hovered { + self.hover_since.get_or_insert_with(Instant::now); + return; + } + let Some(since) = self.hover_since.take() else { + return; + }; + let fade = self.fade_secs(); + let now = Instant::now(); + for entry in &mut self.history { + if since.saturating_duration_since(entry.created_at).as_secs_f32() < fade { + entry.created_at = now; + } + } } /// Height of the overlaid prompt lines currently shown above the input @@ -1144,16 +1173,20 @@ impl CommandLine { let recent_history: Element<'a, Message> = if self.history_open || !has_recent_history { container(column![]).height(0).into() } else { - container(history_rows) - .style(move |theme: &Theme| container::Style { - background: Some(Background::Color( - theme.palette().background.base.color.scale_alpha(opacity), - )), - ..Default::default() - }) - .width(Length::Fill) - .padding([2, 0]) - .into() + iced::widget::mouse_area( + container(history_rows) + .style(move |theme: &Theme| container::Style { + background: Some(Background::Color( + theme.palette().background.base.color.scale_alpha(opacity), + )), + ..Default::default() + }) + .width(Length::Fill) + .padding([2, 0]), + ) + .on_enter(Message::CommandHistoryHover(true)) + .on_exit(Message::CommandHistoryHover(false)) + .into() }; let history_divider: Element<'a, Message> = if self.history_open { rule::horizontal(1).into()