mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-29 05:16:49 -06:00
Fix Properties palette color swatch (#1466)
This commit is contained in:
@@ -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(¤t.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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user