Enable Project panel Reveal in Finder for clips with a media file.
file.reveal is enabled when a selected clip has a local path, greyed out with no selection, and the OS opener reveals the file (xdg-open of the parent folder on Linux).
This commit is contained in:
parent
4e2d0bc2d3
commit
488259414b
|
|
@ -319,6 +319,7 @@ fn open_path(path: &str, reveal: bool) -> Result<(), String> {
|
|||
}
|
||||
c
|
||||
} else {
|
||||
// Linux file managers have no select-file flag: open the parent folder instead.
|
||||
let mut c = std::process::Command::new("xdg-open");
|
||||
let p = std::path::Path::new(path);
|
||||
c.arg(if reveal { p.parent().unwrap_or(p) } else { p });
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
//! | `file.projectSettings.general` / `.scratchDisks` | File ▸ Project Settings ▸ General… / Scratch Disks… |
|
||||
//! | `edit.find` / `edit.findNext` | Edit ▸ Find… (⌘F) / Find Next |
|
||||
//! | `edit.editOriginal` | Edit ▸ Edit Original (⌘E): the frontend opens the files |
|
||||
//! | `file.reveal` | Project panel ▸ Reveal in Finder / Show in Explorer: the frontend reveals the media file |
|
||||
//! | `clip.editOffline` | Clip ▸ Edit Offline… |
|
||||
//! | `clip.sourceSettings` | Clip ▸ Source Settings… |
|
||||
//! | `clip.restoreCaptionsFromSource` | Clip ▸ Restore Captions from Source Clip (always disabled: no embedded caption reader) |
|
||||
|
|
@ -163,6 +164,8 @@ pub(crate) fn commands() -> Vec<CommandSpec> {
|
|||
spec("edit.find", "Find…", &["Edit"], Some("Cmd+F"), FIND_PARAMS, always, find),
|
||||
spec("edit.findNext", "Find Next", &["Edit"], None, "{}", has_find, find_next),
|
||||
spec("edit.editOriginal", "Edit Original", &["Edit"], Some("Cmd+E"), r#"{"items":[id]?}"#, has_original, edit_original),
|
||||
// Project panel context menu (not in the File menu): reveal the clip's media file.
|
||||
query("file.reveal", "Reveal in Finder", &[], None, r#"{"items":[id]?}"#, has_file_selection, edit_original),
|
||||
// ---------------- Clip ----------------
|
||||
spec(
|
||||
"clip.editOffline",
|
||||
|
|
|
|||
|
|
@ -361,6 +361,18 @@ fn edit_original_update_metadata_and_clip_items() {
|
|||
assert!(std::fs::read_to_string(&side).unwrap().contains("someone else"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_reveal_enabled_for_imported_clip() {
|
||||
let dir = tmp_dir("reveal");
|
||||
let path = dir.join("take.mp4");
|
||||
make_movie(&path, DemoScene::Aurora, 64, 36, 8);
|
||||
let (mut s, items, _) = session_with(&[&path]);
|
||||
assert!(!s.is_enabled("file.reveal"), "disabled with no Project panel selection");
|
||||
s.execute("project.select", json!({"items": [items[0].0]})).unwrap();
|
||||
assert!(s.is_enabled("file.reveal"));
|
||||
assert_eq!(s.execute("file.reveal", json!({})).unwrap()["paths"], json!([path.to_string_lossy()]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edit_offline_sets_the_log_fields() {
|
||||
let mut s = demo();
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
//! Update Metadata, Automate to Sequence, Scene Edit Detection, Normalize Mix Track, Simplify
|
||||
//! Sequence, Transcribe Sequence, Save as Template, Add Flash Cue Marker and the System
|
||||
//! Compatibility Report; plus the frontend side of Edit Original / Reveal Log Files (the OS
|
||||
//! opener), Generate Audio Waveform (the timeline's peak cache), Import from Media Browser (the
|
||||
//! Media Browser selection) and View ▸ Dynamic Audio Waveforms.
|
||||
//! opener), Reveal in Finder, Generate Audio Waveform (the timeline's peak cache), Import from
|
||||
//! Media Browser (the Media Browser selection) and View ▸ Dynamic Audio Waveforms.
|
||||
//!
|
||||
//! The open dialog lives in `UiState::extras.dialog` (serde, like `clip_dialogs`): the engine
|
||||
//! command it runs and the parameters being edited, so agents open it from the menu, change
|
||||
|
|
@ -185,11 +185,12 @@ pub fn route(app: &mut FilmcraftApp, ctx: &egui::Context, id: &str, params: &Val
|
|||
app.ui.extras.dialog = Some(ClipDialogDraft { command: id.into(), params: json!({}), info: report.clone(), error: String::new() });
|
||||
return Some(Ok(json!({"dialog": "systemReport", "report": report})));
|
||||
}
|
||||
"edit.editOriginal" => {
|
||||
"edit.editOriginal" | "file.reveal" => {
|
||||
let reveal = id == "file.reveal";
|
||||
let r = app.session.execute(id, params.clone()).map_err(|e| e.to_string());
|
||||
return Some(r.and_then(|v| {
|
||||
for p in v["paths"].as_array().cloned().unwrap_or_default() {
|
||||
open_path(app, ctx, p.as_str().unwrap_or_default(), false)?;
|
||||
open_path(app, ctx, p.as_str().unwrap_or_default(), reveal)?;
|
||||
}
|
||||
Ok(v)
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -262,6 +262,13 @@ fn item_menu(app: &mut FilmcraftApp, ui: &mut egui::Ui, id: ItemId, kind: &ItemK
|
|||
}
|
||||
}
|
||||
});
|
||||
let can_reveal = app.session.project.resolve_media(id).is_some_and(|(_, m, _)| matches!(m.media, filmcraft_project::MediaRef::File { .. }));
|
||||
let b = ui.add_enabled(can_reveal, egui::Button::new(tl!("Reveal in Finder")));
|
||||
app.auto.add("project.itemMenu.file.reveal", b.rect, "Reveal in Finder");
|
||||
if b.clicked() {
|
||||
actions.push(("file.reveal".into(), json!({"items": [id.0]})));
|
||||
ui.close();
|
||||
}
|
||||
if matches!(kind, ItemKind::Media(m) if matches!(m.media, filmcraft_project::MediaRef::File { .. })) {
|
||||
ui.separator();
|
||||
for (label, cmd) in [(tl!("Link Media…"), "media.linkMedia"), (tl!("Make Offline…"), "media.makeOffline")] {
|
||||
|
|
|
|||
|
|
@ -305,3 +305,20 @@ fn color_matte_dialog_picks_and_changes_the_color() {
|
|||
assert_eq!(d.call("ui.menu.invoke", json!({"id": "project.matteColor"}))["ok"], json!(false));
|
||||
assert!(!d.dialog_open());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reveal_in_finder_opens_an_imported_clip() {
|
||||
let dir = std::env::temp_dir().join(format!("fc-ui-reveal-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("clip.png");
|
||||
image::RgbaImage::from_pixel(8, 8, image::Rgba([10, 20, 30, 255])).save(&path).unwrap();
|
||||
let mut d = Driver::new();
|
||||
let item = d.exec("file.import", json!({"paths": [path.to_string_lossy()]}))["items"][0].as_u64().unwrap();
|
||||
assert!(!d.app().session.is_enabled("file.reveal"));
|
||||
d.exec("project.select", json!({"items": [item]}));
|
||||
assert!(d.app().session.is_enabled("file.reveal"));
|
||||
let r = d.ok("ui.menu.invoke", json!({"id": "file.reveal"}));
|
||||
assert_eq!(r["paths"][0], path.to_string_lossy().as_ref());
|
||||
assert_eq!(d.app().ui.extras.opened.last().map(String::as_str), Some(path.to_string_lossy().as_ref()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue