Color Picker: laid out as InDesign's, with a component field and slider, every space's values and Add CMYK Swatch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
moppeeerandClaude Opus 5.5 committed 2026-10-15 21:58:30 +02:00
1 parent a930276fd7
commit 2a7f4d9505
10 files changed
+450 -32

No files matched your search

+1
View File
@@ -347,6 +347,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
Newest first.
- 2026-10-15: The Color Picker is laid out as InDesign's: a field of two components and a slider for the third (H, S, B, L, a, b, R, G or B by radio button; RGB or Lab Color Space View), the new colour over the old one, H/S/B, L/a/b, R/G/B and C/M/Y/K values, the hex code, and Add CMYK Swatch.
- **2026-10-14** · The Type tool draws a new text frame over a locked empty frame, as InDesign does, instead of turning the locked frame into a text frame. No readiness percentage change.
- **2026-10-14** · 100% black shows as pure black on screen by default, as InDesign's Appearance of Black does out of the box (Display All Blacks as Rich Black); Preferences › Appearance of Black › Display All Blacks Accurately still shows it as the dark grey it prints, and a saved choice is kept. No readiness percentage change.
- **2026-10-14** · Pathfinder keeps the frontmost object, as InDesign does: a photo placed over shapes and intersected with them shows through the shapes (it was dropped, and the shapes kept their black fill); Add, Exclude Overlap and Minus Back take the front object's fill too, Subtract still keeps the back one. No readiness percentage change.
+399 -32
View File
@@ -1828,7 +1828,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
}
"insertXref" => insert_xref(app, ui, &mut d),
"findFont" => find_font(app, ui, &mut d),
"colorPicker" => color_picker(ui, &mut d),
"colorPicker" => color_picker(app, ui, &mut d),
"preferences" => preferences(app, ui, &mut d, body_height),
"print" => print_dialog(app, ui, &mut d),
"pdfImport" => {
@@ -4710,44 +4710,356 @@ fn insert_xref(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
}
}
/// Color Picker: saturation/brightness field and hue slider, with RGB, CMYK and hex fields.
fn color_picker(ui: &mut egui::Ui, d: &mut Dialog) {
let hex = d.s("hex");
let mut c = designcraft_color::Color::from_hex(&hex).map(|c| c.to_rgb()).map_or(egui::Color32::BLACK, |[r, g, b]| {
egui::Color32::from_rgb((r * 255.0).round() as u8, (g * 255.0).round() as u8, (b * 255.0).round() as u8)
});
let before = c;
/// The Color Picker's components: the one picked runs along the slider, the other two of its
/// space across the field (InDesign's, as Photoshop's: R puts B across and G up).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PickComp {
H,
S,
Br,
L,
A,
Lb,
R,
G,
Bl,
}
impl PickComp {
const ALL: [PickComp; 9] =
[PickComp::H, PickComp::S, PickComp::Br, PickComp::L, PickComp::A, PickComp::Lb, PickComp::R, PickComp::G, PickComp::Bl];
fn key(self) -> &'static str {
match self {
PickComp::H => "h",
PickComp::S => "s",
PickComp::Br => "b",
PickComp::L => "l",
PickComp::A => "la",
PickComp::Lb => "lb",
PickComp::R => "r",
PickComp::G => "g",
PickComp::Bl => "bl",
}
}
fn from_key(k: &str) -> PickComp {
PickComp::ALL.into_iter().find(|c| c.key() == k).unwrap_or(PickComp::R)
}
fn lab(self) -> bool {
matches!(self, PickComp::L | PickComp::A | PickComp::Lb)
}
}
/// The colour the picker shows, in each of its spaces (HSB keeps its hue through greys).
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Picked {
pub rgb: [f32; 3],
pub hsb: [f32; 3],
pub lab: [f32; 3],
}
impl Picked {
pub fn from_rgb(rgb: [f32; 3], hue: f32) -> Picked {
let rgb = rgb.map(|v| v.clamp(0.0, 1.0));
let c = designcraft_color::Color::Rgb { r: rgb[0], g: rgb[1], b: rgb[2] };
let mut hsb = c.to_hsb();
if hsb[1] <= 1e-4 || hsb[2] <= 1e-4 {
hsb[0] = hue;
}
let l = designcraft_color::cms::lab::srgb_to_lab(rgb);
Picked { rgb, hsb, lab: [l.l, l.a, l.b] }
}
fn from_hsb(hsb: [f32; 3]) -> Picked {
let hsb = [hsb[0].rem_euclid(360.0), hsb[1].clamp(0.0, 1.0), hsb[2].clamp(0.0, 1.0)];
let mut p = Picked::from_rgb(designcraft_color::Color::from_hsb(hsb[0], hsb[1], hsb[2]).to_rgb_uncalibrated(), hsb[0]);
p.hsb = hsb;
p
}
fn from_lab(lab: [f32; 3], hue: f32) -> Picked {
let lab = [lab[0].clamp(0.0, 100.0), lab[1].clamp(-128.0, 127.0), lab[2].clamp(-128.0, 127.0)];
let mut p = Picked::from_rgb(designcraft_color::cms::lab::lab_to_srgb(designcraft_color::cms::Lab::new(lab[0], lab[1], lab[2])), hue);
p.lab = lab;
p
}
/// (slider, across, up), each 0..1, for component `c`.
pub fn coords(&self, c: PickComp) -> (f32, f32, f32) {
let [r, g, b] = self.rgb;
let [h, s, v] = self.hsb;
let [l, la, lb] = self.lab;
let (a, bb) = ((la + 128.0) / 255.0, (lb + 128.0) / 255.0);
match c {
PickComp::R => (r, b, g),
PickComp::G => (g, b, r),
PickComp::Bl => (b, r, g),
PickComp::H => (h / 360.0, s, v),
PickComp::S => (s, h / 360.0, v),
PickComp::Br => (v, h / 360.0, s),
PickComp::L => (l / 100.0, a, bb),
PickComp::A => (a, bb, l / 100.0),
PickComp::Lb => (bb, a, l / 100.0),
}
}
/// The colour at (slider `t`, across `u`, up `v`) for component `c`.
pub fn at(&self, c: PickComp, t: f32, u: f32, v: f32) -> Picked {
let hue = self.hsb[0];
let ab = |x: f32| x * 255.0 - 128.0;
match c {
PickComp::R => Picked::from_rgb([t, v, u], hue),
PickComp::G => Picked::from_rgb([v, t, u], hue),
PickComp::Bl => Picked::from_rgb([u, v, t], hue),
PickComp::H => Picked::from_hsb([t * 360.0, u, v]),
PickComp::S => Picked::from_hsb([u * 360.0, t, v]),
PickComp::Br => Picked::from_hsb([u * 360.0, v, t]),
PickComp::L => Picked::from_lab([t * 100.0, ab(u), ab(v)], hue),
PickComp::A => Picked::from_lab([v * 100.0, ab(t), ab(u)], hue),
PickComp::Lb => Picked::from_lab([v * 100.0, ab(u), ab(t)], hue),
}
}
/// The slider's colour at `t`: the hue slider shows pure hues, the others this colour with
/// the component at `t`.
fn slider_at(&self, c: PickComp, t: f32) -> [f32; 3] {
if c == PickComp::H {
return designcraft_color::Color::from_hsb(t * 360.0, 1.0, 1.0).to_rgb_uncalibrated();
}
let (_, u, v) = self.coords(c);
self.at(c, t, u, v).rgb
}
pub fn hex(&self) -> String {
let q = |x: f32| (x.clamp(0.0, 1.0) * 255.0).round() as u8;
format!("#{:02x}{:02x}{:02x}", q(self.rgb[0]), q(self.rgb[1]), q(self.rgb[2]))
}
}
fn color32(rgb: [f32; 3]) -> egui::Color32 {
let q = |x: f32| (x.clamp(0.0, 1.0) * 255.0).round() as u8;
egui::Color32::from_rgb(q(rgb[0]), q(rgb[1]), q(rgb[2]))
}
/// A `cols` × `rows` grid of quads over `r`, coloured at each vertex by `f(x, y)` (0..1, y up).
fn gradient_mesh(painter: &egui::Painter, r: egui::Rect, cols: usize, rows: usize, f: impl Fn(f32, f32) -> [f32; 3]) {
let mut mesh = egui::Mesh::default();
for j in 0..=rows {
for i in 0..=cols {
let (x, y) = (i as f32 / cols as f32, j as f32 / rows as f32);
mesh.colored_vertex(egui::pos2(r.left() + x * r.width(), r.bottom() - y * r.height()), color32(f(x, y)));
}
}
let w = (cols + 1) as u32;
for j in 0..rows as u32 {
for i in 0..cols as u32 {
let a = j * w + i;
mesh.add_triangle(a, a + 1, a + w);
mesh.add_triangle(a + 1, a + w + 1, a + w);
}
}
painter.add(egui::Shape::mesh(mesh));
}
/// `n` numbers from an array field.
fn nums<const N: usize>(d: &Dialog, key: &str) -> Option<[f64; N]> {
let a = d.fields.get(key)?.as_array()?;
let mut out = [0.0; N];
for (o, v) in out.iter_mut().zip(a) {
*o = v.as_f64().filter(|v| v.is_finite())?;
}
(a.len() == N).then_some(out)
}
/// The colour in the dialog's fields: `hex`, with the HSB hue and the Lab values kept while
/// they round-trip (`_hsb`, `_lab`).
fn picked_of(d: &Dialog) -> Picked {
let rgb = designcraft_color::Color::from_hex(&d.s("hex")).map_or([0.0; 3], |c| c.to_rgb_uncalibrated());
let hue = nums::<3>(d, "_hsb").map_or(0.0, |h| h[0] as f32);
let mut p = Picked::from_rgb(rgb, hue);
// The exact values the picker last set, while they still give this colour.
if let Some(h) = nums::<3>(d, "_hsb").map(|h| h.map(|v| v as f32))
&& Picked::from_hsb(h).hex() == p.hex()
{
p.hsb = h;
}
if let Some(l) = nums::<3>(d, "_lab").map(|l| l.map(|v| v as f32))
&& Picked::from_lab(l, p.hsb[0]).hex() == p.hex()
{
p.lab = l;
}
p
}
fn store_picked(d: &mut Dialog, p: &Picked) {
d.fields.insert("hex".into(), json!(p.hex()));
d.fields.insert("_hsb".into(), json!(p.hsb));
d.fields.insert("_lab".into(), json!(p.lab));
}
/// The Color Picker, laid out as InDesign's: the field of two components and the slider of the
/// third (the one whose radio button is on, R at first), the new colour over the one it replaces,
/// H/S/B, L/a/b, R/G/B and C/M/Y/K values, the hex code, and Add CMYK Swatch.
fn color_picker(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
let lang = app.ui.language.clone();
let tr = |s: &str| crate::i18n::tr(&lang, s).to_string();
if !d.fields.contains_key("old") {
let old = d.s("hex");
d.fields.insert("old".into(), json!(old));
}
let comp = PickComp::from_key(&d.s("component"));
let mut p = picked_of(d);
let before = p;
let t = crate::theme::Tokens::get(ui.ctx());
ui.horizontal_top(|ui| {
egui::color_picker::color_picker_color32(ui, &mut c, egui::color_picker::Alpha::Opaque);
// The field and the slider.
ui.vertical(|ui| {
let mut rgb = [c.r() as f32, c.g() as f32, c.b() as f32];
egui::Grid::new("cp_rgb").num_columns(2).spacing([6.0, 4.0]).show(ui, |ui| {
for (k, l) in ["R", "G", "B"].iter().enumerate() {
ui.label(*l);
ui.add(egui::DragValue::new(&mut rgb[k]).range(0.0..=255.0));
ui.end_row();
crate::rtl::label(ui, egui::RichText::new(tr(if comp.lab() { "Lab Color Space View" } else { "RGB Color Space View" })).size(11.5));
ui.add_space(4.0);
ui.horizontal_top(|ui| {
let (fr, fresp) = ui.allocate_exact_size(egui::vec2(190.0, 200.0), egui::Sense::click_and_drag());
let (tv, _, _) = p.coords(comp);
let cells = if matches!(comp, PickComp::R | PickComp::G | PickComp::Bl | PickComp::H) { 2 } else { 24 };
gradient_mesh(ui.painter(), fr, cells, cells, |x, y| p.at(comp, tv, x, y).rgb);
ui.painter().rect_stroke(fr, 0.0, egui::Stroke::new(1.0, t.field_border), egui::StrokeKind::Outside);
fresp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Other, true, "Color field"));
if let Some(pos) = fresp.interact_pointer_pos().filter(|_| fresp.is_pointer_button_down_on() || fresp.clicked()) {
let u = ((pos.x - fr.left()) / fr.width()).clamp(0.0, 1.0);
let v = ((fr.bottom() - pos.y) / fr.height()).clamp(0.0, 1.0);
p = p.at(comp, tv, u, v);
}
});
c = egui::Color32::from_rgb(rgb[0] as u8, rgb[1] as u8, rgb[2] as u8);
let col = designcraft_color::Color::rgb8(c.r(), c.g(), c.b());
let [cc, m, y, k] = col.to_cmyk();
ui.add_space(6.0);
ui.label(format!("C {:.0}% M {:.0}% Y {:.0}% K {:.0}%", cc * 100.0, m * 100.0, y * 100.0, k * 100.0));
ui.add_space(6.0);
ui.horizontal(|ui| {
ui.label("#");
let mut h = format!("{:02x}{:02x}{:02x}", c.r(), c.g(), c.b());
if ui.add(egui::TextEdit::singleline(&mut h).desired_width(70.0)).changed()
&& let Some(n) = designcraft_color::Color::from_hex(&h)
{
let [r, g, b] = n.to_rgb();
c = egui::Color32::from_rgb((r * 255.0).round() as u8, (g * 255.0).round() as u8, (b * 255.0).round() as u8);
let (_, u, v) = p.coords(comp);
let mark = egui::pos2(fr.left() + u * fr.width(), fr.bottom() - v * fr.height());
ui.painter().circle_stroke(mark, 5.0, egui::Stroke::new(1.5, egui::Color32::WHITE));
ui.painter().circle_stroke(mark, 6.0, egui::Stroke::new(1.0, egui::Color32::BLACK));
ui.add_space(6.0);
let (sr, sresp) = ui.allocate_exact_size(egui::vec2(14.0, 200.0), egui::Sense::click_and_drag());
gradient_mesh(ui.painter(), sr, 1, 48, |_, y| p.slider_at(comp, y));
ui.painter().rect_stroke(sr, 0.0, egui::Stroke::new(1.0, t.field_border), egui::StrokeKind::Outside);
sresp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Slider, true, "Color slider"));
if let Some(pos) = sresp.interact_pointer_pos().filter(|_| sresp.is_pointer_button_down_on() || sresp.clicked()) {
let tn = ((sr.bottom() - pos.y) / sr.height()).clamp(0.0, 1.0);
let (_, u, v) = p.coords(comp);
p = p.at(comp, tn, u, v);
}
let (tn, _, _) = p.coords(comp);
let y = sr.bottom() - tn * sr.height();
for (x, dir) in [(sr.left() - 1.0, 1.0), (sr.right() + 1.0, -1.0)] {
ui.painter().add(egui::Shape::convex_polygon(
vec![egui::pos2(x, y), egui::pos2(x - 5.0 * dir, y - 4.0), egui::pos2(x - 5.0 * dir, y + 4.0)],
t.text_strong,
egui::Stroke::NONE,
));
}
});
});
ui.add_space(10.0);
ui.vertical(|ui| {
// The new colour over the one it replaces.
let (pr, _) = ui.allocate_exact_size(egui::vec2(60.0, 60.0), egui::Sense::hover());
let old = designcraft_color::Color::from_hex(&d.s("old")).map_or([0.0; 3], |c| c.to_rgb_uncalibrated());
ui.painter().rect_filled(egui::Rect::from_min_max(pr.min, egui::pos2(pr.max.x, pr.center().y)), 0.0, color32(p.rgb));
ui.painter().rect_filled(egui::Rect::from_min_max(egui::pos2(pr.min.x, pr.center().y), pr.max), 0.0, color32(old));
ui.painter().rect_stroke(pr, 0.0, egui::Stroke::new(1.0, t.field_border), egui::StrokeKind::Outside);
ui.add_space(10.0);
let mut comp_now = comp;
let mut edited: Option<Picked> = None;
let [cc, cm, cy, ck] = designcraft_color::Color::Rgb { r: p.rgb[0], g: p.rgb[1], b: p.rgb[2] }
.to_cmyk_managed(designcraft_color::cms::active_settings().intent)
.map(|v| v * 100.0);
let mut cmyk = [cc, cm, cy, ck];
egui::Grid::new("cp_values").num_columns(6).spacing([4.0, 4.0]).show(ui, |ui| {
let mut field =
|ui: &mut egui::Ui, c: Option<PickComp>, label: &str, v: &mut f32, range: std::ops::RangeInclusive<f32>, suffix: &str| -> bool {
match c {
Some(c) => {
ui.radio_value(&mut comp_now, c, "");
}
None => {
ui.label("");
}
}
ui.label(label);
ui.add(egui::DragValue::new(v).range(range).max_decimals(0).suffix(suffix)).changed()
};
let (mut h, mut s, mut b) = (p.hsb[0], p.hsb[1] * 100.0, p.hsb[2] * 100.0);
let (mut l, mut la, mut lb) = (p.lab[0], p.lab[1], p.lab[2]);
let (mut r, mut g, mut bl) = (p.rgb[0] * 255.0, p.rgb[1] * 255.0, p.rgb[2] * 255.0);
// Rows as InDesign's: H S B beside L a b, R G B beside C M Y K.
let hsb_changed =
field(ui, Some(PickComp::H), "H:", &mut h, 0.0..=360.0, "°") | field(ui, Some(PickComp::L), "L:", &mut l, 0.0..=100.0, "");
ui.end_row();
let hsb_changed2 =
field(ui, Some(PickComp::S), "S:", &mut s, 0.0..=100.0, "%") | field(ui, Some(PickComp::A), "a:", &mut la, -128.0..=127.0, "");
ui.end_row();
let hsb_changed3 =
field(ui, Some(PickComp::Br), "B:", &mut b, 0.0..=100.0, "%") | field(ui, Some(PickComp::Lb), "b:", &mut lb, -128.0..=127.0, "");
ui.end_row();
if hsb_changed || hsb_changed2 || hsb_changed3 {
// Whichever space changed wins: HSB if its values moved, else Lab.
edited = Some(if (h, s, b) != (p.hsb[0], p.hsb[1] * 100.0, p.hsb[2] * 100.0) {
Picked::from_hsb([h, s / 100.0, b / 100.0])
} else {
Picked::from_lab([l, la, lb], p.hsb[0])
});
}
let rgb_changed = field(ui, Some(PickComp::R), "R:", &mut r, 0.0..=255.0, "");
let c1 = field(ui, None, "C:", &mut cmyk[0], 0.0..=100.0, "%");
ui.end_row();
let rgb_changed2 = field(ui, Some(PickComp::G), "G:", &mut g, 0.0..=255.0, "");
let c2 = field(ui, None, "M:", &mut cmyk[1], 0.0..=100.0, "%");
ui.end_row();
let rgb_changed3 = field(ui, Some(PickComp::Bl), "B:", &mut bl, 0.0..=255.0, "");
let c3 = field(ui, None, "Y:", &mut cmyk[2], 0.0..=100.0, "%");
ui.end_row();
ui.label("");
ui.label("");
ui.label("");
let c4 = field(ui, None, "K:", &mut cmyk[3], 0.0..=100.0, "%");
ui.end_row();
if rgb_changed || rgb_changed2 || rgb_changed3 {
edited = Some(Picked::from_rgb([r / 255.0, g / 255.0, bl / 255.0], p.hsb[0]));
}
if c1 || c2 || c3 || c4 {
let [c, m, y, k] = cmyk.map(|v| v / 100.0);
edited = Some(Picked::from_rgb(designcraft_color::Color::Cmyk { c, m, y, k }.to_rgb(), p.hsb[0]));
}
});
if let Some(e) = edited {
p = e;
}
if comp_now != comp {
d.fields.insert("component".into(), json!(comp_now.key()));
}
ui.add_space(4.0);
ui.horizontal(|ui| {
ui.label("#");
let id = egui::Id::new("color_picker_hex");
let editing = ui.memory(|m| m.has_focus(id));
let mut h = if editing { d.s("_hexEdit") } else { p.hex().trim_start_matches('#').to_string() };
if ui.add(egui::TextEdit::singleline(&mut h).id(id).desired_width(70.0)).changed()
&& let Some(c) = designcraft_color::Color::from_hex(&h)
{
p = Picked::from_rgb(c.to_rgb_uncalibrated(), p.hsb[0]);
}
d.fields.insert("_hexEdit".into(), json!(h));
});
ui.add_space(8.0);
if ui.button(crate::rtl::widget(ui, tr("Add CMYK Swatch"))).clicked() {
let [c, m, y, k] = designcraft_color::Color::Rgb { r: p.rgb[0], g: p.rgb[1], b: p.rgb[2] }
.to_cmyk_managed(designcraft_color::cms::active_settings().intent)
.map(|v| (v * 100.0).round());
match app.run("swatch.create", json!({"color": {"c": c, "m": m, "y": y, "k": k}})) {
Ok(r) => app.status(format!("{} {}", tr("Added swatch"), r["name"].as_str().unwrap_or(""))),
Err(e) => app.status(e),
}
}
});
});
if c != before {
d.fields.insert("hex".into(), json!(format!("#{:02x}{:02x}{:02x}", c.r(), c.g(), c.b())));
if p != before {
store_picked(d, &p);
}
}
@@ -6794,6 +7106,61 @@ mod tests {
(0..doc.page_count()).map(|abs| doc.page(abs).unwrap().guides.len()).collect()
}
/// The Color Picker laid out as InDesign's: a field and a slider for the component whose radio
/// is on, the new colour over the old one, and the values in every space.
#[test]
fn the_color_picker_picks_on_its_field_and_slider_and_adds_a_cmyk_swatch() {
use egui_kittest::kittest::Queryable;
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
app.run("frame.create", json!({"rect": [72, 72, 200, 200], "shape": "rectangle"})).unwrap();
app.ui.dialog = Some(Dialog::new("colorPicker", json!({"target": "fill", "hex": "#7c1315", "text": false})));
let mut h = crate::test_window::open(app, egui::vec2(1400.0, 900.0));
h.run_steps(3);
// R at first: R up the slider, B across and G up the field.
let _ = h.get_by_label("RGB Color Space View");
let field = h.get_by_label("Color field").rect();
crate::test_window::click_at(&mut h, egui::pos2(field.right() - 1.0, field.top() + 1.0));
h.run_steps(2);
let p = picked_of(h.state().app.ui.dialog.as_ref().unwrap());
assert!((p.rgb[0] - 124.0 / 255.0).abs() < 0.01 && p.rgb[1] > 0.97 && p.rgb[2] > 0.97, "{p:?}: R kept, G and B full");
// The slider sets R alone.
let slider = h.get_by_label("Color slider").rect();
crate::test_window::click_at(&mut h, egui::pos2(slider.center().x, slider.bottom() - 1.0));
h.run_steps(2);
let p = picked_of(h.state().app.ui.dialog.as_ref().unwrap());
assert!(p.rgb[0] < 0.02 && p.rgb[1] > 0.97, "{p:?}");
// Hue on the slider: the field is saturation across, brightness up.
h.state_mut().app.ui.dialog.as_mut().unwrap().fields.insert("hex".into(), json!("#7c1315"));
h.state_mut().app.ui.dialog.as_mut().unwrap().fields.insert("component".into(), json!("h"));
h.run_steps(2);
crate::test_window::click_at(&mut h, egui::pos2(field.left() + 1.0, field.top() + 1.0));
h.run_steps(2);
let p = picked_of(h.state().app.ui.dialog.as_ref().unwrap());
assert!(p.hsb[1] < 0.02 && p.hsb[2] > 0.98 && (p.hsb[0] - 359.0).abs() < 2.0, "{p:?}: no saturation, full brightness, the hue kept");
// Lab shows its own view.
h.state_mut().app.ui.dialog.as_mut().unwrap().fields.insert("component".into(), json!("l"));
h.run_steps(2);
let _ = h.get_by_label("Lab Color Space View");
// Add CMYK Swatch adds the colour as a CMYK swatch; OK applies it.
h.state_mut().app.ui.dialog.as_mut().unwrap().fields.insert("hex".into(), json!("#7c1315"));
h.run_steps(2);
let before = h.state().app.session.active().unwrap().doc.swatches.len();
h.get_by_label("Add CMYK Swatch").click();
h.run_steps(3);
let doc = &h.state().app.session.active().unwrap().doc;
assert_eq!(doc.swatches.len(), before + 1);
assert!(matches!(
doc.swatches.last().unwrap().value,
designcraft_color::swatch::SwatchValue::Color { color: designcraft_color::Color::Cmyk { .. }, .. }
));
confirm(&mut h.state_mut().app).unwrap();
let st = h.state().app.session.active().unwrap();
let id = st.selection.items[0];
let fill = st.doc.item(id).unwrap().fill.swatch.clone();
assert_eq!(st.doc.resolve_color(&fill, 1.0).unwrap().to_hex(), "#7c1315");
}
#[test]
fn create_guides_dialog_targets_the_page_in_view() {
let mut app = facing_app(6);
+22
View File
@@ -58,6 +58,28 @@ const TABLE: &[(&str, [&str; 5])] = &[
("Cut", ["Ausschneiden", "Couper", "Cortar", "カット", "剪切"]),
("Copy", ["Kopieren", "Copier", "Copiar", "コピー", "复制"]),
("Paste", ["Einfügen", "Coller", "Pegar", "ペースト", "粘贴"]),
(
"RGB Color Space View",
[
"RGB-Farbraumansicht",
"Affichage de l’espace colorimétrique RVB",
"Vista de espacio de color RGB",
"RGB カラースペース表示",
"RGB 色彩空间视图",
],
),
(
"Lab Color Space View",
[
"Lab-Farbraumansicht",
"Affichage de l’espace colorimétrique Lab",
"Vista de espacio de color Lab",
"Lab カラースペース表示",
"Lab 色彩空间视图",
],
),
("Add CMYK Swatch", ["CMYK-Farbfeld hinzufügen", "Ajouter une nuance CMJN", "Añadir muestra CMYK", "CMYK スウォッチを追加", "添加 CMYK 色板"]),
("Added swatch", ["Farbfeld hinzugefügt", "Nuance ajoutée", "Muestra añadida", "スウォッチを追加しました", "已添加色板"]),
(
"Bulleted & Numbered Lists",
[
+4
View File
@@ -30,6 +30,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "قص"),
("Copy", "نسخ"),
("Paste", "لصق"),
("RGB Color Space View", "عرض مساحة ألوان RGB"),
("Lab Color Space View", "عرض مساحة ألوان Lab"),
("Add CMYK Swatch", "إضافة حامل ألوان CMYK"),
("Added swatch", "تمت إضافة حامل الألوان"),
("Bulleted & Numbered Lists", "القوائم ذات التعداد النقطي والرقمي"),
("Current Page Number", "رقم الصفحة الحالية"),
("Apply Color (,)", "تطبيق اللون (,)"),
+4
View File
@@ -30,6 +30,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "Ausschneiden"),
("Copy", "Kopieren"),
("Paste", "Einfügen"),
("RGB Color Space View", "RGB-Farbraumansicht"),
("Lab Color Space View", "Lab-Farbraumansicht"),
("Add CMYK Swatch", "CMYK-Farbfeld hinzufügen"),
("Added swatch", "Farbfeld hinzugefügt"),
("Clear", "Löschen"),
("Duplicate", "Duplizieren"),
("Select All", "Alles auswählen"),
+4
View File
@@ -28,6 +28,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "Taglia"),
("Copy", "Copia"),
("Paste", "Incolla"),
("RGB Color Space View", "Vista spazio colore RGB"),
("Lab Color Space View", "Vista spazio colore Lab"),
("Add CMYK Swatch", "Aggiungi campione CMYK"),
("Added swatch", "Campione aggiunto"),
("Bulleted & Numbered Lists", "Elenchi puntati e numerati"),
("Current Page Number", "Numero di pagina corrente"),
("Apply Color (,)", "Applica colore (,)"),
+4
View File
@@ -30,6 +30,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "カット"),
("Copy", "コピー"),
("Paste", "ペースト"),
("RGB Color Space View", "RGB カラースペース表示"),
("Lab Color Space View", "Lab カラースペース表示"),
("Add CMYK Swatch", "CMYK スウォッチを追加"),
("Added swatch", "スウォッチを追加しました"),
("Bulleted & Numbered Lists", "箇条書き"),
("Apply Color (,)", "カラーを適用 (,)"),
("Apply Gradient (.)", "グラデーションを適用 (.)"),
+4
View File
@@ -31,6 +31,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "ამოჭრა"),
("Copy", "კოპირება"),
("Paste", "ჩასმა"),
("RGB Color Space View", "RGB ფერთა სივრცის ხედი"),
("Lab Color Space View", "Lab ფერთა სივრცის ხედი"),
("Add CMYK Swatch", "CMYK ნიმუშის დამატება"),
("Added swatch", "ნიმუში დაემატა"),
("Reverse Path", "ტრაექტორიის შებრუნება"),
("Set Anchor", "საყრდენი წერტილის დაყენება"),
("Clear", "გასუფთავება"),
+4
View File
@@ -31,6 +31,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "Recortar"),
("Copy", "Copiar"),
("Paste", "Colar"),
("RGB Color Space View", "Exibição do espaço de cores RGB"),
("Lab Color Space View", "Exibição do espaço de cores Lab"),
("Add CMYK Swatch", "Adicionar amostra CMYK"),
("Added swatch", "Amostra adicionada"),
("Bulleted & Numbered Lists", "Listas com marcadores e numeradas"),
("Show Hidden Characters", "Mostrar caracteres ocultos"),
("Current Page Number", "Número da página atual"),
+4
View File
@@ -31,6 +31,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "Вирізати"),
("Copy", "Копіювати"),
("Paste", "Вставити"),
("RGB Color Space View", "Подання колірного простору RGB"),
("Lab Color Space View", "Подання колірного простору Lab"),
("Add CMYK Swatch", "Додати зразок CMYK"),
("Added swatch", "Додано зразок"),
("Bulleted & Numbered Lists", "Марковані й нумеровані списки"),
("Current Page Number", "Номер поточної сторінки"),
("Apply Color (,)", "Застосувати колір (,)"),