mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 12:17:14 -06:00
Merge pull request #4532 from dotCooCoo/fix/issue-3137
Part of #3137: Color Guide takes its base from colors chosen in Color, Swatches and the Color Picker
This commit is contained in:
@@ -45,7 +45,7 @@ fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
|
||||
ui.add_space(10.0);
|
||||
// Preview: the base colour's row of variations.
|
||||
let o = options(app, d);
|
||||
let base = base_color(app, ui.ctx());
|
||||
let base = base_color(app);
|
||||
let n = o.steps as i32;
|
||||
let (row, _) = ui.allocate_exact_size(vec2(ui.available_width(), 22.0), Sense::hover());
|
||||
let w = row.width() / (2 * n + 1) as f32;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
//! the out-of-gamut and out-of-web warnings (click to correct); New/Original chips; an eyedropper
|
||||
//! button that samples a color anywhere on the screen ([`crate::screen_color`]); and a Color
|
||||
//! Swatches list. With art selected the colour shows on it live as it is picked; OK keeps it as one
|
||||
//! undo step through `paint.setFill` / `paint.setStroke`, Cancel puts the art back.
|
||||
//! undo step through `paint.setFill` / `paint.setStroke` and makes the color the Color Guide's
|
||||
//! base color; Cancel puts the art back.
|
||||
//! Opened for an artboard (Properties › Artboards › Fill), it shows the color on the artboard's
|
||||
//! paper live, and OK keeps it as one undo step through `artboard.setProps {index, color}`.
|
||||
//! Opened over Recolor Artwork (double-click a row's new color), its OK sets that row's new color
|
||||
@@ -636,7 +637,13 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
let (cmd, params) = paint_command(app, d);
|
||||
super::form::commit_preview(app, cmd, params)
|
||||
let c = in_document_model(app, picked(d).color);
|
||||
let r = super::form::commit_preview(app, cmd, params)?;
|
||||
// A color picked for the fill or stroke also becomes the Color Guide's base color.
|
||||
if !d.fields.contains_key("artboard") {
|
||||
crate::panels::color_guide::choose_base(app, c);
|
||||
}
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -275,6 +275,30 @@ fn open_set_hex_and_confirm_fills() {
|
||||
assert_eq!(crate::panels::current_paints(&app).0.color(), Some(Color::cmyk(0.0, 1.0, 1.0, 0.0)));
|
||||
}
|
||||
|
||||
/// OK makes the picked fill or stroke color the Color Guide's base color (#3137); Cancel and a
|
||||
/// color picked for an artboard leave the base as it is.
|
||||
#[test]
|
||||
fn ok_makes_the_picked_color_the_color_guides_base() {
|
||||
let mut app = app();
|
||||
let base = |app: &VectorcraftApp| crate::panels::color_guide::base_color(app).to_hex();
|
||||
app.ui.color_guide_base = Some(Color::WHITE);
|
||||
app.run("ui.colorPicker", json!({})).unwrap();
|
||||
app.ui.dialog.as_mut().unwrap().fields.insert("hex".into(), json!("2050a0"));
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
assert_eq!(base(&app), "#2050a0");
|
||||
app.run("ui.colorPicker", json!({"stroke": true})).unwrap();
|
||||
app.ui.dialog.as_mut().unwrap().fields.insert("hex".into(), json!("a02050"));
|
||||
frame(&mut app);
|
||||
super::super::cancel(&mut app);
|
||||
assert_eq!(base(&app), "#2050a0", "Cancel keeps the base");
|
||||
app.run("select.set", json!({"ids": []})).unwrap();
|
||||
app.run("artboard.new", json!({})).unwrap();
|
||||
app.run("ui.colorPicker", json!({"artboard": 1})).unwrap();
|
||||
app.ui.dialog.as_mut().unwrap().fields.insert("hex".into(), json!("00ff00"));
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
assert_eq!(base(&app), "#2050a0", "an artboard's color leaves the base");
|
||||
}
|
||||
|
||||
/// #1692: Tab goes through one color model's fields before the next: H, S, B, then R, G, B, the
|
||||
/// hex field, then L, a, b, then C, M, Y, K. Each Lab or CMYK field is level with the field on
|
||||
/// its left.
|
||||
|
||||
@@ -432,6 +432,15 @@ fn apply(app: &mut VectorcraftApp, tgt: &Target, c: Color, phase: Live, behind:
|
||||
}
|
||||
}
|
||||
|
||||
/// [`apply`] a color chosen in the panel; once the pick ends, it also becomes the Color Guide's
|
||||
/// base color.
|
||||
fn choose(app: &mut VectorcraftApp, tgt: &Target, c: Color, phase: Live, behind: bool) {
|
||||
if phase == Live::Released {
|
||||
super::color_guide::choose_base(app, c);
|
||||
}
|
||||
apply(app, tgt, c, phase, behind);
|
||||
}
|
||||
|
||||
/// Set the target's tint of its swatch to `t` (0..1), keeping the link (the T slider).
|
||||
fn apply_tint(app: &mut VectorcraftApp, tgt: &Target, tint: &Tint, t: f32, phase: Live) {
|
||||
match tgt {
|
||||
@@ -580,7 +589,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
})
|
||||
.inner;
|
||||
match hit {
|
||||
Some(SpectrumHit::Pick(c, phase)) => apply(app, &tgt, c, phase, alt),
|
||||
Some(SpectrumHit::Pick(c, phase)) => choose(app, &tgt, c, phase, alt),
|
||||
Some(SpectrumHit::Cycle) => set_mode(app, &tgt, mode.next()),
|
||||
Some(SpectrumHit::Recolor { invert }) => recolor(app, ui, &tgt, invert, alt),
|
||||
None => {}
|
||||
@@ -718,15 +727,18 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
None => {}
|
||||
}
|
||||
if let Some((c, phase)) = behind {
|
||||
apply(app, &tgt, c, phase, true);
|
||||
choose(app, &tgt, c, phase, true);
|
||||
}
|
||||
if let (Some((t, phase)), Some(tint)) = (tinted, tgt.tint()) {
|
||||
if phase == Live::Released {
|
||||
super::color_guide::choose_base(app, tint.base.tinted(t));
|
||||
}
|
||||
apply_tint(app, &tgt, tint, t, phase);
|
||||
}
|
||||
if let Some((c, phase, comps2)) = new {
|
||||
let key = (c.to_hex(), mode as u8);
|
||||
set_pstate(ui.ctx(), "color-comps", Some((key, comps2)));
|
||||
apply(app, &tgt, c, phase, false);
|
||||
choose(app, &tgt, c, phase, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -318,6 +318,22 @@ fn alt_click_on_the_spectrum_paints_the_stroke() {
|
||||
assert_eq!(paints_hex(&app).0, "#ff0000");
|
||||
}
|
||||
|
||||
/// A color picked in the panel, for either proxy, becomes the Color Guide's base color (#3137).
|
||||
#[test]
|
||||
fn a_picked_color_becomes_the_color_guides_base() {
|
||||
let mut app = app();
|
||||
let ctx = egui::Context::default();
|
||||
app.run("paint.setFill", json!({"color": "#ffffff"})).unwrap();
|
||||
app.ui.color_guide_base = Some(Color::WHITE);
|
||||
click_spectrum(&mut app, &ctx, (0.0, 0.5), egui::Modifiers::NONE);
|
||||
assert_eq!(crate::panels::color_guide::base_color(&app).to_hex(), "#ff0000");
|
||||
// Alt: the stroke takes it, and it is still the base.
|
||||
click_spectrum(&mut app, &ctx, (0.5, 0.5), egui::Modifiers::ALT);
|
||||
let stroke = paints_hex(&app).1;
|
||||
assert_ne!(stroke, "#ff0000");
|
||||
assert_eq!(crate::panels::color_guide::base_color(&app).to_hex(), stroke);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shift_click_on_the_spectrum_cycles_modes() {
|
||||
let mut app = app();
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
//! Color Guide panel: the base colour, the harmony-rule dropdown with the harmony strip, and the
|
||||
//! variation grid: a row per harmony colour with the colour itself in the centre column,
|
||||
//! shades/cool/muted to the left and tints/warm/vivid to the right (steps and reach from Color
|
||||
//! Guide Options). The base colour stays put until "Set base color" takes the current colour. A
|
||||
//! click on a cell applies it to the active proxy and selects it; Shift- or Cmd/Ctrl-click adds
|
||||
//! Guide Options). The base color is the color last chosen in the Color panel, the Swatches panel
|
||||
//! or the Color Picker ([`choose_base`]); selecting artwork leaves it as it is, and the base chip
|
||||
//! ("Set base color to the current color") takes the current color, such as a cell applied from
|
||||
//! the guide or the selected artwork's color. A click on a cell applies it to the active proxy,
|
||||
//! without changing the base, and selects it; Shift- or Cmd/Ctrl-click adds
|
||||
//! cells to the selection that Save Colors as Swatches saves. Limit to Library (the library button,
|
||||
//! `ui.colorGuideLimit`) snaps every colour to a swatch library or the document's swatches; Edit or
|
||||
//! Apply Colors opens Recolor Artwork with the harmony colours, limited the same way.
|
||||
@@ -21,14 +24,16 @@ use crate::widgets::{self, menu_item};
|
||||
/// The base colour before one is set: an orange.
|
||||
const DEFAULT_BASE: Color = Color::Rgb { r: 230.0 / 255.0, g: 120.0 / 255.0, b: 40.0 / 255.0 };
|
||||
|
||||
/// The guide's base colour: fixed once set; the current colour when the panel first shows.
|
||||
pub(crate) fn base_color(app: &VectorcraftApp, ctx: &egui::Context) -> Color {
|
||||
if let Some(c) = pstate::<Option<Color>>(ctx, "cg-base") {
|
||||
return c;
|
||||
}
|
||||
let c = active_paint(app).color().unwrap_or(DEFAULT_BASE);
|
||||
set_pstate(ctx, "cg-base", Some(c));
|
||||
c
|
||||
/// The guide's base color: the one last chosen or set with the base chip; before that, the current
|
||||
/// color.
|
||||
pub(crate) fn base_color(app: &VectorcraftApp) -> Color {
|
||||
app.ui.color_guide_base.unwrap_or_else(|| active_paint(app).color().unwrap_or(DEFAULT_BASE))
|
||||
}
|
||||
|
||||
/// Make `c` the guide's base color: a color chosen in the Color panel, the Swatches panel or the
|
||||
/// Color Picker. A color applied from the guide itself leaves the base as it is.
|
||||
pub(crate) fn choose_base(app: &mut VectorcraftApp, c: Color) {
|
||||
app.ui.color_guide_base = Some(c);
|
||||
}
|
||||
|
||||
fn rule(ctx: &egui::Context) -> Harmony {
|
||||
@@ -59,7 +64,7 @@ struct GuideKey {
|
||||
|
||||
/// The guide the panel shows, limited to the Limit to Library colours.
|
||||
fn guide(app: &VectorcraftApp, ctx: &egui::Context) -> Guide {
|
||||
let (base, rule, opts) = (base_color(app, ctx), rule(ctx), app.ui.color_guide);
|
||||
let (base, rule, opts) = (base_color(app), rule(ctx), app.ui.color_guide);
|
||||
let limit = &app.ui.color_guide_limit;
|
||||
if limit.is_empty() {
|
||||
return Guide::new(base, rule, &opts);
|
||||
@@ -132,6 +137,15 @@ fn save_selected(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let t = crate::theme::Tokens::get(ui.ctx());
|
||||
let ctx = ui.ctx().clone();
|
||||
// The current color becomes the base when the panel first shows; selecting artwork later
|
||||
// leaves it as it is.
|
||||
let first = base_color(app);
|
||||
let set = *app.ui.color_guide_base.get_or_insert(first);
|
||||
// Selected cells belong to the guide they were picked from: a new base clears them.
|
||||
if pstate::<Option<Color>>(&ctx, "cg-sel-base") != Some(set) {
|
||||
set_pstate(&ctx, "cg-sel-base", Some(set));
|
||||
clear_selection(&ctx);
|
||||
}
|
||||
let h = rule(&ctx);
|
||||
let g = guide(app, &ctx);
|
||||
let base = g.colors[0];
|
||||
@@ -142,7 +156,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
if resp.on_hover_text(tl!("Set base color to the current color")).clicked()
|
||||
&& let Some(c) = active_paint(app).color()
|
||||
{
|
||||
set_pstate(&ctx, "cg-base", Some(c));
|
||||
app.ui.color_guide_base = Some(c);
|
||||
clear_selection(&ctx);
|
||||
}
|
||||
let labels: Vec<&str> = Harmony::ALL.iter().map(|h| h.label()).collect();
|
||||
@@ -294,22 +308,83 @@ mod tests {
|
||||
assert_eq!(role_and_state(&shown, "Limit colors to swatch library: Earth Tones"), (Role::Button, Some(Toggled::True)));
|
||||
}
|
||||
|
||||
/// The first show takes the current color; a color chosen in the Color panel, Swatches or the
|
||||
/// Color Picker becomes the base; selecting artwork with another color leaves it.
|
||||
#[test]
|
||||
fn base_stays_until_set_and_the_grid_follows_the_options() {
|
||||
fn a_chosen_color_becomes_the_base_and_selecting_art_keeps_it() {
|
||||
let mut app = app();
|
||||
let red = app.run("shape.rectangle", json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap()["id"].clone();
|
||||
app.run("paint.setFill", json!({"color": "#ff0000"})).unwrap();
|
||||
let blue = app.run("shape.rectangle", json!({"x": 20, "y": 0, "width": 10, "height": 10})).unwrap()["id"].clone();
|
||||
app.run("paint.setFill", json!({"color": "#3366cc"})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
frame(&mut app, &ctx);
|
||||
assert_eq!(base_color(&app, &ctx).to_hex(), "#3366cc", "the first show takes the current colour");
|
||||
// Another current colour leaves the base alone.
|
||||
app.run("paint.setFill", json!({"color": "#ff0000"})).unwrap();
|
||||
assert_eq!(base_color(&app).to_hex(), "#3366cc", "the first show takes the current color");
|
||||
app.run("select.set", json!({"ids": [red]})).unwrap();
|
||||
frame(&mut app, &ctx);
|
||||
assert_eq!(base_color(&app).to_hex(), "#3366cc", "selecting art keeps the base");
|
||||
choose_base(&mut app, Color::from_hex("#20a060").unwrap());
|
||||
frame(&mut app, &ctx);
|
||||
assert_eq!(base_color(&app, &ctx).to_hex(), "#3366cc");
|
||||
app.ui.color_guide.steps = 6;
|
||||
frame(&mut app, &ctx);
|
||||
let g = guide(&app, &ctx);
|
||||
assert_eq!(g.grid[0].len(), 13);
|
||||
assert_eq!(g.grid[0][6].to_hex(), "#3366cc", "the base sits in the centre of the first row");
|
||||
assert_eq!(g.grid[0][6].to_hex(), "#20a060", "the chosen base sits in the centre of the first row");
|
||||
app.run("select.set", json!({"ids": [blue]})).unwrap();
|
||||
frame(&mut app, &ctx);
|
||||
assert_eq!(base_color(&app).to_hex(), "#20a060");
|
||||
}
|
||||
|
||||
/// The rect of the panel widget `id` after one frame.
|
||||
fn rect_of(app: &mut VectorcraftApp, ctx: &egui::Context, id: impl std::hash::Hash + std::fmt::Debug) -> Rect {
|
||||
let mut r = None;
|
||||
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| {
|
||||
show(app, ui);
|
||||
r = ctx.read_response(ui.scope_id().with(&id)).map(|r| r.rect);
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
r.expect("the widget is drawn")
|
||||
}
|
||||
|
||||
fn click(app: &mut VectorcraftApp, ctx: &egui::Context, at: egui::Pos2) {
|
||||
let button =
|
||||
|pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: egui::Modifiers::NONE };
|
||||
for events in [vec![egui::Event::PointerMoved(at), button(true)], vec![button(false)]] {
|
||||
let mut out = ctx.run_ui(egui::RawInput { events, ..Default::default() }, |ui| show(app, ui));
|
||||
out.textures_delta.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// A harmony color or a variation clicked in the guide paints the art without changing the
|
||||
/// base; the base chip then makes the current color the base.
|
||||
#[test]
|
||||
fn a_guide_color_applies_without_moving_the_base_until_set_as_base() {
|
||||
let mut app = app();
|
||||
app.run("shape.rectangle", json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap();
|
||||
app.run("paint.setFill", json!({"color": "#3366cc"})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
frame(&mut app, &ctx);
|
||||
let g = guide(&app, &ctx);
|
||||
let fill = |app: &VectorcraftApp| crate::panels::current_paints(app).0.color().map(|c| c.to_hex());
|
||||
let at = rect_of(&mut app, &ctx, ("cg-h", 1)).center();
|
||||
click(&mut app, &ctx, at);
|
||||
assert_eq!(fill(&app), Some(g.colors[1].to_hex()), "the harmony color paints the art");
|
||||
assert_eq!(base_color(&app).to_hex(), "#3366cc", "and leaves the base");
|
||||
let at = rect_of(&mut app, &ctx, ("cg-v", 0usize, 2usize)).center();
|
||||
click(&mut app, &ctx, at);
|
||||
let variation = g.grid[0][2].to_hex();
|
||||
assert_eq!(fill(&app), Some(variation.clone()), "a variation paints the art");
|
||||
assert_eq!(base_color(&app).to_hex(), "#3366cc");
|
||||
assert_eq!(guide(&app, &ctx).grid, g.grid, "the guide stays as it was");
|
||||
// Set base color to the current color: the applied variation becomes the base.
|
||||
let mut chip = None;
|
||||
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| {
|
||||
show(&mut app, ui);
|
||||
chip = Some(ui.min_rect().left_top() + egui::vec2(8.0, 8.0));
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
click(&mut app, &ctx, chip.expect("the panel is drawn"));
|
||||
assert_eq!(base_color(&app).to_hex(), variation, "the base chip adopts the current color");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -70,6 +70,18 @@ fn clicking_a_library_swatch_adds_and_applies_it_in_one_step() {
|
||||
assert!(last_open(&app.ui).is_none());
|
||||
}
|
||||
|
||||
/// A color clicked in a swatch library becomes the Color Guide's base color (#3137).
|
||||
#[test]
|
||||
fn a_clicked_library_color_becomes_the_color_guides_base() {
|
||||
let mut app = app();
|
||||
let ctx = context();
|
||||
app.run("window.swatchLibrary", json!({"library": "pastels"})).unwrap();
|
||||
app.ui.color_guide_base = Some(vectorcraft_color::Color::WHITE);
|
||||
click(&mut app, &ctx, "Pastel Teal", Modifiers::NONE, 0.0);
|
||||
let teal = app.session.doc().unwrap().doc.swatch("Pastel Teal").and_then(|w| w.paint.color()).unwrap();
|
||||
assert_eq!(crate::panels::color_guide::base_color(&app), teal);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modifier_clicks_select_and_add_to_swatches_adds_the_selection() {
|
||||
let mut app = app();
|
||||
|
||||
@@ -187,9 +187,15 @@ fn swatch_params(e: &Entry) -> Option<Value> {
|
||||
|
||||
/// Apply a clicked swatch to the active proxy (Alt: the inactive one). An Alt-click on a color
|
||||
/// swatch with a gradient stop selected recolors the stop as dropping the swatch on it does,
|
||||
/// and with a freeform gradient point selected it recolors the point.
|
||||
/// and with a freeform gradient point selected it recolors the point. A color swatch also becomes
|
||||
/// the Color Guide's base color.
|
||||
fn apply(app: &mut VectorcraftApp, ui: &Ui, e: &Entry) {
|
||||
let Some(params) = swatch_params(e) else { return };
|
||||
if let Entry::Swatch { paint, .. } = e
|
||||
&& let Some(c) = paint.color()
|
||||
{
|
||||
super::color_guide::choose_base(app, c);
|
||||
}
|
||||
if super::alt_held(ui)
|
||||
&& let Some(c) = drag_of(e, vec![]).color()
|
||||
&& super::gradient::recolor_selected(app, c, ¶ms)
|
||||
|
||||
@@ -30,8 +30,10 @@ pub(super) fn body(app: &mut VectorcraftApp, ui: &mut Ui, salt: &'static str) {
|
||||
}
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
// A recent colour paints the active proxy (Alt: the inactive one), as a swatch does.
|
||||
// A recent colour paints the active proxy (Alt: the inactive one), as a swatch does, and
|
||||
// becomes the Color Guide's base color.
|
||||
if let Some(c) = crate::panels::recent_colors_row(app, ui) {
|
||||
crate::panels::color_guide::choose_base(app, c);
|
||||
crate::panels::apply_click(app, ui, json!({"color": crate::panels::color_json(&c)}));
|
||||
}
|
||||
widgets::divider(ui);
|
||||
|
||||
@@ -91,8 +91,12 @@ impl LibraryKind for SwatchLibraries {
|
||||
fn describe(w: &Swatch) -> String {
|
||||
describe(&w.paint, w.global, w.spot)
|
||||
}
|
||||
/// Adds the swatch and applies it to the active proxy (Alt: the inactive one), as one step.
|
||||
/// Adds the swatch and applies it to the active proxy (Alt: the inactive one), as one step. A
|
||||
/// color swatch also becomes the Color Guide's base color.
|
||||
fn click(app: &mut VectorcraftApp, ui: &Ui, id: &str, name: &str) {
|
||||
if let Some(c) = swatchlib::library(&app.session, id).and_then(|(_, lib)| lib.iter().find(|w| w.name == name).and_then(|w| w.paint.color())) {
|
||||
crate::panels::color_guide::choose_base(app, c);
|
||||
}
|
||||
let alt = crate::panels::alt_held(ui);
|
||||
let apply = if crate::panels::proxy_cmd(app, alt) == "paint.setFill" { "fill" } else { "stroke" };
|
||||
add_from_library(app, json!({"library": id, "names": [name], "apply": apply, "focus": !alt}));
|
||||
|
||||
@@ -133,6 +133,34 @@ fn modifier_clicks_select_several_swatches_and_groups() {
|
||||
assert_eq!(selected(&ctx), ["Grays"], "a plain click on a group selects it alone");
|
||||
}
|
||||
|
||||
/// A color swatch or a recent color clicked in the panel becomes the Color Guide's base color
|
||||
/// (#3137); a modifier click only selects.
|
||||
#[test]
|
||||
fn a_clicked_color_swatch_becomes_the_color_guides_base() {
|
||||
let mut app = app();
|
||||
let ctx = context();
|
||||
app.ui.color_guide_base = Some(Color::WHITE);
|
||||
click_tile(&mut app, &ctx, "Red", Modifiers::NONE, 0.0);
|
||||
let base = |app: &VectorcraftApp| crate::panels::color_guide::base_color(app).to_hex();
|
||||
assert_eq!(base(&app), "#ed1c24");
|
||||
click_tile(&mut app, &ctx, "Amber", Modifiers::SHIFT, 2.0);
|
||||
assert_eq!(base(&app), "#ed1c24", "a modifier click only selects");
|
||||
// A recent color: the panel's first recent chip.
|
||||
app.run("paint.setFill", json!({"color": "#2050a0"})).unwrap();
|
||||
app.ui.color_guide_base = Some(Color::WHITE);
|
||||
let mut chip = None;
|
||||
let screen_rect = Some(Rect::from_min_size(Pos2::ZERO, vec2(280.0, 700.0)));
|
||||
let mut out = ctx.run_ui(egui::RawInput { time: Some(4.0), screen_rect, ..Default::default() }, |ui| {
|
||||
show(&mut app, ui);
|
||||
chip = ctx.read_response(ui.scope_id().with(("recent", 0usize))).map(|r| r.rect.center());
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
let at = chip.expect("the recent chip is drawn");
|
||||
let button = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE };
|
||||
frame(&mut app, &ctx, vec![Event::PointerMoved(at), button(true), button(false)], 5.0, show);
|
||||
assert_eq!(base(&app), "#2050a0");
|
||||
}
|
||||
|
||||
/// The hex colors of the stops of the selection's gradient fill, and its stroke's color.
|
||||
fn stops_and_stroke(app: &VectorcraftApp) -> (Vec<String>, String) {
|
||||
let (fill, stroke) = crate::panels::current_paints(app);
|
||||
|
||||
@@ -483,6 +483,11 @@ pub struct UiState {
|
||||
/// swatches) or "" (none) (`ui.colorGuideLimit`).
|
||||
#[serde(default)]
|
||||
pub color_guide_limit: String,
|
||||
/// Color Guide panel: the base color, set by a color chosen in the Color panel, the Swatches
|
||||
/// panel or the Color Picker and by the panel's base chip (none: the current color). Not
|
||||
/// saved.
|
||||
#[serde(skip)]
|
||||
pub color_guide_base: Option<vectorcraft_color::Color>,
|
||||
/// Align panel: the Align To choice, Selection or Artboard, which Object → Align and the align
|
||||
/// buttons follow ([`crate::panels::align::with_align_to`]).
|
||||
#[serde(default, deserialize_with = "crate::panels::align::deserialize_align_to")]
|
||||
@@ -855,6 +860,7 @@ impl Default for UiState {
|
||||
library_panels: vec![],
|
||||
flattener_preview: Default::default(),
|
||||
color_guide_limit: String::new(),
|
||||
color_guide_base: None,
|
||||
align_to: Default::default(),
|
||||
align_spacing: 0.0,
|
||||
color_mode: None,
|
||||
|
||||
Reference in New Issue
Block a user