Merge pull request #2136 from dotCooCoo/feat/color-panel-options-command

Color panel: Hide Options comes back after a restart, and `ui.colorPanel` sets the mode and options
This commit is contained in:
BFlat
2026-10-16 04:19:47 +07:00
committed by GitHub
19 changed files with 169 additions and 9 deletions
+2
View File
@@ -1344,6 +1344,7 @@ lighting Light Světlo
Roman Baseline Latinková účaří
Select a text object to edit its character attributes. Vyberte textový objekt, abyste mohli upravit atributy jeho znaků.
Show Options Zobrazit možnosti
Color Panel Options Možnosti panelu Barva
Small Caps Kapitálky
Small Caps (on the roadmap) Kapitálky (v plánu)
Standard Vertical Roman Alignment Standardní svislé zarovnání latinky
@@ -3910,6 +3911,7 @@ imageTrace Outline Obrys
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` musí být mezi {min} a {max} (zadáno {x})
@msg `{key}` must be one of {_1} `{key}` musí být jedno z {_1}
@msg `{key}` must be true or false `{key}` musí být true nebo false
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` musí být grayscale, rgb, hsb, cmyk nebo websafe
@msg `{k}` must be [x, y] `{k}` musí být [x, y]
@msg `{name}` has no options `{name}` nemá žádné možnosti
@msg `{name}` is a built-in workspace; choose another name `{name}` je vestavěný pracovní prostor; zvolte jiný název
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2694,6 +2694,7 @@
Show Modified Geänderte einblenden
Show Note Notiz einblenden
Show Options Optionen einblenden
Color Panel Options Optionen für das Farbbedienfeld
Show Package Paket anzeigen
Show Panel Bedienfeld einblenden
Show Pattern Swatches Musterfelder einblenden
@@ -3516,6 +3517,7 @@ theme Appearance Erscheinungsbild
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` muss zwischen {min} und {max} liegen (erhalten: {x})
@msg `{key}` must be one of {_1} `{key}` muss einer der folgenden Werte sein: {_1}
@msg `{key}` must be true or false `{key}` muss true oder false sein
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` muss grayscale, rgb, hsb, cmyk oder websafe sein
@msg `{k}` must be [x, y] `{k}` muss [x, y] sein
@msg `{name}` has no options `{name}` hat keine Optionen
@msg `{name}` is a built-in workspace; choose another name `{name}` ist ein integrierter Arbeitsbereich; wählen Sie einen anderen Namen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2743,6 +2743,7 @@
Show Modified Mostrar modificados
Show Note Mostrar nota
Show Options Mostrar opciones
Color Panel Options Opciones del panel Color
Show Package Mostrar paquete
Show Panel Mostrar panel
Show Pattern Swatches Mostrar muestras de motivo
@@ -3614,6 +3615,7 @@ axis Both Ambos
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` debe estar entre {min} y {max} (se recibió {x})
@msg `{key}` must be one of {_1} `{key}` debe ser uno de estos: {_1}
@msg `{key}` must be true or false `{key}` debe ser true o false
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` debe ser grayscale, rgb, hsb, cmyk o websafe
@msg `{k}` must be [x, y] `{k}` debe ser [x, y]
@msg `{name}` has no options `{name}` no tiene opciones
@msg `{name}` is a built-in workspace; choose another name `{name}` es un espacio de trabajo integrado; elige otro nombre
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2740,6 +2740,7 @@
Show Modified Afficher les éléments modifiés
Show Note Afficher la note
Show Options Afficher les options
Color Panel Options Options du panneau Couleur
Show Package Afficher le dossier d’assemblage
Show Panel Afficher le panneau
Show Pattern Swatches Afficher les nuances de motif
@@ -3611,6 +3612,7 @@ axis Both Les deux
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` doit être compris entre {min} et {max} (reçu : {x})
@msg `{key}` must be one of {_1} `{key}` doit valoir l’une de ces valeurs : {_1}
@msg `{key}` must be true or false `{key}` doit valoir true ou false
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` doit valoir grayscale, rgb, hsb, cmyk ou websafe
@msg `{k}` must be [x, y] `{k}` doit être [x, y]
@msg `{name}` has no options `{name}` n’a pas d’options
@msg `{name}` is a built-in workspace; choose another name `{name}` est un espace de travail intégré ; choisissez un autre nom
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3325,6 +3325,7 @@
Show Modified Módosultak megjelenítése
Show Note Megjegyzés megjelenítése
Show Options Beállítások megjelenítése
Color Panel Options Szín panel beállításai
Show Package Csomag megjelenítése
Show Panel Panel megjelenítése
Show Pattern Swatches Mintázat-színminták megjelenítése
@@ -4103,6 +4104,7 @@ imageTrace Outline Körvonal
@msg `{key}` must be between {min} and {max} (got {x}) `{key}`: {min} és {max} közötti érték kell (kapott érték: {x})
@msg `{key}` must be one of {_1} `{key}`: ezek egyike kell: {_1}
@msg `{key}` must be true or false `{key}`: true vagy false kell
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode`: grayscale, rgb, hsb, cmyk vagy websafe kell
@msg `{k}` must be [x, y] `{k}`: [x, y] kell
@msg `{name}` has no options ennek nincsenek beállításai: `{name}`
@msg `{name}` is a built-in workspace; choose another name beépített munkaterület: `{name}`; válassz másik nevet
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2745,6 +2745,7 @@
Show Modified Mostra modificati
Show Note Mostra nota
Show Options Mostra opzioni
Color Panel Options Opzioni pannello Colore
Show Package Mostra pacchetto
Show Panel Mostra pannello
Show Pattern Swatches Mostra campioni motivo
@@ -3616,6 +3617,7 @@ axis Both Entrambi
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` deve essere compreso tra {min} e {max} (ricevuto {x})
@msg `{key}` must be one of {_1} `{key}` deve essere uno dei seguenti: {_1}
@msg `{key}` must be true or false `{key}` deve essere true o false
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` deve essere grayscale, rgb, hsb, cmyk o websafe
@msg `{k}` must be [x, y] `{k}` deve essere [x, y]
@msg `{name}` has no options `{name}` non ha opzioni
@msg `{name}` is a built-in workspace; choose another name `{name}` è un'area di lavoro integrata; scegli un altro nome
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2564,6 +2564,7 @@
Show List View リスト表示
Show Note メモを表示
Show Options オプションを表示
Color Panel Options カラーパネルオプション
Show Package パッケージを表示
Show Panel パネルを表示
Show Perspective Grid 遠近グリッドを表示
@@ -4013,6 +4014,7 @@ theme Appearance 外観
@msg Opened {url} {url} を開きました
@msg No saved views (View → New View…) 保存した表示はありません (表示 → 新規表示…)
@msg `command` must be a command whose menu item opens a dialog (see ui.menuDialog) `command` には、メニュー項目がダイアログを開くコマンドを指定してください (ui.menuDialog を参照)
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` には grayscale、rgb、hsb、cmyk、websafe のいずれかを指定してください
@msg `{key}` must be true or false `{key}` には true または false を指定してください
@msg brightness must be dark|mediumDark|mediumLight|light brightness には dark|mediumDark|mediumLight|light のいずれかを指定してください
@msg index must be a number or first, previous, next or last index には数値、または first、previous、next、last のいずれかを指定してください
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2558,6 +2558,7 @@
Show List View 목록 보기 표시
Show Note 메모 표시
Show Options 옵션 표시
Color Panel Options 색상 패널 옵션
Show Package 패키지 표시
Show Panel 패널 표시
Show Perspective Grid 원근 격자 표시
@@ -3991,6 +3992,7 @@ theme Appearance 외관
@msg Opened {url} {url}을(를) 열었습니다
@msg No saved views (View → New View…) 저장된 보기가 없습니다(보기 → 새 보기…)
@msg `command` must be a command whose menu item opens a dialog (see ui.menuDialog) `command`는 메뉴 항목이 대화상자를 여는 명령이어야 합니다(ui.menuDialog 참조)
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode`은(는) grayscale, rgb, hsb, cmyk, websafe 중 하나여야 합니다
@msg `{key}` must be true or false `{key}`은(는) true 또는 false여야 합니다
@msg brightness must be dark|mediumDark|mediumLight|light brightness는 dark|mediumDark|mediumLight|light 중 하나여야 합니다
@msg index must be a number or first, previous, next or last index는 숫자이거나 first, previous, next, last 중 하나여야 합니다
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1256,6 +1256,7 @@ lighting Light Światło
Roman Baseline Linia bazowa łacińska
Select a text object to edit its character attributes. Zaznacz obiekt tekstowy, aby edytować atrybuty znaków.
Show Options Pokaż opcje
Color Panel Options Opcje panelu Kolor
Small Caps Kapitaliki
Small Caps (on the roadmap) Kapitaliki (w planach)
Standard Vertical Roman Alignment Standardowe pionowe wyrównanie łacińskie
@@ -3724,6 +3725,7 @@ imageTrace Outline Kontur
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` musi mieścić się w zakresie od {min} do {max} (podano {x})
@msg `{key}` must be one of {_1} `{key}` musi mieć jedną z wartości: {_1}
@msg `{key}` must be true or false `{key}` musi mieć wartość true lub false
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` musi mieć wartość grayscale, rgb, hsb, cmyk lub websafe
@msg `{k}` must be [x, y] `{k}` musi mieć postać [x, y]
@msg `{name}` has no options `{name}` nie ma opcji
@msg `{name}` is a built-in workspace; choose another name `{name}` to wbudowany obszar roboczy; wybierz inną nazwę
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2741,6 +2741,7 @@
Show Modified Показать изменённые
Show Note Показать примечание
Show Options Показать параметры
Color Panel Options Параметры панели «Цвет»
Show Package Показать пакет
Show Panel Показать панель
Show Pattern Swatches Показать образцы узоров
@@ -3612,6 +3613,7 @@ axis Both Оба
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` должно быть от {min} до {max} (получено {x})
@msg `{key}` must be one of {_1} `{key}` должно быть одним из: {_1}
@msg `{key}` must be true or false `{key}` должно быть true или false
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` должно быть grayscale, rgb, hsb, cmyk или websafe
@msg `{k}` must be [x, y] `{k}` должно быть [x, y]
@msg `{name}` has no options `{name}` не имеет параметров
@msg `{name}` is a built-in workspace; choose another name `{name}` — встроенная рабочая среда; выберите другое имя
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2737,6 +2737,7 @@
Show Modified Показати змінені
Show Note Показати примітку
Show Options Показати параметри
Color Panel Options Параметри панелі «Колір»
Show Package Показати пакет
Show Panel Показати панель
Show Pattern Swatches Показати зразки візерунків
@@ -3600,6 +3601,7 @@ axis Both Обидві
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` має бути від {min} до {max} (отримано {x})
@msg `{key}` must be one of {_1} `{key}` має бути одним з: {_1}
@msg `{key}` must be true or false `{key}` має бути true або false
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` має бути grayscale, rgb, hsb, cmyk або websafe
@msg `{k}` must be [x, y] `{k}` має бути [x, y]
@msg `{name}` has no options `{name}` не має параметрів
@msg `{name}` is a built-in workspace; choose another name `{name}` - вбудоване робоче середовище; виберіть інше ім’я
Can't render this file because it contains an unexpected character in line 715 and column 40.
+2
View File
@@ -3048,6 +3048,7 @@
Show Modified Hiện đã sửa đổi
Show Note Hiện ghi chú
Show Options Hiện tùy chọn
Color Panel Options Tùy chọn bảng màu
Show Package Hiện gói
Show Panel Hiện bảng
Show Pattern Swatches Hiện mẫu họa tiết
@@ -3733,6 +3734,7 @@ theme Appearance Giao diện
@msg `{key}` must be between {min} and {max} (got {x}) `{key}` phải nằm trong khoảng {min} đến {max} (nhận được {x})
@msg `{key}` must be one of {_1} `{key}` phải là một trong {_1}
@msg `{key}` must be true or false `{key}` phải là true hoặc false
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` phải là grayscale, rgb, hsb, cmyk hoặc websafe
@msg `{k}` must be [x, y] `{k}` phải là [x, y]
@msg `{name}` has no options `{name}` không có tùy chọn
@msg `{name}` is a built-in workspace; choose another name `{name}` là không gian làm việc tích hợp sẵn; hãy chọn tên khác
Can't render this file because it contains an unexpected character in line 3706 and column 10.
+1
View File
@@ -2594,6 +2594,7 @@
Show Modified 显示已修改
Show Note 显示注解
Show Options 显示选项
Color Panel Options 颜色面板选项
Show Package 显示打包
Show Panel 显示面板
Show Pattern Swatches 显示图案色板
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2723,6 +2723,7 @@
Show Modified 顯示已修改
Show Note 顯示註解
Show Options 顯示選項
Color Panel Options 顏色面板選項
Show Package 顯示封裝
Show Panel 顯示面板
Show Pattern Swatches 顯示圖樣色票
Can't render this file because it contains an unexpected character in line 6 and column 47.
+7
View File
@@ -620,6 +620,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
"",
"{library: swatch library id or name (see swatch.library.list) | \"document\" (the document's swatches) | \"\" or null (no limit)} limit the Color Guide panel's colours to that library: every harmony colour and variation snaps to its nearest colour, as color.harmony {limitTo} answers (ui.inspect: ui.color_guide_limit) → {limitTo, name}",
),
(
"ui.colorPanel",
"Color Panel Options",
"",
"{mode?: grayscale|rgb|hsb|cmyk|websafe, options?: bool} pick the Color panel's mode as its panel menu does (the active color converts to the mode's model as one undo step), and Show Options (true) or Hide Options (false); both are kept with the preferences (ui.inspect: ui.color_mode, ui.color_hide_options) → {mode (null: none picked yet), options, shown: the mode the panel shows the color in, or tint for a tint of a global or spot swatch}",
),
(
"ui.savePdfDialog",
"Save PDF Dialog",
@@ -1425,6 +1431,7 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
"ui.widthPointEdit" => crate::dialogs::width_point::open(app, p),
"ui.corners" => crate::dialogs::corners::open(app, p),
"ui.colorGuideLimit" => crate::panels::color_guide::set_limit(app, p),
"ui.colorPanel" => crate::panels::color::command(app, p),
"ui.savePdfDialog" => crate::dialogs::open_save_pdf(app, p),
"file.exportAs" if s("path").is_none() => {
crate::dialogs::open_export_as(app, s("format").as_deref(), p);
+123 -9
View File
@@ -9,10 +9,11 @@
//! model and shows the defaults for new art; the app keeps it between sessions). Alt-clicking the
//! spectrum or a chip paints the inactive proxy; Shift-clicking the spectrum cycles the modes;
//! Shift-dragging a slider moves the others in tandem; RGB and HSB show the out-of-gamut warning;
//! Hide Options leaves just the proxy and the spectrum.
//! Hide Options leaves just the proxy and the spectrum (the app keeps it between sessions too).
//! `ui.colorPanel` picks a mode and shows or hides the options as the panel menu does.
use egui::{Color32, Rect, Sense, Stroke, StrokeKind, Ui, vec2};
use serde_json::json;
use serde_json::{Value, json};
use vectorcraft_color::{Color, GradientKind, Paint};
use super::{active_paint, color_json, live_run, pstate, set_pstate};
@@ -251,8 +252,21 @@ pub(crate) fn gamut_fix(app: &mut VectorcraftApp, ctx: &egui::Context, key: &str
/// Serde reader for the saved panel mode ([`crate::UiState::color_mode`]): a menu mode, else none,
/// so an unknown value doesn't lose the rest of the preferences.
pub fn deserialize_mode<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Option<Mode>, D::Error> {
let v = <serde_json::Value as serde::Deserialize>::deserialize(d)?;
Ok(Mode::ALL.into_iter().find(|m| serde_json::to_value(m).is_ok_and(|id| id == v)))
let v = <Value as serde::Deserialize>::deserialize(d)?;
Ok(menu_mode(&v))
}
/// The menu mode whose id (`"grayscale"`, `"rgb"`, `"hsb"`, `"cmyk"`, `"websafe"`) is `v`.
fn menu_mode(v: &Value) -> Option<Mode> {
Mode::ALL.into_iter().find(|m| serde_json::to_value(m).is_ok_and(|id| id == *v))
}
/// Serde reader for the saved Hide Options ([`crate::UiState::color_hide_options`]): `true` hides
/// the options and any other value shows them, so an unknown value doesn't lose the rest of the
/// preferences.
pub fn deserialize_hide_options<'de, D: serde::Deserializer<'de>>(d: D) -> Result<bool, D::Error> {
let v = <Value as serde::Deserialize>::deserialize(d)?;
Ok(v.as_bool().unwrap_or_default())
}
/// The mode the panel shows `color` in: with nothing selected (the defaults for new art) the mode
@@ -366,7 +380,13 @@ impl Target {
/// The active proxy's colour or gradient stop; no colour for None, patterns and a "?" proxy.
fn target(app: &VectorcraftApp, ui: &Ui) -> Target {
if super::active_mixed(app, ui.ctx()) {
target_with(app, super::active_mixed(app, ui.ctx()))
}
/// [`target`] with `mixed` for a "?" active proxy: the panel reads it from its per-frame cache,
/// `ui.colorPanel` from the session.
fn target_with(app: &VectorcraftApp, mixed: bool) -> Target {
if mixed {
return Target::Paint(None, None);
}
let p = active_paint(app);
@@ -439,6 +459,32 @@ fn set_mode(app: &mut VectorcraftApp, tgt: &Target, mode: Mode) {
}
}
/// `ui.colorPanel {mode?, options?}`: pick a mode as the panel menu does ([`set_mode`]), and Show
/// Options (true) or Hide Options (false). → {mode, options, shown}: the mode picked last (null:
/// none yet), whether the options show, and the mode the panel shows the color in (`tint` for a
/// tint of a global or spot swatch).
pub(crate) fn command(app: &mut VectorcraftApp, p: &Value) -> Result<Value, String> {
let mode = match p.get("mode") {
None | Some(Value::Null) => None,
Some(v) => Some(menu_mode(v).ok_or("`mode` must be grayscale, rgb, hsb, cmyk or websafe")?),
};
let options = crate::menus::opt_bool(p, "options")?;
let (fill, stroke) = app.session.proxy_mixed();
let mixed = if app.session.fill_active { fill } else { stroke };
if let Some(m) = mode {
set_mode(app, &target_with(app, mixed), m);
}
if let Some(shown) = options {
app.ui.color_hide_options = !shown;
}
let tgt = target_with(app, mixed);
let shown = match tgt.tint() {
Some(_) => json!("tint"),
None => json!(shown_mode(app, tgt.color().as_ref())),
};
Ok(json!({"mode": app.ui.color_mode, "options": !app.ui.color_hide_options, "shown": shown}))
}
/// Tint mode's rows: the swatch's chip and name, and the T slider and field. Returns a new tint
/// (0..1) and its phase.
fn tint_rows(ui: &mut Ui, tint: &Tint, slider_w: f32, field_w: f32) -> Option<(f32, Live)> {
@@ -494,7 +540,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
let mode = shown_mode(app, color.as_ref());
let alt = super::alt_held(ui);
// Hide Options: just the proxy and the spectrum.
if pstate::<bool>(ui.ctx(), "color-hide-options") {
if app.ui.color_hide_options {
let hit = ui
.horizontal(|ui| {
super::proxy(app, ui, 40.0);
@@ -650,9 +696,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
}
pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
let hidden: bool = pstate(ui.ctx(), "color-hide-options");
let hidden = app.ui.color_hide_options;
if menu_item(ui, if hidden { tl!("Show Options") } else { tl!("Hide Options") }, true, false) {
set_pstate(ui.ctx(), "color-hide-options", !hidden);
command(app, &json!({ "options": hidden })).ok();
}
ui.separator();
let tgt = target(app, ui);
@@ -1047,9 +1093,77 @@ mod tests {
let ctx = egui::Context::default();
let (spectrum, full) = frame(&mut app, &ctx, vec![], egui::Modifiers::NONE);
assert!(spectrum.is_some());
set_pstate(&ctx, "color-hide-options", true);
assert_eq!(app.run("ui.colorPanel", json!({"options": false})).unwrap()["options"], false);
let (_, hidden) = frame(&mut app, &ctx, vec![], egui::Modifiers::NONE);
assert!(hidden <= 44.0 && full > 150.0, "{hidden} / {full}");
app.run("ui.colorPanel", json!({"options": true})).unwrap();
assert_eq!(frame(&mut app, &ctx, vec![], egui::Modifiers::NONE).1, full);
}
/// Hide Options is kept with the UI state, which the app saves with its preferences. A saved
/// value that isn't true or false reads as Show Options, and the rest of the state still loads.
#[test]
fn hide_options_comes_back_after_a_restart() {
let mut app = app();
app.run("ui.colorPanel", json!({"options": false})).unwrap();
let saved: crate::UiState = serde_json::from_value(serde_json::to_value(&app.ui).unwrap()).unwrap();
assert!(saved.color_hide_options);
assert_eq!(crate::control::inspect(&app, &egui::Context::default())["ui"]["color_hide_options"], true);
for v in [json!(7), json!("hide"), json!({}), Value::Null] {
let ui: crate::UiState = serde_json::from_value(json!({"color_hide_options": v, "color_mode": "future", "place_link": false})).unwrap();
assert_eq!((ui.color_hide_options, ui.color_mode, ui.place_link), (false, None, false), "{v}");
}
}
/// `ui.colorPanel {mode}` picks a mode as the panel menu does: HSB shows an RGB fill as H, S and
/// B, and CMYK converts it as one undo step.
#[test]
fn ui_color_panel_picks_a_mode_as_the_menu_does() {
let mut app = app();
let ctx = egui::Context::default();
app.run("paint.setFill", json!({"color": "#336699"})).unwrap();
let r = app.run("ui.colorPanel", json!({"mode": "hsb"})).unwrap();
assert_eq!(r, json!({"mode": "hsb", "options": true, "shown": "hsb"}));
assert_eq!(slider_labels(&mut app, &ctx), ["H", "S", "B"]);
assert_eq!(crate::control::inspect(&app, &ctx)["ui"]["color_mode"], "hsb");
assert_eq!(paints_hex(&app).0, "#336699", "HSB keeps the RGB color");
assert_eq!(app.run("ui.colorPanel", json!({"mode": "cmyk"})).unwrap()["shown"], "cmyk");
assert!(matches!(crate::panels::current_paints(&app).0.color(), Some(Color::Cmyk { .. })), "CMYK converts the color");
app.run("edit.undo", json!({})).unwrap();
assert!(matches!(crate::panels::current_paints(&app).0.color(), Some(Color::Rgb { .. })), "undo restores it");
assert_eq!(paints_hex(&app).0, "#336699");
// `{}` reads the settings: CMYK stays picked, and the RGB fill shows in its own model.
assert_eq!(app.run("ui.colorPanel", json!({})).unwrap(), json!({"mode": "cmyk", "options": true, "shown": "rgb"}));
// A tint of a global swatch shows Tint mode.
app.run("swatch.new", json!({"name": "Ink", "color": {"c": 0, "m": 1, "y": 0, "k": 0}, "spot": true})).unwrap();
app.run("paint.setFill", json!({"swatch": "Ink", "tint": 50})).unwrap();
assert_eq!(app.run("ui.colorPanel", json!({})).unwrap()["shown"], "tint");
}
/// A mode the panel menu doesn't list, Lab included, and an options value that isn't true or
/// false answer an error, and the command then changes nothing.
#[test]
fn ui_color_panel_answers_an_error_for_a_mode_off_the_menu() {
let mut app = app();
for m in [json!("lab"), json!("oklch"), json!("hsl"), json!("future"), json!("RGB"), json!(3)] {
let e = app.run("ui.colorPanel", json!({"mode": m})).unwrap_err();
assert_eq!(e, "`mode` must be grayscale, rgb, hsb, cmyk or websafe", "{m}");
}
assert_eq!(app.run("ui.colorPanel", json!({"mode": "cmyk", "options": "no"})).unwrap_err(), "`options` must be true or false");
assert_eq!((app.ui.color_mode, app.ui.color_hide_options), (None, false));
}
/// `ui.colorPanel` answers every junk parameter, with and without a document, without a panic.
#[test]
fn ui_color_panel_answers_junk_params_without_a_panic() {
let doc = crate::menus::UI_COMMANDS.iter().find(|c| c.0 == "ui.colorPanel").expect("registered").3;
let mut app = app();
app.run("paint.setFill", json!({"color": "#336699"})).unwrap();
for p in vectorcraft_testkit::strategies::junk_params(doc, "/nonexistent").into_iter().chain(vectorcraft_testkit::strategies::junk_values()) {
let _ = app.run("ui.colorPanel", p);
}
let mut app = VectorcraftApp::new(Session::new(), Default::default());
assert_eq!(app.run("ui.colorPanel", json!({"mode": "cmyk"})).unwrap()["mode"], "cmyk");
}
#[test]
+5
View File
@@ -380,6 +380,10 @@ pub struct UiState {
/// shows the defaults for new art ([`crate::panels::color::deserialize_mode`]).
#[serde(default, deserialize_with = "crate::panels::color::deserialize_mode")]
pub color_mode: Option<crate::panels::color::Mode>,
/// Color panel: Hide Options, which leaves just the proxy and the spectrum (`ui.colorPanel`,
/// [`crate::panels::color::deserialize_hide_options`]).
#[serde(default, deserialize_with = "crate::panels::color::deserialize_hide_options")]
pub color_hide_options: bool,
/// The SVG Options chosen last (`svg` object of the export/save commands; null: never used).
#[serde(default)]
pub svg_options: Value,
@@ -715,6 +719,7 @@ impl Default for UiState {
color_guide_limit: String::new(),
align_to: Default::default(),
color_mode: None,
color_hide_options: false,
svg_options: Value::Null,
place_link: true,
dialog_file: None,
+7
View File
@@ -207,6 +207,13 @@ Edit or Apply Colors runs `ui.recolorDialog {colors, library?}` with the harmony
recolours as usual; with nothing selected the colours themselves are the rows and `ui.dialog.confirm` saves them as a
new colour group (`swatch.newGroup`, named by the `groupName` field).
Color panel options: `ui.colorPanel {mode?, options?}` picks the panel's mode as its panel menu does (`grayscale`,
`rgb`, `hsb`, `cmyk` or `websafe`; the active color converts to the mode's model as one undo step) and shows
(`options: true`) or hides (`false`) the options. It answers `{mode, options, shown}`: `mode` is null until a mode is
picked, and `shown` is the mode the panel shows the color in, or `tint` for a tint of a global or spot swatch.
Another mode, `lab` included, answers an error. The desktop app keeps both settings with its preferences, and
`ui.inspect` reports them as `ui.color_mode` and `ui.color_hide_options`.
Color Themes panel (`window.panel {panel: "colorThemes"}`): its Create tab makes a theme on a harmony wheel and its
My Themes tab lists the saved themes; every action runs a command (`colorTheme.save`, `colorTheme.delete`,
`colorTheme.addToSwatches`, `swatch.newGroup` for the theme being made), and `colorTheme.list` reads the library.
File diff suppressed because one or more lines are too long