diff --git a/src/app/mod.rs b/src/app/mod.rs index d1ed4cee1..b7d4250b6 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -767,6 +767,8 @@ pub enum Message { ToggleSelectionCycling, /// Add an object from the selection-cycling list box to the selection. CycleSelect(acadrust::Handle), + /// Preview (highlight) a cycling-list row's object, or clear with `None`. + CycleHover(Option), /// Dismiss the selection-cycling list box without picking. CycleCancel, /// Toggle the selection-filter type picker open/closed. diff --git a/src/app/update.rs b/src/app/update.rs index 0f40a1605..e3ebb2615 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -3631,19 +3631,27 @@ impl OpenCADStudio { Message::ToggleSelectionCycling => { self.selection_cycling ^= true; self.cycle_candidates = None; + self.tabs[self.active_tab].scene.set_hover_highlight(None); Task::none() } Message::CycleSelect(handle) => { // Add the picked object to the current selection (accumulate). self.cycle_candidates = None; let i = self.active_tab; + self.tabs[i].scene.set_hover_highlight(None); self.tabs[i].scene.select_entity(handle, false); self.tabs[i].scene.expand_selection_for_groups(&[handle]); self.refresh_properties(); Task::none() } + Message::CycleHover(handle) => { + let i = self.active_tab; + self.tabs[i].scene.set_hover_highlight(handle); + Task::none() + } Message::CycleCancel => { self.cycle_candidates = None; + self.tabs[self.active_tab].scene.set_hover_highlight(None); Task::none() } Message::ToggleSelectionFilterPopup => { diff --git a/src/scene/mod.rs b/src/scene/mod.rs index b057098f5..0d2e071d5 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -553,6 +553,9 @@ pub struct Scene { /// `tessellate_block`'s visibility test skips these, so they neither /// render nor hit-test until isolation ends. pub hidden: HashSet, + /// Entity drawn with the selection-highlight colour without being part + /// of the real selection — used to preview a row in the cycling list box. + pub hover_highlight: Option, /// Whether entity transparency is honoured on screen. When false the /// wire shader forces every line opaque (a uniform toggle, no retessellate). pub transparency_display: bool, @@ -704,6 +707,7 @@ impl Scene { document: CadDocument::new(), selected: HashSet::new(), hidden: HashSet::new(), + hover_highlight: None, transparency_display: true, selection_filter: HashSet::new(), preview_wires: vec![], @@ -1334,6 +1338,15 @@ impl Scene { !self.hidden.is_empty() } + /// Set (or clear) the previewed entity that renders with the selection + /// highlight without joining the real selection. Re-tessellates on change. + pub fn set_hover_highlight(&mut self, handle: Option) { + if self.hover_highlight != handle { + self.hover_highlight = handle; + self.bump_geometry(); + } + } + /// Hide every drawable entity except the current selection (Isolate). pub fn isolate_selected(&mut self) { if self.selected.is_empty() { @@ -2005,7 +2018,19 @@ impl Scene { // Tessellate in parallel across all available CPU cores. use rayon::prelude::*; let doc = &self.document; - let sel = &self.selected; + // Fold the hover-preview entity into the highlight set so it renders + // selected without being part of the real selection. + let hl_set; + let sel: &HashSet = if let Some(h) = self.hover_highlight { + hl_set = { + let mut s = self.selected.clone(); + s.insert(h); + s + }; + &hl_set + } else { + &self.selected + }; let avp = self.active_viewport; // A paper-space content viewport renders MODEL block entities while // the user is sitting in a paper layout — that path expects diff --git a/src/ui/cycle_popup.rs b/src/ui/cycle_popup.rs index 214353744..7ed1ce598 100644 --- a/src/ui/cycle_popup.rs +++ b/src/ui/cycle_popup.rs @@ -15,7 +15,7 @@ pub fn cycle_popup_overlay( ) -> Element<'static, Message> { let rows: Vec> = items .into_iter() - .map(|(handle, label)| item_row(label, Message::CycleSelect(handle))) + .map(|(handle, label)| item_row(handle, label)) .collect(); let panel = container(column(rows)) @@ -43,10 +43,10 @@ pub fn cycle_popup_overlay( mouse_area(positioned).on_press(Message::CycleCancel).into() } -fn item_row(label: String, msg: Message) -> Element<'static, Message> { +fn item_row(handle: acadrust::Handle, label: String) -> Element<'static, Message> { let content = text(label).size(11).color(LABEL).align_y(iced::Center); - button(content) - .on_press(msg) + let btn = button(content) + .on_press(Message::CycleSelect(handle)) .style(|_: &Theme, status| button::Style { background: Some(Background::Color(match status { button::Status::Hovered => ROW_HOVER, @@ -55,7 +55,11 @@ fn item_row(label: String, msg: Message) -> Element<'static, Message> { ..Default::default() }) .width(Fill) - .padding([4, 10]) + .padding([4, 10]); + // Highlight the underlying object while the cursor is over this row. + mouse_area(btn) + .on_enter(Message::CycleHover(Some(handle))) + .on_exit(Message::CycleHover(None)) .into() }