explorer actions

This commit is contained in:
vcoppe committed 2026-10-17 23:23:29 +02:00
1 parent adbf89e318
commit 3ef2bf135a
20 files changed
+1183 -65

No files matched your search

+102 -3
View File
@@ -14,6 +14,7 @@ use tauri::{AppHandle, Runtime, State};
use tauri_plugin_dialog::{DialogExt, FilePath};
use uuid::Uuid;
use crate::commands::edit;
use crate::state::AppState;
use crate::workspace::{Entry, Unsaved, WorkspaceState, free_path};
@@ -40,16 +41,31 @@ pub fn open_files<R: Runtime>(app: AppHandle<R>, state: State<AppState>) -> api:
let Some(picked) = picked else {
return api::Outcome::failed_none();
};
open_paths(state, paths(picked))
open_paths(state, paths(picked), None)
}
/// Opens files that are on the disk, like the ones of the tree of the open folder. They get selected.
/// With `preview`, a single file is open as a preview (a single click in the tree, see
/// `Workspace::open_preview`); otherwise they are open for good, even the preview.
#[tauri::command(async)]
pub fn open_paths(state: State<AppState>, paths: Vec<PathBuf>) -> api::Outcome {
pub fn open_paths(
state: State<AppState>,
paths: Vec<PathBuf>,
preview: Option<bool>,
) -> api::Outcome {
state.core(move |core| {
let outcome = core.workspace.open_files(&mut core.engine, &paths, true);
let outcome = match paths.as_slice() {
[path] if preview == Some(true) => core.workspace.open_preview(&mut core.engine, path),
_ => {
let outcome = core.workspace.open_files(&mut core.engine, &paths, true);
let ids = core.workspace.ids_at(&paths);
core.workspace.keep(&ids);
outcome
}
};
core.keep_workspace();
core.persist();
core.announce(None);
outcome
})
}
@@ -391,3 +407,86 @@ pub fn export_files<R: Runtime>(
}
}
}
/// Makes a folder in a folder of the open folder, called `name` (numbered if that is taken). Gives
/// its path.
#[tauri::command(async)]
pub fn create_folder(
state: State<AppState>,
parent: PathBuf,
name: String,
) -> Result<PathBuf, String> {
state.core(move |core| core.workspace.create_folder(&parent, &name))
}
/// Renames a file or a folder of the open folder. An open file is renamed like in its info (it is
/// selected), which can be undone; the rest is renamed on the disk, and the open files that are in
/// a folder that is renamed follow.
#[tauri::command(async)]
pub fn rename_path(
state: State<AppState>,
path: PathBuf,
name: String,
) -> Result<api::Outcome, String> {
state.core(move |core| {
if let Some(id) = core.workspace.linked(&path) {
let selected = api::select(&mut core.engine, &[id], gpx_engine::SelectMode::Replace);
let desc = core
.engine
.file_structure(&gpx_engine::FileId(id))
.and_then(|structure| structure.desc.clone())
.unwrap_or_default();
return Ok(selected.merge(edit::rename_selection(core, name, desc)));
}
core.workspace.rename_path(&path, &name)?;
core.keep_workspace();
core.announce(None);
Ok(api::Outcome::failed_none())
})
}
/// Moves files and folders of the open folder into a folder of it. The open files follow.
#[tauri::command(async)]
pub fn move_paths(
state: State<AppState>,
paths: Vec<PathBuf>,
folder: PathBuf,
) -> Result<(), String> {
state.core(move |core| {
let result = core.workspace.move_paths(&paths, &folder);
core.keep_workspace();
core.announce(None);
result
})
}
/// Moves files and folders of the open folder to the trash. The open files that were among them
/// are closed, unless they have edits: they stay open, as gone from the disk.
#[tauri::command(async)]
pub fn delete_paths(state: State<AppState>, paths: Vec<PathBuf>) -> Result<api::Outcome, String> {
state.core(move |core| {
let outcome = core
.workspace
.delete_paths(&mut core.engine, &paths, to_trash)?;
core.keep_workspace();
core.persist();
core.announce(None);
Ok(outcome)
})
}
/// Moves files and folders to the trash of the system.
fn to_trash(paths: &[PathBuf]) -> Result<(), String> {
#[allow(unused_mut)]
let mut trash = trash::TrashContext::default();
// without asking the Finder, which needs a permission
#[cfg(target_os = "macos")]
{
use trash::macos::{DeleteMethod, TrashContextExtMacos};
trash.set_delete_method(DeleteMethod::NsFileManager);
}
trash.delete_all(paths).map_err(|error| {
eprintln!("the files cannot be deleted: {error}");
crate::workspace::DELETE_FAILED.to_owned()
})
}
+14 -11
View File
@@ -5,24 +5,27 @@ use gpx_engine::{CleanType, FileId, MergeType, Selection, SplitType, api};
use tauri::State;
use uuid::Uuid;
use crate::state::AppState;
use crate::state::{AppState, Core};
/// Names and describes the selected files or tracks. A file on the disk is renamed with it (and back
/// when it is undone): its name is one that a file can have, and not the one of another file.
#[tauri::command(async)]
pub fn metadata(state: State<AppState>, name: String, desc: String) -> api::Outcome {
state.core(move |core| {
let mut name = name;
if let Selection::File { file_ids } = core.engine.selection() {
for FileId(id) in file_ids.clone() {
name = core.workspace.valid_name(id, &name);
if let Err(error) = core.workspace.renamed_path(id, &name) {
return api::Outcome::failed(error);
}
state.core(move |core| rename_selection(core, name, desc))
}
/// See `metadata`.
pub fn rename_selection(core: &mut Core, name: String, desc: String) -> api::Outcome {
let mut name = name;
if let Selection::File { file_ids } = core.engine.selection() {
for FileId(id) in file_ids.clone() {
name = core.workspace.valid_name(id, &name);
if let Err(error) = core.workspace.renamed_path(id, &name) {
return api::Outcome::failed(error);
}
}
core.edit(move |e| api::metadata(e, &name, &desc))
})
}
core.edit(move |e| api::metadata(e, &name, &desc))
}
#[tauri::command(async)]
+10
View File
@@ -1,5 +1,7 @@
//! What the app asks of the system, outside of the files.
use std::path::PathBuf;
use tauri::{AppHandle, Runtime};
use tauri_plugin_opener::OpenerExt;
@@ -16,3 +18,11 @@ pub fn open_url<R: Runtime>(app: AppHandle<R>, url: String) -> Result<(), String
.open_url(url, None::<&str>)
.map_err(|error| error.to_string())
}
/// Shows a file or a folder in the file manager of the system (the Finder...).
#[tauri::command(async)]
pub fn reveal_path<R: Runtime>(app: AppHandle<R>, path: PathBuf) -> Result<(), String> {
app.opener()
.reveal_item_in_dir(path)
.map_err(|error| error.to_string())
}