164 lines
7.5 KiB
Rust
164 lines
7.5 KiB
Rust
//! Perspective Grid dialogs: Define Grid, the presets manager and the tool options.
|
||
|
||
use serde_json::{Value, json};
|
||
use vectorcraft_engine::Session;
|
||
use vectorcraft_tools::distort::perspective::PerspectiveGrid;
|
||
|
||
use crate::{VectorcraftApp, theme};
|
||
|
||
fn frame(app: &mut VectorcraftApp) {
|
||
let ctx = egui::Context::default();
|
||
theme::install_fonts(&ctx);
|
||
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| super::show(app, ui.ctx()));
|
||
out.textures_delta.clear();
|
||
}
|
||
|
||
fn app() -> VectorcraftApp {
|
||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||
app.run("file.new", json!({"width": 800, "height": 600})).unwrap();
|
||
app
|
||
}
|
||
|
||
fn kind(app: &VectorcraftApp) -> Option<&str> {
|
||
app.ui.dialog.as_ref().map(|d| d.kind.as_str())
|
||
}
|
||
|
||
fn field(app: &VectorcraftApp, k: &str) -> Value {
|
||
app.ui.dialog.as_ref().and_then(|d| d.fields.get(k).cloned()).unwrap_or_default()
|
||
}
|
||
|
||
fn set(app: &mut VectorcraftApp, k: &str, v: Value) {
|
||
app.ui.dialog.as_mut().expect("a dialog is open").fields.insert(k.into(), v);
|
||
}
|
||
|
||
fn grid(app: &VectorcraftApp) -> PerspectiveGrid {
|
||
PerspectiveGrid::effective(&app.session.active().unwrap().doc)
|
||
}
|
||
|
||
#[test]
|
||
fn define_grid_opens_on_the_grid_and_applies_its_fields() {
|
||
let mut app = app();
|
||
app.run("perspective.grid.preset", json!({"kind": 3})).unwrap();
|
||
let g = grid(&app);
|
||
app.run("ui.perspectiveGridDialog", json!({})).unwrap();
|
||
assert_eq!(kind(&app), Some(super::perspective_grid::KIND));
|
||
assert_eq!(field(&app, "kind"), json!(3), "prefilled from the grid, not a fixed type");
|
||
assert!((field(&app, "gridline").as_f64().unwrap() - g.cell).abs() < 1e-9);
|
||
frame(&mut app);
|
||
// A new unit converts the lengths shown.
|
||
set(&mut app, "units", json!("inches"));
|
||
frame(&mut app);
|
||
assert!((field(&app, "gridline").as_f64().unwrap() - g.cell / 72.0).abs() < 1e-9);
|
||
// OK with nothing changed leaves the grid as it was.
|
||
super::confirm(&mut app).unwrap();
|
||
assert_eq!(kind(&app), None);
|
||
assert_eq!((grid(&app).cell, grid(&app).vp_left, grid(&app).kind), (g.cell, g.vp_left, 3));
|
||
// A new viewing angle and colour apply in one undo step.
|
||
let undo = app.session.active().unwrap().history.undo.len();
|
||
app.run("ui.perspectiveGridDialog", json!({})).unwrap();
|
||
set(&mut app, "angle", json!(30));
|
||
set(&mut app, "rightColor", json!("#112233"));
|
||
frame(&mut app);
|
||
super::confirm(&mut app).unwrap();
|
||
let n = grid(&app);
|
||
assert!((n.viewing_angle() - 30.0).abs() < 1e-9);
|
||
assert_eq!(n.right_color.hex(), "#112233");
|
||
assert_eq!(app.session.active().unwrap().history.undo.len(), undo + 1);
|
||
// A refused value keeps the dialog open.
|
||
app.run("ui.perspectiveGridDialog", json!({})).unwrap();
|
||
set(&mut app, "distance", json!(-5));
|
||
assert!(super::confirm(&mut app).is_err());
|
||
assert_eq!(kind(&app), Some(super::perspective_grid::KIND));
|
||
}
|
||
|
||
#[test]
|
||
fn define_grid_is_a_view_menu_item() {
|
||
let app = app();
|
||
let entries = crate::menus::menu_entries(&app);
|
||
let e = entries.iter().find(|e| e.command.as_deref() == Some("ui.perspectiveGridDialog")).expect("View › Perspective Grid › Define Grid…");
|
||
assert!(e.path.iter().any(|p| p == "Perspective Grid"), "{:?}", e.path);
|
||
assert!(e.enabled);
|
||
}
|
||
|
||
#[test]
|
||
fn define_grid_loads_and_saves_presets() {
|
||
let mut app = app();
|
||
app.run("ui.perspectiveGridDialog", json!({})).unwrap();
|
||
frame(&mut app);
|
||
assert_eq!(field(&app, "name"), json!("[2P-Normal View]"));
|
||
// Choosing a preset (an agent sets its fields; the menu loads them) keeps its name while the
|
||
// fields are its; a changed field makes the fields [Custom].
|
||
set(&mut app, "angle", json!(20));
|
||
frame(&mut app);
|
||
assert_eq!(field(&app, "name"), json!(""));
|
||
// Save Preset… asks for a name; OK saves the fields and comes back to Define Grid on it.
|
||
app.ui.dialog.as_mut().unwrap().fields.insert("__mode".into(), json!("save"));
|
||
set(&mut app, "__from", json!("define"));
|
||
set(&mut app, "name", json!("Shallow"));
|
||
frame(&mut app);
|
||
super::confirm(&mut app).unwrap();
|
||
assert_eq!(kind(&app), Some(super::perspective_grid::KIND));
|
||
assert_eq!((field(&app, "name"), field(&app, "__mode")), (json!("Shallow"), Value::Null));
|
||
assert_eq!(app.session.prefs.perspective_presets[0].angle, 20.0);
|
||
frame(&mut app);
|
||
assert_eq!(field(&app, "name"), json!("Shallow"), "the fields are the saved preset's");
|
||
super::confirm(&mut app).unwrap();
|
||
assert_eq!(grid(&app).name, "Shallow");
|
||
// View › Perspective Grid › Save Grid as Preset…: the grid under a new name.
|
||
app.run("ui.savePerspectivePreset", json!({})).unwrap();
|
||
assert_eq!(field(&app, "name"), json!("Perspective Preset 1"));
|
||
frame(&mut app);
|
||
super::confirm(&mut app).unwrap();
|
||
assert_eq!(kind(&app), None);
|
||
assert_eq!(app.session.prefs.perspective_presets.len(), 2);
|
||
}
|
||
|
||
#[test]
|
||
fn the_presets_manager_creates_edits_and_deletes_presets() {
|
||
use super::perspective_presets::{self, Action};
|
||
let mut app = app();
|
||
app.run("ui.perspectivePresetsDialog", json!({"selected": "[3P-Normal View]"})).unwrap();
|
||
frame(&mut app);
|
||
let press = |app: &mut VectorcraftApp, act: Action, current: &str| {
|
||
let mut d = app.ui.dialog.take().unwrap();
|
||
perspective_presets::run(app, &mut d, act, current).unwrap();
|
||
app.ui.dialog = Some(d);
|
||
frame(app);
|
||
};
|
||
// New… opens the editor on a copy of the selected preset; its OK comes back here.
|
||
press(&mut app, Action::New, "[3P-Normal View]");
|
||
assert_eq!((kind(&app), field(&app, "kind")), (Some(super::perspective_grid::KIND), json!(3)));
|
||
set(&mut app, "name", json!("Canyon"));
|
||
set(&mut app, "opacity", json!(90));
|
||
super::confirm(&mut app).unwrap();
|
||
assert_eq!((kind(&app), field(&app, "selected")), (Some(perspective_presets::KIND), json!("Canyon")));
|
||
// Edit… renames it.
|
||
press(&mut app, Action::Edit, "Canyon");
|
||
set(&mut app, "name", json!("Gorge"));
|
||
super::confirm(&mut app).unwrap();
|
||
assert_eq!(field(&app, "selected"), json!("Gorge"));
|
||
assert_eq!(app.session.prefs.perspective_presets.iter().map(|p| p.name.as_str()).collect::<Vec<_>>(), ["Gorge"]);
|
||
assert_eq!(app.session.prefs.perspective_presets[0].opacity, 90.0);
|
||
// Its type's menu lists it in a slot, which applies it.
|
||
assert_eq!(crate::menus::dynamic_label(&app, "ui.perspectiveUserPreset3.1", ""), "Gorge");
|
||
assert!(crate::menus::hidden_slot(&app, "ui.perspectiveUserPreset3.2") && crate::menus::hidden_slot(&app, "ui.perspectiveUserPreset2.1"));
|
||
app.run("ui.perspectiveUserPreset3.1", json!({})).unwrap();
|
||
assert_eq!(grid(&app).name, "Gorge");
|
||
// Delete selects the one above it.
|
||
press(&mut app, Action::Delete, "Gorge");
|
||
assert!(app.session.prefs.perspective_presets.is_empty());
|
||
assert_eq!(field(&app, "selected"), json!("[3P-Low View]"));
|
||
assert!(app.run("ui.perspectiveUserPreset3.1", json!({})).is_err());
|
||
}
|
||
|
||
#[test]
|
||
fn perspective_preset_menus_list_the_views_of_each_type() {
|
||
let app = app();
|
||
let entries = crate::menus::menu_entries(&app);
|
||
let under = |sub: &str| entries.iter().filter(|e| e.path.iter().any(|p| p == sub)).map(|e| e.label.clone()).collect::<Vec<_>>();
|
||
assert_eq!(under("One Point Perspective"), ["[1P-Normal View]", "[1P-Low View]", "[1P-High View]"]);
|
||
assert_eq!(under("Three Point Perspective"), ["[3P-Normal View]", "[3P-Low View]"]);
|
||
let presets = entries.iter().find(|e| e.command.as_deref() == Some("ui.perspectivePresetsDialog")).unwrap();
|
||
assert_eq!((presets.label.as_str(), presets.path.first().map(String::as_str)), ("Perspective Grid Presets…", Some("Edit")));
|
||
}
|