mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 18:56:55 -06:00
Merge pull request #4263 from nrq6993-a11y/stream/vc-feat-4122-open-last-folder
#4122: File → Open starts in the last opened file's folder
This commit is contained in:
@@ -74,7 +74,7 @@ pub use native::with_compression_pref;
|
||||
pub use pdfimport::{LoadOptions, page_document};
|
||||
pub(crate) use save::job_for;
|
||||
pub use save::{
|
||||
SAVE_FORMATS, SaveJob, SaveMode, SavePlan, StreamWrite, TEMPLATE_FORMATS, export_folder, reads_option as save_reads_option,
|
||||
SAVE_FORMATS, SaveJob, SaveMode, SavePlan, StreamWrite, TEMPLATE_FORMATS, dialog_folder, export_folder, reads_option as save_reads_option,
|
||||
remembered_save_options, save_filters, save_format, save_job, save_plan, save_with, stamp_save_dates, template_filters, templates_dialog_folder,
|
||||
templates_folder,
|
||||
};
|
||||
|
||||
@@ -185,6 +185,14 @@ fn parent_folder(path: &str) -> Option<String> {
|
||||
std::path::Path::new(path).parent().map(|p| p.to_string_lossy().to_string()).filter(|p| !p.is_empty())
|
||||
}
|
||||
|
||||
/// A file dialog's starting folder for a path on disk (#4122): the path's parent when it is a
|
||||
/// folder, else the nearest ancestor directory that exists.
|
||||
pub fn dialog_folder(path: &str) -> Option<String> {
|
||||
let folder = parent_folder(path)?;
|
||||
let path = std::path::PathBuf::from(folder);
|
||||
path.ancestors().find(|a| a.is_dir()).map(|a| a.to_string_lossy().into_owned())
|
||||
}
|
||||
|
||||
/// A save format by id or extension, or why it isn't one.
|
||||
fn save_format_of(f: &str) -> std::result::Result<&'static Format, String> {
|
||||
match format(f) {
|
||||
@@ -300,7 +308,7 @@ fn disagreeing_option(p: &Value, svg: bool) -> std::result::Result<Option<(Strin
|
||||
.as_object()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter(|(k, v)| !(v.is_null() || matches!(k.as_str(), "path" | "format" | "options") || (svg && k.as_str() == "svg")))
|
||||
.filter(|(k, v)| !(v.is_null() || matches!(k.as_str(), "path" | "format" | "options") || svg && k.as_str() == "svg"))
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect();
|
||||
if svg {
|
||||
@@ -1020,6 +1028,20 @@ mod tests_folders {
|
||||
assert_eq!(super::xdg_documents("XDG_MUSIC_DIR=\"$HOME/Musica\"", home), None);
|
||||
}
|
||||
|
||||
/// #4122: File → Open starts in the folder of the last opened file.
|
||||
#[test]
|
||||
fn dialog_folder_is_the_nearest_existing_parent() {
|
||||
let base = std::env::temp_dir().join(format!("vc-open-folder-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
std::fs::create_dir_all(&base).unwrap();
|
||||
let file = base.join("poster.svg");
|
||||
assert_eq!(super::dialog_folder(&file.to_string_lossy()).map(PathBuf::from), Some(base.clone()));
|
||||
let missing = base.join("gone").join("card.ai");
|
||||
assert_eq!(super::dialog_folder(&missing.to_string_lossy()).map(PathBuf::from), Some(base.clone()));
|
||||
assert!(super::dialog_folder("poster.svg").is_none());
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
|
||||
/// #703: the Templates folder a dialog starts in exists: made when missing, else the nearest
|
||||
/// folder above it.
|
||||
#[test]
|
||||
|
||||
@@ -189,13 +189,20 @@ pub fn install_module(app: &mut VectorcraftApp, module: Value) -> Result<Value,
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Where File → Open… starts: the folder of the most recently opened file, else the active
|
||||
/// document's (#4122).
|
||||
fn open_dialog_folder(app: &VectorcraftApp) -> Option<String> {
|
||||
app.ui.recent_files.iter().find_map(|p| fileio::dialog_folder(p)).or_else(|| suggested(app, "").1)
|
||||
}
|
||||
|
||||
/// File → Open…
|
||||
pub fn open_dialog(app: &mut VectorcraftApp) -> Result<(), String> {
|
||||
if let Some(f) = app.services.open_async.as_mut() {
|
||||
f();
|
||||
return Ok(());
|
||||
}
|
||||
let path = pick_open(app, &FilePick { filters: FilePick::filters(fileio::open_filters()), ..Default::default() })?;
|
||||
let path =
|
||||
pick_open(app, &FilePick { filters: FilePick::filters(fileio::open_filters()), folder: open_dialog_folder(app), ..Default::default() })?;
|
||||
open_reporting(app, &path).map(|_| ())
|
||||
}
|
||||
|
||||
|
||||
@@ -154,12 +154,28 @@ fn saved_document(app: &mut VectorcraftApp, tag: &str) -> (std::path::PathBuf, S
|
||||
|
||||
// ------------------------------------------------------------------------------------- the tests
|
||||
|
||||
#[test]
|
||||
fn file_open_starts_in_the_last_opened_files_folder() {
|
||||
let (mut app, shown, _) = app();
|
||||
let dir = std::env::temp_dir().join(format!("vc-open-dialog-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("poster.svg").to_string_lossy().into_owned();
|
||||
crate::io::note_recent(&mut app, &path);
|
||||
assert!(app.run("file.open", json!({})).is_err());
|
||||
poll(&mut app);
|
||||
let folder = dir.to_string_lossy().into_owned();
|
||||
assert!(matches!(shown.requests().as_slice(), [PickRequest::Open(p)] if p.folder == Some(folder)));
|
||||
shown.answer(&[]);
|
||||
poll(&mut app);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_open_opens_what_its_dialog_picks_when_it_answers() {
|
||||
let (mut app, shown, _) = app();
|
||||
// The dialog shows; File › Open gives up for now, without a "cancelled".
|
||||
assert!(app.run("file.open", json!({})).is_err());
|
||||
assert!(matches!(shown.requests().as_slice(), [PickRequest::Open(_)]));
|
||||
assert!(matches!(shown.requests().as_slice(), [PickRequest::Open(p)] if p.folder.is_none()));
|
||||
poll(&mut app);
|
||||
assert!(app.session.documents().is_empty() && app.ui.status.is_empty(), "{}", app.ui.status);
|
||||
// Another dialog waits for this one.
|
||||
|
||||
Reference in New Issue
Block a user