mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Brushes: Yes in Delete Brush asks again when a stroke started using a brush meanwhile
The question for unused brushes checks again when Yes is chosen. If a stroke uses one of the brushes by then, the dialog asks with Remove Strokes and Cancel instead of deleting.
This commit is contained in:
@@ -28,6 +28,21 @@ pub fn ask(app: &mut VectorcraftApp, params: Value, used: bool) {
|
||||
app.ui.dialog = Some(Dialog::new(KIND, json!({"params": params, "used": used, "message": message})));
|
||||
}
|
||||
|
||||
/// Whether a stroke uses one of `names` (`brush.unused`), or `None` when the query fails.
|
||||
pub fn used(app: &mut VectorcraftApp, names: &[String]) -> Option<bool> {
|
||||
let unused = app.run("brush.unused", json!({})).ok()?;
|
||||
let unused = unused.get("names").and_then(Value::as_array).cloned().unwrap_or_default();
|
||||
Some(names.iter().any(|name| !unused.iter().any(|n| n.as_str() == Some(name))))
|
||||
}
|
||||
|
||||
/// The brush names in `brush.delete` params (`name` or `names`).
|
||||
fn names(params: &Value) -> Vec<String> {
|
||||
match params.get("name").and_then(Value::as_str) {
|
||||
Some(name) => vec![name.to_string()],
|
||||
None => params.get("names").and_then(Value::as_array).into_iter().flatten().filter_map(Value::as_str).map(str::to_string).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn body(_: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> DialogAction {
|
||||
let mut action = DialogAction::None;
|
||||
ui.horizontal(|ui| {
|
||||
@@ -50,9 +65,14 @@ fn body(_: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> DialogActi
|
||||
action
|
||||
}
|
||||
|
||||
/// Closes before running, so a dialog the command opens stays open.
|
||||
/// Closes before running, so a dialog the command opens stays open. Yes on brushes that a stroke
|
||||
/// has started to use since the question opened asks again with Remove Strokes and Cancel.
|
||||
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
|
||||
let params = d.fields.get("params").cloned().unwrap_or_else(|| json!({}));
|
||||
if !d.bool("used") && used(app, &names(¶ms)) == Some(true) {
|
||||
ask(app, params, true);
|
||||
return Ok(json!({}));
|
||||
}
|
||||
app.ui.dialog = None;
|
||||
app.run("brush.delete", params)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,25 @@ fn visible_yes_and_no_buttons_control_unused_deletion() {
|
||||
assert_eq!(app.session.active().unwrap().history.undo.len(), before + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn yes_asks_again_when_a_stroke_started_using_the_brush() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
|
||||
let name = "10 pt. Oval";
|
||||
let ctx = context();
|
||||
ask(&mut app, json!({"names": [name]}), false);
|
||||
let path = NodeId(app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 60, "height": 40})).unwrap()["id"].as_u64().unwrap());
|
||||
app.run("brush.apply", json!({"name": name})).unwrap();
|
||||
let before = app.session.active().unwrap().history.undo.len();
|
||||
press(&ctx, &mut app, "Yes", 0.0);
|
||||
assert!(app.ui.dialog.as_ref().is_some_and(|d| d.kind == KIND && d.bool("used")), "asks again with Remove Strokes");
|
||||
assert_eq!(app.session.active().unwrap().history.undo.len(), before);
|
||||
let stroke = app.session.active().unwrap().doc.node(path).and_then(|n| n.appearance.stroke()).unwrap();
|
||||
assert_eq!(stroke.brush.as_deref(), Some(name));
|
||||
press(&ctx, &mut app, "Remove Strokes", 1.0);
|
||||
assert!(app.run("brush.get", json!({"name": name})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visible_remove_strokes_and_cancel_buttons_control_used_deletion() {
|
||||
for choice in ["Cancel", "Remove Strokes"] {
|
||||
|
||||
@@ -207,11 +207,9 @@ fn run_on(app: &mut VectorcraftApp, id: &str, names: &[String]) {
|
||||
/// Delete `names` after asking: Yes or No when none of them is in use, else Remove Strokes or
|
||||
/// Cancel. With `now` (Alt-click on the bottom button), brushes that are not in use go at once.
|
||||
fn delete(app: &mut VectorcraftApp, names: &[String], now: bool) {
|
||||
let Ok(unused) = app.run("brush.unused", json!({})) else {
|
||||
let Some(used) = crate::dialogs::brush_delete::used(app, names) else {
|
||||
return;
|
||||
};
|
||||
let unused = unused.get("names").and_then(Value::as_array).cloned().unwrap_or_default();
|
||||
let used = names.iter().any(|name| !unused.iter().any(|n| n.as_str() == Some(name)));
|
||||
if now && !used {
|
||||
run_on(app, "brush.delete", names);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user