mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-12 13:24:56 +00:00
Swatches: a row's accessible name says what its marks show
Screen readers got only the swatch name; the painted marks (colour type and mode, can't be edited) were not in it. The row is now named "Cyan, Process, CMYK" or "[Black], Process, CMYK, Cannot be edited" (translated; one new string). Tests find rows by name with names_swatch. Found by CodeRabbit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3cf44071fc
commit
7907cde270
13 files changed
+68
-8
No files matched your search
@@ -342,7 +342,8 @@ mod tests {
|
||||
}
|
||||
|
||||
fn double_click(h: &mut Harness<'static, DesignApp>, label: &str) {
|
||||
let at = h.get_by_label(label).rect().center();
|
||||
// A swatch row's accessible name is its name, then what its marks show.
|
||||
let at = h.get_by(|n| n.label().is_some_and(|l| crate::panels::swatches::names_swatch(&l, label))).rect().center();
|
||||
h.hover_at(at);
|
||||
// Two clicks without the pointer leaving (`drop_at` would end the double click).
|
||||
for pressed in [true, false, true, false] {
|
||||
|
||||
@@ -1010,6 +1010,7 @@ const TABLE: &[(&str, [&str; 5])] = &[
|
||||
],
|
||||
),
|
||||
("Load Styles", ["Formate laden", "Charger les styles", "Cargar estilos", "スタイルを読み込み", "加载样式"]),
|
||||
("Cannot be edited", ["Nicht bearbeitbar", "Non modifiable", "No editable", "編集不可", "不可编辑"]),
|
||||
(
|
||||
"Differs from this document",
|
||||
["Weicht von diesem Dokument ab", "Diffère de ce document", "Difiere de este documento", "このドキュメントと異なります", "与此文档不同"],
|
||||
|
||||
@@ -30,6 +30,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "قص"),
|
||||
("Copy", "نسخ"),
|
||||
("Paste", "لصق"),
|
||||
("Cannot be edited", "غير قابل للتحرير"),
|
||||
("Paste Vector Art", "لصق رسم متجه"),
|
||||
("Baseline Options", "خيارات خط الأساس"),
|
||||
("Fixed Number", "عدد ثابت"),
|
||||
|
||||
@@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Vyjmout"),
|
||||
("Copy", "Kopírovat"),
|
||||
("Paste", "Vložit"),
|
||||
("Cannot be edited", "Nelze upravit"),
|
||||
("Reverse Path", "Obrátit cestu"),
|
||||
("Set Anchor", "Nastavit kotvu"),
|
||||
("Clear", "Vymazat"),
|
||||
|
||||
@@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Ausschneiden"),
|
||||
("Copy", "Kopieren"),
|
||||
("Paste", "Einfügen"),
|
||||
("Cannot be edited", "Nicht bearbeitbar"),
|
||||
("Inches Decimal", "Zoll dezimal"),
|
||||
("Back", "Zurück"),
|
||||
("Warning", "Warnung"),
|
||||
|
||||
@@ -28,6 +28,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Taglia"),
|
||||
("Copy", "Copia"),
|
||||
("Paste", "Incolla"),
|
||||
("Cannot be edited", "Non modificabile"),
|
||||
("Paste Vector Art", "Incolla grafica vettoriale"),
|
||||
("Baseline Options", "Opzioni linea di base"),
|
||||
("Fixed Number", "Numero fisso"),
|
||||
|
||||
@@ -30,6 +30,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "カット"),
|
||||
("Copy", "コピー"),
|
||||
("Paste", "ペースト"),
|
||||
("Cannot be edited", "編集不可"),
|
||||
("Paste Vector Art", "ベクトルアートをペースト"),
|
||||
("Baseline Options", "ベースラインオプション"),
|
||||
("Column Rules", "段落境界線"),
|
||||
|
||||
@@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "ამოჭრა"),
|
||||
("Copy", "კოპირება"),
|
||||
("Paste", "ჩასმა"),
|
||||
("Cannot be edited", "რედაქტირება შეუძლებელია"),
|
||||
("Inches Decimal", "ინჩები (ათობითი)"),
|
||||
("Back", "უკან"),
|
||||
("Warning", "გაფრთხილება"),
|
||||
|
||||
@@ -29,6 +29,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Wytnij"),
|
||||
("Copy", "Kopiuj"),
|
||||
("Paste", "Wklej"),
|
||||
("Cannot be edited", "Nie można edytować"),
|
||||
("Clear", "Wyczyść"),
|
||||
("Duplicate", "Powiel"),
|
||||
("Select All", "Zaznacz wszystko"),
|
||||
|
||||
@@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Recortar"),
|
||||
("Copy", "Copiar"),
|
||||
("Paste", "Colar"),
|
||||
("Cannot be edited", "Não editável"),
|
||||
("Paste Vector Art", "Colar arte vetorial"),
|
||||
("Baseline Options", "Opções de linha de base"),
|
||||
("Fixed Number", "Número fixo"),
|
||||
|
||||
@@ -33,6 +33,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Вырезать"),
|
||||
("Copy", "Копировать"),
|
||||
("Paste", "Вставить"),
|
||||
("Cannot be edited", "Нельзя изменить"),
|
||||
("Clear", "Очистить"),
|
||||
("Duplicate", "Дублировать"),
|
||||
("Select All", "Выделить все"),
|
||||
|
||||
@@ -29,6 +29,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Вирізати"),
|
||||
("Copy", "Копіювати"),
|
||||
("Paste", "Вставити"),
|
||||
("Cannot be edited", "Не можна змінити"),
|
||||
("Paste Vector Art", "Вставити векторну графіку"),
|
||||
("Baseline Options", "Параметри базової лінії"),
|
||||
("Fixed Number", "Фіксована кількість"),
|
||||
|
||||
@@ -281,6 +281,34 @@ fn row_marks(sw: &designcraft_color::swatch::Swatch) -> RowMarks {
|
||||
}
|
||||
}
|
||||
|
||||
/// A row's accessible name: the swatch's name, then what its marks show (colour type, colour
|
||||
/// mode, that it can't be edited). [None] and [Registration] say their mode by name.
|
||||
fn row_label(lang: &str, name: &str, marks: RowMarks) -> String {
|
||||
let tr = |s: &str| crate::i18n::tr(lang, s).to_string();
|
||||
let mut parts = vec![name.to_string()];
|
||||
match marks.kind {
|
||||
Some(ColorType::Process) => parts.push(tr("Process")),
|
||||
Some(ColorType::Spot) => parts.push(tr("Spot")),
|
||||
None => {}
|
||||
}
|
||||
match marks.mode {
|
||||
Some(RowMode::Cmyk) => parts.push(tr("CMYK")),
|
||||
Some(RowMode::Rgb) => parts.push(tr("RGB")),
|
||||
Some(RowMode::Gray) => parts.push(tr("Gray")),
|
||||
Some(RowMode::None | RowMode::Registration) | None => {}
|
||||
}
|
||||
if marks.not_editable {
|
||||
parts.push(tr("Cannot be edited"));
|
||||
}
|
||||
parts.join(", ")
|
||||
}
|
||||
|
||||
/// Whether accessible name `label` is swatch `name`'s row (its name, then its marks).
|
||||
#[cfg(test)]
|
||||
pub(crate) fn names_swatch(label: &str, name: &str) -> bool {
|
||||
label.strip_prefix(name).is_some_and(|rest| rest.is_empty() || rest.starts_with(", "))
|
||||
}
|
||||
|
||||
/// A pencil with a stroke through it: the swatch can't be edited. Drawn in code.
|
||||
fn paint_not_editable(p: &egui::Painter, r: egui::Rect, ink: egui::Color32) {
|
||||
// The pencil: its tip at the lower left, its body up to the right.
|
||||
@@ -379,7 +407,10 @@ fn swatch_row(
|
||||
let t = Tokens::get(ui.ctx());
|
||||
// Rows can be dragged onto the Gradient panel's ramp.
|
||||
let (row, resp) = ui.allocate_exact_size(vec2(ui.available_width(), 22.0), Sense::click_and_drag());
|
||||
resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, true, &sw.name));
|
||||
// What the row's marks show, for screen readers too: "Cyan, Process, CMYK".
|
||||
let marks = row_marks(sw);
|
||||
let spoken = row_label(&app.ui.language, &sw.name, marks);
|
||||
resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, true, &spoken));
|
||||
resp.dnd_set_drag_payload(SwatchDrag(sw.name.clone()));
|
||||
if resp.dragged()
|
||||
&& let Some(p) = ui.ctx().pointer_interact_pos()
|
||||
@@ -401,7 +432,6 @@ fn swatch_row(
|
||||
crate::widgets::paint_chip(ui.painter(), chip, c, g);
|
||||
ui.painter().text(row.min + vec2(28.0 + indent, 11.0), egui::Align2::LEFT_CENTER, &sw.name, egui::FontId::proportional(12.5), t.text);
|
||||
// InDesign's columns on the right: not editable, colour type, colour mode.
|
||||
let marks = row_marks(sw);
|
||||
let col = |k: f32| egui::Rect::from_min_size(egui::pos2(row.max.x - 19.0 - 18.0 * k, row.min.y + 4.5), vec2(13.0, 13.0));
|
||||
if marks.not_editable {
|
||||
paint_not_editable(ui.painter(), col(2.0), t.text_dim);
|
||||
@@ -1205,7 +1235,7 @@ mod gradient_panel_tests {
|
||||
// The thumbnail sits left of the Type row.
|
||||
let ty = h.get_by_label("Type:").rect();
|
||||
let from = egui::pos2(ty.min.x - 21.0, ty.min.y + 12.0);
|
||||
let to = h.get_by_label(label).rect().center();
|
||||
let to = h.get_by(|n| n.label().is_some_and(|l| crate::panels::swatches::names_swatch(&l, label))).rect().center();
|
||||
h.event(Event::PointerMoved(from));
|
||||
h.step();
|
||||
h.event(Event::PointerButton { pos: from, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
|
||||
@@ -1317,14 +1347,14 @@ mod swatches_header_tests {
|
||||
let mut h = harness(app);
|
||||
h.run_steps(3);
|
||||
// Fill in front: a swatch is the fill.
|
||||
h.get_by_label("C=0 M=100 Y=0 K=0").click();
|
||||
h.get_by(|n| n.label().is_some_and(|l| crate::panels::swatches::names_swatch(&l, "C=0 M=100 Y=0 K=0"))).click();
|
||||
h.run_steps(2);
|
||||
assert_eq!(items(h.state())[0].fill.swatch, "C=0 M=100 Y=0 K=0");
|
||||
// The proxy brings the stroke to the front; the next swatch is the stroke.
|
||||
h.get_by_label("Fill").click();
|
||||
h.run_steps(2);
|
||||
assert!(h.state().ui.stroke_front);
|
||||
h.get_by_label("C=100 M=0 Y=0 K=0").click();
|
||||
h.get_by(|n| n.label().is_some_and(|l| crate::panels::swatches::names_swatch(&l, "C=100 M=0 Y=0 K=0"))).click();
|
||||
h.run_steps(2);
|
||||
let it = &items(h.state())[0];
|
||||
assert_eq!((it.fill.swatch.as_str(), it.stroke.swatch.as_str()), ("C=0 M=100 Y=0 K=0", "C=100 M=0 Y=0 K=0"));
|
||||
@@ -1336,7 +1366,7 @@ mod swatches_header_tests {
|
||||
h.run_steps(2);
|
||||
assert!(!h.state().ui.formatting_affects_text);
|
||||
// Delete Swatch removes the swatch last clicked.
|
||||
h.get_by_label("Brand").click();
|
||||
h.get_by(|n| n.label().is_some_and(|l| crate::panels::swatches::names_swatch(&l, "Brand"))).click();
|
||||
h.run_steps(2);
|
||||
h.get_by_label("Delete Swatch").click();
|
||||
h.run_steps(2);
|
||||
@@ -1375,7 +1405,25 @@ mod row_mark_tests {
|
||||
use designcraft_color::swatch::{Swatch, default_swatches};
|
||||
use designcraft_color::{Color, ColorType, SwatchValue};
|
||||
|
||||
use super::{RowMarks, RowMode, row_marks};
|
||||
use super::{RowMarks, RowMode, row_label, row_marks};
|
||||
|
||||
/// A row's accessible name says what its marks show.
|
||||
#[test]
|
||||
fn swatch_rows_say_their_marks() {
|
||||
let all = default_swatches();
|
||||
let label = |name: &str| row_label("", name, row_marks(all.iter().find(|s| s.name == name).unwrap()));
|
||||
assert_eq!(label("[Black]"), "[Black], Process, CMYK, Cannot be edited");
|
||||
assert_eq!(label("[None]"), "[None], Cannot be edited");
|
||||
assert_eq!(label("[Paper]"), "[Paper]");
|
||||
let cyan = all.iter().find(|s| matches!(&s.value, SwatchValue::Color { .. }) && !s.locked).unwrap();
|
||||
let spot = Swatch {
|
||||
name: "Pantone".into(),
|
||||
value: SwatchValue::Color { color: Color::Rgb { r: 1.0, g: 0.0, b: 0.0 }, color_type: ColorType::Spot },
|
||||
locked: false,
|
||||
..cyan.clone()
|
||||
};
|
||||
assert_eq!(row_label("it", &spot.name, row_marks(&spot)), "Pantone, Tinta piatta, RGB");
|
||||
}
|
||||
|
||||
/// The Swatches panel's right-hand columns, as InDesign's: [None], [Registration] and [Black]
|
||||
/// can't be edited, [Paper] can; colours show their type and mode.
|
||||
|
||||
Reference in new issue
Block a user