Layer locks: locking the pixels clears the transparency lock, as in Photoshop (#4892) (#4898)

This commit is contained in:
F4LCON authored and GitHub committed 2026-10-13 12:47:10 -04:00
1 parent 1bac788706
commit ef8dc9ea30
4 files changed
+52 -15

No files matched your search

+21
View File
@@ -99,6 +99,27 @@ pub struct Locks {
}
impl Locks {
/// Set the lock a command names (`transparency`, `pixels`, `position`, `artboard`, `all`);
/// false for an unknown name. Lock Image Pixels includes Lock Transparent Pixels, so, as in
/// Photoshop, locking the pixels clears the transparency lock, and that one stays off while the
/// pixels are locked (#4892).
pub fn set(&mut self, key: &str, on: bool) -> bool {
match key {
"transparency" => self.transparency = on && !self.pixels,
"pixels" => {
self.pixels = on;
if on {
self.transparency = false;
}
}
"position" => self.position = on,
"artboard" => self.artboard = on,
"all" => self.all = on,
_ => return false,
}
true
}
/// Whether any lock is set: Photoshop then shows the lock icon on the layer's row.
pub fn any(self) -> bool {
self.transparency || self.pixels || self.position || self.artboard || self.all
+2 -7
View File
@@ -701,13 +701,8 @@ fn build() -> Vec<CommandSpec> {
if let Some(Value::Object(m)) = p.get("locks") {
for (k, v) in m {
let v = v.as_bool().unwrap_or(false);
match k.as_str() {
"transparency" => l.locks.transparency = v,
"pixels" => l.locks.pixels = v,
"position" => l.locks.position = v,
"artboard" => l.locks.artboard = v,
"all" => l.locks.all = v,
other => return Err(bad("layer.setProps", format!("unknown lock `{other}`"))),
if !l.locks.set(k, v) {
return Err(bad("layer.setProps", format!("unknown lock `{k}`")));
}
}
}
+26 -7
View File
@@ -1168,13 +1168,7 @@ fn lock_layers(s: &mut Session, p: &Value) -> Result<Value> {
for id in &sel {
let l = doc.layer_mut(*id).ok_or(EngineError::NoLayer(*id))?;
for (k, v) in &given {
match *k {
"transparency" => l.locks.transparency = *v,
"pixels" => l.locks.pixels = *v,
"position" => l.locks.position = *v,
"artboard" => l.locks.artboard = *v,
_ => l.locks.all = *v,
}
l.locks.set(k, *v);
}
}
Ok(())
@@ -2606,6 +2600,31 @@ mod tests {
assert_eq!(s.active().unwrap().history.entries().last().map(|e| e.as_str()), Some("Reverse"));
}
/// #4892: Lock Image Pixels includes Lock Transparent Pixels, so the two are never both on:
/// locking the pixels clears the transparency lock, which stays off while they are locked,
/// through both `layer.setProps` and `layer.lockLayers`.
#[test]
fn locking_pixels_clears_and_holds_off_the_transparency_lock() {
let mut s = Session::new();
s.execute("file.new", json!({"width": 8, "height": 8})).unwrap();
let id = LayerId(s.execute("layer.new.layer", json!({})).unwrap()["layer"].as_u64().unwrap());
let locks = |s: &Session| doc(s).layer(id).unwrap().locks;
s.execute("layer.setProps", json!({"locks": {"transparency": true}})).unwrap();
assert!(locks(&s).transparency);
s.execute("layer.setProps", json!({"locks": {"pixels": true}})).unwrap();
assert!(locks(&s).pixels && !locks(&s).transparency, "locking the pixels clears the transparency lock");
s.execute("layer.setProps", json!({"locks": {"transparency": true}})).unwrap();
assert!(!locks(&s).transparency, "and it stays off while the pixels are locked");
s.execute("layer.setProps", json!({"locks": {"transparency": true, "pixels": true}})).unwrap();
assert!(locks(&s).pixels && !locks(&s).transparency, "both at once: the pixel lock wins");
s.execute("layer.lockLayers", json!({"pixels": false})).unwrap();
s.execute("layer.lockLayers", json!({"transparency": true})).unwrap();
assert!(locks(&s).transparency && !locks(&s).pixels, "with the pixels unlocked it can be set again");
s.execute("layer.lockLayers", json!({"pixels": true})).unwrap();
assert!(locks(&s).pixels && !locks(&s).transparency);
assert!(s.execute("layer.setProps", json!({"locks": {"nope": true}})).is_err());
}
#[test]
fn inspect_reports_selection() {
let mut s = session(8);
+3 -1
View File
@@ -2285,7 +2285,9 @@ fn layers(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
("artboard", "scan", tl!("Prevent auto-nesting in and out of Artboards and Frames"), lk.artboard),
("all", "lock", tl!("Lock all"), lk.all),
] {
if icons::button(ui, icon, 20.0, on, tip).clicked() {
// Lock Image Pixels includes Lock Transparent Pixels, which waits for it (#4892).
let enabled = key != "transparency" || !lk.pixels;
if ui.add_enabled_ui(enabled, |ui| icons::button(ui, icon, 20.0, on, tip)).inner.clicked() {
actions.push(("layer.setProps".into(), json!({"layer": l.id.0, "locks": { key: !on }})));
}
}