feat(qselect): remember last settings

Refs #892
This commit is contained in:
Hakan Seven committed 2026-08-29 21:18:22 +03:00
1 parent 204c427d57
commit 4fc8446df7
3 files changed
+90 -20

No files matched your search

+27
View File
@@ -202,6 +202,31 @@ pub struct QSelectState {
pub error: Option<String>,
}
#[derive(Clone, Debug)]
struct QSelectSettings {
scope: QSelectScope,
type_filter: Option<String>,
property_field: Option<String>,
operator: QSelectOp,
value: String,
mode: QSelectMode,
append: bool,
}
impl From<&QSelectState> for QSelectSettings {
fn from(state: &QSelectState) -> Self {
Self {
scope: state.scope,
type_filter: state.type_filter.clone(),
property_field: state.property.as_ref().map(|property| property.field.clone()),
operator: state.operator,
value: state.value.clone(),
mode: state.mode,
append: state.append,
}
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct FindReplaceState {
pub search: String,
@@ -578,6 +603,7 @@ pub(super) struct OpenCADStudio {
/// applied via `Message::QSelectApply`; the panel is dismissed on
/// Apply / Cancel / Esc / outside-click.
qselect: Option<QSelectState>,
qselect_settings: Option<QSelectSettings>,
/// Show the UCS icon in the bottom-left corner of model space (UCSICON).
show_ucs_icon: bool,
/// Anchor the UCS icon to the projected UCS origin when it is on-screen,
@@ -3252,6 +3278,7 @@ impl OpenCADStudio {
grip_text_verts: Vec::new(),
grip_text_slide: false,
qselect: None,
qselect_settings: None,
show_ucs_icon: true,
ucs_icon_at_origin: true,
show_viewcube: true,
+59 -19
View File
@@ -826,7 +826,8 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
}
// Cancel layout rename first, then fall through.
let i_e = self.active_tab;
if self.qselect.take().is_some() {
if let Some(state) = self.qselect.take() {
self.qselect_settings = Some((&state).into());
self.reset_modal_geometry();
return Task::none();
}
@@ -1546,35 +1547,74 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
pub(super) fn on_qselect_open(&mut self) -> Task<Message> {
let i = self.active_tab;
self.tabs[i].scene.selection.borrow_mut().context_menu = None;
// Seed the type filter from the first selected entity so a
// right-click → Quick Select on a known object opens the
// panel pre-tuned to that entity's type. Property defaults
// to "(Any property)" so the user immediately picks what
// they want to compare.
let mut type_filter: Option<String> = None;
if let Some(&h) = self.tabs[i].scene.selected.iter().next() {
if let Some(e) = self.tabs[i].scene.document.get_entity(h) {
use crate::entities::traits::entity_type_name;
type_filter = Some(entity_type_name(e).to_string());
}
}
let scope = crate::app::QSelectScope::CurrentSpace;
let remembered = self.qselect_settings.clone();
let scope = remembered.as_ref().map_or(
crate::app::QSelectScope::CurrentSpace,
|settings| settings.scope,
);
let available_types = self.tabs[i].scene.qselect_entity_type_names(scope);
let type_filter = if let Some(settings) = remembered.as_ref() {
settings
.type_filter
.as_ref()
.filter(|selected| available_types.iter().any(|item| item == *selected))
.cloned()
} else {
self.tabs[i]
.scene
.selected
.iter()
.next()
.and_then(|handle| self.tabs[i].scene.document.get_entity(*handle))
.map(crate::entities::traits::entity_type_name)
.map(str::to_string)
};
let available_properties = self.tabs[i]
.scene
.qselect_properties(type_filter.as_deref(), scope);
let candidate_count = self.tabs[i].scene.qselect_candidate_count(scope);
let property = remembered
.as_ref()
.and_then(|settings| settings.property_field.as_ref())
.and_then(|field| {
available_properties
.iter()
.find(|property| property.field == *field)
.cloned()
});
let mut operator = remembered
.as_ref()
.map_or(crate::app::QSelectOp::Eq, |settings| settings.operator);
if matches!(operator, crate::app::QSelectOp::Gt | crate::app::QSelectOp::Lt)
&& !property.as_ref().is_some_and(|property| {
matches!(property.editor, crate::app::QSelectValueEditor::Number)
})
{
operator = crate::app::QSelectOp::Eq;
}
let value = remembered.as_ref().map_or_else(String::new, |settings| {
if settings.property_field.is_none() || property.is_some() {
settings.value.clone()
} else {
String::new()
}
});
let mode = remembered
.as_ref()
.map_or(crate::app::QSelectMode::Include, |settings| settings.mode);
let append = matches!(scope, crate::app::QSelectScope::CurrentSpace)
&& remembered.as_ref().is_some_and(|settings| settings.append);
self.qselect = Some(crate::app::QSelectState {
scope,
available_types,
available_properties,
candidate_count,
type_filter,
property: None,
operator: crate::app::QSelectOp::Eq,
value: String::new(),
mode: crate::app::QSelectMode::Include,
append: false,
property,
operator,
value,
mode,
append,
error: None,
});
self.reset_modal_geometry();
+4 -1
View File
@@ -4349,7 +4349,9 @@ impl OpenCADStudio {
Message::QSelectOpen => self.on_qselect_open(),
Message::QSelectClose => {
self.qselect = None;
if let Some(state) = self.qselect.take() {
self.qselect_settings = Some((&state).into());
}
self.reset_modal_geometry();
Task::none()
}
@@ -4530,6 +4532,7 @@ impl OpenCADStudio {
let Some(state) = self.qselect.take() else {
return Task::none();
};
self.qselect_settings = Some((&state).into());
self.reset_modal_geometry();
let i = self.active_tab;
let matched = self.tabs[i].scene.qselect(