forked from 1b-agent/opencadstudio-upstream
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:
1 parent
1037be2d53
commit
a156a63edd
3 files changed
+62
-18
No files matched your search
@@ -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`
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user