Properties: open a dropdown only on the surface that asked (#1766)

The docked panel and Quick Properties render the same rows from one
state, so a picked colour opened both dropdowns and each swallowed the
other's click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-14 12:34:16 +03:00
1 parent 1037be2d53
commit a156a63edd
3 files changed
+62 -18

No files matched your search

+2
View File
@@ -3625,6 +3625,8 @@ pub enum Message {
PropPointerPressed, PropPointerPressed,
/// Toggle the inline color picker dropdown open/closed. /// Toggle the inline color picker dropdown open/closed.
PropColorPickerToggle, PropColorPickerToggle,
/// A Properties dropdown toggle and whether Quick Properties sent it.
PropPickerSurface(bool, Box<Message>),
/// Toggle the MTEXT background-colour picker dropdown open/closed. /// Toggle the MTEXT background-colour picker dropdown open/closed.
PropBgColorPickerToggle, PropBgColorPickerToggle,
/// User picked an MTEXT background fill colour — sets `background_color` /// User picked an MTEXT background fill colour — sets `background_color`
+7
View File
@@ -7856,6 +7856,13 @@ impl OpenCADStudio {
Task::none() Task::none()
} }
Message::PropPickerSurface(quick, message) => {
let task = self.update(*message);
let i = self.active_tab;
self.tabs[i].properties.picker_in_quick = quick;
task
}
Message::PropColorPickerToggle => { Message::PropColorPickerToggle => {
let i = self.active_tab; let i = self.active_tab;
self.tabs[i].properties.color_picker_open = self.tabs[i].properties.color_picker_open =
+53 -18
View File
@@ -621,6 +621,12 @@ pub struct PropertiesPanel {
pub collapsed_sections: HashSet<String>, pub collapsed_sections: HashSet<String>,
/// Whether the editable-dropdown (block Name) option list is open. /// Whether the editable-dropdown (block Name) option list is open.
pub edit_choice_open: bool, pub edit_choice_open: bool,
/// The open dropdown belongs to Quick Properties, not the docked panel:
/// both render these rows, and two open copies swallow each other's
/// clicks (#1766).
pub picker_in_quick: bool,
/// Set while `quick_view` builds its rows.
pub rendering_quick: std::cell::Cell<bool>,
/// Field key of the value row currently being edited, or `None`. Marked when /// Field key of the value row currently being edited, or `None`. Marked when
/// the user focuses an editable value field and cleared on commit / when the /// the user focuses an editable value field and cleared on commit / when the
/// selection changes, so the active row can be highlighted. Geometry fields /// selection changes, so the active row can be highlighted. Geometry fields
@@ -659,6 +665,8 @@ impl Default for PropertiesPanel {
expanded_groups: HashSet::default(), expanded_groups: HashSet::default(),
collapsed_sections: HashSet::default(), collapsed_sections: HashSet::default(),
edit_choice_open: false, edit_choice_open: false,
picker_in_quick: false,
rendering_quick: std::cell::Cell::new(false),
active_field: None, active_field: None,
field_key_by_id: HashMap::default(), field_key_by_id: HashMap::default(),
} }
@@ -680,6 +688,11 @@ impl PropertiesPanel {
self.field_key_by_id.get(id).cloned() self.field_key_by_id.get(id).cloned()
} }
/// Whether the open dropdown belongs to the surface being built.
fn picker_here(&self) -> bool {
self.picker_in_quick == self.rendering_quick.get()
}
pub fn selected_handles(&self) -> Vec<Handle> { pub fn selected_handles(&self) -> Vec<Handle> {
self.selected_group self.selected_group
.as_ref() .as_ref()
@@ -744,11 +757,13 @@ impl PropertiesPanel {
for section in &self.sections { for section in &self.sections {
col = col.push(self.render_section(section)); col = col.push(self.render_section(section));
} }
scrollable(col.width(Length::Fill)) Element::from(
.spacing(8) scrollable(col.width(Length::Fill))
.width(Length::Fill) .spacing(8)
.height(Length::Fill) .width(Length::Fill)
.into() .height(Length::Fill),
)
.map(|message| tag_picker(false, message))
}; };
container(column![header, title_bar, content]) container(column![header, title_bar, content])
@@ -797,14 +812,19 @@ impl PropertiesPanel {
.padding([4, 10]); .padding([4, 10]);
let mut sections = column![].spacing(0); let mut sections = column![].spacing(0);
self.rendering_quick.set(true);
for section in &self.sections { for section in &self.sections {
sections = sections.push(self.render_section(section)); sections = sections.push(self.render_section(section));
} }
self.rendering_quick.set(false);
let content = scrollable(sections.width(Length::Fill)) let content = Element::from(
.spacing(8) scrollable(sections.width(Length::Fill))
.width(Length::Fill) .spacing(8)
.height(Length::Shrink); .width(Length::Fill)
.height(Length::Shrink),
)
.map(|message| tag_picker(true, message));
Some( Some(
container(column![title, content].spacing(0)) container(column![title, content].spacing(0))
@@ -1002,7 +1022,7 @@ impl PropertiesPanel {
if field == "background_color" { if field == "background_color" {
let selector = crate::ui::color_select::color_selector( let selector = crate::ui::color_select::color_selector(
color, color,
self.bg_color_picker_open, self.bg_color_picker_open && self.picker_here(),
crate::ui::color_select::ColorExtras { crate::ui::color_select::ColorExtras {
by_layer: true, by_layer: true,
by_block: true, by_block: true,
@@ -1035,7 +1055,7 @@ impl PropertiesPanel {
| "background_fill_color" | "background_fill_color"
| "indicator_fill_color" | "indicator_fill_color"
) { ) {
let open = self.open_color_field.as_deref() == Some(field); let open = self.open_color_field.as_deref() == Some(field) && self.picker_here();
let fsel = field.to_string(); let fsel = field.to_string();
let full_palette_field = field.to_string(); let full_palette_field = field.to_string();
let extras = if field == "dim_text_fill_color" { let extras = if field == "dim_text_fill_color" {
@@ -1078,7 +1098,7 @@ impl PropertiesPanel {
let selector = crate::ui::color_select::color_selector_with_name( let selector = crate::ui::color_select::color_selector_with_name(
color, color,
display_name, display_name,
self.color_picker_open, self.color_picker_open && self.picker_here(),
crate::ui::color_select::ColorExtras { crate::ui::color_select::ColorExtras {
by_layer: true, by_layer: true,
by_block: true, by_block: true,
@@ -1093,7 +1113,7 @@ impl PropertiesPanel {
fn render_color_varies_row<'a>(&'a self, label: &'a str) -> Element<'a, Message> { fn render_color_varies_row<'a>(&'a self, label: &'a str) -> Element<'a, Message> {
let selector = crate::ui::color_select::color_selector_varies( let selector = crate::ui::color_select::color_selector_varies(
self.color_picker_open, self.color_picker_open && self.picker_here(),
crate::ui::color_select::ColorExtras { crate::ui::color_select::ColorExtras {
by_layer: true, by_layer: true,
by_block: true, by_block: true,
@@ -1352,7 +1372,7 @@ impl PropertiesPanel {
}) })
.width(Length::Fill); .width(Length::Fill);
let caret = button( let caret = button(
container(if self.edit_choice_open { container(if self.edit_choice_open && self.picker_here() {
crate::ui::icons::themed_arrow_up(9.0) crate::ui::icons::themed_arrow_up(9.0)
} else { } else {
crate::ui::icons::themed_arrow_down(9.0) crate::ui::icons::themed_arrow_down(9.0)
@@ -1405,7 +1425,7 @@ impl PropertiesPanel {
}) })
.width(Length::Fill); .width(Length::Fill);
if !self.edit_choice_open { if !(self.edit_choice_open && self.picker_here()) {
return prop_row_with_active(label, head.into(), active); return prop_row_with_active(label, head.into(), active);
} }
@@ -1609,7 +1629,7 @@ impl PropertiesPanel {
text(crate::ui::text_util::elide(current, 16)) text(crate::ui::text_util::elide(current, 16))
.size(FONT_SZ) .size(FONT_SZ)
.width(Length::Fill), .width(Length::Fill),
if self.hatch_pattern_picker_open { if self.hatch_pattern_picker_open && self.picker_here() {
crate::ui::icons::themed_arrow_up(FONT_SZ) crate::ui::icons::themed_arrow_up(FONT_SZ)
} else { } else {
crate::ui::icons::themed_arrow_down(FONT_SZ) crate::ui::icons::themed_arrow_down(FONT_SZ)
@@ -1629,7 +1649,7 @@ impl PropertiesPanel {
})), })),
text_color: palette.background.base.text, text_color: palette.background.base.text,
border: Border { border: Border {
color: if self.hatch_pattern_picker_open { color: if self.hatch_pattern_picker_open && self.picker_here() {
palette.primary.base.color palette.primary.base.color
} else { } else {
palette.background.neutral.color palette.background.neutral.color
@@ -1643,7 +1663,7 @@ impl PropertiesPanel {
.padding([COMBO_PAD_V, 6.0]) .padding([COMBO_PAD_V, 6.0])
.width(Length::Fill); .width(Length::Fill);
if !self.hatch_pattern_picker_open { if !(self.hatch_pattern_picker_open && self.picker_here()) {
return prop_row_widget(label, head.into()); return prop_row_widget(label, head.into());
} }
@@ -2370,3 +2390,18 @@ mod tests {
assert!((0.01..=100.0).contains(&scale)); assert!((0.01..=100.0).contains(&scale));
} }
} }
/// Marks a dropdown toggle with the surface it came from (see
/// `PropertiesPanel::picker_in_quick`).
fn tag_picker(quick: bool, message: Message) -> Message {
match message {
Message::PropColorPickerToggle
| Message::PropBgColorPickerToggle
| Message::PropColorFieldToggle(_)
| Message::PropEditChoiceToggle
| Message::PropHatchPatternPickerToggle(_) => {
Message::PropPickerSurface(quick, Box::new(message))
}
other => other,
}
}