diff --git a/crates/ui-egui/src/panels/color_wheel.rs b/crates/ui-egui/src/panels/color_wheel.rs index f466584c0..7011c6c70 100644 --- a/crates/ui-egui/src/panels/color_wheel.rs +++ b/crates/ui-egui/src/panels/color_wheel.rs @@ -5,7 +5,7 @@ //! proxy and Shift steps the ring by 45°. A pick is display sRGB, converted into the working RGB //! space: an RGB, CMYK or Lab color keeps its model, and a Gray color or a tint of a global swatch //! becomes a process color in the document's color mode. The HSB picked last stays while the -//! objects, the proxy and the color are unchanged, so a gray keeps its hue. +//! objects, the proxy, the color and the color settings are unchanged, so a gray keeps its hue. use egui::{Ui, UiBuilder}; use vectorcraft_color::cms::{self, Cms}; @@ -20,7 +20,7 @@ use crate::widgets::{self, FieldAxis, Live}; pub const ID: &str = "colorWheel"; /// The widest the wheel is drawn. const MAX_WHEEL: f32 = 200.0; -/// Panel state: the HSB picked last and its [`Owner`]. +/// Panel state: the HSB picked last, its [`Owner`] and the display sRGB of the color it stored. const MEMORY: &str = "color-wheel-hsb"; /// What the HSB picked last belongs to: the document, the selected objects, the proxy, the @@ -59,6 +59,13 @@ pub(crate) fn display_hsb(cms: &Cms, c: &Color) -> [f32; 3] { rgb_to_hsb(cms.display_rgb(c)) } +/// The HSB the wheel shows for `c` through `cms` when `hsb` was picked last and `c` then displayed +/// as the sRGB `shown`: `hsb` while `c` still displays as `shown`, else `c`'s own display HSB (after +/// a change of the color settings or the document's profile). +pub(crate) fn remembered_hsb(cms: &Cms, c: &Color, shown: [f32; 3], hsb: [f32; 3]) -> [f32; 3] { + if cms.display_rgb(c) == shown { hsb } else { display_hsb(cms, c) } +} + /// The color a pick of display HSB `hsb` stores in place of `was`, through `cms`. The picked sRGB /// goes into the working RGB space. An RGB color stays RGB, and a CMYK or Lab color converts to its /// model with the color settings. Anything else (no color, a Gray color, a tint of a global swatch) @@ -104,8 +111,8 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { let cms = cms::active(); // With None, a pattern or a "?" proxy the wheel shows the last solid color without markers, // and a click, an arrow key or a screen reader's action applies the picked color. - let hsb = match (color, pstate::>(ui.ctx(), MEMORY)) { - (Some(c), Some((owner, hsb))) if owner == Owner::of(app, &tgt, c) => hsb, + let hsb = match (color, pstate::>(ui.ctx(), MEMORY)) { + (Some(c), Some((owner, shown, hsb))) if owner == Owner::of(app, &tgt, c) => remembered_hsb(&cms, &c, shown, hsb), (c, _) => display_hsb(&cms, &c.unwrap_or(app.session.last_solid)), }; let marked = color.is_some(); @@ -144,7 +151,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { let cmyk_doc = app.session.active().is_some_and(|d| d.doc.color_mode == ColorMode::Cmyk); let c = stored(&cms, hsb, replaced(app, &tgt, behind).as_ref(), cmyk_doc); if !behind { - set_pstate(ui.ctx(), MEMORY, Some((Owner::of(app, &tgt, c), hsb))); + set_pstate(ui.ctx(), MEMORY, Some((Owner::of(app, &tgt, c), cms.display_rgb(&c), hsb))); } apply(app, &tgt, c, phase, behind); } diff --git a/crates/ui-egui/src/panels/tests_color_wheel.rs b/crates/ui-egui/src/panels/tests_color_wheel.rs index a3dfa3fba..83d78a5e3 100644 --- a/crates/ui-egui/src/panels/tests_color_wheel.rs +++ b/crates/ui-egui/src/panels/tests_color_wheel.rs @@ -11,7 +11,7 @@ use vectorcraft_color::cms::{self, Cms, ColorSettings, delta_e2000}; use vectorcraft_color::{Color, Paint}; use vectorcraft_engine::Session; -use super::color_wheel::{ID, display_hsb, show, stored}; +use super::color_wheel::{ID, display_hsb, remembered_hsb, show, stored}; use crate::VectorcraftApp; /// An app with a selected rectangle filled with `fill`. @@ -308,6 +308,21 @@ fn the_remembered_hue_stays_with_its_object() { assert!(!(1.0..359.0).contains(&hue), "the white object's own hue is 0°, not 120°: {hue}"); } +/// The remembered HSB holds only while the color displays as it did when picked: in another +/// working RGB space the wheel shows the color's own HSB there. +#[test] +fn the_remembered_hsb_follows_a_change_of_working_space() { + let srgb = Cms::default(); + let p3 = Cms::new(&ColorSettings { rgb: cms::DISPLAY_P3.into(), ..ColorSettings::default() }).unwrap(); + let c = Color::rgb(0.5, 0.3, 0.2); + let picked = [25.0, 0.6, 0.5]; + let shown = srgb.display_rgb(&c); + assert_eq!(remembered_hsb(&srgb, &c, shown, picked), picked); + let own = display_hsb(&p3, &c); + assert_ne!(own, picked); + assert_eq!(remembered_hsb(&p3, &c, shown, picked), own); +} + /// A CMYK fill stays CMYK and keeps its look (ΔE 2000 under 2) when its marker is clicked; the /// working profile separates the picked color again, so its inks can move. #[test]