forked from 1b-agent/opencadstudio-upstream
Unify mixed color selection
Use the shared color selector for mixed property values and remove the legacy inline palette and its state.
This commit is contained in:
1 parent
8c9ef80b90
commit
93b441d4b8
6 files changed
+76
-284
No files matched your search
@@ -2523,8 +2523,6 @@ pub enum Message {
|
||||
/// properties-panel dropdown (a combo_box) opens, so at most one panel
|
||||
/// dropdown is open at a time and they can't overlap. (#235)
|
||||
PropColorPickerClose,
|
||||
/// Toggle the full ACI colour palette expansion.
|
||||
PropColorPaletteToggle,
|
||||
/// Enter the model-space editing mode inside the given viewport (MSPACE).
|
||||
EnterViewport(acadrust::Handle),
|
||||
/// Exit MSPACE and return to paper-space editing (PSPACE).
|
||||
|
||||
@@ -42,7 +42,6 @@ impl OpenCADStudio {
|
||||
// Note: the color-picker dropdown is intentionally NOT carried over — a
|
||||
// rebuild means the selection (or a property) changed, so the dropdown
|
||||
// closes, matching the deselect / reselect / click-away expectation.
|
||||
let color_palette_open = self.tabs[i].properties.color_palette_open;
|
||||
let edit_buf = std::mem::take(&mut self.tabs[i].properties.edit_buf);
|
||||
let active_field = std::mem::take(&mut self.tabs[i].properties.active_field);
|
||||
// Expanded coordinate groups persist across rebuilds AND selection
|
||||
@@ -2224,7 +2223,6 @@ impl OpenCADStudio {
|
||||
// Precompute the focused-id → field-key map for O(1) lookups on
|
||||
// `PropSyncActive`; derived from `sections`, so rebuild it here.
|
||||
panel.field_key_by_id = crate::ui::properties::build_field_key_map(&panel.sections);
|
||||
panel.color_palette_open = color_palette_open;
|
||||
let new_handles: Vec<acadrust::Handle> = selected.iter().map(|(h, _)| *h).collect();
|
||||
// Carry the in-progress edits only when the selection is unchanged
|
||||
// (a commit-triggered rebuild); a genuine selection change starts
|
||||
@@ -2265,7 +2263,6 @@ impl OpenCADStudio {
|
||||
panel.edit_buf.clear();
|
||||
panel.active_field = None;
|
||||
panel.color_picker_open = false;
|
||||
panel.color_palette_open = false;
|
||||
panel.bg_color_picker_open = false;
|
||||
panel.open_color_field = None;
|
||||
panel.hatch_pattern_picker_open = false;
|
||||
|
||||
@@ -4653,7 +4653,6 @@ impl OpenCADStudio {
|
||||
let i = self.active_tab;
|
||||
let handles = self.property_target_handles(i);
|
||||
if handles.is_empty() {
|
||||
self.tabs[i].properties.color_palette_open = false;
|
||||
let task = self.on_ribbon_color_changed(color);
|
||||
self.refresh_properties();
|
||||
return task;
|
||||
@@ -4666,7 +4665,6 @@ impl OpenCADStudio {
|
||||
}
|
||||
});
|
||||
self.tabs[i].properties.color_picker_open = false;
|
||||
self.tabs[i].properties.color_palette_open = false;
|
||||
Task::none()
|
||||
}
|
||||
|
||||
@@ -4751,7 +4749,6 @@ impl OpenCADStudio {
|
||||
panel.hatch_pattern_picker_open = !panel.hatch_pattern_picker_open;
|
||||
if panel.hatch_pattern_picker_open {
|
||||
panel.color_picker_open = false;
|
||||
panel.color_palette_open = false;
|
||||
panel.open_color_field = None;
|
||||
panel.edit_choice_open = false;
|
||||
panel.hatch_pattern_focus =
|
||||
@@ -5069,7 +5066,6 @@ impl OpenCADStudio {
|
||||
self.tabs[i].properties.color_picker_open =
|
||||
!self.tabs[i].properties.color_picker_open;
|
||||
if self.tabs[i].properties.color_picker_open {
|
||||
self.tabs[i].properties.color_palette_open = false;
|
||||
self.tabs[i].properties.hatch_pattern_picker_open = false;
|
||||
self.tabs[i].properties.hatch_pattern_search.clear();
|
||||
}
|
||||
@@ -5311,18 +5307,11 @@ impl OpenCADStudio {
|
||||
Message::PropColorPickerClose => {
|
||||
let i = self.active_tab;
|
||||
self.tabs[i].properties.color_picker_open = false;
|
||||
self.tabs[i].properties.color_palette_open = false;
|
||||
self.tabs[i].properties.hatch_pattern_picker_open = false;
|
||||
self.tabs[i].properties.hatch_pattern_search.clear();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::PropColorPaletteToggle => {
|
||||
self.tabs[self.active_tab].properties.color_palette_open =
|
||||
!self.tabs[self.active_tab].properties.color_palette_open;
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::LayoutSwitch(name) => {
|
||||
self.layout_list_open = false;
|
||||
self.on_layout_switch(name)
|
||||
|
||||
@@ -2657,7 +2657,6 @@ impl OpenCADStudio {
|
||||
// press starts a box selection, so without this they'd only
|
||||
// close on the second click (issue #104).
|
||||
self.tabs[i].properties.color_picker_open = false;
|
||||
self.tabs[i].properties.color_palette_open = false;
|
||||
// Click anywhere outside the popup dismisses it. The
|
||||
// menu's own buttons live above this mouse_area, so a
|
||||
// press that reaches here means the cursor is not on
|
||||
|
||||
+61
-2
@@ -149,13 +149,72 @@ pub fn color_selector_with_name<'a>(
|
||||
let cur_name = display_name
|
||||
.map(str::to_string)
|
||||
.unwrap_or_else(|| color_display_name(current));
|
||||
|
||||
color_selector_with_indicator(
|
||||
swatch(cur_bg),
|
||||
cur_name,
|
||||
open,
|
||||
extras,
|
||||
on_select,
|
||||
on_toggle,
|
||||
on_more,
|
||||
)
|
||||
}
|
||||
|
||||
/// Build the shared colour selector for a mixed selection.
|
||||
pub fn color_selector_varies<'a>(
|
||||
open: bool,
|
||||
extras: ColorExtras,
|
||||
on_select: impl Fn(AcadColor) -> Message + 'a,
|
||||
on_toggle: Message,
|
||||
on_more: Message,
|
||||
) -> Element<'a, Message> {
|
||||
let indicator = container(text("?").size(10))
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(palette.background.strong.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
text_color: Some(palette.background.strong.text),
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.width(13)
|
||||
.height(13)
|
||||
.align_x(iced::Center)
|
||||
.align_y(iced::Center);
|
||||
|
||||
color_selector_with_indicator(
|
||||
indicator.into(),
|
||||
"*VARIES*".to_string(),
|
||||
open,
|
||||
extras,
|
||||
on_select,
|
||||
on_toggle,
|
||||
on_more,
|
||||
)
|
||||
}
|
||||
|
||||
fn color_selector_with_indicator<'a>(
|
||||
indicator: Element<'a, Message>,
|
||||
name: String,
|
||||
open: bool,
|
||||
extras: ColorExtras,
|
||||
on_select: impl Fn(AcadColor) -> Message + 'a,
|
||||
on_toggle: Message,
|
||||
on_more: Message,
|
||||
) -> Element<'a, Message> {
|
||||
let on_dismiss = on_toggle.clone();
|
||||
|
||||
// Closed button: current swatch + name + caret.
|
||||
let head = button(
|
||||
row![
|
||||
swatch(cur_bg),
|
||||
text(cur_name).size(11),
|
||||
indicator,
|
||||
text(name).size(11),
|
||||
iced::widget::Space::new().width(Length::Fill),
|
||||
crate::ui::icons::themed_arrow_toggle(open, 9.0),
|
||||
]
|
||||
|
||||
+15
-265
@@ -25,7 +25,6 @@ use crate::t;
|
||||
// ── Row-height-derived constants ─────────────────────────────────────────
|
||||
const FONT_SZ: f32 = ROW_H * 0.42; // ≈11 px
|
||||
const COMBO_PAD_V: f32 = (ROW_H - FONT_SZ * 1.3 - 2.0) / 2.0; // fills combo to ROW_H
|
||||
const SWATCH_SZ: f32 = ROW_H * 0.54; // ≈14 px color swatch
|
||||
const PATTERN_CARD_W: f32 = 158.0;
|
||||
const PATTERN_PREVIEW_H: f32 = 58.0;
|
||||
const PATTERN_PICKER_W: f32 = 348.0;
|
||||
@@ -430,8 +429,6 @@ pub struct PropertiesPanel {
|
||||
pub source_handles: Vec<Handle>,
|
||||
/// Whether the quick color picker dropdown is open.
|
||||
pub color_picker_open: bool,
|
||||
/// Whether the full 16×16 ACI palette is expanded inside the color picker.
|
||||
pub color_palette_open: bool,
|
||||
/// Whether the MTEXT background-colour picker dropdown is open. Separate
|
||||
/// from `color_picker_open` so the entity colour and the background colour
|
||||
/// pickers are independent.
|
||||
@@ -483,7 +480,6 @@ impl Default for PropertiesPanel {
|
||||
edit_buf: HashMap::default(),
|
||||
source_handles: vec![],
|
||||
color_picker_open: false,
|
||||
color_palette_open: false,
|
||||
bg_color_picker_open: false,
|
||||
open_color_field: None,
|
||||
prop_vertex: 0,
|
||||
@@ -955,59 +951,21 @@ impl PropertiesPanel {
|
||||
}
|
||||
|
||||
fn render_color_varies_row<'a>(&'a self, label: &'a str) -> Element<'a, Message> {
|
||||
let color_btn = button(
|
||||
row![
|
||||
container(text("?").size(10))
|
||||
.style(move |theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(palette.background.strong.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 2.0.into()
|
||||
},
|
||||
text_color: Some(palette.background.strong.text),
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.width(SWATCH_SZ)
|
||||
.height(SWATCH_SZ)
|
||||
.align_x(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
text(VARIES_LABEL).size(FONT_SZ),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.on_press(Message::PropColorPickerToggle)
|
||||
.style(combo_btn_style)
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.width(Length::Fill);
|
||||
|
||||
let color_row = prop_row_widget(label, color_btn.into());
|
||||
if self.color_picker_open {
|
||||
column![color_row, self.render_color_picker()]
|
||||
.spacing(0)
|
||||
.into()
|
||||
} else {
|
||||
color_row
|
||||
}
|
||||
}
|
||||
|
||||
fn render_color_picker(&self) -> Element<'_, Message> {
|
||||
color_picker_dropdown(
|
||||
self.color_palette_open,
|
||||
Message::PropColorPaletteToggle,
|
||||
Some(Message::PropColorChanged(AcadColor::ByLayer)),
|
||||
Some(Message::PropColorChanged(AcadColor::ByBlock)),
|
||||
|aci| Message::PropColorChanged(AcadColor::Index(aci)),
|
||||
)
|
||||
let selector = crate::ui::color_select::color_selector_varies(
|
||||
self.color_picker_open,
|
||||
crate::ui::color_select::ColorExtras {
|
||||
by_layer: true,
|
||||
by_block: true,
|
||||
..Default::default()
|
||||
},
|
||||
Message::PropColorChanged,
|
||||
Message::PropColorPickerToggle,
|
||||
Message::OpenColorWindow(
|
||||
crate::app::ColorPickTarget::Properties,
|
||||
AcadColor::ByLayer,
|
||||
),
|
||||
);
|
||||
prop_row_widget(label, selector)
|
||||
}
|
||||
|
||||
// ── Lineweight row (combo_box) ────────────────────────────────────────
|
||||
@@ -1527,187 +1485,6 @@ impl PropertiesPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Shared color picker widget ────────────────────────────────────────────
|
||||
|
||||
/// Builds the color picker dropdown content (standard swatches + optional
|
||||
/// ByLayer/ByBlock + "More Colors…" expanding to full ACI palette).
|
||||
/// Use this from both the Properties panel and the Layer Manager.
|
||||
pub fn color_picker_dropdown<'a>(
|
||||
palette_open: bool,
|
||||
palette_toggle_msg: Message,
|
||||
by_layer_msg: Option<Message>,
|
||||
by_block_msg: Option<Message>,
|
||||
on_aci: impl Fn(u8) -> Message + 'a,
|
||||
) -> Element<'a, Message> {
|
||||
// ByLayer / ByBlock row (optional)
|
||||
let extras: Option<Element<'a, Message>> = match (by_layer_msg, by_block_msg) {
|
||||
(Some(bl), Some(bb)) => Some(
|
||||
row![
|
||||
picker_text_btn("ByLayer", bl),
|
||||
picker_text_btn("ByBlock", bb)
|
||||
]
|
||||
.spacing(4)
|
||||
.into(),
|
||||
),
|
||||
(Some(bl), None) => Some(picker_text_btn("ByLayer", bl)),
|
||||
(None, Some(bb)) => Some(picker_text_btn("ByBlock", bb)),
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
// 9 standard ACI swatches (1-9)
|
||||
let standard: Element<'a, Message> = (1u8..=9u8)
|
||||
.fold(row![].spacing(2).width(Length::Fill), |r, idx| {
|
||||
let c = AcadColor::Index(idx);
|
||||
let (bg, _) = acad_color_display(c);
|
||||
let msg = on_aci(idx);
|
||||
r.push(
|
||||
button(text("").width(18).height(18))
|
||||
.width(Length::FillPortion(1))
|
||||
.on_press(msg)
|
||||
.style(move |theme: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(bg)),
|
||||
border: Border {
|
||||
color: if matches!(status, button::Status::Hovered) {
|
||||
theme.palette().primary.base.color
|
||||
} else {
|
||||
theme.palette().background.neutral.color
|
||||
},
|
||||
width: if matches!(status, button::Status::Hovered) {
|
||||
1.5
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
text_color: theme.palette().background.base.text,
|
||||
..Default::default()
|
||||
})
|
||||
.padding(0),
|
||||
)
|
||||
})
|
||||
.into();
|
||||
|
||||
// "More Colors…" toggle button
|
||||
let more_btn = button(
|
||||
row![
|
||||
if palette_open {
|
||||
crate::ui::icons::themed_arrow_up(9.0)
|
||||
} else {
|
||||
crate::ui::icons::themed_arrow_down(9.0)
|
||||
},
|
||||
text(if palette_open {
|
||||
t!("Less").into_owned()
|
||||
} else {
|
||||
t!("More Colors…").into_owned()
|
||||
})
|
||||
.size(10)
|
||||
.style(hint_text_style),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.on_press(palette_toggle_msg)
|
||||
.style(button::subtle)
|
||||
.padding([2, 6])
|
||||
.width(Length::Fill);
|
||||
|
||||
let inner = if let Some(e) = extras {
|
||||
column![e, standard, more_btn].spacing(4)
|
||||
} else {
|
||||
column![standard, more_btn].spacing(4)
|
||||
};
|
||||
|
||||
let mut col = column![container(inner)
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(palette.background.base.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 0.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.padding([6, 8])
|
||||
.width(Length::Fill)]
|
||||
.spacing(0);
|
||||
|
||||
// Full ACI palette (expanded)
|
||||
if palette_open {
|
||||
const COLS: u16 = 16;
|
||||
let mut rows = column![].spacing(1).width(Length::Fill);
|
||||
let mut idx: u16 = 1;
|
||||
while idx <= 255 {
|
||||
let mut r = row![].spacing(1).width(Length::Fill);
|
||||
for _ in 0..COLS {
|
||||
if idx <= 255 {
|
||||
let ci = idx as u8;
|
||||
let (bg, _) = acad_color_display(AcadColor::Index(ci));
|
||||
let msg = on_aci(ci);
|
||||
r = r.push(
|
||||
button(text("").width(12).height(12))
|
||||
.width(Length::FillPortion(1))
|
||||
.on_press(msg)
|
||||
.style(move |theme: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(bg)),
|
||||
border: Border {
|
||||
color: if matches!(status, button::Status::Hovered) {
|
||||
theme.palette().primary.base.color
|
||||
} else {
|
||||
theme.palette().background.neutral.color
|
||||
},
|
||||
width: if matches!(status, button::Status::Hovered) {
|
||||
1.5
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
radius: 1.0.into(),
|
||||
},
|
||||
text_color: theme.palette().background.base.text,
|
||||
..Default::default()
|
||||
})
|
||||
.padding(0),
|
||||
);
|
||||
idx += 1;
|
||||
} else {
|
||||
r = r.push(
|
||||
Space::new()
|
||||
.width(Length::FillPortion(1))
|
||||
.height(12),
|
||||
);
|
||||
}
|
||||
}
|
||||
rows = rows.push(r);
|
||||
}
|
||||
col = col.push(
|
||||
container(
|
||||
scrollable(rows)
|
||||
.height(160)
|
||||
.width(Length::Fill)
|
||||
.spacing(2),
|
||||
)
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(palette.background.base.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 0.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.padding([4, 6])
|
||||
.width(Length::Fill),
|
||||
);
|
||||
}
|
||||
|
||||
col.into()
|
||||
}
|
||||
|
||||
// ── Standalone helpers ────────────────────────────────────────────────────
|
||||
|
||||
/// A boolean toggle button row (for "Invisible" etc.).
|
||||
@@ -2066,15 +1843,6 @@ fn prop_row_with_active<'a>(
|
||||
.into()
|
||||
}
|
||||
|
||||
/// A plain text button used inside the color picker for ByLayer / ByBlock.
|
||||
fn picker_text_btn(label: &str, msg: Message) -> Element<'_, Message> {
|
||||
button(text(t!(label)).size(FONT_SZ))
|
||||
.on_press(msg)
|
||||
.style(button::secondary)
|
||||
.padding([2, 8])
|
||||
.into()
|
||||
}
|
||||
|
||||
// ── Color display helper ──────────────────────────────────────────────────
|
||||
|
||||
/// Returns an (iced::Color swatch_bg, display_label) pair for an AcadColor.
|
||||
@@ -2130,24 +1898,6 @@ fn aci_label(idx: u8) -> &'static str {
|
||||
|
||||
// ── Widget style helpers ──────────────────────────────────────────────────
|
||||
|
||||
fn combo_btn_style(theme: &Theme, status: button::Status) -> button::Style {
|
||||
let palette = theme.palette();
|
||||
let pair = match status {
|
||||
button::Status::Hovered | button::Status::Pressed => palette.background.weak,
|
||||
_ => palette.background.base,
|
||||
};
|
||||
button::Style {
|
||||
background: Some(Background::Color(pair.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
text_color: pair.text,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn text_input_style(theme: &Theme, status: text_input::Status) -> text_input::Style {
|
||||
let palette = theme.palette();
|
||||
let border_color = match status {
|
||||
|
||||
Reference in new issue
Block a user