mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 20:27:05 -06:00
Brushes: Pattern Brush Options' OK leaves corner tiles that were not set
OK used to save None for a corner tile the dialog showed as None because the brush had no art or source for it, so that corner stopped drawing the other kind of corner's art once the brush got some. It also copied the art a corner borrows from the other kind of corner when that corner kept its art. Now OK saves a corner tile's choice only when it was set from its menu or with `ui.dialog.set`, or when the art it borrows goes away.
This commit is contained in:
@@ -327,7 +327,7 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context, req: &ControlReques
|
||||
return err(format!("the `{}` dialog has no field `{f}`; its fields: {names}", d.kind));
|
||||
}
|
||||
for (f, value) in fields {
|
||||
d.fields.insert(f, value);
|
||||
crate::dialogs::set_field(d, f, value);
|
||||
}
|
||||
ok(serde_json::to_value(&*d).unwrap_or_default())
|
||||
}
|
||||
|
||||
@@ -410,6 +410,15 @@ pub fn accepts_field(d: &Dialog, field: &str) -> bool {
|
||||
|| (spec.passes)(d, field)
|
||||
}
|
||||
|
||||
/// Set `field` of `d` to `value` for `ui.dialog.set`, as the dialog's own control for it does: a
|
||||
/// Pattern Brush Options corner tile set this way is saved by OK, as one chosen from its menu is.
|
||||
pub fn set_field(d: &mut Dialog, field: String, value: Value) {
|
||||
if d.kind == brush_options::KIND {
|
||||
brush_art_options::note_set(d, &field);
|
||||
}
|
||||
d.fields.insert(field, value);
|
||||
}
|
||||
|
||||
/// The fields `ui.dialog.set` can set on `d`, sorted, without the `__` ones: those it holds,
|
||||
/// `discard`, `preview` and the spec's `inputs` for its kind.
|
||||
pub fn field_names(d: &Dialog) -> Vec<String> {
|
||||
|
||||
@@ -63,6 +63,24 @@ fn corner_art_field<'a>(def: Option<&Value>, field: &'a str) -> Option<&'a str>
|
||||
}
|
||||
}
|
||||
|
||||
/// The dialog field that records that corner tile `key`'s menu was set.
|
||||
fn corner_set_key(key: &str) -> String {
|
||||
format!("__{key}Set")
|
||||
}
|
||||
|
||||
/// Sets corner tile `key` to `choice` from its menu.
|
||||
pub(super) fn choose_corner(d: &mut Dialog, key: &str, choice: &str) {
|
||||
d.fields.insert(key.into(), json!(choice));
|
||||
note_set(d, key);
|
||||
}
|
||||
|
||||
/// Records that field `field` was set, from its menu or `ui.dialog.set`, when it is a corner tile.
|
||||
pub(super) fn note_set(d: &mut Dialog, field: &str) {
|
||||
if CORNER_TILES.iter().any(|(key, ..)| *key == field) {
|
||||
d.fields.insert(corner_set_key(field), json!(true));
|
||||
}
|
||||
}
|
||||
|
||||
/// The most key colours offered.
|
||||
const MAX_COLORS: usize = 16;
|
||||
|
||||
@@ -236,14 +254,21 @@ pub(super) fn params(d: &Dialog) -> Value {
|
||||
});
|
||||
// None or an automatic tile replaces the corner art. Original keeps the art the corner
|
||||
// draws, the other kind of corner's art included, so changing the other kind leaves it.
|
||||
// A None the menu was not set to, on a corner without art or a source, leaves the
|
||||
// definition as it is: that corner still draws the other kind's art when it gets some.
|
||||
let def = d.fields.get("__def");
|
||||
for (key, art, source, _) in CORNER_TILES {
|
||||
for (key, art, source, other) in CORNER_TILES {
|
||||
let choice = d.str(key);
|
||||
let unset = |f: &str| def.and_then(|d| d.get(f)).is_none_or(Value::is_null);
|
||||
if choice == CornerSource::None.id() && !d.bool(&corner_set_key(key)) && unset(source) && corner_art_field(def, art).is_none() {
|
||||
continue;
|
||||
}
|
||||
if let Some(s) = CornerSource::parse(&choice) {
|
||||
p[art] = Value::Null;
|
||||
p[source] = json!(s.id());
|
||||
} else if choice == "original"
|
||||
&& let Some(from) = corner_art_field(def, art).filter(|f| *f != art)
|
||||
&& CORNER_TILES.iter().find(|(_, a, _, _)| *a == other).is_some_and(|(k, ..)| d.str(k) != "original")
|
||||
{
|
||||
p[art] = def.and_then(|d| d.get(from)).cloned().unwrap_or_default();
|
||||
}
|
||||
@@ -480,7 +505,7 @@ fn tiles(ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
if let Some((id, _)) =
|
||||
widgets::dropdown_named(ui, ("brush-pattern-corner", key), Some(name), current, &labels, 76.0).and_then(|i| choices.get(i))
|
||||
{
|
||||
d.fields.insert(key.into(), json!(id));
|
||||
choose_corner(d, key, id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -509,6 +509,44 @@ fn pattern_options_keep_the_corner_art_a_corner_borrows() {
|
||||
assert_eq!(def["outer_source"], json!("none"));
|
||||
}
|
||||
|
||||
/// OK leaves the corner fields of a corner tile the menus were not set to as they were: a corner
|
||||
/// without art keeps borrowing the other kind's art when it gets some, and a corner that borrows
|
||||
/// art still borrows it. A None picked from the menu is saved, after another choice too.
|
||||
#[test]
|
||||
fn pattern_options_ok_leaves_corner_tiles_the_menus_were_not_set_to() {
|
||||
let mut app = app();
|
||||
app.run("ui.brushOptions", json!({"name": "Diamonds"})).unwrap();
|
||||
set(&mut app, json!({"scale": 150}));
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
let def = app.run("brush.get", json!({"name": "Diamonds"})).unwrap();
|
||||
assert!(def.get("outer_source").is_none() && def.get("inner_source").is_none(), "{def}");
|
||||
let art = app.run("brush.get", json!({"name": "Chain"})).unwrap()["outer_corner"].clone();
|
||||
app.run("brush.options", json!({"name": "Diamonds", "params": {"outer_corner": art.clone()}})).unwrap();
|
||||
app.run("ui.brushOptions", json!({"name": "Diamonds"})).unwrap();
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().str("innerCornerTile"), "original", "the inner corners draw the outer art");
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
let def = app.run("brush.get", json!({"name": "Diamonds"})).unwrap();
|
||||
assert!(def["outer_corner"].is_object() && def.get("inner_corner").is_none(), "{def}");
|
||||
|
||||
app.run("brush.new", json!({"type": "pattern", "name": "Plain", "params": {"side": art}})).unwrap();
|
||||
app.run("ui.brushOptions", json!({"name": "Plain"})).unwrap();
|
||||
let d = app.ui.dialog.as_mut().unwrap();
|
||||
crate::dialogs::brush_art_options::choose_corner(d, "outerCornerTile", "autoCentered");
|
||||
crate::dialogs::brush_art_options::choose_corner(d, "outerCornerTile", "none");
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
let def = app.run("brush.get", json!({"name": "Plain"})).unwrap();
|
||||
assert_eq!(def["outer_source"], json!("none"));
|
||||
assert!(def.get("inner_source").is_none(), "{def}");
|
||||
// None set through the control channel is saved too.
|
||||
app.run("ui.brushOptions", json!({"name": "Plain"})).unwrap();
|
||||
let (set, _) = crate::control::ControlRequest::new("ui.dialog.set", json!({"field": "innerCornerTile", "value": "none"}));
|
||||
let crate::control::Outcome::Done(r) = crate::control::handle(&mut app, &egui::Context::default(), &set) else { panic!("not done") };
|
||||
assert!(r.get("error").is_none(), "{r}");
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
let def = app.run("brush.get", json!({"name": "Plain"})).unwrap();
|
||||
assert_eq!(def["inner_source"], json!("none"));
|
||||
}
|
||||
|
||||
/// Art Brush Options' Width and Pattern Brush Options' Scale each have a slider beside their
|
||||
/// field, which screen readers name as the row, with the field's number and text over the
|
||||
/// value's whole range.
|
||||
|
||||
Reference in New Issue
Block a user