Files
photocraft/crates/ui-egui/src/replace_prompt.rs
T
dotCooCoo a78e9f05fc Save: ask before replacing a file that an added or lowercased extension names
The save dialog asks before replacing only the file it answers with.
After it closes, pick_save can change that path: it gives a name
without an extension the suggested file's extension (#5039), and
Lowercase Extension changes CAT.PSD to CAT.psd. The save then wrote to
the changed path at once, and a file there was replaced without a
question. On Linux this happens when the Swatches panel exports to a
folder that holds Swatches.aco (the one-type portal dialog shows and
asks about "Swatches"), when CAT.PSD is typed where CAT.psd exists, and
in rfd's zenity fallback dialog.

When the path written differs from the dialog's answer and a file
exists there, the save now asks "“{name}” already exists. Replace it?".
Replace writes the file, and ui.dialog.confirm returns the save's
result. Cancel writes nothing and ends the save like the dialog's
Cancel, and Save in the close prompt of an untitled document returns
to that prompt. PhotoCraft counts the file dialog as open until the
question is answered.

A name that reaches the same file, as CAT.PSD and CAT.psd do on
Windows and macOS by default, is not asked about again. The new
same_file service decides this: the desktop app compares device and
inode on Unix and the canonical paths on Windows. The web has no file
stamps and does not ask. The two new strings have rows in all 20
catalogs.
2026-10-17 03:08:09 -07:00

47 lines
2.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! A save asks before replacing a file that the save dialog did not ask about. The dialog asks
//! only about the name it answers, and the app can change that name afterwards: it gives a name
//! without an extension the suggested file's (#5039), and lowercases the extension when
//! Preferences ▸ File Handling › Lowercase Extension is on. When a file exists under the changed
//! name, Replace writes over it and Cancel ends the save like the save dialog's Cancel.
//!
//! The prompt is an ordinary dialog in the UI state: `ui.dialog.confirm` replaces the file and
//! `ui.dialog.cancel` keeps it. The file dialog stays open until it is answered
//! ([`PhotocraftApp::file_dialog_open`]).
use serde_json::{Map, Value, json};
use crate::PhotocraftApp;
use crate::file_open::display_name;
use crate::state::DialogKind;
const MARK: &str = "__replaceSaved";
pub fn owns(fields: &Map<String, Value>) -> bool {
fields.contains_key(MARK)
}
/// Opens the prompt about `path`, the file a save writes to for `answered`, the save dialog's
/// answer.
pub(crate) fn open(app: &mut PhotocraftApp, answered: &str, path: &str) {
let message = crate::i18n::fmt(tl!("“{name}” already exists. Replace it?"), &[("name", display_name(path).as_str())]);
let detail = crate::i18n::fmt(tl!("The name in the save dialog was “{name}”."), &[("name", display_name(answered).as_str())]);
let fields = json!({MARK: true, "__label": "Save", "message": message, "detail": detail, "path": path});
app.ui.open_dialog(DialogKind::Command, fields.as_object().cloned().unwrap_or_default());
}
pub fn body(ui: &mut egui::Ui, fields: &Map<String, Value>) {
ui.label(fields.get("message").and_then(Value::as_str).unwrap_or_default());
ui.add_space(6.0);
ui.label(fields.get("detail").and_then(Value::as_str).unwrap_or_default());
}
/// Replace: the save the prompt held back.
pub fn confirm(app: &mut PhotocraftApp) -> Result<Value, String> {
app.answer_replace(true)
}
/// Cancel: nothing is written.
pub fn cancel(app: &mut PhotocraftApp) -> Result<Value, String> {
app.answer_replace(false)
}