forked from 1b-agent/opencadstudio-upstream
feat(color): add CAD indexed color picker
This commit is contained in:
1 parent
60f5bca0d4
commit
4697e8cc33
4 files changed
+293
-21
No files matched your search
@@ -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()
|
||||
}
|
||||
Reference in new issue
Block a user