#678: dropped Photoshop files show the Import Photoshop File dialog

Problem: dropping a PSD/PSB file on the window imported it straight away.
The drop handler always passed `importAs` (Settings > Import > Default Drag
Import As) to `file.import`, and the Import Photoshop File form only opens
when `importAs` is missing, so the dialog that File > Import > File...
shows never appeared for drops.

Fix: a drop that includes a Photoshop file leaves `importAs` out, so the
form asks (other drops keep passing the preference). For dropped files the
form's Import Kind starts at Default Drag Import As; File > Import keeps
starting at Composition. The engine already falls back to the preference
for `drag` imports without `importAs`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
vangel76
2026-10-11 11:28:18 +02:00
co-authored by Claude Opus 5.5
parent 9a7d3e7699
commit f9db3afd34
3 changed files with 45 additions and 11 deletions
+9 -10
View File
@@ -215,16 +215,15 @@ pub fn open_form(app: &mut EffectcraftApp, id: &str, p: &Value) -> bool {
let lt = layer.map(|l| l.layer_time(t)).unwrap_or(t);
let base = p.clone();
let (title, fields): (String, Vec<Field>) = match id {
// Photoshop files: Import Kind (Footage / Composition / – Retain Layer Sizes).
"file.import" if !has(p, &["importAs"]) && is_layered(p) => (
"Import Photoshop File".into(),
vec![Field::choice(
"importAs",
"Import Kind",
&[("Footage", json!("footage")), ("Composition", json!("composition")), ("Composition - Retain Layer Sizes", json!("compositionLayerSizes"))],
1,
)],
),
// Photoshop files: Import Kind (Footage / Composition / – Retain Layer Sizes). Dropped
// files start at Settings ▸ Import ▸ Default Drag Import As.
"file.import" if !has(p, &["importAs"]) && is_layered(p) => {
let kinds =
[("Footage", json!("footage")), ("Composition", json!("composition")), ("Composition - Retain Layer Sizes", json!("compositionLayerSizes"))];
let drag = p.get("drag").and_then(Value::as_bool).unwrap_or(false);
let sel = if drag { kinds.iter().position(|(_, v)| v.as_str() == Some(s.prefs.drag_import_as())).unwrap_or(0) } else { 1 };
("Import Photoshop File".into(), vec![Field::choice("importAs", "Import Kind", &kinds, sel)])
}
// PDF / Illustrator files: Import Kind and the page.
"file.import" if !has(p, &["importAs", "page"]) && is_pdf(p) => (
"Import PDF / Illustrator File".into(),
+6 -1
View File
@@ -91,14 +91,19 @@ pub fn dropped_files(app: &mut EffectcraftApp, ui: &egui::Ui, bounds: Rect) {
// Dropped files import as stills and folders' numbered stills as sequences; Alt-drag
// imports a folder's files one by one (After Effects).
let alt = ctx.input(|i| i.modifiers.alt);
// A dropped Photoshop file asks how to import it (the Import Photoshop File dialog, its
// Import Kind starting at Default Drag Import As), as File ▸ Import ▸ File… does (#678).
let layered = rest.iter().any(|p| p.rsplit('.').next().is_some_and(|e| matches!(e.to_ascii_lowercase().as_str(), "psd" | "psb")));
let mut params = json!({
"paths": rest,
"drag": true,
"importAs": app.session.prefs.drag_import_as(),
"addToComp": at.is_some(),
"position": at,
"background": true
});
if !layered {
params["importAs"] = json!(app.session.prefs.drag_import_as());
}
if alt {
params["sequence"] = json!(false);
}
+30
View File
@@ -15,6 +15,7 @@ use effectcraft_ui_egui::dock::PanelKind;
use effectcraft_ui_egui::panels::viewer::{comp_to_screen, last_fit};
use egui::{Event, Modifiers, Pos2, Rect, pos2, vec2};
use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;
use serde_json::json;
/// Comp "Main" (4 s at 30 fps, current time 1 s) with solids Top, Middle and Bottom, and comp
@@ -379,6 +380,35 @@ fn dropped_file_on_viewer_imports_and_adds_layer_at_pointer() {
assert!((p[0] - 80.0).abs() <= tol && (p[1] - 45.0).abs() <= tol, "{p:?}");
}
/// #678: a dropped Photoshop file asks how to import it, as File ▸ Import ▸ File… does; the
/// Import Kind starts at Settings ▸ Import ▸ Default Drag Import As, and OK imports the drop.
#[test]
fn dropped_photoshop_file_shows_the_import_photoshop_file_dialog() {
let (mut h, _) = harness();
h.state_mut().session.importer = Some(Arc::new(Gated { started: AtomicUsize::new(0), allow: Arc::new(AtomicUsize::new(usize::MAX)) }));
h.state_mut().session.prefs.import.drag_import_as = "compLayerSizes".into();
h.event(Event::PointerMoved(rect(&h, "panel.Project").center()));
h.step();
h.input_mut().dropped_files.push(Arc::new(Dropped(std::env::temp_dir().join("Layers.PSD"))));
h.run_steps(2);
assert_eq!(h.state().dialog, Some(effectcraft_ui_egui::Dialog::Form), "the drop asks first");
h.run_steps(2);
assert!(h.query_by_label("Import Photoshop File").is_some());
assert!(h.query_by_value("Composition - Retain Layer Sizes").is_some(), "Import Kind starts at Default Drag Import As");
assert!(!h.state().session.project.items.values().any(|i| i.name == "Layers.PSD"), "nothing imported before OK");
click_id(&mut h, "form.cancel");
assert_eq!(h.state().dialog, None);
// With Default Drag Import As at Footage, OK imports the file as one item.
h.state_mut().session.prefs.import.drag_import_as = "footage".into();
h.input_mut().dropped_files.push(Arc::new(Dropped(std::env::temp_dir().join("Layers.PSD"))));
h.run_steps(4);
assert_eq!(h.state().dialog, Some(effectcraft_ui_egui::Dialog::Form));
assert!(h.query_by_value("Footage").is_some());
click_id(&mut h, "form.ok");
step_until(&mut h, |h| h.state().session.project.items.values().any(|i| i.name == "Layers.PSD"));
}
#[test]
fn an_unsupported_drop_reports_the_error_and_keeps_valid_files() {
let (mut h, _) = harness();