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,
/// Toggle the inline color picker dropdown open/closed.
PropColorPickerToggle,
/// A Properties dropdown toggle and whether Quick Properties sent it.
PropPickerSurface(bool, Box<Message>),
/// Toggle the MTEXT background-colour picker dropdown open/closed.
PropBgColorPickerToggle,
/// User picked an MTEXT background fill colour — sets `background_color`
+7
View File
@@ -7856,6 +7856,13 @@ impl OpenCADStudio {
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 => {
let i = self.active_tab;
self.tabs[i].properties.color_picker_open =
+53 -18
View File
@@ -621,6 +621,12 @@ pub struct PropertiesPanel {
pub collapsed_sections: HashSet<String>,
/// Whether the editable-dropdown (block Name) option list is open.
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
/// the user focuses an editable value field and cleared on commit / when the
/// selection changes, so the active row can be highlighted. Geometry fields
@@ -659,6 +665,8 @@ impl Default for PropertiesPanel {
expanded_groups: HashSet::default(),
collapsed_sections: HashSet::default(),
edit_choice_open: false,
picker_in_quick: false,
rendering_quick: std::cell::Cell::new(false),
active_field: None,
field_key_by_id: HashMap::default(),
}
@@ -680,6 +688,11 @@ impl PropertiesPanel {
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> {
self.selected_group
.as_ref()
@@ -744,11 +757,13 @@ impl PropertiesPanel {
for section in &self.sections {
col = col.push(self.render_section(section));
}
scrollable(col.width(Length::Fill))
.spacing(8)
.width(Length::Fill)
.height(Length::Fill)
.into()
Element::from(
scrollable(col.width(Length::Fill))
.spacing(8)
.width(Length::Fill)
.height(Length::Fill),
)
.map(|message| tag_picker(false, message))
};
container(column![header, title_bar, content])
@@ -797,14 +812,19 @@ impl PropertiesPanel {
.padding([4, 10]);
let mut sections = column![].spacing(0);
self.rendering_quick.set(true);
for section in &self.sections {
sections = sections.push(self.render_section(section));
}
self.rendering_quick.set(false);
let content = scrollable(sections.width(Length::Fill))
.spacing(8)
.width(Length::Fill)
.height(Length::Shrink);
let content = Element::from(
scrollable(sections.width(Length::Fill))
.spacing(8)
.width(Length::Fill)
.height(Length::Shrink),
)
.map(|message| tag_picker(true, message));
Some(
container(column![title, content].spacing(0))
@@ -1002,7 +1022,7 @@ impl PropertiesPanel {
if field == "background_color" {
let selector = crate::ui::color_select::color_selector(
color,
self.bg_color_picker_open,
self.bg_color_picker_open && self.picker_here(),
crate::ui::color_select::ColorExtras {
by_layer: true,
by_block: true,
@@ -1035,7 +1055,7 @@ impl PropertiesPanel {
| "background_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 full_palette_field = field.to_string();
let extras = if field == "dim_text_fill_color" {
@@ -1078,7 +1098,7 @@ impl PropertiesPanel {
let selector = crate::ui::color_select::color_selector_with_name(
color,
display_name,
self.color_picker_open,
self.color_picker_open && self.picker_here(),
crate::ui::color_select::ColorExtras {
by_layer: true,
by_block: true,
@@ -1093,7 +1113,7 @@ impl PropertiesPanel {
fn render_color_varies_row<'a>(&'a self, label: &'a str) -> Element<'a, Message> {
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 {
by_layer: true,
by_block: true,
@@ -1352,7 +1372,7 @@ impl PropertiesPanel {
})
.width(Length::Fill);
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)
} else {
crate::ui::icons::themed_arrow_down(9.0)
@@ -1405,7 +1425,7 @@ impl PropertiesPanel {
})
.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);
}
@@ -1609,7 +1629,7 @@ impl PropertiesPanel {
text(crate::ui::text_util::elide(current, 16))
.size(FONT_SZ)
.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)
} else {
crate::ui::icons::themed_arrow_down(FONT_SZ)
@@ -1629,7 +1649,7 @@ impl PropertiesPanel {
})),
text_color: palette.background.base.text,
border: Border {
color: if self.hatch_pattern_picker_open {
color: if self.hatch_pattern_picker_open && self.picker_here() {
palette.primary.base.color
} else {
palette.background.neutral.color
@@ -1643,7 +1663,7 @@ impl PropertiesPanel {
.padding([COMBO_PAD_V, 6.0])
.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());
}
@@ -2370,3 +2390,18 @@ mod tests {
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,
}
}