Confirm layer deletion only from the Layers trash button (#2197)
* fix(ui): confirm deletion only from the Layers trash button * Trash confirmation: Alt-click deletes without asking (Photoshop); drop docs page and screenshot Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: kotulsky <7805437+kotulsky@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2c15f13f02
commit
26cc6ed6bc
|
|
@ -0,0 +1,177 @@
|
|||
//! Confirmation for a Layers-panel trash-button click; other deletion routes stay immediate.
|
||||
use crate::{PhotocraftApp, state::DialogKind};
|
||||
use serde_json::{Map, Value, json};
|
||||
const MARK: &str = "__deleteLayer";
|
||||
const PREF: &str = "layer.delete.confirmation";
|
||||
|
||||
pub fn owns(fields: &Map<String, Value>) -> bool {
|
||||
fields.contains_key(MARK)
|
||||
}
|
||||
|
||||
pub fn intercept(app: &mut PhotocraftApp, command: &str, params: &Value) -> Option<Result<Value, String>> {
|
||||
if app.automation_input
|
||||
|| command != "layer.delete"
|
||||
|| params.get("__trash").and_then(Value::as_bool) != Some(true)
|
||||
|| app.session.prefs().dialogs.get(PREF).and_then(|v| v.get("skip")).and_then(Value::as_bool) == Some(true)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let state = app.session.active()?;
|
||||
let selected = state.selected_layers();
|
||||
let ids = if let Some(id) = params.get("layer").and_then(Value::as_u64) { vec![photocraft_doc::LayerId(id)] } else { selected.clone() };
|
||||
if ids.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let names = ids.iter().filter_map(|id| state.doc.layer(*id).map(|layer| layer.name.as_str())).take(4).collect::<Vec<_>>().join(", ");
|
||||
let message = if ids.len() == 1 {
|
||||
crate::i18n::fmt(tl!("Delete layer ‘{name}’?"), &[("name", &names)])
|
||||
} else {
|
||||
crate::i18n::fmt(tl!("Delete {count} layers? {names}"), &[("count", &ids.len().to_string()), ("names", &names)])
|
||||
};
|
||||
let fields = json!({MARK: true, "__command": command, "__label": "Delete Layer", "message": message,
|
||||
"document": state.doc.id.0, "selected": selected.iter().map(|id| id.0).collect::<Vec<_>>(), "params": params, "skip": false});
|
||||
if app.ui.dialogs.iter().any(|d| owns(&d.fields)) {
|
||||
return Some(Ok(json!({"confirmationPending": true})));
|
||||
}
|
||||
let id = app.ui.open_dialog(DialogKind::Command, fields.as_object().cloned().unwrap_or_default());
|
||||
Some(Ok(json!({"dialog": id, "confirmationPending": true})))
|
||||
}
|
||||
|
||||
pub fn body(ui: &mut egui::Ui, fields: &mut Map<String, Value>) {
|
||||
ui.label(fields.get("message").and_then(Value::as_str).unwrap_or(tl!("Delete layer?")));
|
||||
ui.add_space(8.0);
|
||||
let mut skip = fields.get("skip").and_then(Value::as_bool).unwrap_or(false);
|
||||
ui.checkbox(&mut skip, tl!("Don't show again"));
|
||||
fields.insert("skip".into(), json!(skip));
|
||||
}
|
||||
|
||||
pub fn confirm(app: &mut PhotocraftApp, fields: &Map<String, Value>) -> Result<Value, String> {
|
||||
let state = app.session.active().ok_or(tl!("The document is closed"))?;
|
||||
if fields.get("document").and_then(Value::as_u64) != Some(state.doc.id.0)
|
||||
|| fields.get("selected") != Some(&json!(state.selected_layers().iter().map(|id| id.0).collect::<Vec<_>>()))
|
||||
{
|
||||
return Err(tl!("The document or selected layers changed. Please request deletion again.").into());
|
||||
}
|
||||
let command = fields.get("__command").and_then(Value::as_str).ok_or(tl!("Missing delete command"))?;
|
||||
if command != "layer.delete" {
|
||||
return Err(tl!("Invalid delete command").into());
|
||||
}
|
||||
let result = app.run_command(command, fields.get("params").cloned().unwrap_or(Value::Null))?;
|
||||
if fields.get("skip").and_then(Value::as_bool) == Some(true) {
|
||||
app.session.edit_prefs(|prefs| {
|
||||
prefs.dialogs.insert(PREF.into(), json!({"skip": true}));
|
||||
});
|
||||
if let Err(error) = crate::prefs_ui::save_preferences(app) {
|
||||
app.ui.status = crate::i18n::fmt(tl!("Layer deleted, but confirmation preference could not be saved: {error}"), &[("error", &error)]);
|
||||
app.ui.status_error = true;
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use egui_kittest::kittest::Queryable;
|
||||
fn make_app() -> PhotocraftApp {
|
||||
let mut s = photocraft_engine::Session::new();
|
||||
s.execute("file.new", json!({"width":8,"height":8})).unwrap();
|
||||
s.execute("layer.new.layer", json!({"name":"Ink"})).unwrap();
|
||||
PhotocraftApp::new(s, crate::Services::default())
|
||||
}
|
||||
#[test]
|
||||
fn cancel_keeps_layer_and_confirmation_preference() {
|
||||
let mut app = make_app();
|
||||
let id = app.session.active().unwrap().active_layer.unwrap();
|
||||
let result = app.run("layer.delete", json!({"__trash":true})).unwrap();
|
||||
let dialog = result["dialog"].as_u64().unwrap();
|
||||
assert!(owns(&app.ui.dialogs[0].fields));
|
||||
assert!(app.ui.dialogs[0].fields["message"].as_str().unwrap().contains("Ink"));
|
||||
app.ui.dialog_mut(dialog).unwrap().fields.insert("skip".into(), json!(true));
|
||||
app.ui.close_dialog(dialog);
|
||||
assert!(app.session.active().unwrap().doc.layer(id).is_some());
|
||||
assert!(!app.session.prefs().dialogs.contains_key(PREF));
|
||||
}
|
||||
#[test]
|
||||
fn confirmation_deletes_once_and_remembers_across_restart() {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
let mut app = make_app();
|
||||
let saved = Rc::new(RefCell::new(String::new()));
|
||||
let copy = saved.clone();
|
||||
app.services.save_prefs = Some(Box::new(move |text| {
|
||||
*copy.borrow_mut() = text.to_string();
|
||||
Ok(())
|
||||
}));
|
||||
let target = app.session.active().unwrap().active_layer.unwrap();
|
||||
let before = app.session.active().unwrap().history.past_len();
|
||||
let dialog = app.run("layer.delete", json!({"__trash":true})).unwrap()["dialog"].as_u64().unwrap();
|
||||
app.ui.dialog_mut(dialog).unwrap().fields.insert("skip".into(), json!(true));
|
||||
crate::dialogs::confirm(&mut app, dialog).unwrap();
|
||||
assert!(app.session.active().unwrap().doc.layer(target).is_none());
|
||||
assert_eq!(app.session.active().unwrap().history.past_len(), before + 1);
|
||||
assert!(app.ui.dialogs.is_empty());
|
||||
app.session.undo();
|
||||
assert!(app.session.active().unwrap().doc.layer(target).is_some());
|
||||
let text = saved.borrow().clone();
|
||||
assert!(text.contains(PREF));
|
||||
let mut session = photocraft_engine::Session::new();
|
||||
session.execute("file.new", json!({"width":8,"height":8})).unwrap();
|
||||
session.execute("layer.new.layer", json!({"name":"Fresh"})).unwrap();
|
||||
let mut fresh = PhotocraftApp::new(session, crate::Services { load_prefs: Some(Box::new(move || Some(text.clone()))), ..Default::default() });
|
||||
fresh.run("layer.delete", json!({"__trash":true})).unwrap();
|
||||
assert!(fresh.ui.dialogs.is_empty());
|
||||
}
|
||||
#[test]
|
||||
fn changed_document_refuses_confirmation_and_clear_does_not_prompt() {
|
||||
let mut app = make_app();
|
||||
let dialog = app.run("layer.delete", json!({"__trash":true})).unwrap()["dialog"].as_u64().unwrap();
|
||||
app.session.execute("file.new", json!({"width":8,"height":8})).unwrap();
|
||||
assert!(crate::dialogs::confirm(&mut app, dialog).is_err());
|
||||
assert_eq!(app.session.documents()[0].doc.layer_count(), 2);
|
||||
app.run("edit.clear", json!({})).unwrap();
|
||||
assert!(app.ui.dialogs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_trash_click_prompts_and_default_delete_binding_is_unchanged() {
|
||||
let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1200.0, 800.0)).with_max_steps(64).build_eframe(|cc| {
|
||||
PhotocraftApp::setup_context(&cc.egui_ctx, Default::default());
|
||||
make_app()
|
||||
});
|
||||
h.run_steps(4);
|
||||
let id = h.state().session.active().unwrap().active_layer.unwrap();
|
||||
h.get_by_label("Delete layer").click();
|
||||
h.run_steps(4);
|
||||
assert!(h.state().ui.dialogs.iter().any(|d| owns(&d.fields)));
|
||||
assert!(h.state().session.active().unwrap().doc.layer(id).is_some());
|
||||
let dialog = h.state().ui.dialogs[0].id;
|
||||
h.state_mut().ui.close_dialog(dialog);
|
||||
h.state_mut().run("layer.delete", json!({})).unwrap();
|
||||
assert!(h.state().ui.dialogs.is_empty());
|
||||
assert!(h.state().session.active().unwrap().doc.layer(id).is_none());
|
||||
let clear = photocraft_engine::commands::find("edit.clear").unwrap();
|
||||
let delete = photocraft_engine::commands::find("layer.delete").unwrap();
|
||||
assert_eq!(clear.shortcut, Some("Delete"));
|
||||
assert_eq!(delete.shortcut, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirmation_strings_are_translated_in_every_supported_catalog() {
|
||||
for language in crate::i18n::LANGUAGES.iter().filter(|l| l.code != "en") {
|
||||
let lang = crate::i18n::Lang::from_code(language.code).unwrap();
|
||||
for key in [
|
||||
"Delete layer ‘{name}’?",
|
||||
"Delete {count} layers? {names}",
|
||||
"Delete layer?",
|
||||
"Don't show again",
|
||||
"The document is closed",
|
||||
"The document or selected layers changed. Please request deletion again.",
|
||||
"Missing delete command",
|
||||
"Invalid delete command",
|
||||
"Layer deleted, but confirmation preference could not be saved: {error}",
|
||||
] {
|
||||
assert_ne!(crate::i18n::tr(lang, key), key, "{}: {key}", language.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -190,6 +190,7 @@ pub fn show(app: &mut PhotocraftApp, ctx: &egui::Context) {
|
|||
DialogKind::Command if crate::fill_ui::owns(&fields) => crate::fill_ui::body(app, ui, &mut fields),
|
||||
DialogKind::Command if crate::stroke_ui::owns(&fields) => crate::stroke_ui::body(ui, &mut fields),
|
||||
DialogKind::Command if crate::shape_dialog::owns(&fields) => crate::shape_dialog::body(app, ui, &mut fields),
|
||||
DialogKind::Command if crate::delete_layer_prompt::owns(&fields) => crate::delete_layer_prompt::body(ui, &mut fields),
|
||||
DialogKind::Command if crate::rasterize_prompt::owns(&fields) => crate::rasterize_prompt::body(ui, &fields),
|
||||
DialogKind::Command if crate::variables_ui::owns(&fields) => crate::variables_ui::body(app, ui, &mut fields),
|
||||
DialogKind::Command if crate::file_ui::owns(&fields) => crate::file_ui::body(app, ui, &mut fields),
|
||||
|
|
@ -245,6 +246,8 @@ pub fn show(app: &mut PhotocraftApp, ctx: &egui::Context) {
|
|||
} else {
|
||||
let ok_label = if d.kind == DialogKind::NewDocument {
|
||||
tl!("Create")
|
||||
} else if crate::delete_layer_prompt::owns(&d.fields) {
|
||||
tl!("Delete")
|
||||
} else if d.fields.contains_key("__export") {
|
||||
tl!("Export")
|
||||
} else {
|
||||
|
|
@ -407,6 +410,7 @@ pub fn confirm(app: &mut PhotocraftApp, id: u64) -> Result<Value, String> {
|
|||
DialogKind::Command if crate::fill_ui::owns(&d.fields) => crate::fill_ui::confirm(app, &d.fields),
|
||||
DialogKind::Command if crate::stroke_ui::owns(&d.fields) => crate::stroke_ui::confirm(app, &d.fields),
|
||||
DialogKind::Command if crate::shape_dialog::owns(&d.fields) => crate::shape_dialog::confirm(app, &d.fields),
|
||||
DialogKind::Command if crate::delete_layer_prompt::owns(&d.fields) => crate::delete_layer_prompt::confirm(app, &d.fields),
|
||||
DialogKind::Command if crate::rasterize_prompt::owns(&d.fields) => crate::rasterize_prompt::confirm(app, &d.fields),
|
||||
DialogKind::Command if crate::variables_ui::owns(&d.fields) => crate::variables_ui::confirm(app, &d.fields),
|
||||
DialogKind::Command if crate::file_ui::owns(&d.fields) => crate::file_ui::confirm(app, &d.fields),
|
||||
|
|
|
|||
|
|
@ -963,6 +963,17 @@
|
|||
Delete current channel Odstranit aktuální kanál
|
||||
Delete current state Odstranit aktuální stav
|
||||
Delete effect layer Odstranit vrstvu efektu
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Smazat vrstvu „{name}“?
|
||||
Delete {count} layers? {names} Smazat vrstvy ({count})? {names}
|
||||
Delete layer? Smazat vrstvu?
|
||||
The document is closed Dokument je zavřený
|
||||
The document or selected layers changed. Please request deletion again. Dokument nebo vybrané vrstvy se změnily. Požádejte o smazání znovu.
|
||||
Missing delete command Chybí příkaz ke smazání
|
||||
Invalid delete command Neplatný příkaz ke smazání
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Vrstva byla smazána, ale nastavení potvrzení se nepodařilo uložit: {error}
|
||||
|
||||
Delete layer Odstranit vrstvu
|
||||
Delete layer comp Odstranit kompozici vrstev
|
||||
Delete note Odstranit poznámku
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -947,6 +947,17 @@
|
|||
Delete current channel Aktuellen Kanal löschen
|
||||
Delete current state Aktuellen Status löschen
|
||||
Delete effect layer Effektebene löschen
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Ebene „{name}“ löschen?
|
||||
Delete {count} layers? {names} {count} Ebenen löschen? {names}
|
||||
Delete layer? Ebene löschen?
|
||||
The document is closed Das Dokument ist geschlossen
|
||||
The document or selected layers changed. Please request deletion again. Das Dokument oder die ausgewählten Ebenen haben sich geändert. Bitte die Löschung erneut anfordern.
|
||||
Missing delete command Löschbefehl fehlt
|
||||
Invalid delete command Ungültiger Löschbefehl
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Die Ebene wurde gelöscht, aber die Bestätigungseinstellung konnte nicht gespeichert werden: {error}
|
||||
|
||||
Delete layer Ebene löschen
|
||||
Delete layer comp Ebenenkomposition löschen
|
||||
Delete note Notiz löschen
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -2188,6 +2188,17 @@
|
|||
Delete Pins Διαγραφή καρφιτσών
|
||||
Delete Workspace Διαγραφή χώρου εργασίας
|
||||
Delete Workspace… Διαγραφή χώρου εργασίας…
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Διαγραφή του επιπέδου «{name}»;
|
||||
Delete {count} layers? {names} Διαγραφή {count} επιπέδων; {names}
|
||||
Delete layer? Διαγραφή επιπέδου;
|
||||
The document is closed Το έγγραφο είναι κλειστό
|
||||
The document or selected layers changed. Please request deletion again. Το έγγραφο ή τα επιλεγμένα επίπεδα άλλαξαν. Ζητήστε ξανά τη διαγραφή.
|
||||
Missing delete command Λείπει η εντολή διαγραφής
|
||||
Invalid delete command Μη έγκυρη εντολή διαγραφής
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Το επίπεδο διαγράφηκε, αλλά δεν ήταν δυνατή η αποθήκευση της προτίμησης επιβεβαίωσης: {error}
|
||||
|
||||
Delete layer Διαγραφή επιπέδου
|
||||
Density: Πυκνότητα:
|
||||
Depth Βάθος
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 5.
|
|
|
@ -948,6 +948,17 @@
|
|||
Delete current channel Eliminar el canal actual
|
||||
Delete current state Eliminar el estado actual
|
||||
Delete effect layer Eliminar capa de efecto
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? ¿Eliminar la capa «{name}»?
|
||||
Delete {count} layers? {names} ¿Eliminar {count} capas? {names}
|
||||
Delete layer? ¿Eliminar capa?
|
||||
The document is closed El documento está cerrado
|
||||
The document or selected layers changed. Please request deletion again. El documento o las capas seleccionadas han cambiado. Solicita la eliminación de nuevo.
|
||||
Missing delete command Falta el comando de eliminación
|
||||
Invalid delete command Comando de eliminación no válido
|
||||
Layer deleted, but confirmation preference could not be saved: {error} La capa se eliminó, pero no se pudo guardar la preferencia de confirmación: {error}
|
||||
|
||||
Delete layer Eliminar capa
|
||||
Delete layer comp Eliminar composición de capa
|
||||
Delete note Eliminar nota
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
|
|
@ -938,6 +938,17 @@
|
|||
Delete current channel Supprimer le canal actif
|
||||
Delete current state Supprimer l'état actuel
|
||||
Delete effect layer Supprimer le calque d'effet
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Supprimer le calque « {name} » ?
|
||||
Delete {count} layers? {names} Supprimer {count} calques ? {names}
|
||||
Delete layer? Supprimer le calque ?
|
||||
The document is closed Le document est fermé
|
||||
The document or selected layers changed. Please request deletion again. Le document ou les calques sélectionnés ont changé. Demandez à nouveau la suppression.
|
||||
Missing delete command Commande de suppression manquante
|
||||
Invalid delete command Commande de suppression non valide
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Le calque a été supprimé, mais la préférence de confirmation n’a pas pu être enregistrée : {error}
|
||||
|
||||
Delete layer Supprimer le calque
|
||||
Delete layer comp Supprimer la composition de calques
|
||||
Delete note Supprimer la note
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -932,6 +932,17 @@
|
|||
Delete current channel Hapus channel saat ini
|
||||
Delete current state Hapus kondisi saat ini
|
||||
Delete effect layer Hapus layer efek
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Hapus layer ‘{name}’?
|
||||
Delete {count} layers? {names} Hapus {count} layer? {names}
|
||||
Delete layer? Hapus layer?
|
||||
The document is closed Dokumen sudah ditutup
|
||||
The document or selected layers changed. Please request deletion again. Dokumen atau layer yang dipilih telah berubah. Minta penghapusan lagi.
|
||||
Missing delete command Perintah hapus tidak tersedia
|
||||
Invalid delete command Perintah hapus tidak valid
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Layer dihapus, tetapi preferensi konfirmasi tidak dapat disimpan: {error}
|
||||
|
||||
Delete layer Hapus layer
|
||||
Delete layer comp Hapus layer comp
|
||||
Delete note Hapus catatan
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
|
|
@ -949,6 +949,17 @@
|
|||
Delete current channel Elimina canale corrente
|
||||
Delete current state Elimina stato corrente
|
||||
Delete effect layer Elimina livello effetto
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Eliminare il livello «{name}»?
|
||||
Delete {count} layers? {names} Eliminare {count} livelli? {names}
|
||||
Delete layer? Eliminare il livello?
|
||||
The document is closed Il documento è chiuso
|
||||
The document or selected layers changed. Please request deletion again. Il documento o i livelli selezionati sono cambiati. Richiedi nuovamente l’eliminazione.
|
||||
Missing delete command Comando di eliminazione mancante
|
||||
Invalid delete command Comando di eliminazione non valido
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Il livello è stato eliminato, ma non è stato possibile salvare la preferenza di conferma: {error}
|
||||
|
||||
Delete layer Elimina livello
|
||||
Delete layer comp Elimina composizione livelli
|
||||
Delete note Elimina nota
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 14.
|
|
|
@ -953,6 +953,17 @@
|
|||
Delete current channel 現在のチャンネルを削除
|
||||
Delete current state 現在の状態を削除
|
||||
Delete effect layer 効果レイヤーを削除
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? レイヤー「{name}」を削除しますか?
|
||||
Delete {count} layers? {names} {count} 個のレイヤーを削除しますか? {names}
|
||||
Delete layer? レイヤーを削除しますか?
|
||||
The document is closed ドキュメントは閉じられています
|
||||
The document or selected layers changed. Please request deletion again. ドキュメントまたは選択したレイヤーが変更されました。もう一度削除してください。
|
||||
Missing delete command 削除コマンドがありません
|
||||
Invalid delete command 無効な削除コマンドです
|
||||
Layer deleted, but confirmation preference could not be saved: {error} レイヤーは削除されましたが、確認設定を保存できませんでした: {error}
|
||||
|
||||
Delete layer レイヤーを削除
|
||||
Delete layer comp レイヤーカンプを削除
|
||||
Delete note 注釈を削除
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -930,6 +930,17 @@
|
|||
Delete current channel 현재 채널 삭제
|
||||
Delete current state 현재 상태 삭제
|
||||
Delete effect layer 효과 레이어 삭제
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? 레이어 ‘{name}’을(를) 삭제하시겠습니까?
|
||||
Delete {count} layers? {names} 레이어 {count}개를 삭제하시겠습니까? {names}
|
||||
Delete layer? 레이어를 삭제하시겠습니까?
|
||||
The document is closed 문서가 닫혔습니다
|
||||
The document or selected layers changed. Please request deletion again. 문서 또는 선택한 레이어가 변경되었습니다. 삭제를 다시 요청하세요.
|
||||
Missing delete command 삭제 명령이 없습니다
|
||||
Invalid delete command 잘못된 삭제 명령입니다
|
||||
Layer deleted, but confirmation preference could not be saved: {error} 레이어가 삭제되었지만 확인 설정을 저장할 수 없습니다: {error}
|
||||
|
||||
Delete layer 레이어 삭제
|
||||
Delete layer comp 레이어 구성 요소 삭제
|
||||
Delete note 메모 삭제
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 10.
|
|
|
@ -941,6 +941,17 @@
|
|||
Delete current channel Huidig kanaal verwijderen
|
||||
Delete current state Huidige staat verwijderen
|
||||
Delete effect layer Effectlaag verwijderen
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Laag ‘{name}’ verwijderen?
|
||||
Delete {count} layers? {names} {count} lagen verwijderen? {names}
|
||||
Delete layer? Laag verwijderen?
|
||||
The document is closed Het document is gesloten
|
||||
The document or selected layers changed. Please request deletion again. Het document of de geselecteerde lagen zijn gewijzigd. Vraag de verwijdering opnieuw aan.
|
||||
Missing delete command Verwijderopdracht ontbreekt
|
||||
Invalid delete command Ongeldige verwijderopdracht
|
||||
Layer deleted, but confirmation preference could not be saved: {error} De laag is verwijderd, maar de bevestigingsvoorkeur kon niet worden opgeslagen: {error}
|
||||
|
||||
Delete layer Laag verwijderen
|
||||
Delete layer comp Laagcompositie verwijderen
|
||||
Delete note Notitie verwijderen
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
|
|
@ -952,6 +952,17 @@
|
|||
Delete current channel Usuń bieżący kanał
|
||||
Delete current state Usuń bieżący stan
|
||||
Delete effect layer Usuń warstwę efektu
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Usunąć warstwę „{name}”?
|
||||
Delete {count} layers? {names} Usunąć warstwy ({count})? {names}
|
||||
Delete layer? Usunąć warstwę?
|
||||
The document is closed Dokument jest zamknięty
|
||||
The document or selected layers changed. Please request deletion again. Dokument lub zaznaczone warstwy uległy zmianie. Ponownie zażądaj usunięcia.
|
||||
Missing delete command Brak polecenia usuwania
|
||||
Invalid delete command Nieprawidłowe polecenie usuwania
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Warstwa została usunięta, ale nie udało się zapisać ustawienia potwierdzenia: {error}
|
||||
|
||||
Delete layer Usuń warstwę
|
||||
Delete layer comp Usuń kompozycję warstw
|
||||
Delete note Usuń notatkę
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -937,6 +937,17 @@
|
|||
Delete current channel Excluir o canal atual
|
||||
Delete current state Excluir o estado atual
|
||||
Delete effect layer Excluir camada de efeito
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Excluir a camada “{name}”?
|
||||
Delete {count} layers? {names} Excluir {count} camadas? {names}
|
||||
Delete layer? Excluir camada?
|
||||
The document is closed O documento está fechado
|
||||
The document or selected layers changed. Please request deletion again. O documento ou as camadas selecionadas mudaram. Solicite a exclusão novamente.
|
||||
Missing delete command Comando de exclusão ausente
|
||||
Invalid delete command Comando de exclusão inválido
|
||||
Layer deleted, but confirmation preference could not be saved: {error} A camada foi excluída, mas não foi possível salvar a preferência de confirmação: {error}
|
||||
|
||||
Delete layer Excluir camada
|
||||
Delete layer comp Excluir composição de camadas
|
||||
Delete note Excluir anotação
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -2644,3 +2644,13 @@ fontWeight Italic Курсив
|
|||
Quick Selection found nothing there Быстрое выделение ничего здесь не нашло
|
||||
Object Selection found nothing there — try a tighter rectangle Выделение объектов ничего здесь не нашло — попробуйте более узкий прямоугольник
|
||||
No subject found — try a rectangle around the object Объект не найден — попробуйте прямоугольник вокруг объекта
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Удалить слой «{name}»?
|
||||
Delete {count} layers? {names} Удалить слои ({count})? {names}
|
||||
Delete layer? Удалить слой?
|
||||
The document is closed Документ закрыт
|
||||
The document or selected layers changed. Please request deletion again. Документ или выбранные слои изменились. Повторите запрос на удаление.
|
||||
Missing delete command Отсутствует команда удаления
|
||||
Invalid delete command Недопустимая команда удаления
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Слой удалён, но не удалось сохранить настройку подтверждения: {error}
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -2575,6 +2575,17 @@ fontWeight Italic Курсивний
|
|||
Create new snapshot Створити новий знімок стану
|
||||
Delete Cropped Pixels Видаляти обрізані пікселі
|
||||
Delete current state Видалити поточний стан
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? Видалити шар «{name}»?
|
||||
Delete {count} layers? {names} Видалити шари ({count})? {names}
|
||||
Delete layer? Видалити шар?
|
||||
The document is closed Документ закрито
|
||||
The document or selected layers changed. Please request deletion again. Документ або вибрані шари змінилися. Повторіть запит на видалення.
|
||||
Missing delete command Відсутня команда видалення
|
||||
Invalid delete command Недійсна команда видалення
|
||||
Layer deleted, but confirmation preference could not be saved: {error} Шар видалено, але не вдалося зберегти налаштування підтвердження: {error}
|
||||
|
||||
Delete layer Видалити шар
|
||||
Diamond Gradient Ромбічний градієнт
|
||||
Enable airbrush-style build-up effects Увімкнути ефекти накопичення як в аерографі
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 12.
|
|
|
@ -953,6 +953,17 @@
|
|||
Delete current channel 删除当前通道
|
||||
Delete current state 删除当前状态
|
||||
Delete effect layer 删除效果图层
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? 删除图层“{name}”?
|
||||
Delete {count} layers? {names} 删除 {count} 个图层?{names}
|
||||
Delete layer? 删除图层?
|
||||
The document is closed 文档已关闭
|
||||
The document or selected layers changed. Please request deletion again. 文档或所选图层已更改。请重新请求删除。
|
||||
Missing delete command 缺少删除命令
|
||||
Invalid delete command 无效的删除命令
|
||||
Layer deleted, but confirmation preference could not be saved: {error} 图层已删除,但无法保存确认设置:{error}
|
||||
|
||||
Delete layer 删除图层
|
||||
Delete layer comp 删除图层复合
|
||||
Delete note 删除注释
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 14.
|
|
|
@ -954,6 +954,17 @@
|
|||
Delete current channel 刪除目前色版
|
||||
Delete current state 刪除目前狀態
|
||||
Delete effect layer 刪除效果圖層
|
||||
|
||||
# Layers-panel trash confirmation
|
||||
Delete layer ‘{name}’? 刪除圖層「{name}」?
|
||||
Delete {count} layers? {names} 刪除 {count} 個圖層?{names}
|
||||
Delete layer? 刪除圖層?
|
||||
The document is closed 文件已關閉
|
||||
The document or selected layers changed. Please request deletion again. 文件或所選圖層已變更。請重新要求刪除。
|
||||
Missing delete command 缺少刪除指令
|
||||
Invalid delete command 無效的刪除指令
|
||||
Layer deleted, but confirmation preference could not be saved: {error} 圖層已刪除,但無法儲存確認設定:{error}
|
||||
|
||||
Delete layer 刪除圖層
|
||||
Delete layer comp 刪除圖層構圖
|
||||
Delete note 刪除註解
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -50,6 +50,7 @@ pub mod crop_overlay;
|
|||
pub mod crop_shield;
|
||||
pub mod crop_straighten;
|
||||
pub mod crop_ui;
|
||||
pub mod delete_layer_prompt;
|
||||
pub mod dialog_blend_ui;
|
||||
pub mod dialogs;
|
||||
pub mod direct_select;
|
||||
|
|
@ -722,6 +723,9 @@ impl PhotocraftApp {
|
|||
|
||||
/// Run an engine command, reporting errors in the status bar.
|
||||
pub fn run(&mut self, id: &str, params: Value) -> Result<Value, String> {
|
||||
if let Some(result) = delete_layer_prompt::intercept(self, id, ¶ms) {
|
||||
return result;
|
||||
}
|
||||
// Automation input also gates every step a command runs on its behalf (`actions.play`).
|
||||
let gate = if self.automation_input && self.session.authorize.is_none() { self.services.automation_authorize } else { None };
|
||||
if gate.is_some() {
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ fn footer_trash_deletes_only_the_targeted_mask_linked_or_unlinked() {
|
|||
let at = h.get_by_label(label).rect().center();
|
||||
click_with(&mut h, at, Modifiers::NONE);
|
||||
let l = layer(&h, masked);
|
||||
assert!(h.state().ui.dialogs.is_empty(), "mask deletion remains immediate");
|
||||
let original = before.layer(photocraft_doc::LayerId(masked)).unwrap();
|
||||
assert_eq!(l.content, original.content);
|
||||
assert_eq!(layer(&h, shape), *before.layer(photocraft_doc::LayerId(shape)).unwrap());
|
||||
|
|
@ -130,6 +131,23 @@ fn footer_trash_still_deletes_the_layer_when_its_content_is_targeted() {
|
|||
assert!(!h.state().ui.mask_target && !h.state().ui.vector_mask_target);
|
||||
let at = h.get_by_label("Delete layer").rect().center();
|
||||
click_with(&mut h, at, Modifiers::NONE);
|
||||
assert!(h.state().session.active().unwrap().doc.layer(photocraft_doc::LayerId(masked)).is_some());
|
||||
assert!(h.state().ui.dialogs.iter().any(|d| crate::delete_layer_prompt::owns(&d.fields)));
|
||||
h.get_by_label("Delete").click();
|
||||
h.run_steps(3);
|
||||
assert!(h.state().session.active().unwrap().doc.layer(photocraft_doc::LayerId(masked)).is_none());
|
||||
}
|
||||
|
||||
/// Like Photoshop, ⌥/Alt-clicking the trash deletes the layer without asking.
|
||||
#[test]
|
||||
fn alt_click_on_the_footer_trash_deletes_without_confirmation() {
|
||||
let (s, masked, _) = session();
|
||||
let mut h = harness(s, 0, 1.0, 290.0);
|
||||
let row = crate::layer_row_ui::recorded(&h.ctx).into_iter().find(|r| r.layer == masked).unwrap().row;
|
||||
click_with(&mut h, pos2(row.left() + 46.0, row.center().y), Modifiers::NONE);
|
||||
let at = h.get_by_label("Delete layer").rect().center();
|
||||
click_with(&mut h, at, Modifiers::ALT);
|
||||
assert!(h.state().ui.dialogs.is_empty(), "no confirmation");
|
||||
assert!(h.state().session.active().unwrap().doc.layer(photocraft_doc::LayerId(masked)).is_none());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1905,7 +1905,10 @@ fn layers(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
|
|||
};
|
||||
let trash = icons::button(ui, "trash", 26.0, false, tl!(label));
|
||||
if trash.clicked() {
|
||||
actions.push((delete.into(), json!({})));
|
||||
// Like Photoshop, ⌥/Alt-click deletes without asking.
|
||||
let confirm = delete == "layer.delete" && !ui.input(|i| i.modifiers.alt);
|
||||
let params = if confirm { json!({"__trash": true}) } else { json!({}) };
|
||||
actions.push((delete.into(), params));
|
||||
}
|
||||
actions.extend(footer_drop(ui, &trash, footer_drag, "layer.delete"));
|
||||
// A click follows the selected thumbnail; name that action for screen readers even
|
||||
|
|
|
|||
Loading…
Reference in New Issue