Color Picker: reopens on the component it was left on

The picker opened with R selected every time, so anyone who works in H (the hue slider with the saturation/brightness field) chose it again on every open. The component now lives in the UI preferences (colorPickerComponent, default r, so existing preference files and openers that name a component behave as before) and the picker reads it on open and writes it when a radio button is clicked, as Photoshop's picker keeps its last mode.

Verified locally on a288a87b: fmt, clippy, the picker's tests; tried on macOS.
This commit is contained in:
Paul Gemignani committed 2026-11-02 08:44:19 -08:00
1 parent a288a87bf6
commit 885793f763
3 files changed
+43

No files matched your search

+3
View File
@@ -9021,6 +9021,8 @@ fn color_picker(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
let tr = |s: &str| crate::i18n::tr(&lang, s).to_string();
let old = d.s("hex");
d.fields.entry("old").or_insert_with(|| json!(old));
// The component the picker was last left on, unless the opener chose one.
d.fields.entry("component").or_insert_with(|| json!(app.ui.color_picker_component));
let comp = PickComp::from_key(&d.s("component"));
let mut p = picked_of(d);
let before = p;
@@ -9215,6 +9217,7 @@ fn color_picker(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
}
if comp_now != comp {
d.fields.insert("component".into(), json!(comp_now.key()));
app.ui.color_picker_component = comp_now.key().to_string();
}
// The hex code, and the eyedropper beside it.
let cy = area.min.y + at::ROWS[7];
+36
View File
@@ -3698,6 +3698,42 @@ fn the_color_picker_picks_on_its_field_and_slider_and_adds_a_cmyk_swatch() {
assert_eq!(st.doc.resolve_color(&fill, 1.0).unwrap().to_hex(), "#7c1315");
}
/// The Color Picker reopens on the component it was left on, and the choice survives the
/// preferences file; an opener that names a component still gets it.
#[test]
fn the_color_picker_remembers_its_component() {
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();
assert_eq!(app.ui.color_picker_component, "r", "R at first, as before");
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);
assert_eq!(h.state().app.ui.dialog.as_ref().unwrap().s("component"), "r");
h.get_by_role_and_label(egui::accesskit::Role::RadioButton, "H:").click();
h.run_steps(2);
assert_eq!(h.state().app.ui.dialog.as_ref().unwrap().s("component"), "h");
assert_eq!(h.state().app.ui.color_picker_component, "h", "the choice is kept outside the dialog");
h.get_by_label("Cancel").click();
h.run_steps(2);
assert!(h.state().app.ui.dialog.is_none());
// Reopened: H, not R.
h.state_mut().app.ui.dialog = Some(Dialog::new("colorPicker", json!({"target": "fill", "hex": "#7c1315", "text": false})));
h.run_steps(3);
assert_eq!(h.state().app.ui.dialog.as_ref().unwrap().s("component"), "h");
h.get_by_label("Cancel").click();
h.run_steps(2);
// An opener's own choice wins.
h.state_mut().app.ui.dialog = Some(Dialog::new("colorPicker", json!({"target": "fill", "hex": "#7c1315", "text": false, "component": "l"})));
h.run_steps(3);
assert_eq!(h.state().app.ui.dialog.as_ref().unwrap().s("component"), "l");
// Saved with the preferences, and read back; a file without it is R.
let saved = serde_json::to_vec(&h.state().app.ui).unwrap();
assert_eq!(crate::UiState::from_preferences_json(&saved).unwrap().color_picker_component, "h");
assert_eq!(crate::UiState::from_preferences_json(b"{}").unwrap().color_picker_component, "r");
}
#[test]
fn create_guides_dialog_covers_the_spread_in_view() {
let mut app = facing_app(6);
+4
View File
@@ -226,6 +226,9 @@ pub struct UiState {
pub story_editor_size: f32,
/// View › Story Editor › Show/Hide Depth Ruler.
pub story_editor_depth_ruler: bool,
/// The Color Picker's component (its radio button, as `PickComp::key`: `h`, `s`, `b`, `l`,
/// `la`, `lb`, `r`, `g`, `bl`), kept between openings and sessions, as Photoshop's picker is.
pub color_picker_component: String,
/// Preferences › General › Interface Language: supported codes are listed in `i18n::LANGUAGES`.
pub language: String,
/// Edit › Transparency Flattener Presets: "" (none), "high", "medium" or "low" for PDF export.
@@ -399,6 +402,7 @@ impl Default for UiState {
show_full_menus: false,
story_editor_size: 14.0,
story_editor_depth_ruler: true,
color_picker_component: "r".into(),
scripts: vec![
("Number the pages".into(), "# A page-number frame at the bottom of the current page.\nframe.create {\"rect\": [288, 740, 324, 760], \"content\": \"text\", \"text\": \"\"}\ntext.select {\"story\": \"$0.story\", \"anchor\": 0, \"focus\": 0}\ntext.insert {\"text\": \"\\ue000\", \"raw\": true}".into()),
("Two-column grid".into(), "# Four text frames in a 2 × 2 grid.\nframe.grid {\"rect\": [36, 36, 576, 756], \"cols\": 2, \"rows\": 2, \"gutter\": 12}".into()),