forked from 1b-agent/opencadstudio-upstream
Select Color and ribbon: ByLayer shows the layer's colour
ByLayer swatches were always gray. The Select Color panel's Current/New band and info line now show the colour of the layer the colour is for (the newest selected object's layer, or the current layer for new objects); the ribbon's colour control and the ByLayer row of its colour dropdown show the current layer's colour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
This commit is contained in:
5 files changed
+79
-12
No files matched your search
@@ -11563,12 +11563,14 @@ impl OpenCADStudio {
|
|||||||
let mut picker = crate::ui::color_select::ColorPicker::new(target, current, varies);
|
let mut picker = crate::ui::color_select::ColorPicker::new(target, current, varies);
|
||||||
if !matches!(picker.target, crate::app::ColorPickTarget::Interface(_)) {
|
if !matches!(picker.target, crate::app::ColorPickTarget::Interface(_)) {
|
||||||
self.read_color_books();
|
self.read_color_books();
|
||||||
|
let by_layer = self.color_picker_layer_color(&picker.target);
|
||||||
let tab = &self.tabs[self.active_tab];
|
let tab = &self.tabs[self.active_tab];
|
||||||
picker = picker.with_drawing(
|
picker = picker.with_drawing(
|
||||||
crate::ui::color_select::document::collect(&tab.scene.document),
|
crate::ui::color_select::document::collect(&tab.scene.document),
|
||||||
&tab.recent_colors,
|
&tab.recent_colors,
|
||||||
std::sync::Arc::clone(&self.color_books),
|
std::sync::Arc::clone(&self.color_books),
|
||||||
name,
|
name,
|
||||||
|
by_layer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.color_picker = Some(picker);
|
self.color_picker = Some(picker);
|
||||||
@@ -11734,6 +11736,29 @@ impl OpenCADStudio {
|
|||||||
(newest, true, name_of(newest_handle))
|
(newest, true, name_of(newest_handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The colour ByLayer stands for in the Select Color panel: the layer of
|
||||||
|
/// the newest selected object, or the current layer for new objects.
|
||||||
|
fn color_picker_layer_color(
|
||||||
|
&self,
|
||||||
|
target: &crate::app::ColorPickTarget,
|
||||||
|
) -> Option<codec::types::Color> {
|
||||||
|
let i = self.active_tab;
|
||||||
|
let document = &self.tabs[i].scene.document;
|
||||||
|
let layer = match target {
|
||||||
|
crate::app::ColorPickTarget::Properties => self
|
||||||
|
.property_target_handles(i)
|
||||||
|
.into_iter()
|
||||||
|
.max()
|
||||||
|
.and_then(|handle| document.get_entity(handle))
|
||||||
|
.map_or(self.tabs[i].active_layer.as_str(), |entity| {
|
||||||
|
entity.as_entity().layer()
|
||||||
|
}),
|
||||||
|
crate::app::ColorPickTarget::Ribbon => self.tabs[i].active_layer.as_str(),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
document.layers.get(layer).map(|layer| layer.color)
|
||||||
|
}
|
||||||
|
|
||||||
/// Read the built-in colour books and the ones remembered in the
|
/// Read the built-in colour books and the ones remembered in the
|
||||||
/// settings, once per session. A loaded book replaces a built-in one of
|
/// settings, once per session. A loaded book replaces a built-in one of
|
||||||
/// the same name.
|
/// the same name.
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ pub struct ColorPicker {
|
|||||||
book_query: String,
|
book_query: String,
|
||||||
/// Why the last colour book did not load.
|
/// Why the last colour book did not load.
|
||||||
status: Option<String>,
|
status: Option<String>,
|
||||||
|
/// What ByLayer looks like here: the layer's colour.
|
||||||
|
by_layer: Option<AcadColor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorPicker {
|
impl ColorPicker {
|
||||||
@@ -127,18 +129,23 @@ impl ColorPicker {
|
|||||||
book_page: 0,
|
book_page: 0,
|
||||||
book_query: String::new(),
|
book_query: String::new(),
|
||||||
status: None,
|
status: None,
|
||||||
|
by_layer: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fill the Document and Color books tabs. `current_name` is the
|
/// Fill the Document and Color books tabs. `current_name` is the
|
||||||
/// `BOOK$NAME` of the current colour; the Color books tab shows its page.
|
/// `BOOK$NAME` of the current colour; the Color books tab shows its page.
|
||||||
|
/// `by_layer` is the colour of the layer the colour is for, so ByLayer
|
||||||
|
/// shows that colour instead of gray.
|
||||||
pub fn with_drawing(
|
pub fn with_drawing(
|
||||||
mut self,
|
mut self,
|
||||||
mut doc: Vec<DocColor>,
|
mut doc: Vec<DocColor>,
|
||||||
recent: &[AcadColor],
|
recent: &[AcadColor],
|
||||||
books: Arc<Vec<ColorBook>>,
|
books: Arc<Vec<ColorBook>>,
|
||||||
current_name: Option<String>,
|
current_name: Option<String>,
|
||||||
|
by_layer: Option<AcadColor>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
self.by_layer = by_layer;
|
||||||
document::sort(&mut doc, self.doc_sort, recent);
|
document::sort(&mut doc, self.doc_sort, recent);
|
||||||
self.doc = doc;
|
self.doc = doc;
|
||||||
self.recent = recent.to_vec();
|
self.recent = recent.to_vec();
|
||||||
@@ -330,6 +337,14 @@ impl ColorPicker {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How a colour looks; ByLayer takes the layer's colour when known.
|
||||||
|
fn shown(&self, color: AcadColor) -> Color {
|
||||||
|
match (color, self.by_layer) {
|
||||||
|
(AcadColor::ByLayer, Some(layer)) => acad_color_display(layer).0,
|
||||||
|
_ => acad_color_display(color).0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn pick_book(&mut self, index: usize) {
|
fn pick_book(&mut self, index: usize) {
|
||||||
let books = Arc::clone(&self.books);
|
let books = Arc::clone(&self.books);
|
||||||
let Some(book) = books.get(self.book) else {
|
let Some(book) = books.get(self.book) else {
|
||||||
@@ -366,8 +381,7 @@ impl ColorPicker {
|
|||||||
if self.tab == ColorPickerTab::Index {
|
if self.tab == ColorPickerTab::Index {
|
||||||
body = body.push(Space::new().height(Length::Fill));
|
body = body.push(Space::new().height(Length::Fill));
|
||||||
}
|
}
|
||||||
let fill =
|
let fill = self.shown(self.pending);
|
||||||
math::color_rgb(self.pending).map_or(Color::from_rgb8(128, 128, 128), rgb_color);
|
|
||||||
let info = match (&self.status, &self.pending_name) {
|
let info = match (&self.status, &self.pending_name) {
|
||||||
(Some(status), _) => status.clone(),
|
(Some(status), _) => status.clone(),
|
||||||
(None, Some(name)) => {
|
(None, Some(name)) => {
|
||||||
@@ -469,8 +483,8 @@ impl ColorPicker {
|
|||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
};
|
};
|
||||||
let (current_bg, _) = acad_color_display(self.current);
|
let current_bg = self.shown(self.current);
|
||||||
let (new_bg, _) = acad_color_display(self.pending);
|
let new_bg = self.shown(self.pending);
|
||||||
// A colour-book colour shows its own name, without the book.
|
// A colour-book colour shows its own name, without the book.
|
||||||
let name = |color: AcadColor, identity: &Option<String>| match identity {
|
let name = |color: AcadColor, identity: &Option<String>| match identity {
|
||||||
Some(identity) => identity
|
Some(identity) => identity
|
||||||
@@ -1336,7 +1350,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn book_colours_keep_their_name_until_edited() {
|
fn book_colours_keep_their_name_until_edited() {
|
||||||
let mut p = picker(AcadColor::Index(30)).with_drawing(Vec::new(), &[], ral(), None);
|
let mut p = picker(AcadColor::Index(30)).with_drawing(Vec::new(), &[], ral(), None, None);
|
||||||
p.update(ColorPickerMsg::PickBook(2));
|
p.update(ColorPickerMsg::PickBook(2));
|
||||||
assert_eq!(p.book_page, 1, "turns to the colour's page");
|
assert_eq!(p.book_page, 1, "turns to the colour's page");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1367,7 +1381,13 @@ mod tests {
|
|||||||
g: 113,
|
g: 113,
|
||||||
b: 179,
|
b: 179,
|
||||||
})
|
})
|
||||||
.with_drawing(Vec::new(), &[], ral(), Some("RAL CLASSIC$RAL 5015".into()));
|
.with_drawing(
|
||||||
|
Vec::new(),
|
||||||
|
&[],
|
||||||
|
ral(),
|
||||||
|
Some("RAL CLASSIC$RAL 5015".into()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
assert_eq!(p.book_selection(), Some(2));
|
assert_eq!(p.book_selection(), Some(2));
|
||||||
assert_eq!(p.book_page, 1);
|
assert_eq!(p.book_page, 1);
|
||||||
}
|
}
|
||||||
@@ -1377,7 +1397,7 @@ mod tests {
|
|||||||
// A new drawing's layer 0 is white.
|
// A new drawing's layer 0 is white.
|
||||||
let doc = document::collect(&codec::CadDocument::new());
|
let doc = document::collect(&codec::CadDocument::new());
|
||||||
assert!(doc.iter().any(|c| c.color == AcadColor::Index(7)));
|
assert!(doc.iter().any(|c| c.color == AcadColor::Index(7)));
|
||||||
let mut p = picker(AcadColor::Index(30)).with_drawing(doc, &[], Arc::default(), None);
|
let mut p = picker(AcadColor::Index(30)).with_drawing(doc, &[], Arc::default(), None, None);
|
||||||
p.update(ColorPickerMsg::Tab(ColorPickerTab::Document));
|
p.update(ColorPickerMsg::Tab(ColorPickerTab::Document));
|
||||||
p.update(ColorPickerMsg::Arrow(ArrowKey::Right));
|
p.update(ColorPickerMsg::Arrow(ArrowKey::Right));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1587,6 +1607,7 @@ mod tests {
|
|||||||
&[],
|
&[],
|
||||||
books,
|
books,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
p.update(ColorPickerMsg::Tab(tab));
|
p.update(ColorPickerMsg::Tab(tab));
|
||||||
if name == "books" {
|
if name == "books" {
|
||||||
|
|||||||
@@ -157,12 +157,14 @@ fn divider<'a>() -> Element<'a, Message> {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `shown` overrides the swatch, so ByLayer can show the layer's colour.
|
||||||
fn logical_row<'a>(
|
fn logical_row<'a>(
|
||||||
color: AcadColor,
|
color: AcadColor,
|
||||||
cur_color: AcadColor,
|
cur_color: AcadColor,
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
|
shown: Option<iced::Color>,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let (bg, _) = acad_color_display(color);
|
let bg = shown.unwrap_or_else(|| acad_color_display(color).0);
|
||||||
let selected = color == cur_color;
|
let selected = color == cur_color;
|
||||||
|
|
||||||
let swatch = container(
|
let swatch = container(
|
||||||
@@ -212,13 +214,15 @@ fn more_colors_row<'a>(active_color: AcadColor) -> Element<'a, Message> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the complete 5-section color dropdown panel element.
|
/// Builds the complete 5-section color dropdown panel element.
|
||||||
|
/// `layer_color` is the current layer's colour, shown on the ByLayer row.
|
||||||
pub fn color_dropdown_panel<'a>(
|
pub fn color_dropdown_panel<'a>(
|
||||||
active_color: AcadColor,
|
active_color: AcadColor,
|
||||||
recent_colors: &[AcadColor],
|
recent_colors: &[AcadColor],
|
||||||
|
layer_color: Option<iced::Color>,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
// 1. ByLayer and ByBlock
|
// 1. ByLayer and ByBlock
|
||||||
let by_layer = logical_row(AcadColor::ByLayer, active_color, "ByLayer");
|
let by_layer = logical_row(AcadColor::ByLayer, active_color, "ByLayer", layer_color);
|
||||||
let by_block = logical_row(AcadColor::ByBlock, active_color, "ByBlock");
|
let by_block = logical_row(AcadColor::ByBlock, active_color, "ByBlock", None);
|
||||||
let section1 = column![by_layer, by_block].spacing(2);
|
let section1 = column![by_layer, by_block].spacing(2);
|
||||||
|
|
||||||
// 2. 45-swatch Quick Pick Grid (5 rows × 9 columns)
|
// 2. 45-swatch Quick Pick Grid (5 rows × 9 columns)
|
||||||
|
|||||||
+13
-1
@@ -174,6 +174,14 @@ fn position_ribbon_dropdown<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ribbon {
|
impl Ribbon {
|
||||||
|
/// The current layer's colour: what ByLayer looks like for new objects.
|
||||||
|
pub fn active_layer_color(&self) -> Option<Color> {
|
||||||
|
self.layer_infos
|
||||||
|
.iter()
|
||||||
|
.find(|layer| layer.name == self.active_layer)
|
||||||
|
.map(|layer| layer.color)
|
||||||
|
}
|
||||||
|
|
||||||
/// The colour-book identity of the active colour, while the active colour
|
/// The colour-book identity of the active colour, while the active colour
|
||||||
/// is still the one picked from the book.
|
/// is still the one picked from the book.
|
||||||
pub fn active_color_name(&self) -> Option<String> {
|
pub fn active_color_name(&self) -> Option<String> {
|
||||||
@@ -1232,7 +1240,11 @@ impl Ribbon {
|
|||||||
win: (f32, f32),
|
win: (f32, f32),
|
||||||
recent_colors: &[AcadColor],
|
recent_colors: &[AcadColor],
|
||||||
) -> Option<Element<'_, Message>> {
|
) -> Option<Element<'_, Message>> {
|
||||||
let panel = color_dropdown::color_dropdown_panel(self.active_color, recent_colors);
|
let panel = color_dropdown::color_dropdown_panel(
|
||||||
|
self.active_color,
|
||||||
|
recent_colors,
|
||||||
|
self.active_layer_color(),
|
||||||
|
);
|
||||||
|
|
||||||
let (align_right, h_pad, top) =
|
let (align_right, h_pad, top) =
|
||||||
self.dd_anchor(PROP_COLOR_ID, color_dropdown::PANEL_W, win.0);
|
self.dd_anchor(PROP_COLOR_ID, color_dropdown::PANEL_W, win.0);
|
||||||
|
|||||||
@@ -1167,7 +1167,12 @@ pub(super) fn render_large<'a>(item: &RibbonItem, ctx: &RenderCtx<'_>) -> Elemen
|
|||||||
.width(Length::Fixed(PROP_W))
|
.width(Length::Fixed(PROP_W))
|
||||||
};
|
};
|
||||||
|
|
||||||
let (color_swatch, _) = acad_color_display(active_color);
|
// ByLayer shows the current layer's colour.
|
||||||
|
let color_swatch = layer_infos
|
||||||
|
.iter()
|
||||||
|
.find(|layer| layer.name == active_layer)
|
||||||
|
.filter(|_| active_color == AcadColor::ByLayer)
|
||||||
|
.map_or_else(|| acad_color_display(active_color).0, |layer| layer.color);
|
||||||
let color_row = prop_row(
|
let color_row = prop_row(
|
||||||
crate::ui::color_select::color_display_name(active_color),
|
crate::ui::color_select::color_display_name(active_color),
|
||||||
PROP_COLOR_ID,
|
PROP_COLOR_ID,
|
||||||
|
|||||||
Reference in new issue
Block a user