Fix Properties palette color swatch (#1466)

This commit is contained in:
Rusu Alex
2026-10-19 02:32:39 -04:00
committed by GitHub
parent 582d9535b6
commit f17be3d7ad
2 changed files with 76 additions and 27 deletions
+16 -5
View File
@@ -3,7 +3,7 @@
//! VP Color columns. Every change runs an engine command (`layer.*`, `layerstate.*`, `vplayer`,
//! `linetype`).
use cadcraft_color::{Color, aci_rgb};
use cadcraft_color::{Color, Rgb, aci_rgb};
use cadcraft_doc::{Handle, Layer, Lineweight};
use egui::{Color32, Rect, RichText, Sense, Stroke, pos2, vec2};
use serde_json::{Value, json};
@@ -28,13 +28,16 @@ struct LpmState {
state_sel: Option<String>,
}
fn rgb32(c: Color) -> Color32 {
let r = c.resolve(Color::Index(7), Color::Index(7));
fn rgb32(r: Rgb) -> Color32 {
Color32::from_rgb(r.0, r.1, r.2)
}
/// A colour swatch.
pub fn swatch(p: &egui::Painter, r: Rect, c: Color) {
swatch_rgb(p, r, c.resolve(Color::WHITE, Color::WHITE));
}
fn swatch_rgb(p: &egui::Painter, r: Rect, c: Rgb) {
p.rect_filled(r, 1.0, rgb32(c));
p.rect_stroke(r, 1.0, Stroke::new(1.0, Color32::from_gray(25)), egui::StrokeKind::Inside);
}
@@ -42,14 +45,22 @@ pub fn swatch(p: &egui::Painter, r: Rect, c: Color) {
/// A colour button (swatch + name) that opens the Select Color popup: the AutoCAD Color Index
/// palette and a true-colour editor. Returns the picked colour. `by` adds ByLayer/ByBlock.
pub fn color_button(ui: &mut egui::Ui, id: egui::Id, current: Color, by: bool, width: f32) -> Option<Color> {
let label = current.name();
color_button_resolved(ui, id, current, current.resolve(Color::WHITE, Color::WHITE), &label, by, width)
}
/// A colour button whose swatch has already been resolved through its layer/block context.
/// `current` remains the value selected in the palette and `label` remains its logical name
/// (for example, a blue swatch can still be labelled `ByLayer`).
pub fn color_button_resolved(ui: &mut egui::Ui, id: egui::Id, current: Color, resolved: Rgb, label: &str, by: bool, width: f32) -> Option<Color> {
let t = Tokens::get();
let (rect, resp) = ui.allocate_exact_size(vec2(width, 20.0), Sense::click());
let p = ui.painter();
if resp.hovered() {
p.rect_filled(rect, 2.0, t.control_hover.gamma_multiply(0.5));
}
swatch(p, Rect::from_min_size(pos2(rect.left() + 3.0, rect.center().y - 6.0), vec2(12.0, 12.0)), current);
p.text(pos2(rect.left() + 20.0, rect.center().y), egui::Align2::LEFT_CENTER, crate::i18n::t(&current.name()), crate::theme::body(), t.text);
swatch_rgb(p, Rect::from_min_size(pos2(rect.left() + 3.0, rect.center().y - 6.0), vec2(12.0, 12.0)), resolved);
p.text(pos2(rect.left() + 20.0, rect.center().y), egui::Align2::LEFT_CENTER, crate::i18n::t(label), crate::theme::body(), t.text);
let mut out = None;
egui::Popup::from_toggle_button_response(&resp).id(id).width(300.0).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| {
out = color_palette(ui, id, current, by);
+60 -22
View File
@@ -1,6 +1,6 @@
//! Tool Sets (left) and the Layers / Properties palettes (right).
use cadcraft_color::Color;
use cadcraft_color::{Color, Rgb};
use cadcraft_doc::EntityKind;
use egui::{Color32, Rect, Sense, Stroke, pos2, vec2};
use serde_json::{Value, json};
@@ -854,7 +854,18 @@ pub(crate) fn properties_section(app: &mut CadApp, ui: &mut egui::Ui) {
_ => "*VARIES*".into(),
}
};
let color = same(&|e| e.common.color.name());
let selected_color = Some(e.common.color).filter(|c| sel.iter().filter_map(|h| d.entity(*h)).all(|e| e.common.color == *c));
let color = selected_color.map_or_else(|| "*VARIES*".into(), Color::name);
let effective_color = |e: &cadcraft_doc::Entity| {
let layer = d.layer(&e.common.layer).map_or(Color::WHITE, |l| l.color);
e.common.color.resolve(layer, Color::WHITE)
};
let resolved_color = sel
.first()
.and_then(|h| d.entity(*h))
.map(|e| effective_color(e))
.filter(|c| sel.iter().filter_map(|h| d.entity(*h)).all(|e| effective_color(e) == *c))
.unwrap_or(Rgb(255, 255, 255));
let layer = same(&|e| e.common.layer.clone());
let linetype = same(&|e| e.common.linetype.clone());
let lts = same(&|e| format!("{:.4}", e.common.ltscale));
@@ -872,26 +883,18 @@ pub(crate) fn properties_section(app: &mut CadApp, ui: &mut egui::Ui) {
};
group(ui, "General");
prop_row(ui, "Color", |ui| {
ui.menu_button(format!("■ {}", crate::i18n::t(&color)), |ui| {
for (n, c) in [
("ByLayer", 256),
("ByBlock", 0),
("Red", 1),
("Yellow", 2),
("Green", 3),
("Cyan", 4),
("Blue", 5),
("Magenta", 6),
("White", 7),
("Color 8", 8),
("Color 9", 9),
] {
if ui.button(crate::i18n::t(n)).clicked() {
set = Some(json!({ "handles": ids, "color": c }));
ui.close();
}
}
});
let width = (ui.available_width() - 10.0).max(0.0);
if let Some(c) = crate::layers::color_button_resolved(
ui,
egui::Id::new(("properties_color", &ids)),
selected_color.unwrap_or(Color::ByLayer),
resolved_color,
&color,
true,
width,
) {
set = Some(json!({ "handles": ids, "color": c.name() }));
}
});
prop_row(ui, "Layer", |ui| {
ui.menu_button(layer.clone(), |ui| {
@@ -1135,3 +1138,38 @@ pub(crate) fn properties_section(app: &mut CadApp, ui: &mut egui::Ui) {
let _ = app.run("blocks.reference.set", p);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Services;
use cadcraft_engine::Session;
use egui::{Color32, Shape};
fn contains_swatch(shape: &Shape, color: Color32) -> bool {
match shape {
Shape::Rect(r) => r.fill == color && (r.rect.width() - 12.0).abs() < 0.01 && (r.rect.height() - 12.0).abs() < 0.01,
Shape::Vec(shapes) => shapes.iter().any(|shape| contains_swatch(shape, color)),
_ => false,
}
}
#[test]
fn selected_bylayer_entity_paints_its_layer_color_swatch() {
let mut app = CadApp::new(Session::default(), Services::default());
app.run("layer.new", json!({ "name": "Blue", "color": 5, "current": true })).unwrap();
let line = app.run("line", json!({ "points": [[0, 0], [10, 0]] })).unwrap();
app.run("select", json!({ "handles": [line["handles"][0].clone()] })).unwrap();
let ctx = egui::Context::default();
let input = egui::RawInput { screen_rect: Some(Rect::from_min_size(egui::Pos2::ZERO, vec2(400.0, 1200.0))), ..Default::default() };
let mut painted_blue = false;
for _ in 0..2 {
app.logic(&ctx);
let mut output = ctx.run_ui(input.clone(), |ui| properties_section(&mut app, ui));
painted_blue = output.shapes.iter().any(|shape| contains_swatch(&shape.shape, Color32::from_rgb(0, 0, 255)));
output.textures_delta.clear();
}
assert!(painted_blue, "a ByLayer entity on a blue layer needs a blue Properties swatch");
}
}