feat(statusbar): Select All/Clear All in filter popup, drop osnap settings row

The selection-filter popup now has the same Select All / Clear All header
as the OSNAP popup: Select All clears every exclusion (all types
selectable), Clear All excludes every present type. Each button disables
when it would be a no-op.

Also remove the "Object Snap Settings…" footer from the OSNAP popup.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-01 09:47:13 +03:00
1 parent d8862ee9cf
commit 7764b6cbf9
4 files changed
+84 -29

No files matched your search

+4
View File
@@ -1317,6 +1317,10 @@ pub enum Message {
CloseSelectionFilterPopup,
/// Include/exclude an entity type from interactive selection.
ToggleSelectionFilterType(String),
/// Make every entity type selectable again (clear the filter).
SelectionFilterSelectAll,
/// Exclude every present entity type from selection.
SelectionFilterClearAll,
/// Toggle the drawing-units picker open/closed.
ToggleUnitsPopup,
/// Close the drawing-units picker.
+13
View File
@@ -1486,6 +1486,19 @@ impl OpenCADStudio {
}
Task::none()
}
Message::SelectionFilterSelectAll => {
self.tabs[self.active_tab].scene.selection_filter.clear();
Task::none()
}
Message::SelectionFilterClearAll => {
let i = self.active_tab;
let types = self.tabs[i].scene.entity_type_names_in_layout();
let f = &mut self.tabs[i].scene.selection_filter;
for t in types {
f.insert(t.to_string());
}
Task::none()
}
Message::ToggleUnitsPopup => {
self.units_popup_open ^= true;
Task::none()
+66 -2
View File
@@ -18,6 +18,34 @@ pub fn selection_filter_popup_overlay(
types: Vec<String>,
excluded: &HashSet<String>,
) -> Element<'static, Message> {
// "Select All / Clear All" header, mirroring the OSNAP popup: Select All
// clears every exclusion, Clear All excludes every present type.
let has_types = !types.is_empty();
let all_included = excluded.is_empty();
let all_excluded = has_types && types.iter().all(|t| excluded.contains(t));
let header = row![
header_btn(
"Select All",
Message::SelectionFilterSelectAll,
has_types && !all_included,
),
header_btn(
"Clear All",
Message::SelectionFilterClearAll,
has_types && !all_excluded,
),
]
.spacing(1)
.padding([4u16, 8]);
let divider = container(iced::widget::Space::new().height(1))
.style(|_: &Theme| container::Style {
background: Some(Background::Color(DIVIDER)),
..Default::default()
})
.width(Fill)
.padding([0, 4]);
let rows: Vec<Element<'static, Message>> = if types.is_empty() {
vec![empty_row()]
} else {
@@ -30,7 +58,7 @@ pub fn selection_filter_popup_overlay(
.collect()
};
let panel = container(column(rows))
let panel = container(column![header, divider, column(rows)])
.style(|_: &Theme| container::Style {
background: Some(Background::Color(PANEL_BG)),
border: Border {
@@ -40,7 +68,7 @@ pub fn selection_filter_popup_overlay(
},
..Default::default()
})
.width(Length::Fixed(160.0));
.width(Length::Fixed(180.0));
let positioned = container(panel)
.align_right(Fill)
@@ -88,6 +116,30 @@ fn empty_row() -> Element<'static, Message> {
.into()
}
fn header_btn(label: &str, msg: Message, enabled: bool) -> Element<'_, Message> {
let color = if enabled {
Color { r: 0.70, g: 0.70, b: 0.70, a: 1.0 }
} else {
Color { r: 0.38, g: 0.38, b: 0.38, a: 1.0 }
};
let b = button(text(label).size(10).color(color));
let b = if enabled { b.on_press(msg) } else { b };
b.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => ROW_HOVER,
_ => BTN_BG,
})),
border: Border {
color: PANEL_BORDER,
width: 1.0,
radius: 2.0.into(),
},
..Default::default()
})
.padding([3, 8])
.into()
}
// ── Colours ───────────────────────────────────────────────────────────────
const PANEL_BG: Color = Color {
@@ -108,6 +160,18 @@ const ROW_HOVER: Color = Color {
b: 0.22,
a: 1.0,
};
const DIVIDER: Color = Color {
r: 0.28,
g: 0.28,
b: 0.28,
a: 1.0,
};
const BTN_BG: Color = Color {
r: 0.20,
g: 0.20,
b: 0.20,
a: 1.0,
};
const CHECK_COLOR: Color = Color {
r: 0.35,
g: 0.75,
+1 -27
View File
@@ -40,33 +40,7 @@ pub fn snap_popup_overlay<'a>(snapper: &'a Snapper, right_offset: f32) -> Elemen
rows.push(snap_row(snap_type, label, snapper.is_on(snap_type)));
}
// "Object Snap Settings…" footer
let footer_divider = container(iced::widget::Space::new().height(1))
.style(|_: &Theme| container::Style {
background: Some(Background::Color(DIVIDER)),
..Default::default()
})
.width(Fill)
.padding([0, 4]);
let footer = button(text("Object Snap Settings…").size(11).color(Color {
r: 0.75,
g: 0.75,
b: 0.75,
a: 1.0,
}))
.on_press(Message::Command("DSETTINGS".into()))
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => ROW_HOVER,
_ => Color::TRANSPARENT,
})),
..Default::default()
})
.width(Fill)
.padding([5, 12]);
let panel_content = column![header, divider, column(rows), footer_divider, footer,];
let panel_content = column![header, divider, column(rows)];
let panel = container(panel_content)
.style(|_: &Theme| container::Style {