feat(color): add CAD indexed color picker

This commit is contained in:
gianlucafiore committed 2026-08-09 23:40:55 -03:00
1 parent 60f5bca0d4
commit 4697e8cc33
4 files changed
+293 -21

No files matched your search

+195
View File
@@ -244,3 +244,198 @@ pub fn drop_down_below<'a>(
.on_dismiss(on_dismiss)
.into()
}
/// Full CAD indexed-colour page used by the standalone Select Color dialog.
///
/// This only edits the pending colour. The caller commits it with
/// `Message::ColorWindowPick`, so choosing a swatch does not immediately close
/// the dialog.
pub fn index_color_page<'a>(
current: AcadColor,
recent_colors: &'a [AcadColor],
) -> Element<'a, Message> {
let swatch_button = |color: AcadColor, size: f32| -> Element<'a, Message> {
let (bg, _) = acad_color_display(color);
let selected = current == color;
button(
text("")
.width(Length::Fixed(size))
.height(Length::Fixed(size)),
)
.on_press(Message::ColorPickerColorChanged(color))
.style(move |theme: &Theme, status| {
let hovered = matches!(
status,
button::Status::Hovered | button::Status::Pressed
);
button::Style {
background: Some(Background::Color(bg)),
border: Border {
color: if selected || hovered {
theme.palette().primary.base.color
} else {
theme.palette().background.neutral.color
},
width: if selected {
2.5
} else if hovered {
1.5
} else {
1.0
},
radius: 1.0.into(),
},
..Default::default()
}
})
.padding(0)
.into()
};
// AutoCAD's first standard indexed colours.
let mut standard = row![].spacing(4);
for idx in 1u8..=9 {
standard = standard.push(swatch_button(AcadColor::Index(idx), 22.0));
}
// ACI 10-249.
//
// Each group of ten indices belongs together. Arrange the 24 colour
// families horizontally and their ten shade/intensity variants vertically.
// This makes the palette read as a colour spectrum instead of a raw numeric
// sequence.
let mut palette_rows = column![].spacing(2);
for variant in 0u8..10 {
let mut palette_row = row![].spacing(2);
for family in 1u8..=24 {
let idx = family * 10 + variant;
palette_row = palette_row.push(swatch_button(AcadColor::Index(idx), 16.0));
}
palette_rows = palette_rows.push(palette_row);
}
// ACI 250-255 are the grayscale entries and are shown separately.
let mut grayscale = row![].spacing(4);
for idx in 250u8..=255 {
grayscale =
grayscale.push(swatch_button(AcadColor::Index(idx), 22.0));
}
let recent: Element<'a, Message> = if recent_colors.is_empty() {
text("No recent colors yet")
.size(10)
.style(|theme: &Theme| iced::widget::text::Style {
color: Some(
theme
.palette()
.background
.base
.text
.scale_alpha(0.55),
),
})
.into()
} else {
let mut recent_row = row![].spacing(4);
for &color in recent_colors.iter().take(12) {
recent_row = recent_row.push(swatch_button(color, 22.0));
}
recent_row.into()
};
let selected_text = match current {
AcadColor::Index(i) => {
let (r, g, b) =
acadrust::types::aci_table::aci_to_rgb(i).unwrap_or((128, 128, 128));
format!("ACI {i} RGB {r}, {g}, {b}")
}
AcadColor::Rgb { r, g, b } => {
format!("True Color RGB {r}, {g}, {b}")
}
AcadColor::ByLayer => "ByLayer".to_string(),
AcadColor::ByBlock => "ByBlock".to_string(),
AcadColor::None => "None".to_string(),
};
let tabs = row![
button(text("Index Color").size(12))
.style(button::primary)
.padding([5, 14]),
button(text("True Color").size(12))
.on_press(Message::ColorPickerTabChanged(
crate::app::ColorPickerTab::TrueColor
))
.style(button::secondary)
.padding([5, 14]),
]
.spacing(4);
let actions = container(
row![
button(text("Cancel").size(11))
.on_press(Message::CloseColorPicker)
.style(button::secondary)
.padding([5, 14]),
button(text("OK").size(11))
.on_press(Message::ColorWindowPick(current))
.style(button::primary)
.padding([5, 18]),
]
.spacing(8),
)
.width(Length::Fill)
.align_x(iced::alignment::Horizontal::Right);
container(
column![
tabs,
text("Standard colors").size(11),
standard,
text("AutoCAD Color Index (ACI) 10–249").size(11),
text("Color family → · Shade / intensity ↓")
.size(9)
.style(|theme: &Theme| iced::widget::text::Style {
color: Some(theme.palette().background.base.text.scale_alpha(0.65)),
}),
container(palette_rows)
.padding(6)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme.palette().background.weakest.color
)),
border: Border {
color: theme.palette().background.neutral.color,
width: 1.0,
radius: 3.0.into(),
},
..Default::default()
}),
text("Grayscale 250–255").size(11),
grayscale,
text("Recently used").size(11),
recent,
text(selected_text).size(11),
actions,
]
.spacing(9),
)
.padding(10)
.width(Length::Fixed(470.0))
.into()
}