diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs index ee8e0d7fc..71a8a72bc 100644 --- a/src/app/view/mod.rs +++ b/src/app/view/mod.rs @@ -2534,11 +2534,10 @@ impl OpenCADStudio { Subscription::none() }; // While the command-line overlay is still displaying any - // recently-pushed history entry, re-render every frame so the + // recently-pushed history entry that is expiring, re-render every frame so the // entry disappears at the moment its visible window expires. - // The subscription auto-stops once no entry is fresh enough - // (typically within a few seconds of the last command). - let history_tick = if self.command_line.has_visible_history() { + // The subscription auto-stops once no entry is expiring. + let history_tick = if self.command_line.has_expiring_history() { window::frames().map(Message::Tick) } else { Subscription::none() diff --git a/src/ui/command_line.rs b/src/ui/command_line.rs index 34a40e547..20634352f 100644 --- a/src/ui/command_line.rs +++ b/src/ui/command_line.rs @@ -25,7 +25,7 @@ pub const HISTORY_SCROLL_ID: &str = "command_history_scroll"; /// Default for `COMMANDLINEFADETIME` (ms); the live value lives on /// [`CommandLine::fade_ms`] so it can be changed via SETVAR. pub const DEFAULT_COMMANDLINE_FADE_MS: u32 = 3000; -/// COMMANDLINEFADETIME bounds in milliseconds: 0 skips the overlay entirely, +/// COMMANDLINEFADETIME bounds in milliseconds: 0 means never fade, /// 60000 keeps lines for a full minute. pub const COMMANDLINE_FADE_MIN_MS: u32 = 0; pub const COMMANDLINE_FADE_MAX_MS: u32 = 60000; @@ -190,8 +190,8 @@ pub struct CommandLine { /// are displayed above the command window (0–50, default 3). cliprompt_lines: u8, /// COMMANDLINEFADETIME: how long overlay history lines stay visible, - /// in milliseconds (0–60000, default 3000). 0 skips drawing transient - /// lines entirely; the pinned step prompt still shows. + /// in milliseconds (0–60000, default 3000). 0 means history never fades; + /// the pinned step prompt still shows. fade_ms: u32, } @@ -500,8 +500,8 @@ impl CommandLine { self.cliprompt_lines = n.min(50); } - /// COMMANDLINEFADETIME mirror (ms, 0–60000). `0` skips transient overlay - /// lines entirely; the pinned step prompt still shows. + /// COMMANDLINEFADETIME mirror (ms, 0–60000). `0` means overlay history + /// lines never fade; the pinned step prompt still shows. pub fn set_commandline_fade_ms(&mut self, ms: u32) { self.fade_ms = ms.min(COMMANDLINE_FADE_MAX_MS); } @@ -515,7 +515,7 @@ 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()) + e.pinned || self.fade_ms == 0 || e.created_at.elapsed().as_secs_f32() < self.fade_secs() } /// Height of the overlaid prompt lines currently shown above the input @@ -547,10 +547,8 @@ impl CommandLine { visible.len().min(self.cliprompt_lines as usize) } - /// `true` while at least one history entry is still within the - /// visible window — the host app uses this to drive a low-frequency - /// tick subscription so the overlay re-renders and fades the entry - /// once it expires. + /// `true` while at least one history entry is currently visible in the + /// overlay area (subject to CLIPROMPTLINES). pub fn has_visible_history(&self) -> bool { if self.cliprompt_lines == 0 { return false; @@ -558,6 +556,19 @@ impl CommandLine { self.history.iter().any(|e| self.entry_visible(e)) } + /// `true` while at least one visible history entry is waiting to fade out. + /// Used by the host app to drive an animation frame subscription only when + /// an entry actually needs to be removed after its visible window expires. + /// When `fade_ms == 0` (never fade) or entries are pinned, no tick is needed. + pub fn has_expiring_history(&self) -> bool { + if self.cliprompt_lines == 0 || self.fade_ms == 0 { + return false; + } + self.history + .iter() + .any(|e| !e.pinned && e.created_at.elapsed().as_secs_f32() < self.fade_secs()) + } + pub fn toggle_history(&mut self) { self.history_open = !self.history_open; } @@ -656,7 +667,7 @@ impl CommandLine { graph_open: bool, ) -> Element<'a, Message> { // Only the most recent entries pushed within COMMANDLINEFADETIME - // show on the overlay (0 skips transient lines). The dropdown button + // show on the overlay (0 means never fade). The dropdown button // keeps the full backlog reachable when the user actually wants it. let mut visible: Vec<&HistoryEntry> = self.history.iter().filter(|e| self.entry_visible(e)).collect(); @@ -1431,17 +1442,20 @@ mod tests { } #[test] - fn commandline_fade_zero_hides_unpinned_but_keeps_pinned() { + fn commandline_fade_zero_never_fades() { let mut line = CommandLine::new(); line.push_info("transient"); assert!(line.has_visible_history()); + assert!(line.has_expiring_history()); line.set_commandline_fade_ms(0); - // Non-pinned entries are skipped entirely at 0. - assert!(!line.has_visible_history()); - assert_eq!(line.visible_history_count(), 0); + // At 0, entries never fade so history stays visible, but no timer tick is needed. + assert!(line.has_visible_history()); + assert_eq!(line.visible_history_count(), 3); + assert!(!line.has_expiring_history()); // Pinned step prompt still shows at 0. line.set_step_prompt(Some("Specify point:".to_string())); assert!(line.has_visible_history()); + assert!(!line.has_expiring_history()); } #[test] diff --git a/src/ui/window/options.rs b/src/ui/window/options.rs index bce947795..f0e5d24d7 100644 --- a/src/ui/window/options.rs +++ b/src/ui/window/options.rs @@ -848,7 +848,7 @@ pub fn view_window<'a>( .step(250) .width(Fill), text(if prefs.commandline_fade_ms <= 0 { - crate::t!("Off").into_owned() + crate::t!("Never").into_owned() } else { format!("{:.1} s", prefs.commandline_fade_ms as f32 / 1000.0) }) @@ -861,7 +861,7 @@ pub fn view_window<'a>( .push(Space::new().height(6)) .push( text(crate::t!( - "How long overlay history lines stay visible; 0 skips them (COMMANDLINEFADETIME)." + "How long overlay history lines stay visible; 0 means never fade (COMMANDLINEFADETIME)." )) .size(11) .width(sizing.width),