mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-22 11:52:57 -05:00
Merge pull request #3975 from abc2cb/codex/fill-mask-preview-20261012
Fill layers: match masks in creation and Pattern Fill preview (part 3/3)
This commit is contained in:
27 files changed
+186
-14
No files matched your search
@@ -419,7 +419,7 @@ pub(crate) fn layer_panel_option(s: &Session, key: &str, fallback: bool) -> bool
|
||||
/// mask, which makes a Solid Color fill a shape (#1419); without one, the selection becomes its
|
||||
/// layer mask ([`selection_mask`]); without either, a white mask reveals the whole layer (#1768).
|
||||
/// A path that isn't there is an error.
|
||||
pub(crate) fn mask_new_layer(doc: &Document, l: &mut Layer, p: &Value, cmd: &str) -> Result<()> {
|
||||
pub(crate) fn mask_new_layer(doc: &Document, l: &mut Layer, p: &Value, cmd: &str, default_mask: bool) -> Result<()> {
|
||||
let path = match p.get("path") {
|
||||
None | Some(Value::Null) => None,
|
||||
Some(Value::String(n)) if crate::vector_cmds::is_work(Some(n)) => Some(doc.work_path.clone().ok_or_else(|| bad(cmd, "no work path"))?),
|
||||
@@ -430,7 +430,9 @@ pub(crate) fn mask_new_layer(doc: &Document, l: &mut Layer, p: &Value, cmd: &str
|
||||
};
|
||||
match path {
|
||||
Some(path) => l.vector_mask = Some(photocraft_doc::VectorMask::new(path)),
|
||||
None => l.mask = Some(mask_at_depth(selection_mask(doc)?.unwrap_or_else(LayerMask::reveal_all), doc.depth)?),
|
||||
None => {
|
||||
l.mask = selection_mask(doc)?.or_else(|| default_mask.then(LayerMask::reveal_all)).map(|mask| mask_at_depth(mask, doc.depth)).transpose()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -439,12 +441,13 @@ pub(crate) fn mask_new_layer(doc: &Document, l: &mut Layer, p: &Value, cmd: &str
|
||||
pub(crate) const NEW_LAYER_PATH: &str =
|
||||
r##""path":"work"|saved path name|{…path}? (the active path: becomes the vector mask; else the selection is the layer mask, or white with no selection)"##;
|
||||
|
||||
/// Add an adjustment layer above the active one; it takes the mask `mask_new_layer` builds from `p` (a path, the selection, or the default white mask).
|
||||
fn new_adjustment(s: &mut Session, adj: Adjustment, p: &Value, cmd: &str) -> Result<Value> {
|
||||
let label = format!("New {} Layer", adj.label());
|
||||
let name = adj.label().to_string();
|
||||
let id = s.edit(&label, |doc, active| {
|
||||
let mut l = Layer::new(doc.next_layer_name(&name), LayerContent::Adjustment(adj));
|
||||
mask_new_layer(doc, &mut l, p, cmd)?;
|
||||
mask_new_layer(doc, &mut l, p, cmd, true)?;
|
||||
let id = doc.insert_above(*active, l);
|
||||
*active = Some(id);
|
||||
Ok(id)
|
||||
@@ -452,6 +455,7 @@ fn new_adjustment(s: &mut Session, adj: Adjustment, p: &Value, cmd: &str) -> Res
|
||||
Ok(json!({ "layer": id.0 }))
|
||||
}
|
||||
|
||||
/// Apply `adj` in place to the targeted layer mask, the targeted alpha channel or Quick Mask, or else the active layer's pixels, as one history step named `label`.
|
||||
fn destructive_adjust(s: &mut Session, label: &str, adj: Adjustment, p: &Value) -> Result<Value> {
|
||||
if is_mask_target(p) {
|
||||
// The targeted layer mask (#780): ⌘I inverts it, as in Photoshop.
|
||||
@@ -1035,10 +1039,11 @@ fn build() -> Vec<CommandSpec> {
|
||||
has_doc,
|
||||
|s, p| {
|
||||
let c = color_param("layer.newFillLayer.solidColor", p, "color", s.tools.foreground)?;
|
||||
let default_mask = layer_panel_option(s, "useDefaultMasksOnFillLayers", true);
|
||||
let id = s.edit("New Color Fill Layer", |doc, active| {
|
||||
let fill = Fill::Solid(Color::rgba(c[0], c[1], c[2], c[3])).in_mode(doc.mode);
|
||||
let mut l = Layer::new(doc.next_layer_name("Color Fill"), LayerContent::Fill(fill));
|
||||
mask_new_layer(doc, &mut l, p, "layer.newFillLayer.solidColor")?;
|
||||
mask_new_layer(doc, &mut l, p, "layer.newFillLayer.solidColor", default_mask)?;
|
||||
let id = doc.insert_above(*active, l);
|
||||
*active = Some(id);
|
||||
Ok(id)
|
||||
@@ -1059,6 +1064,7 @@ fn build() -> Vec<CommandSpec> {
|
||||
let angle = crate::gradient_fill_cmds::angle_param("layer.newFillLayer.gradient", p, 90.0)?;
|
||||
let style = crate::layer_style::gradient_style(p.get("style").and_then(Value::as_str).unwrap_or("linear"));
|
||||
let reverse = p.get("reverse").and_then(Value::as_bool).unwrap_or(false);
|
||||
let default_mask = layer_panel_option(s, "useDefaultMasksOnFillLayers", true);
|
||||
let id = s.edit("New Gradient Fill Layer", |doc, active| {
|
||||
let fill = Fill::gradient(
|
||||
vec![(0.0, Color::rgba(a[0], a[1], a[2], a[3])), (1.0, Color::rgba(b[0], b[1], b[2], b[3]))],
|
||||
@@ -1069,7 +1075,7 @@ fn build() -> Vec<CommandSpec> {
|
||||
)
|
||||
.in_mode(doc.mode);
|
||||
let mut l = Layer::new(doc.next_layer_name("Gradient Fill"), LayerContent::Fill(fill));
|
||||
mask_new_layer(doc, &mut l, p, "layer.newFillLayer.gradient")?;
|
||||
mask_new_layer(doc, &mut l, p, "layer.newFillLayer.gradient", default_mask)?;
|
||||
let id = doc.insert_above(*active, l);
|
||||
*active = Some(id);
|
||||
Ok(id)
|
||||
|
||||
@@ -3123,6 +3123,70 @@ mod layers_panel_parity_tests {
|
||||
assert!(s.active().unwrap().fx_collapsed.contains(&target));
|
||||
}
|
||||
|
||||
/// New Solid Color and Gradient fill layers get the default mask only while Use Default Masks on Fill Layers is on.
|
||||
#[test]
|
||||
fn fill_layers_respect_use_default_masks_option() {
|
||||
let (mut s, _) = new_session();
|
||||
option(&mut s, "useDefaultMasksOnFillLayers", false);
|
||||
let new = s.execute("layer.newFillLayer.solidColor", json!({"color": "#112233"})).unwrap();
|
||||
let id = LayerId(new["layer"].as_u64().unwrap());
|
||||
assert!(s.active().unwrap().doc.layer(id).unwrap().mask.is_none());
|
||||
option(&mut s, "useDefaultMasksOnFillLayers", true);
|
||||
let new = s.execute("layer.newFillLayer.gradient", json!({})).unwrap();
|
||||
let id = LayerId(new["layer"].as_u64().unwrap());
|
||||
assert!(s.active().unwrap().doc.layer(id).unwrap().mask.is_some());
|
||||
}
|
||||
|
||||
/// With the option off only the default reveal-all mask goes away: an active selection still
|
||||
/// becomes the layer mask and an explicit path still becomes the vector mask (path first).
|
||||
#[test]
|
||||
fn fill_layers_without_default_masks_keep_selection_and_explicit_path_masks() {
|
||||
const KINDS: [(&str, &str); 3] = [
|
||||
("layer.newFillLayer.solidColor", r##"{"color": "#112233"}"##),
|
||||
("layer.newFillLayer.gradient", "{}"),
|
||||
("layer.newFillLayer.pattern", r#"{"pattern": "Checkerboard"}"#),
|
||||
];
|
||||
let path = json!({"subpaths": [{"closed": true, "knots": [[2, 2], [10, 2], [10, 10], [2, 10]]}]});
|
||||
let (mut s, _) = new_session();
|
||||
option(&mut s, "useDefaultMasksOnFillLayers", false);
|
||||
// Creates the fill layer; returns its raster mask and whether it has a vector mask.
|
||||
let create = |s: &mut Session, cmd: &str, params: &str, path: Option<&Value>| {
|
||||
let mut p: Value = serde_json::from_str(params).unwrap();
|
||||
if let Some(path) = path {
|
||||
p["path"] = path.clone();
|
||||
}
|
||||
let id = LayerId(s.execute(cmd, p).unwrap()["layer"].as_u64().unwrap());
|
||||
let l = s.active().unwrap().doc.layer(id).unwrap();
|
||||
(l.mask.clone(), l.vector_mask.is_some())
|
||||
};
|
||||
|
||||
for (cmd, params) in KINDS {
|
||||
let (mask, vector) = create(&mut s, cmd, params, None);
|
||||
assert!(mask.is_none() && !vector, "{cmd}: no selection, no path: no mask at all");
|
||||
}
|
||||
|
||||
s.execute("select.rect", json!({"x": 4, "y": 4, "width": 8, "height": 6})).unwrap();
|
||||
for (cmd, params) in KINDS {
|
||||
let (mask, vector) = create(&mut s, cmd, params, None);
|
||||
let mask = mask.unwrap_or_else(|| panic!("{cmd}: the selection still becomes the layer mask"));
|
||||
assert!(!vector, "{cmd}");
|
||||
assert!(mask.surface.pixel(6, 6)[0] > 0.99, "{cmd}: inside the selection reveals");
|
||||
assert!(mask.surface.pixel(20, 12)[0] < 0.01, "{cmd}: outside the selection hides");
|
||||
}
|
||||
|
||||
// An explicit path wins over the selection, as with the option on.
|
||||
for selected in [true, false] {
|
||||
if !selected {
|
||||
s.execute("select.deselect", json!({})).unwrap();
|
||||
}
|
||||
for (cmd, params) in KINDS {
|
||||
let (mask, vector) = create(&mut s, cmd, params, Some(&path));
|
||||
assert!(vector, "{cmd}: the explicit path becomes the vector mask (selection: {selected})");
|
||||
assert!(mask.is_none(), "{cmd}: no raster mask next to the vector mask (selection: {selected})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether new effects start expanded is view state: it adds no history step, and editing an existing effect keeps its disclosure.
|
||||
#[test]
|
||||
fn expand_new_effects_is_view_state_and_keeps_edit_history_clean() {
|
||||
|
||||
@@ -317,23 +317,32 @@ pub(crate) fn placement(cmd: &str, p: &Value) -> Result<(f32, f32, bool, (f32, f
|
||||
|
||||
/// The edit of `layer.newFillLayer.pattern`: a Pattern Fill layer of `pat` placed as `p` asks,
|
||||
/// above `active`. The Pattern Fill dialog previews it on a copy of the document.
|
||||
pub(crate) fn insert_fill_layer(doc: &mut Document, active: &mut Option<photocraft_doc::LayerId>, pat: &Pattern, p: &Value) -> Result<photocraft_doc::LayerId> {
|
||||
pub(crate) fn insert_fill_layer(
|
||||
doc: &mut Document,
|
||||
active: &mut Option<photocraft_doc::LayerId>,
|
||||
pat: &Pattern,
|
||||
p: &Value,
|
||||
default_mask: bool,
|
||||
) -> Result<photocraft_doc::LayerId> {
|
||||
let (scale, angle, link, phase) = placement("layer.newFillLayer.pattern", p)?;
|
||||
ensure_in_doc(doc, pat);
|
||||
let fill = Fill::Pattern { name: pat.name.clone(), scale, id: pat.id.clone(), angle, link, phase };
|
||||
let mut l = Layer::new(doc.next_layer_name("Pattern Fill"), LayerContent::Fill(fill));
|
||||
crate::commands::mask_new_layer(doc, &mut l, p, "layer.newFillLayer.pattern")?;
|
||||
crate::commands::mask_new_layer(doc, &mut l, p, "layer.newFillLayer.pattern", default_mask)?;
|
||||
let id = doc.insert_above(*active, l);
|
||||
*active = Some(id);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// `layer.newFillLayer.pattern`: a Pattern Fill layer above the active one, masked as the Layers panel's Use Default Masks on Fill Layers option says.
|
||||
fn new_fill_layer(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let pat = resolve_param(s, "layer.newFillLayer.pattern", p)?;
|
||||
let id = s.edit("New Pattern Fill Layer", |doc, active| insert_fill_layer(doc, active, &pat, p))?;
|
||||
let default_mask = crate::commands::layer_panel_option(s, "useDefaultMasksOnFillLayers", true);
|
||||
let id = s.edit("New Pattern Fill Layer", |doc, active| insert_fill_layer(doc, active, &pat, p, default_mask))?;
|
||||
Ok(json!({"layer": id.0, "pattern": pat.id}))
|
||||
}
|
||||
|
||||
/// `brush.texturePattern`: use a pattern as the brush texture, converted to grey as the luminance of its composite over white.
|
||||
fn brush_texture(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let pat = resolve_param(s, "brush.texturePattern", p)?;
|
||||
let n = pat.is_paintable().then(|| usize::try_from(pat.pixel_count()).ok()).flatten();
|
||||
|
||||
@@ -124,13 +124,15 @@ pub fn preview(s: &Session, cmd: &str, p: &Value) -> Result<Document> {
|
||||
CREATE => {
|
||||
let pat = crate::pattern_cmds::resolve_param(s, CREATE, p)?;
|
||||
let mut active = st.active_layer;
|
||||
crate::pattern_cmds::insert_fill_layer(&mut doc, &mut active, &pat, p)?;
|
||||
let default_mask = crate::commands::layer_panel_option(s, "useDefaultMasksOnFillLayers", true);
|
||||
crate::pattern_cmds::insert_fill_layer(&mut doc, &mut active, &pat, p, default_mask)?;
|
||||
}
|
||||
other => return Err(EngineError::BadParams { cmd: other.into(), msg: "no Pattern Fill preview for this command".into() }),
|
||||
}
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
/// Register `layer.fill.pattern.set` (the Pattern Fill dialog's command), journaled like any other edit.
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![CommandSpec {
|
||||
id: SET,
|
||||
@@ -216,6 +218,40 @@ mod tests {
|
||||
assert!(preview(&s, "edit.fill", &json!({})).is_err());
|
||||
}
|
||||
|
||||
/// At 8, 16 and 32 bits the Pattern Fill preview and the committed layer carry the default mask only while the option is on, and creating one is one undoable step.
|
||||
#[test]
|
||||
fn create_preview_and_commit_honor_default_masks_option() {
|
||||
for depth in [8, 16, 32] {
|
||||
for default_mask in [false, true] {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", json!({"width": 40, "height": 30, "depth": depth})).unwrap();
|
||||
s.edit_prefs(|prefs| {
|
||||
prefs.dialogs.insert("ui.layersPanelOptions".into(), json!({"useDefaultMasksOnFillLayers": default_mask}));
|
||||
});
|
||||
let before = s.active().unwrap().doc.clone();
|
||||
let count = s.active().unwrap().history.entries().len();
|
||||
let p = json!({"pattern": "Grid", "scale": 50});
|
||||
let shown = preview(&s, CREATE, &p).unwrap();
|
||||
assert!(Arc::ptr_eq(&before, &s.active().unwrap().doc));
|
||||
assert_eq!(s.active().unwrap().history.entries().len(), count);
|
||||
let previewed = shown.layers.iter().find(|l| matches!(l.content, LayerContent::Fill(Fill::Pattern { .. }))).unwrap();
|
||||
assert_eq!(previewed.mask.is_some(), default_mask);
|
||||
if let Some(mask) = &previewed.mask {
|
||||
assert_eq!(mask.surface.format(), photocraft_color::PixelFormat::new(photocraft_color::ColorMode::Grayscale, shown.depth, false));
|
||||
}
|
||||
let id = LayerId(s.execute(CREATE, p).unwrap()["layer"].as_u64().unwrap());
|
||||
let committed = s.active().unwrap().doc.layer(id).unwrap();
|
||||
assert_eq!(committed.mask, previewed.mask);
|
||||
assert_eq!(committed.content, previewed.content);
|
||||
s.undo();
|
||||
assert_eq!(*s.active().unwrap().doc, *before);
|
||||
s.redo();
|
||||
assert_eq!(s.active().unwrap().doc.layer(id).unwrap().mask.is_some(), default_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Malformed or out-of-range params, and missing layers or patterns, are errors in both the command and its preview, never panics.
|
||||
#[test]
|
||||
fn bad_params_and_targets_are_errors_not_panics() {
|
||||
let (mut s, id) = session(8);
|
||||
|
||||
@@ -2682,6 +2682,7 @@ cameraData Lens Objektiv
|
||||
Show Layers Zobrazit vrstvy
|
||||
Thumbnail Contents Obsah miniatur
|
||||
Thumbnail Size Velikost miniatur
|
||||
Use Default Masks on Fill Layers Používat výchozí masky u vrstev výplně
|
||||
Expand New Effects Rozbalovat nové efekty
|
||||
Add “copy” to Copied Layers and Groups Přidávat „kopie“ ke kopírovaným vrstvám a skupinám
|
||||
Preview on canvas when reordering layers Zobrazovat náhled na plátně při změně pořadí vrstev
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2713,6 +2713,7 @@ cameraData Lens Objektiv
|
||||
Show Layers Ebenen einblenden
|
||||
Thumbnail Contents Miniaturinhalt
|
||||
Thumbnail Size Miniaturgröße
|
||||
Use Default Masks on Fill Layers Standardmasken für Füllebenen verwenden
|
||||
Expand New Effects Neue Effekte aufklappen
|
||||
Add “copy” to Copied Layers and Groups „Kopie“ zu kopierten Ebenen und Gruppen hinzufügen
|
||||
Preview on canvas when reordering layers Vorschau auf der Arbeitsfläche beim Neuanordnen von Ebenen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3764,6 +3764,7 @@ cameraData Lens Φακός
|
||||
Show Layers Εμφάνιση επιπέδων
|
||||
Thumbnail Contents Περιεχόμενο μικρογραφιών
|
||||
Thumbnail Size Μέγεθος μικρογραφιών
|
||||
Use Default Masks on Fill Layers Χρήση προεπιλεγμένων μασκών σε επίπεδα γεμίσματος
|
||||
Expand New Effects Ανάπτυξη νέων εφέ
|
||||
Add “copy” to Copied Layers and Groups Προσθήκη «αντίγραφο» σε αντιγραμμένα επίπεδα και ομάδες
|
||||
Preview on canvas when reordering layers Προεπισκόπηση στον καμβά κατά την αναδιάταξη επιπέδων
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 1030 and column 18.
|
@@ -2753,6 +2753,7 @@ cameraData Lens Objetivo
|
||||
Show Layers Mostrar capas
|
||||
Thumbnail Contents Contenido de miniaturas
|
||||
Thumbnail Size Tamaño de miniaturas
|
||||
Use Default Masks on Fill Layers Usar máscaras predeterminadas en capas de relleno
|
||||
Expand New Effects Expandir nuevos efectos
|
||||
Add “copy” to Copied Layers and Groups Añadir «copia» a las capas y grupos duplicados
|
||||
Preview on canvas when reordering layers Previsualizar en el lienzo al reordenar capas
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -3087,6 +3087,7 @@ cameraData Lens Objectif
|
||||
Show Layers Afficher les calques
|
||||
Thumbnail Contents Contenu des vignettes
|
||||
Thumbnail Size Taille des vignettes
|
||||
Use Default Masks on Fill Layers Utiliser les masques par défaut sur les calques de remplissage
|
||||
Expand New Effects Développer les nouveaux effets
|
||||
Add “copy” to Copied Layers and Groups Ajouter « copie » aux calques et groupes dupliqués
|
||||
Preview on canvas when reordering layers Prévisualiser sur la zone de travail lors du réagencement des calques
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4168,6 +4168,7 @@ cameraRaw Yellow Sárga
|
||||
Show layer mask badges Rétegmaszk-jelvények megjelenítése
|
||||
Thumbnail Contents Bélyegkép tartalma
|
||||
Thumbnail Size Bélyegképméret
|
||||
Use Default Masks on Fill Layers Alapértelmezett maszkok kitöltőrétegeken
|
||||
Accept and Go to Conflict Elfogadás és ugrás az ütközéshez
|
||||
Changes undone. Változások visszavonva.
|
||||
Choose a new shortcut for this command, or leave it without one. Válasszon új billentyűparancsot ehhez a parancshoz, vagy hagyja nélküle.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3074,6 +3074,7 @@ cameraData Lens Lensa
|
||||
Show Layers Tampilkan Lapisan
|
||||
Thumbnail Contents Isi Gambar Mini
|
||||
Thumbnail Size Ukuran Gambar Mini
|
||||
Use Default Masks on Fill Layers Gunakan Masker Default pada Lapisan Isian
|
||||
Expand New Effects Perluas Efek Baru
|
||||
Add “copy” to Copied Layers and Groups Tambahkan “salinan” ke Lapisan dan Grup yang Disalin
|
||||
Preview on canvas when reordering layers Pratinjau pada kanvas saat mengurutkan ulang lapisan
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -2738,6 +2738,7 @@ cameraData Lens Obiettivo
|
||||
Show Layers Mostra livelli
|
||||
Thumbnail Contents Contenuto miniature
|
||||
Thumbnail Size Dimensione miniature
|
||||
Use Default Masks on Fill Layers Usa maschere predefinite sui livelli di riempimento
|
||||
Expand New Effects Espandi nuovi effetti
|
||||
Add “copy” to Copied Layers and Groups Aggiungi «copia» ai livelli e ai gruppi duplicati
|
||||
Preview on canvas when reordering layers Anteprima sulla tela durante il riordino dei livelli
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2658 and column 53.
|
@@ -3679,6 +3679,7 @@ cameraData Lens レンズ
|
||||
Show Layers レイヤーを表示
|
||||
Thumbnail Contents サムネールの内容
|
||||
Thumbnail Size サムネールのサイズ
|
||||
Use Default Masks on Fill Layers 塗りつぶしレイヤーにデフォルトのマスクを使用
|
||||
Expand New Effects 新しい効果を展開
|
||||
Add “copy” to Copied Layers and Groups コピーしたレイヤーとグループの名前に「コピー」を追加
|
||||
Preview on canvas when reordering layers レイヤーの並べ替え時にカンバスでプレビュー
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3257,6 +3257,7 @@ cameraData Lens 렌즈
|
||||
Show Layers 레이어 표시
|
||||
Thumbnail Contents 축소판 내용
|
||||
Thumbnail Size 축소판 크기
|
||||
Use Default Masks on Fill Layers 칠 레이어에 기본 마스크 사용
|
||||
Expand New Effects 새 효과 펼치기
|
||||
Add “copy” to Copied Layers and Groups 복사한 레이어 및 그룹에 “복사” 추가
|
||||
Preview on canvas when reordering layers 레이어 순서 변경 시 캔버스에서 미리보기
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3172 and column 53.
|
@@ -3823,6 +3823,7 @@ cameraData Lens Lens
|
||||
Show Layers Lagen tonen
|
||||
Thumbnail Contents Inhoud van miniaturen
|
||||
Thumbnail Size Grootte van miniaturen
|
||||
Use Default Masks on Fill Layers Standaardmaskers gebruiken op opvullagen
|
||||
Expand New Effects Nieuwe effecten uitvouwen
|
||||
Add “copy” to Copied Layers and Groups „kopie” toevoegen aan gekopieerde lagen en groepen
|
||||
Preview on canvas when reordering layers Voorvertoning op canvas bij herschikken van lagen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -3681,6 +3681,7 @@ cameraData Lens Obiektyw
|
||||
Show Layers Pokaż warstwy
|
||||
Thumbnail Contents Zawartość miniaturek
|
||||
Thumbnail Size Rozmiar miniaturek
|
||||
Use Default Masks on Fill Layers Użyj domyślnych masek na warstwach wypełnienia
|
||||
Expand New Effects Rozwijaj nowe efekty
|
||||
Add “copy” to Copied Layers and Groups Dodawaj „kopia” do skopiowanych warstw i grup
|
||||
Preview on canvas when reordering layers Podgląd na obszarze roboczym podczas zmiany kolejności warstw
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3090,6 +3090,7 @@ cameraData Lens Lente
|
||||
Show Layers Mostrar camadas
|
||||
Thumbnail Contents Conteúdo das miniaturas
|
||||
Thumbnail Size Tamanho das miniaturas
|
||||
Use Default Masks on Fill Layers Usar máscaras padrão nas camadas de preenchimento
|
||||
Expand New Effects Expandir novos efeitos
|
||||
Add “copy” to Copied Layers and Groups Adicionar “cópia” às camadas e grupos copiados
|
||||
Preview on canvas when reordering layers Pré-visualizar na tela ao reordenar camadas
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3011,6 +3011,7 @@ cameraData Lens Объектив
|
||||
Show Layers Показать слои
|
||||
Thumbnail Contents Содержимое миниатюр
|
||||
Thumbnail Size Размер миниатюр
|
||||
Use Default Masks on Fill Layers Использовать маски по умолчанию для слоёв-заливок
|
||||
Expand New Effects Раскрывать новые эффекты
|
||||
Add “copy” to Copied Layers and Groups Добавлять «копия» к дублированным слоям и группам
|
||||
Preview on canvas when reordering layers Предпросмотр на холсте при изменении порядка слоёв
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4322,6 +4322,7 @@ shape Appearance Izgled
|
||||
Show layer mask badges Prikaži oznake maske sloja
|
||||
Thumbnail Contents Sadržaj sličice
|
||||
Thumbnail Size Veličina sličice
|
||||
Use Default Masks on Fill Layers Koristi podrazumevane maske na slojevima popune
|
||||
Accept and Go to Conflict Prihvati i idi na sukob
|
||||
Changes undone. Izmene su poništene.
|
||||
Choose a new shortcut for this command, or leave it without one. Izaberite novu prečicu za ovu komandu ili je ostavite bez prečice.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3979,6 +3979,7 @@ cameraData Lens Lens
|
||||
Show layer mask badges Katman maskesi rozetlerini göster
|
||||
Thumbnail Contents Küçük Resim İçeriği
|
||||
Thumbnail Size Küçük Resim Boyutu
|
||||
Use Default Masks on Fill Layers Dolgu Katmanlarında Varsayılan Maskeleri Kullan
|
||||
Accept and Go to Conflict Kabul Et ve Çakışmaya Git
|
||||
Changes undone. Değişiklikler geri alındı.
|
||||
Choose a new shortcut for this command, or leave it without one. Bu komut için yeni bir kısayol seçin veya kısayolsuz bırakın.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -3755,6 +3755,7 @@ cameraData Lens Об'єктив
|
||||
Show Layers Показати шари
|
||||
Thumbnail Contents Вміст мініатюр
|
||||
Thumbnail Size Розмір мініатюр
|
||||
Use Default Masks on Fill Layers Використовувати типові маски для шарів заливки
|
||||
Expand New Effects Розгортати нові ефекти
|
||||
Add “copy” to Copied Layers and Groups Додавати «копія» до скопійованих шарів і груп
|
||||
Preview on canvas when reordering layers Попередній перегляд на полотні під час зміни порядку шарів
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 1393 and column 18.
|
@@ -3740,6 +3740,7 @@ curves Smooth Làm mượt
|
||||
Show layer mask badges Hiện huy hiệu mặt nạ lớp
|
||||
Thumbnail Contents Nội dung hình thu nhỏ
|
||||
Thumbnail Size Kích thước hình thu nhỏ
|
||||
Use Default Masks on Fill Layers Dùng mặt nạ mặc định cho lớp tô
|
||||
Accept and Go to Conflict Chấp nhận và đến xung đột
|
||||
Changes undone. Đã hoàn tác thay đổi.
|
||||
Choose a new shortcut for this command, or leave it without one. Chọn phím tắt mới cho lệnh này, hoặc để lệnh không có phím tắt.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2690,6 +2690,7 @@ cameraData Lens 镜头
|
||||
Show Layers 显示图层
|
||||
Thumbnail Contents 缩略图内容
|
||||
Thumbnail Size 缩略图大小
|
||||
Use Default Masks on Fill Layers 在填充图层上使用默认蒙版
|
||||
Expand New Effects 展开新效果
|
||||
Add “copy” to Copied Layers and Groups 为复制的图层和组添加“副本”
|
||||
Preview on canvas when reordering layers 重新排列图层时在画布上预览
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2606 and column 53.
|
@@ -2660,6 +2660,7 @@ cameraData Lens 鏡頭
|
||||
Show Layers 顯示圖層
|
||||
Thumbnail Contents 縮圖內容
|
||||
Thumbnail Size 縮圖大小
|
||||
Use Default Masks on Fill Layers 在填色圖層使用預設遮色片
|
||||
Expand New Effects 展開新效果
|
||||
Add “copy” to Copied Layers and Groups 為複製的圖層和群組加上「副本」
|
||||
Preview on canvas when reordering layers 重新排列圖層時在畫布上預覽
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -238,6 +238,8 @@ fn options_body(ui: &mut Ui, draft: &mut LayersPanelOptions) {
|
||||
ui.add_space(4.0);
|
||||
});
|
||||
ui.add_space(9.0);
|
||||
ui.checkbox(&mut draft.use_default_masks_on_fill_layers, tl!("Use Default Masks on Fill Layers"));
|
||||
ui.add_space(3.0);
|
||||
ui.checkbox(&mut draft.expand_new_effects, tl!("Expand New Effects"));
|
||||
ui.add_space(3.0);
|
||||
ui.checkbox(&mut draft.add_copy_to_copied_layers_and_groups, tl!("Add “copy” to Copied Layers and Groups"));
|
||||
@@ -346,6 +348,7 @@ mod tests {
|
||||
assert_eq!(options.thumbnail_size, LayerThumbnailSize::Medium);
|
||||
assert_eq!(options.thumbnail_contents, LayerThumbnailContents::EntireDocument);
|
||||
assert!(options.show_filters);
|
||||
assert!(options.use_default_masks_on_fill_layers);
|
||||
assert!(options.expand_new_effects);
|
||||
assert!(options.add_copy_to_copied_layers_and_groups);
|
||||
assert!(options.preview_on_canvas_when_reordering_layers);
|
||||
@@ -385,6 +388,26 @@ mod tests {
|
||||
assert!(restored.layers_panel_dialog.is_none());
|
||||
}
|
||||
|
||||
/// With the option off a new fill layer has no mask and the mask is not targeted; with it on the new layer's mask is targeted.
|
||||
#[test]
|
||||
fn disabling_default_fill_masks_never_targets_an_absent_mask() {
|
||||
let mut session = photocraft_engine::Session::new();
|
||||
session.execute("file.new", json!({"width": 32, "height": 24})).unwrap();
|
||||
let mut app = PhotocraftApp::new(session, crate::Services::default());
|
||||
app.ui.layers_panel_options.use_default_masks_on_fill_layers = false;
|
||||
persist_options(&mut app);
|
||||
app.run("layer.newFillLayer.solidColor", json!({"color": "#778899"})).unwrap();
|
||||
let layer = app.session.active().and_then(|st| st.active_layer.and_then(|id| st.doc.layer(id))).unwrap();
|
||||
assert!(layer.mask.is_none());
|
||||
assert!(!app.ui.mask_target && !app.ui.vector_mask_target);
|
||||
app.ui.layers_panel_options.use_default_masks_on_fill_layers = true;
|
||||
persist_options(&mut app);
|
||||
app.run("layer.newFillLayer.gradient", json!({})).unwrap();
|
||||
let layer = app.session.active().and_then(|st| st.active_layer.and_then(|id| st.doc.layer(id))).unwrap();
|
||||
assert!(layer.mask.is_some());
|
||||
assert!(app.ui.mask_target);
|
||||
}
|
||||
|
||||
/// Every option, including the mask badges, goes to the preferences and comes back from them.
|
||||
#[test]
|
||||
fn prefs_options_save_and_restore_all_switches() {
|
||||
@@ -395,6 +418,7 @@ mod tests {
|
||||
thumbnail_size: LayerThumbnailSize::Large,
|
||||
thumbnail_contents: LayerThumbnailContents::EntireDocument,
|
||||
show_filters: false,
|
||||
use_default_masks_on_fill_layers: false,
|
||||
expand_new_effects: false,
|
||||
add_copy_to_copied_layers_and_groups: false,
|
||||
preview_on_canvas_when_reordering_layers: false,
|
||||
|
||||
@@ -1247,13 +1247,24 @@ impl PhotocraftApp {
|
||||
jobs_ui::run(self, id, params)
|
||||
};
|
||||
let creates_adjustment_or_fill = id.starts_with("layer.newAdjustmentLayer.") || id.starts_with("layer.newFillLayer.");
|
||||
if r.is_ok() && (ADDS_LAYER_MASK.contains(&id) || creates_adjustment_or_fill) {
|
||||
// Adding a layer mask targets it, as in Photoshop (#2166).
|
||||
if r.is_ok() && ADDS_LAYER_MASK.contains(&id) {
|
||||
// Adding an explicit mask targets it, as in Photoshop (#2166).
|
||||
self.ui.mask_target = true;
|
||||
self.ui.vector_mask_target = false;
|
||||
// Properties still shows the new adjustment or fill layer's settings, not its mask,
|
||||
// as in Photoshop (#3369): clicking the mask thumbnail switches it to the mask.
|
||||
self.ui.properties_layer = if creates_adjustment_or_fill { self.session.active().and_then(|st| st.active_layer).map(|l| l.0) } else { None };
|
||||
self.ui.properties_layer = None;
|
||||
} else if r.is_ok() && creates_adjustment_or_fill {
|
||||
// Panel Options may suppress a fill layer's default mask. Never target a
|
||||
// nonexistent mask; an active path creates a vector mask instead.
|
||||
let masks = self
|
||||
.session
|
||||
.active()
|
||||
.and_then(|st| st.active_layer.and_then(|id| st.doc.layer(id)))
|
||||
.map(|layer| (layer.mask.is_some(), layer.vector_mask.is_some()))
|
||||
.unwrap_or((false, false));
|
||||
self.ui.mask_target = masks.0;
|
||||
self.ui.vector_mask_target = !masks.0 && masks.1;
|
||||
// Keep Properties on the new layer contents, as on main (#3369).
|
||||
self.ui.properties_layer = self.session.active().and_then(|st| st.active_layer).map(|l| l.0);
|
||||
}
|
||||
if r.is_ok() && creates_adjustment_or_fill {
|
||||
// Properties comes to the front, shown and expanded, so the new layer's settings can
|
||||
|
||||
@@ -32,6 +32,7 @@ pub struct LayersPanelOptions {
|
||||
#[serde(skip)]
|
||||
pub thumbnail_contents: LayerThumbnailContents,
|
||||
pub show_filters: bool,
|
||||
pub use_default_masks_on_fill_layers: bool,
|
||||
pub expand_new_effects: bool,
|
||||
pub add_copy_to_copied_layers_and_groups: bool,
|
||||
pub preview_on_canvas_when_reordering_layers: bool,
|
||||
@@ -45,6 +46,7 @@ impl LayersPanelOptions {
|
||||
thumbnail_size: LayerThumbnailSize::Medium,
|
||||
thumbnail_contents: LayerThumbnailContents::EntireDocument,
|
||||
show_filters: true,
|
||||
use_default_masks_on_fill_layers: true,
|
||||
expand_new_effects: true,
|
||||
add_copy_to_copied_layers_and_groups: true,
|
||||
preview_on_canvas_when_reordering_layers: true,
|
||||
|
||||
Reference in new issue
Block a user