From 6d6defa0ad6c4a8d1a18069e05a59584a26dfb96 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Tue, 1 Sep 2026 08:44:28 +0300 Subject: [PATCH] fix(color): show color book identity --- Cargo.lock | 2 +- Cargo.toml | 4 +- src/app/command_driver.rs | 1 + src/app/commands/layerprops.rs | 2 + src/app/commands/styleprops.rs | 4 ++ src/app/properties.rs | 67 +++++++++++++++++++++-------- src/app/update/command.rs | 2 + src/app/update/mod.rs | 4 ++ src/entities/multileader.rs | 2 + src/modules/draw/layers/laytrans.rs | 2 + src/modules/draw/modify/blend.rs | 2 + src/modules/draw/modify/explode.rs | 4 ++ src/scene/cache/properties.rs | 14 +++++- src/scene/model/object.rs | 2 + src/scene/selection.rs | 12 +++++- src/scene/view/dispatch.rs | 4 +- src/ui/color_select.rs | 24 ++++++++++- src/ui/properties.rs | 11 ++++- src/ui/window/layers.rs | 9 +++- 19 files changed, 142 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b66c0e6f..7bcdc4b0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "acadrust" version = "0.4.1" -source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=c91a1c1de89a0fbc006dc360ddb0251ac8c207d1#c91a1c1de89a0fbc006dc360ddb0251ac8c207d1" +source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=a0f7d444f1607bc4b2c881060cbe7ea1014253cb#a0f7d444f1607bc4b2c881060cbe7ea1014253cb" dependencies = [ "ahash 0.8.12", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 79f2cf17a..1536791ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ glam = { version = "0.33", features = ["bytemuck"] } rfd = "0.17" clap = { version = "4", features = ["derive"] } env_logger = "0.11" -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "a0f7d444f1607bc4b2c881060cbe7ea1014253cb", features = ["serde"] } cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "48c634995fa60d3928c37bae49b12b421c56c886", features = ["acis", "offset"] } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" @@ -61,7 +61,7 @@ iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aa iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } [patch."https://github.com/HakanSeven12/cadcodec.git"] -acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1" } +acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "a0f7d444f1607bc4b2c881060cbe7ea1014253cb" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index 4bffb222f..c0bddfc31 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -2466,6 +2466,7 @@ impl OpenCADStudio { dst_common.linetype_handle = common.linetype_handle; dst_common.linetype_scale = common.linetype_scale; dst_common.transparency = common.transparency.clone(); + dst_common.color_name = common.color_name.clone(); dst_common.color_book_handle = common.color_book_handle; dst_common.full_visual_style_handle = common.full_visual_style_handle; diff --git a/src/app/commands/layerprops.rs b/src/app/commands/layerprops.rs index 73460d3f9..053378adc 100644 --- a/src/app/commands/layerprops.rs +++ b/src/app/commands/layerprops.rs @@ -128,6 +128,8 @@ impl OpenCADStudio { if let Some(l) = self.tabs[i].scene.document.layers.get_mut(&layer_name) { l.color = acadrust::types::Color::from_index(idx); + l.color_name = None; + l.book_name = None; self.push_undo_snapshot(i, "LAYER COLOR"); self.tabs[i].dirty = true; // By-layer colour is baked into every wire on diff --git a/src/app/commands/styleprops.rs b/src/app/commands/styleprops.rs index 471c67f08..53d0b232e 100644 --- a/src/app/commands/styleprops.rs +++ b/src/app/commands/styleprops.rs @@ -616,6 +616,8 @@ impl OpenCADStudio { } "COLOR" => { common.color = color_val.unwrap(); + common.color_name = None; + common.color_book_handle = None; changed += 1; } "TRANSPARENCY" => { @@ -661,6 +663,8 @@ impl OpenCADStudio { if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(*handle) { let common = entity.common_mut(); common.color = acadrust::types::Color::ByLayer; + common.color_name = None; + common.color_book_handle = None; common.linetype = "ByLayer".to_string(); common.line_weight = acadrust::types::LineWeight::ByLayer; changed += 1; diff --git a/src/app/properties.rs b/src/app/properties.rs index f43c2a233..6520a0939 100644 --- a/src/app/properties.rs +++ b/src/app/properties.rs @@ -509,37 +509,59 @@ impl OpenCADStudio { { let doc = &self.tabs[i].scene.document; let common = entity.common(); - if let Some(acadrust::objects::ObjectType::BookColor(book)) = common + let named_color = common .color_book_handle .filter(|handle| handle.is_valid()) .and_then(|handle| doc.objects.get(&handle)) - { + .and_then(|object| match object { + acadrust::objects::ObjectType::BookColor(book) => Some(( + book.color, + book.book_name.clone(), + book.color_name.clone(), + )), + _ => None, + }) + .or_else(|| { + let identity = common.color_name.as_deref()?; + let (book_name, color_name) = identity + .split_once('$') + .map(|(book, color)| (book.to_string(), color.to_string())) + .unwrap_or_else(|| (String::new(), identity.to_string())); + Some((common.color, book_name, color_name)) + }); + if let Some((color, book_name, color_name)) = named_color { for section in sections.iter_mut() { if let Some(row) = section.props.iter_mut().find(|row| row.field == "color") { - row.value = - crate::scene::model::object::PropValue::ColorChoice( - book.color, - ); + row.value = crate::scene::model::object::PropValue::NamedColorChoice { + color, + name: color_name.clone(), + }; } } use crate::entities::common::ro_prop; + let mut props = Vec::new(); + if !book_name.is_empty() { + props.push(ro_prop( + t!("Book").as_ref(), + "book_color_book", + book_name, + )); + } + props.push(ro_prop( + t!("Color Name").as_ref(), + "book_color_name", + color_name, + )); + props.push(ro_prop( + t!("Color").as_ref(), + "book_color_value", + format!("{:?}", color), + )); sections.push(crate::scene::model::object::PropSection { title: t!("Color Book").into_owned(), - props: vec![ - ro_prop(t!("Book").as_ref(), "book_color_book", book.book_name.clone()), - ro_prop( - t!("Color Name").as_ref(), - "book_color_name", - book.color_name.clone(), - ), - ro_prop( - t!("Color").as_ref(), - "book_color_value", - format!("{:?}", book.color), - ), - ], + props, }); } @@ -2916,6 +2938,7 @@ fn make_sections_read_only( acadrust::types::Color::Index(index) => index.to_string(), acadrust::types::Color::Rgb { r, g, b } => format!("{r},{g},{b}"), }, + PropValue::NamedColorChoice { name, .. } => name.clone(), PropValue::ColorVaries | PropValue::LwVaries | PropValue::FieldLwVaries { .. } => VARIES_LABEL.to_string(), @@ -3075,6 +3098,9 @@ fn merge_prop_value( PropValue::LayerChoice(VARIES_LABEL.into()) } (PropValue::ColorChoice(_), PropValue::ColorChoice(_)) + | (PropValue::NamedColorChoice { .. }, PropValue::NamedColorChoice { .. }) + | (PropValue::ColorChoice(_), PropValue::NamedColorChoice { .. }) + | (PropValue::NamedColorChoice { .. }, PropValue::ColorChoice(_)) | (PropValue::ColorVaries, _) | (_, PropValue::ColorVaries) => PropValue::ColorVaries, (PropValue::LwChoice(_), PropValue::LwChoice(_)) @@ -3261,6 +3287,9 @@ fn update_row_color( }; match &mut row.value { PropValue::ColorChoice(current) => *current = color, + PropValue::NamedColorChoice { .. } => { + row.value = PropValue::ColorChoice(color) + } PropValue::ReadOnly(current) => *current = label, PropValue::ReadOnlyWithTooltip { value: current, .. } => *current = label, _ => {} diff --git a/src/app/update/command.rs b/src/app/update/command.rs index cd1793a08..5e8e5689d 100644 --- a/src/app/update/command.rs +++ b/src/app/update/command.rs @@ -3226,6 +3226,8 @@ fn apply_attr_row(a: &mut acadrust::entities::AttributeEntity, row: &AttrRow) -> } if a.common.color != row.color { a.common.color = row.color; + a.common.color_name = None; + a.common.color_book_handle = None; ch = true; } if a.common.linetype != row.linetype { diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 4c2e9305c..020fca1c4 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -2633,11 +2633,15 @@ impl OpenCADStudio { for name in &names { if let Some(dl) = self.tabs[i].scene.document.layers.get_mut(name) { dl.color = color; + dl.color_name = None; + dl.book_name = None; } } for pl in self.tabs[i].layers.layers.iter_mut() { if names.contains(&pl.name) { pl.color = color; + pl.color_name = None; + pl.book_name = None; } } self.tabs[i].dirty = true; diff --git a/src/entities/multileader.rs b/src/entities/multileader.rs index 4474ea15c..c3737e28d 100644 --- a/src/entities/multileader.rs +++ b/src/entities/multileader.rs @@ -1750,6 +1750,8 @@ pub(crate) fn block_content_insert( insert.normal = ml.context.block_content_normal; insert.common = ml.common.clone(); insert.common.color = ml.block_content_color.clone(); + insert.common.color_name = None; + insert.common.color_book_handle = None; Some(insert) } diff --git a/src/modules/draw/layers/laytrans.rs b/src/modules/draw/layers/laytrans.rs index f7e81aa1a..f0c0fb4dc 100644 --- a/src/modules/draw/layers/laytrans.rs +++ b/src/modules/draw/layers/laytrans.rs @@ -215,6 +215,8 @@ pub fn merge_layer(scene: &mut Scene, from: &str, to: &str, force_bylayer: bool) common.layer = to.to_string(); if force_bylayer { common.color = acadrust::Color::ByLayer; + common.color_name = None; + common.color_book_handle = None; common.linetype = String::new(); common.linetype_handle = None; common.line_weight = acadrust::LineWeight::ByLayer; diff --git a/src/modules/draw/modify/blend.rs b/src/modules/draw/modify/blend.rs index 11830973f..e368b727c 100644 --- a/src/modules/draw/modify/blend.rs +++ b/src/modules/draw/modify/blend.rs @@ -260,6 +260,8 @@ fn blend_common(source: &EntityCommon) -> EntityCommon { let mut common = EntityCommon::new(); common.layer = source.layer.clone(); common.color = source.color; + common.color_name = source.color_name.clone(); + common.color_book_handle = source.color_book_handle; common.line_weight = source.line_weight; common.linetype = source.linetype.clone(); common.linetype_handle = source.linetype_handle; diff --git a/src/modules/draw/modify/explode.rs b/src/modules/draw/modify/explode.rs index 216e7d5fb..d0869edc7 100644 --- a/src/modules/draw/modify/explode.rs +++ b/src/modules/draw/modify/explode.rs @@ -714,6 +714,8 @@ fn dim_common(base: &acadrust::entities::EntityCommon, clr: i16, lw: i16) -> aca c.handle = Handle::NULL; if clr != 0 && clr != 256 { c.color = acadrust::types::Color::from_index(clr); + c.color_name = None; + c.color_book_handle = None; } if lw >= 0 { c.line_weight = acadrust::types::LineWeight::from_value(lw); @@ -1223,6 +1225,8 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec { // Apply the text colour / lineweight (DIMCLRT) onto the entity. let tc = dim_common(&base.common, met.dimclrt, -2); tent.common_mut().color = tc.color; + tent.common_mut().color_name = tc.color_name; + tent.common_mut().color_book_handle = tc.color_book_handle; tent.common_mut().line_weight = tc.line_weight; result.push(tent); } diff --git a/src/scene/cache/properties.rs b/src/scene/cache/properties.rs index 1e463ccd8..a2b0b3f73 100644 --- a/src/scene/cache/properties.rs +++ b/src/scene/cache/properties.rs @@ -17,6 +17,18 @@ pub fn general_section(entity: &EntityType) -> PropSection { ((alpha as f64 / 255.0 * 100.0).round() as u32).to_string() } }; + let color_value = common.color_name.as_deref().map_or_else( + || PropValue::ColorChoice(common.color), + |identity| PropValue::NamedColorChoice { + color: common.color, + name: identity + .split_once('$') + .map(|(_, color_name)| color_name) + .filter(|color_name| !color_name.is_empty()) + .unwrap_or(identity) + .to_string(), + }, + ); // Hyperlink is stored in XDATA under the "PE_URL" application. let hyperlink = common @@ -36,7 +48,7 @@ pub fn general_section(entity: &EntityType) -> PropSection { Property { label: t!("Color").into_owned(), field: "color", - value: PropValue::ColorChoice(common.color), + value: color_value, }, Property { label: t!("Layer").into_owned(), diff --git a/src/scene/model/object.rs b/src/scene/model/object.rs index e0f30da28..2d0eee684 100644 --- a/src/scene/model/object.rs +++ b/src/scene/model/object.rs @@ -30,6 +30,8 @@ pub enum PropValue { }, /// ACI/RGB/ByLayer/ByBlock color — rendered as a color picker. ColorChoice(AcadColor), + /// Color-book color with its file-provided display name. + NamedColorChoice { color: AcadColor, name: String }, /// Color varies across the current multi-selection. ColorVaries, /// Line weight — rendered as a combo_box. diff --git a/src/scene/selection.rs b/src/scene/selection.rs index 0c67902e7..723a016d8 100644 --- a/src/scene/selection.rs +++ b/src/scene/selection.rs @@ -568,7 +568,8 @@ impl Scene { } QSelectValueEditor::Choice(values) } - PropValue::ColorChoice(_) => QSelectValueEditor::Choice(vec![ + PropValue::ColorChoice(_) + | PropValue::NamedColorChoice { .. } => QSelectValueEditor::Choice(vec![ "ByLayer".into(), "ByBlock".into(), ]), @@ -659,7 +660,13 @@ impl Scene { match field { "handle" => Some(entity.common().handle.value().to_string()), "layer" => Some(entity.common().layer.clone()), - "color" => Some(Self::format_color(entity.common().color)), + "color" => Some( + entity + .common() + .color_name + .clone() + .unwrap_or_else(|| Self::format_color(entity.common().color)), + ), "linetype" => Some(if entity.common().linetype.is_empty() { "ByLayer".to_string() } else { @@ -732,6 +739,7 @@ impl Scene { PropValue::Choice { selected, .. } => selected, PropValue::EditChoice { value, .. } => value, PropValue::ColorChoice(c) => Self::format_color(c), + PropValue::NamedColorChoice { name, .. } => name, PropValue::LwChoice(lw) | PropValue::FieldLwChoice { value: lw, .. } => { Self::format_lineweight(lw) diff --git a/src/scene/view/dispatch.rs b/src/scene/view/dispatch.rs index 4c47da101..a28b2f98f 100644 --- a/src/scene/view/dispatch.rs +++ b/src/scene/view/dispatch.rs @@ -296,7 +296,9 @@ pub fn toggle_invisible(entity: &mut EntityType) { pub fn apply_color(entity: &mut EntityType, color: AcadColor) { entity.as_entity_mut().set_color(color); - entity.common_mut().color_book_handle = None; + let common = entity.common_mut(); + common.color_name = None; + common.color_book_handle = None; } pub fn apply_line_weight(entity: &mut EntityType, lw: LineWeight) { diff --git a/src/ui/color_select.rs b/src/ui/color_select.rs index 0bc203cc4..d7f49715f 100644 --- a/src/ui/color_select.rs +++ b/src/ui/color_select.rs @@ -124,9 +124,31 @@ pub fn color_selector<'a>( on_select: impl Fn(AcadColor) -> Message + 'a, on_toggle: Message, on_more: Message, +) -> Element<'a, Message> { + color_selector_with_name( + current, + None, + open, + extras, + on_select, + on_toggle, + on_more, + ) +} + +pub fn color_selector_with_name<'a>( + current: AcadColor, + display_name: Option<&'a str>, + open: bool, + extras: ColorExtras, + on_select: impl Fn(AcadColor) -> Message + 'a, + on_toggle: Message, + on_more: Message, ) -> Element<'a, Message> { let (cur_bg, _) = acad_color_display(current); - let cur_name = color_display_name(current); + let cur_name = display_name + .map(str::to_string) + .unwrap_or_else(|| color_display_name(current)); let on_dismiss = on_toggle.clone(); // Closed button: current swatch + name + caret. diff --git a/src/ui/properties.rs b/src/ui/properties.rs index fadb6e918..3e3c3ae94 100644 --- a/src/ui/properties.rs +++ b/src/ui/properties.rs @@ -776,7 +776,12 @@ impl PropertiesPanel { label: &'a str, ) -> Element<'a, Message> { match &prop.value { - PropValue::ColorChoice(color) => self.render_color_row(label, prop.field, *color), + PropValue::ColorChoice(color) => { + self.render_color_row(label, prop.field, *color, None) + } + PropValue::NamedColorChoice { color, name } => { + self.render_color_row(label, prop.field, *color, Some(name)) + } PropValue::ColorVaries => self.render_color_varies_row(label), PropValue::LayerChoice(layer) => self.render_layer_row(label, layer), PropValue::LwChoice(lw) => self.render_lw_row(label, *lw), @@ -848,6 +853,7 @@ impl PropertiesPanel { label: &'a str, field: &'static str, color: AcadColor, + display_name: Option<&'a str>, ) -> Element<'a, Message> { // MTEXT background colour uses its own picker state + messages so it // routes to `background_color`, not the entity's main colour. @@ -929,8 +935,9 @@ impl PropertiesPanel { ); return prop_row_widget(label, selector); } - let selector = crate::ui::color_select::color_selector( + let selector = crate::ui::color_select::color_selector_with_name( color, + display_name, self.color_picker_open, crate::ui::color_select::ColorExtras { by_layer: true, diff --git a/src/ui/window/layers.rs b/src/ui/window/layers.rs index 9eb9050dd..f25b08eac 100644 --- a/src/ui/window/layers.rs +++ b/src/ui/window/layers.rs @@ -90,6 +90,8 @@ pub struct Layer { pub locked: bool, pub plottable: bool, pub color: AcadColor, + pub color_name: Option, + pub book_name: Option, pub linetype: String, pub lineweight: LineWeight, pub transparency: i32, @@ -106,6 +108,8 @@ impl Layer { locked: false, plottable: true, color, + color_name: None, + book_name: None, linetype: "Continuous".to_string(), lineweight: LineWeight::Default, transparency: 0, @@ -220,6 +224,8 @@ impl LayerPanel { locked: l.flags.locked, plottable: l.is_plottable, color: l.color, + color_name: l.color_name.clone(), + book_name: l.book_name.clone(), linetype: if l.line_type.is_empty() { "Continuous".to_string() } else { @@ -808,8 +814,9 @@ fn layer_row<'a>( // Color cell — looks like a combo_box input; click opens swatch dropdown below row. // Shared colour selector. Layers carry a concrete colour (no ByLayer / // ByBlock); true colours stay RGB instead of being collapsed to ACI 7. - let color_cell: Element<'_, Message> = container(crate::ui::color_select::color_selector( + let color_cell: Element<'_, Message> = container(crate::ui::color_select::color_selector_with_name( layer.color, + layer.color_name.as_deref(), color_picker_open, crate::ui::color_select::ColorExtras { by_layer: false,