mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
Merge pull request #2633 from dotCooCoo/fix/save-panels-offer-their-file-type
Save: save panels for files other than documents offer the file type they write
This commit is contained in:
@@ -286,14 +286,16 @@ pub(crate) fn is_web(app: &VectorcraftApp) -> bool {
|
||||
app.services.download.is_some()
|
||||
}
|
||||
|
||||
/// Write `bytes` to `path`, else to a file picked with `name` suggested next to the document (the
|
||||
/// web downloads them as `name`) → where they went.
|
||||
/// Write `bytes` to `path`, else to a file picked in a save panel that suggests `name` next to the
|
||||
/// document and shows its file type, [`file_types`] (the web downloads them as `name`) → where
|
||||
/// they went.
|
||||
pub(crate) fn write_named(app: &mut VectorcraftApp, path: Option<String>, name: &str, bytes: &[u8]) -> Result<String, String> {
|
||||
let path = match path {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
let (_, folder) = suggested(app, "");
|
||||
pick_path(app, &FilePick { name: name.to_string(), folder, ..Default::default() })?
|
||||
let filters = file_types(&fileio::extension(name));
|
||||
pick_path(app, &FilePick { name: name.to_string(), folder, filters })?
|
||||
}
|
||||
};
|
||||
write_reporting(app, &path, bytes)?;
|
||||
@@ -344,14 +346,40 @@ fn suggested(app: &VectorcraftApp, ext: &str) -> (String, Option<String>) {
|
||||
(format!("{}.{ext}", fileio::file_stem(&t)), folder.filter(|f| !f.is_empty()))
|
||||
}
|
||||
|
||||
/// The save panel's file type, `(label, extensions)`, for the files `write_named` and
|
||||
/// `target_path` write that are not a document format (`fileio::format` has those).
|
||||
const FILE_TYPES: &[(&str, &[&str])] = &[
|
||||
("Swatch libraries", &["vcswatches"]),
|
||||
("Swatch libraries", &["gpl"]),
|
||||
("Swatch libraries", &["ase"]),
|
||||
("Graphic Style Libraries", &[vectorcraft_doc::style_libs::STYLES_EXT]),
|
||||
("Libraries", vectorcraft_engine::cmd::library::LIBRARY_EXTS),
|
||||
("Flattener presets", vectorcraft_engine::cmd::flatten::PRESET_EXTS),
|
||||
("PDF presets", vectorcraft_engine::cmd::pdfcmds::PRESET_EXTS),
|
||||
("Perspective grid presets", vectorcraft_engine::cmd::perspgrid::PRESET_EXTS),
|
||||
("Print presets", vectorcraft_engine::cmd::printpresets::PRESET_EXTS),
|
||||
("CSS", &["css"]),
|
||||
("PostScript", &["ps"]),
|
||||
];
|
||||
|
||||
/// The file type a save panel shows for a file with extension `ext`: its format's, else its
|
||||
/// [`FILE_TYPES`] row; none for any other extension.
|
||||
fn file_types(ext: &str) -> Vec<(&'static str, &'static [&'static str])> {
|
||||
if let Some(f) = fileio::format(ext) {
|
||||
return vec![(f.label, f.extensions)];
|
||||
}
|
||||
let ext = ext.trim_start_matches('.').to_ascii_lowercase();
|
||||
FILE_TYPES.iter().find(|(_, exts)| exts.contains(&ext.as_str())).copied().into_iter().collect()
|
||||
}
|
||||
|
||||
/// `path`, else one picked in a save panel (the web: the suggested name) for the active document
|
||||
/// with extension `ext`, filtered to that format when it is one.
|
||||
/// with extension `ext`, filtered to its file type when it has one ([`file_types`]).
|
||||
pub(crate) fn target_path(app: &mut VectorcraftApp, path: Option<String>, ext: &str) -> Result<String, String> {
|
||||
if let Some(p) = path {
|
||||
return Ok(p);
|
||||
}
|
||||
let (name, folder) = suggested(app, ext);
|
||||
let filters = fileio::format(ext).map(|f| vec![(f.label, f.extensions)]).unwrap_or_default();
|
||||
let filters = file_types(ext);
|
||||
pick_path(app, &FilePick { name, folder, filters })
|
||||
}
|
||||
|
||||
@@ -684,7 +712,8 @@ pub fn export(app: &mut VectorcraftApp, format: Option<&str>, path: Option<Strin
|
||||
}
|
||||
|
||||
/// Run an engine command that returns `{dataBase64}` and write the bytes to a picked path
|
||||
/// (Export Selection, Save as Template).
|
||||
/// (Export Selection, Save for Office Documents, Text Export Options and the presets dialogs'
|
||||
/// Export…).
|
||||
pub fn save_command_output(app: &mut VectorcraftApp, id: &str, ext: &str, params: Value) -> Result<String, String> {
|
||||
let (path, _) = run_to_file(app, id, ext, params)?;
|
||||
app.status(format!("Saved {path}"));
|
||||
|
||||
@@ -476,7 +476,8 @@ pub fn run_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Resu
|
||||
"shortcuts.export" => {
|
||||
let v = export_json(&app.ui.shortcut_set, &app.ui.shortcut_overrides);
|
||||
let bytes = serde_json::to_vec_pretty(&v).unwrap_or_default();
|
||||
let path = s("path").or_else(|| crate::picks::save(app, &crate::FilePick::named("VectorCraft Shortcuts.json")));
|
||||
let pick = crate::FilePick { filters: vec![("Shortcut sets", &["json"])], ..crate::FilePick::named("VectorCraft Shortcuts.json") };
|
||||
let path = s("path").or_else(|| crate::picks::save(app, &pick));
|
||||
match path {
|
||||
Some(path) => match app.services.write.as_mut() {
|
||||
Some(w) => w(&path, &bytes).map(|_| json!({"path": path})),
|
||||
|
||||
@@ -34,6 +34,73 @@ fn last(w: &Written) -> String {
|
||||
w.borrow().last().cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The file types each save panel was asked to show.
|
||||
type Shown = Rc<RefCell<Vec<Vec<(&'static str, &'static [&'static str])>>>>;
|
||||
|
||||
/// An app with a document whose save panel records the file types it shows and answers the
|
||||
/// suggested name in `/tmp`.
|
||||
fn panel_app() -> (VectorcraftApp, Shown) {
|
||||
let shown = Shown::default();
|
||||
let s = shown.clone();
|
||||
let services = Services {
|
||||
write: Some(Box::new(|_: &str, _: &[u8]| Ok(()))),
|
||||
pick_save: Some(Box::new(move |pick: &FilePick| {
|
||||
s.borrow_mut().push(pick.filters.clone());
|
||||
Some(format!("/tmp/{}", pick.name))
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
let mut app = VectorcraftApp::new(Session::new(), services);
|
||||
app.run("file.new", json!({"width": 100, "height": 100})).unwrap();
|
||||
app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 30, "height": 20})).unwrap();
|
||||
(app, shown)
|
||||
}
|
||||
|
||||
/// The file types the last save panel showed.
|
||||
fn last_shown(s: &Shown) -> Vec<(&'static str, &'static [&'static str])> {
|
||||
s.borrow().last().cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Document Info's, the libraries', the presets', the CSS, the PostScript and the shortcut saves
|
||||
/// show the one file type they write in the save panel, as Save and Export show theirs.
|
||||
#[test]
|
||||
fn save_panels_show_the_file_type_they_write() {
|
||||
use vectorcraft_engine::cmd::{flatten, pdfcmds, perspgrid, printpresets};
|
||||
let (mut app, shown) = panel_app();
|
||||
app.run("docInfo.save", json!({})).unwrap();
|
||||
assert_eq!(last_shown(&shown), [("Text", &["txt"][..])], "Document Info › Save");
|
||||
for format in ["vcswatches", "gpl", "ase", "css"] {
|
||||
app.run("ui.saveSwatchLibrary", json!({})).unwrap();
|
||||
app.ui.dialog.as_mut().unwrap().fields.insert("format".into(), json!(format));
|
||||
crate::dialogs::confirm(&mut app).unwrap();
|
||||
let label = if format == "css" { "CSS" } else { "Swatch libraries" };
|
||||
assert_eq!(last_shown(&shown), [(label, &[format][..])], "Save Swatch Library as .{format}");
|
||||
}
|
||||
app.run("ui.saveGraphicStyleLibrary", json!({})).unwrap();
|
||||
crate::dialogs::confirm(&mut app).unwrap();
|
||||
assert_eq!(last_shown(&shown), [("Graphic Style Libraries", &["vcstyles"][..])], "Save Graphic Style Library");
|
||||
let id = app.run("library.create", json!({"name": "Brand"})).unwrap()["id"].as_str().unwrap().to_string();
|
||||
io::save_command_output_named(&mut app, "library.export", "Brand.vclibrary", json!({"library": id})).unwrap();
|
||||
assert_eq!(last_shown(&shown), [("Libraries", &["vclibrary"][..])], "Libraries panel › Export Library…");
|
||||
app.run("css.exportFile", json!({"scope": "all"})).unwrap();
|
||||
assert_eq!(last_shown(&shown), [("CSS", &["css"][..])], "CSS Properties › Export All…");
|
||||
app.run("file.print", json!({"format": "postscript"})).unwrap();
|
||||
assert_eq!(last_shown(&shown), [("PostScript", &["ps"][..])], "Print to a PostScript file");
|
||||
app.run("shortcuts.export", json!({})).unwrap();
|
||||
assert_eq!(last_shown(&shown), [("Shortcut sets", &["json"][..])], "Keyboard Shortcuts › Export…");
|
||||
let presets = [
|
||||
("pdf.preset.save", json!({"name": "Mine"}), "pdf.preset.export", "PDF presets", pdfcmds::PRESET_EXTS),
|
||||
("print.presets.save", json!({"name": "Mine"}), "print.presets.export", "Print presets", printpresets::PRESET_EXTS),
|
||||
("flattener.presets.save", json!({"name": "Mine"}), "flattener.presets.export", "Flattener presets", flatten::PRESET_EXTS),
|
||||
("perspective.presets.save", json!({"name": "Mine"}), "perspective.presets.export", "Perspective grid presets", perspgrid::PRESET_EXTS),
|
||||
];
|
||||
for (save, params, export, label, exts) in presets {
|
||||
app.run(save, params).unwrap();
|
||||
io::save_command_output(&mut app, export, exts[0], json!({"names": ["Mine"]})).unwrap();
|
||||
assert_eq!(last_shown(&shown), [(label, exts)], "{export}: the presets dialog's Export…");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saves_add_the_format_extension_to_a_bare_name() {
|
||||
let (mut app, w) = saving_app("/tmp/Untitled-1");
|
||||
|
||||
Reference in New Issue
Block a user