mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-10 12:22:22 +00:00
Preferences: Skip Transform when Placing (#8492)
Preferences > General > Skip Transform when Placing (off by default), as in Photoshop: a placed or dropped file lands without Free Transform. It is checked in begin_placed, which every interactive place reaches, so drops still go through main's background place job (#10147) and queue; with the preference on, the next queued file starts as soon as one lands. Scripted file.placeEmbedded/placeLinked never open Free Transform, so the preference doesn't affect them. Label translated in every catalog that translates the neighbouring place preferences (all but hi). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2ff8f419cb
commit
53917dc175
25 files changed
+114
-2
No files matched your search
@@ -227,6 +227,10 @@ pub struct General {
|
||||
pub export_clipboard: bool,
|
||||
pub resize_image_during_place: bool,
|
||||
pub always_create_smart_objects_when_placing: bool,
|
||||
/// Place files without opening Free Transform on them (#8492). Only the interactive place
|
||||
/// (File ▸ Place, canvas drops) opens Free Transform; `file.placeEmbedded`/`file.placeLinked`
|
||||
/// run with a `path` (actions, scripts, automation) never do, so this doesn't affect them.
|
||||
pub skip_transform_when_placing: bool,
|
||||
/// Scroll wheel zooms instead of panning.
|
||||
pub zoom_with_scroll_wheel: bool,
|
||||
/// Wheel zoom eases over the frames of a notch; off, a notch is one instant ×1.1 step.
|
||||
@@ -247,6 +251,7 @@ impl Default for General {
|
||||
export_clipboard: true,
|
||||
resize_image_during_place: true,
|
||||
always_create_smart_objects_when_placing: true,
|
||||
skip_transform_when_placing: false,
|
||||
zoom_with_scroll_wheel: false,
|
||||
animated_zoom: true,
|
||||
zoom_resizes_windows: false,
|
||||
|
||||
@@ -739,6 +739,72 @@ fn cancelling_a_background_place_continues_the_queue() {
|
||||
assert_eq!(layer_count(&app), layers + 1);
|
||||
}
|
||||
|
||||
/// #8492: with Skip Transform when Placing on, dropped files still go through the background place
|
||||
/// job one at a time, but no Free Transform opens when a layer lands: the next file starts as soon
|
||||
/// as the previous one has landed, without waiting for ↩.
|
||||
#[test]
|
||||
fn skip_transform_when_placing_places_background_drops_without_free_transform() {
|
||||
let ctx = egui::Context::default();
|
||||
let mut app = app_with_canvas();
|
||||
app.background_jobs = true;
|
||||
app.session.execute("prefs.set", json!({"path": "general.skipTransformWhenPlacing", "value": true})).unwrap();
|
||||
let (layers, steps) = (layer_count(&app), history_steps(&app));
|
||||
app.open_dropped(&ctx, vec![dropped("first.png", Ok(png_bytes(4, 4))), dropped("second.png", Ok(png_bytes(2, 2)))], Some(egui::pos2(400.0, 300.0)));
|
||||
app.place_next_dropped(&ctx);
|
||||
let first = app.jobs.place.clone().expect("the first file is being placed");
|
||||
assert_eq!(first.name, "first.png");
|
||||
assert!(app.session.job(first.job).is_some(), "as a background job");
|
||||
assert_eq!(app.drop_places.len(), 1, "the second file waits for the first job");
|
||||
tick_until(&mut app, &ctx, |app| app.jobs.place.as_ref().is_some_and(|p| p.name == "second.png"));
|
||||
assert!(app.ui.transform.is_none(), "no Free Transform on the first file");
|
||||
assert_eq!(layer_count(&app), layers + 1);
|
||||
tick_until(&mut app, &ctx, |app| app.jobs.place.is_none());
|
||||
assert!(app.ui.transform.is_none() && app.drop_places.is_empty());
|
||||
let st = app.session.active().unwrap();
|
||||
assert_eq!(st.doc.layer(st.active_layer.unwrap()).unwrap().name, "second");
|
||||
assert_eq!((layer_count(&app), history_steps(&app)), (layers + 2, steps + 2), "one Place Embedded step each");
|
||||
assert_eq!(app.session.active().unwrap().history.undo_label(), Some("Place Embedded"));
|
||||
}
|
||||
|
||||
/// #8492: inline (background jobs off, as on the web) the preference places every queued drop
|
||||
/// without Free Transform, a file that can't be placed reported on the way.
|
||||
#[test]
|
||||
fn skip_transform_when_placing_places_inline_drops_without_free_transform() {
|
||||
let ctx = egui::Context::default();
|
||||
let mut app = app_with_canvas();
|
||||
app.session.execute("prefs.set", json!({"path": "general.skipTransformWhenPlacing", "value": true})).unwrap();
|
||||
let (layers, steps) = (layer_count(&app), history_steps(&app));
|
||||
app.open_dropped(
|
||||
&ctx,
|
||||
vec![dropped("first.png", Ok(png_bytes(4, 4))), dropped("bad.png", Ok(b"bad".to_vec())), dropped("second.png", Ok(png_bytes(2, 2)))],
|
||||
Some(egui::pos2(400.0, 300.0)),
|
||||
);
|
||||
app.place_next_dropped(&ctx);
|
||||
assert!(app.ui.transform.is_none() && app.drop_places.is_empty());
|
||||
assert_eq!((layer_count(&app), history_steps(&app)), (layers + 2, steps + 2));
|
||||
assert_eq!(app.ui.notices.iter().filter(|n| n.error && n.title.contains("bad.png")).count(), 1);
|
||||
}
|
||||
|
||||
/// #8492: Skip Transform when Placing only concerns the interactive place. `file.placeEmbedded`
|
||||
/// with a `path` (actions, scripts, the control channel, MCP) runs the engine command, which never
|
||||
/// opens Free Transform, whatever the preference.
|
||||
#[test]
|
||||
fn a_scripted_place_never_opens_free_transform() {
|
||||
let dir = std::env::temp_dir().join(format!("pc-scripted-place-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("dot.png");
|
||||
std::fs::write(&path, png_bytes(4, 3)).unwrap();
|
||||
let mut app = app_with_canvas();
|
||||
for skip in [false, true] {
|
||||
app.session.execute("prefs.set", json!({"path": "general.skipTransformWhenPlacing", "value": skip})).unwrap();
|
||||
let layers = layer_count(&app);
|
||||
app.run("file.placeEmbedded", json!({"path": path.to_string_lossy()})).unwrap();
|
||||
assert_eq!(layer_count(&app), layers + 1, "skip={skip}");
|
||||
assert!(app.ui.transform.is_none(), "skip={skip}");
|
||||
}
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
/// Frames with nothing to wait for: jobs applied and the drop queue served.
|
||||
fn idle_frames(app: &mut PhotocraftApp, ctx: &egui::Context) {
|
||||
for _ in 0..3 {
|
||||
|
||||
@@ -2029,6 +2029,7 @@
|
||||
Allow scripts to connect Povolit připojení skriptů
|
||||
Always Vždy
|
||||
Always create smart objects when placing Při umísťování vždy vytvořit chytré objekty
|
||||
Skip transform when placing Při umísťování přeskočit transformaci
|
||||
Animated zoom Animované zvětšování
|
||||
Apply auto tone Použít automatický tón
|
||||
Ask Zeptat se
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1990,6 +1990,7 @@
|
||||
Allow scripts to connect Skriptverbindungen erlauben
|
||||
Always Immer
|
||||
Always create smart objects when placing Beim Platzieren immer Smart-Objekte erstellen
|
||||
Skip transform when placing Transformieren beim Platzieren überspringen
|
||||
Animated zoom Animierter Zoom
|
||||
Apply auto tone Automatische Tonwertkorrektur anwenden
|
||||
Ask Nachfragen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1750,6 +1750,7 @@
|
||||
Allow agent control Να επιτρέπεται ο έλεγχος από πράκτορες
|
||||
Allow scripts to connect Να επιτρέπεται η σύνδεση σεναρίων
|
||||
Always create smart objects when placing Να δημιουργούνται πάντα έξυπνα αντικείμενα κατά την τοποθέτηση
|
||||
Skip transform when placing Παράλειψη μετασχηματισμού κατά την τοποθέτηση
|
||||
Animated zoom Κινούμενη μεγέθυνση
|
||||
Apply auto tone Εφαρμογή αυτόματης τονικής διόρθωσης
|
||||
Ask before saving layered tiff Ερώτηση πριν από την αποθήκευση TIFF με επίπεδα
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 47 and column 53.
|
@@ -1960,6 +1960,7 @@
|
||||
Allow scripts to connect Permitir que las secuencias de comandos se conecten
|
||||
Always Siempre
|
||||
Always create smart objects when placing Crear siempre objetos inteligentes al colocar
|
||||
Skip transform when placing Omitir la transformación al colocar
|
||||
Animated zoom Zoom animado
|
||||
Apply auto tone Aplicar el tono automático
|
||||
Ask Preguntar
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -1966,6 +1966,7 @@
|
||||
Allow scripts to connect Autoriser la connexion des scripts
|
||||
Always Toujours
|
||||
Always create smart objects when placing Toujours créer des objets intelligents lors du placement
|
||||
Skip transform when placing Ignorer la transformation lors du placement
|
||||
Animated zoom Zoom animé
|
||||
Apply auto tone Appliquer la tonalité automatique
|
||||
Ask Demander
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2724,6 +2724,7 @@ cameraRaw Yellow Sárga
|
||||
All Slices Összes szelet
|
||||
All User Slices Összes felhasználói szelet
|
||||
Always create smart objects when placing Elhelyezéskor mindig intelligens objektum létrehozása
|
||||
Skip transform when placing Alakítás kihagyása elhelyezéskor
|
||||
Answer the unsaved-changes prompt first Előbb válaszolj a nem mentett módosításokra vonatkozó kérdésre
|
||||
Appearance Settings… Megjelenési beállítások…
|
||||
Apple Watch 45mm Apple Watch 45 mm
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1977,6 +1977,7 @@
|
||||
Allow scripts to connect Izinkan skrip terhubung
|
||||
Always Selalu
|
||||
Always create smart objects when placing Selalu buat smart object saat menempatkan
|
||||
Skip transform when placing Lewati transformasi saat menempatkan
|
||||
Animated zoom Zoom beranimasi
|
||||
Apply auto tone Terapkan auto tone
|
||||
Ask Tanya
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -2005,6 +2005,7 @@
|
||||
Allow scripts to connect Consenti connessione script
|
||||
Always Sempre
|
||||
Always create smart objects when placing Crea sempre oggetti avanzati all'inserimento
|
||||
Skip transform when placing Salta la trasformazione all'inserimento
|
||||
Animated zoom Zoom animato
|
||||
Apply auto tone Applica tono automatico
|
||||
Ask Chiedi
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 79 and column 53.
|
@@ -2016,6 +2016,7 @@
|
||||
Allow scripts to connect スクリプトからの接続を許可
|
||||
Always 常に
|
||||
Always create smart objects when placing 配置時に常にスマートオブジェクトを作成
|
||||
Skip transform when placing 配置時に変形をスキップ
|
||||
Animated zoom アニメーションズーム
|
||||
Apply auto tone 自動トーン補正を適用
|
||||
Ask 確認する
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1989,6 +1989,7 @@
|
||||
Allow scripts to connect 스크립트 연결 허용
|
||||
Always 항상
|
||||
Always create smart objects when placing 가져올 때 항상 스마트 오브젝트 만들기
|
||||
Skip transform when placing 가져올 때 변형 건너뛰기
|
||||
Animated zoom 확대/축소 애니메이션
|
||||
Apply auto tone 자동 톤 적용
|
||||
Ask 묻기
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 63 and column 53.
|
@@ -1937,6 +1937,7 @@
|
||||
Allow scripts to connect Scripts toestaan verbinding te maken
|
||||
Always Altijd
|
||||
Always create smart objects when placing Bij plaatsen altijd slimme objecten maken
|
||||
Skip transform when placing Transformatie overslaan bij plaatsen
|
||||
Animated zoom Geanimeerd zoomen
|
||||
Apply auto tone Automatische tooncorrectie toepassen
|
||||
Ask Vragen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -2002,6 +2002,7 @@
|
||||
Allow scripts to connect Zezwalaj skryptom na połączenie
|
||||
Always Zawsze
|
||||
Always create smart objects when placing Zawsze twórz obiekty inteligentne podczas umieszczania
|
||||
Skip transform when placing Pomijaj przekształcanie podczas umieszczania
|
||||
Animated zoom Animowane powiększanie
|
||||
Apply auto tone Zastosuj automatyczne tony
|
||||
Ask Pytaj
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1960,6 +1960,7 @@
|
||||
Allow scripts to connect Permitir conexão de scripts
|
||||
Always Sempre
|
||||
Always create smart objects when placing Sempre criar objetos inteligentes ao inserir
|
||||
Skip transform when placing Ignorar transformação ao inserir
|
||||
Animated zoom Zoom animado
|
||||
Apply auto tone Aplicar tom automático
|
||||
Ask Perguntar
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2419,6 +2419,7 @@
|
||||
Allow scripts to connect Разрешить подключение сценариев
|
||||
Always Всегда
|
||||
Always create smart objects when placing Всегда создавать смарт-объекты при помещении
|
||||
Skip transform when placing Пропускать трансформирование при помещении
|
||||
Animated zoom Анимированное масштабирование
|
||||
Apply auto tone Применять автотон
|
||||
Ask Спрашивать
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1976,6 +1976,7 @@
|
||||
Allow scripts to connect Dozvoli skriptama da se povežu
|
||||
Always Uvek
|
||||
Always create smart objects when placing Uvek pravi pametne objekte pri postavljanju
|
||||
Skip transform when placing Preskoči transformaciju pri postavljanju
|
||||
Animated zoom Animirano zumiranje
|
||||
Apply auto tone Primeni automatski ton
|
||||
Ask Pitaj
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1890,6 +1890,7 @@
|
||||
Allow scripts to connect Komut dosyalarının bağlanmasına izin ver
|
||||
Always Her zaman
|
||||
Always create smart objects when placing Yerleştirirken daima akıllı nesneler yaratın
|
||||
Skip transform when placing Yerleştirirken dönüştürmeyi atla
|
||||
Animated zoom Animasyonlu yakınlaştırma
|
||||
Apply auto tone Otomatik tonu uygula
|
||||
Ask Sor
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -2385,6 +2385,7 @@
|
||||
Allow scripts to connect Дозволити підключення скриптів
|
||||
Always Завжди
|
||||
Always create smart objects when placing Завжди створювати смартоб'єкти під час розміщення
|
||||
Skip transform when placing Пропускати трансформацію під час розміщення
|
||||
Animated zoom Анімоване масштабування
|
||||
Apply auto tone Застосовувати автоматичну тонову корекцію
|
||||
Ask Запитувати
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 152 and column 53.
|
@@ -2756,6 +2756,7 @@ shape Appearance Diện mạo
|
||||
Allow agent control Cho phép agent điều khiển
|
||||
Allow scripts to connect Cho phép script kết nối
|
||||
Always create smart objects when placing Luôn tạo đối tượng thông minh khi đặt
|
||||
Skip transform when placing Bỏ qua biến đổi khi đặt
|
||||
Animated zoom Thu phóng có chuyển động
|
||||
Apply auto tone Áp dụng tự động tông
|
||||
Auto collapse icon panels Tự động thu gọn các bảng biểu tượng
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2042,6 +2042,7 @@
|
||||
Allow scripts to connect 允许脚本连接
|
||||
Always 始终
|
||||
Always create smart objects when placing 置入时始终创建智能对象
|
||||
Skip transform when placing 置入时跳过变换
|
||||
Animated zoom 动画缩放
|
||||
Apply auto tone 应用自动色调
|
||||
Ask 询问
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 63 and column 53.
|
@@ -2010,6 +2010,7 @@
|
||||
Allow scripts to connect 允許指令碼連線
|
||||
Always 一律
|
||||
Always create smart objects when placing 置入時一律建立智慧型物件
|
||||
Skip transform when placing 置入時略過變形
|
||||
Animated zoom 動畫縮放
|
||||
Apply auto tone 套用自動色調
|
||||
Ask 詢問
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -287,7 +287,11 @@ pub fn begin_copy(app: &mut PhotocraftApp, ctx: &egui::Context) -> Result<(), St
|
||||
|
||||
/// Free Transform on the layer a file dropped on the canvas just placed, like the reference app's
|
||||
/// Place: Esc takes the place back, ↩ makes the place and the transform one Place Embedded step.
|
||||
/// With Preferences ▸ General ▸ Skip Transform when Placing on, the place stands as it is.
|
||||
pub fn begin_placed(app: &mut PhotocraftApp, ctx: &egui::Context) -> Result<(), String> {
|
||||
if app.session.prefs().general.skip_transform_when_placing {
|
||||
return Ok(());
|
||||
}
|
||||
begin(app, ctx)?;
|
||||
if let Some(t) = app.ui.transform.as_mut() {
|
||||
t.made = Some(MadeLayer::Place);
|
||||
|
||||
@@ -554,6 +554,23 @@ fn editing_the_contents_of_a_dropped_file_keeps_the_layer() {
|
||||
assert!(app.transform_preview.is_none(), "the transform preview should end");
|
||||
}
|
||||
|
||||
/// #8492: Preferences ▸ General ▸ Skip Transform when Placing (off by default) places a file
|
||||
/// without opening Free Transform on it, as in the reference app.
|
||||
#[test]
|
||||
fn skip_transform_when_placing_leaves_the_place_without_a_box() {
|
||||
for skip in [false, true] {
|
||||
let mut app = app_with_square(64, photocraft_geom::Rect::new(8, 8, 24, 24));
|
||||
app.session.execute("prefs.set", json!({"path": "general.skipTransformWhenPlacing", "value": skip})).unwrap();
|
||||
app.session.execute("layer.smartObjects.convertToSmartObject", json!({})).unwrap();
|
||||
app.session.execute("layer.duplicate", json!({"inPlace": true})).unwrap();
|
||||
app.sync_views();
|
||||
let placed = app.session.active().unwrap().active_layer.unwrap();
|
||||
begin_placed(&mut app, &egui::Context::default()).unwrap();
|
||||
assert_eq!(app.ui.transform.is_some(), !skip, "skip {skip}: Free Transform opens only with the preference off");
|
||||
assert!(app.session.active().unwrap().doc.layer(placed).is_some(), "skip {skip}: the placed layer stays");
|
||||
}
|
||||
}
|
||||
|
||||
/// Polls the app's background jobs until they have all landed.
|
||||
fn land_jobs(app: &mut PhotocraftApp) {
|
||||
let ctx = egui::Context::default();
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ Generated by `cargo xtask scorecard` from `perf/budgets.toml`, `perf/baseline.js
|
||||
| [Performance](#performance) | 3 | 22 | 51 | 85 scenarios: 3 within budget, 22 over budget or failing, 9 tracked only, 47 not run, 4 not measurable yet (baseline 2026-10-05) |
|
||||
| [File compatibility](#file-compatibility) | 11 | 14 | 2 | io corpus: oracle ≥ 148/170, round trip ≥ 169/170; psd-tools corpus: oracle ≥ 249/309, round trip ≥ 308/309; photoshop oracles: oracle ≥ 190/258, round trip ≥ 258/258 |
|
||||
| [Tools](#tools) | 36 | 9 | 0 | |
|
||||
| [Workspace and UI](#workspace-and-ui) | 15 | 6 | 2 | settings that do nothing: 10 of 192 (target 0) |
|
||||
| [Workspace and UI](#workspace-and-ui) | 15 | 6 | 2 | settings that do nothing: 10 of 193 (target 0) |
|
||||
| [Type](#type) | 5 | 5 | 0 | |
|
||||
| [Automation](#automation) | 5 | 3 | 3 | 660 of 660 menu items live (100.0%) |
|
||||
| [Reliability](#reliability) | 5 | 2 | 0 | never-crash lints on 30 of 30 crates; panic_hunt present |
|
||||
@@ -209,7 +209,7 @@ Corpus floors (`crates/io/tests/it/corpus.rs`): the enforced minimums; the corpu
|
||||
|
||||
## Workspace and UI
|
||||
|
||||
**Settings that do nothing: 10 of 192** (target 0, #204). Counted by the prefs audit (`xtask/src/scorecard.rs`): a `Preferences` field counts as read when code outside the prefs model (`prefs.rs` except its `impl Session` blocks; tests excluded; the schema-driven dialog never names a field) accesses `section.field`, or `.section` and `.field` in the same file, or calls a section method that reads `self.field`. It is a heuristic lower bound: a generic name can look read when it isn't.
|
||||
**Settings that do nothing: 10 of 193** (target 0, #204). Counted by the prefs audit (`xtask/src/scorecard.rs`): a `Preferences` field counts as read when code outside the prefs model (`prefs.rs` except its `impl Session` blocks; tests excluded; the schema-driven dialog never names a field) accesses `section.field`, or `.section` and `.field` in the same file, or calls a section method that reads `self.field`. It is a heuristic lower bound: a generic name can look read when it isn't.
|
||||
|
||||
Unread: `general.color_picker`, `workspace.auto_show_hidden_panels`, `workspace.enable_floating_document_window_docking`, `performance.legacy_compositing`, `units_and_rulers.column_width`, `units_and_rulers.gutter`, `plug_ins.show_extension_panels`, `plug_ins.allow_scripts_to_connect`, `type_.text_engine`, `enhanced_controls.scrubby_slider_acceleration`.
|
||||
|
||||
|
||||
Reference in new issue
Block a user