mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-22 15:14:11 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25228b0d4d |
No files matched your search
@@ -857,11 +857,18 @@ fn path_stroke(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
let lines = vector::flatten_path(&path, 0.1);
|
||||
let id = layer_id(s, p)?;
|
||||
let bg = s.tools.background;
|
||||
let dmg = s.edit("Stroke Path", |doc, _| {
|
||||
let sel = doc.selection.clone();
|
||||
let l = doc.layer_mut(id).ok_or(EngineError::NoLayer(id))?;
|
||||
let lock = l.locks.transparency;
|
||||
let surf = l.surface_mut().ok_or_else(|| EngineError::Other("Stroke Path needs a pixel layer".into()))?;
|
||||
let mut brush = brush.clone();
|
||||
if brush.erase && lock {
|
||||
// Locked transparency (e.g. the Background): the Eraser paints the background colour (#76).
|
||||
brush.erase = false;
|
||||
brush.color = bg;
|
||||
}
|
||||
let mut dmg = Rect::EMPTY;
|
||||
for pl in &lines {
|
||||
let mut pts: Vec<photocraft_paint::StrokePoint> = pl.pts.iter().map(|&(x, y)| photocraft_paint::StrokePoint::new(x, y, 1.0)).collect();
|
||||
|
||||
@@ -92,6 +92,30 @@ fn eraser_on_transparency_locked_layer_paints_background_colour() {
|
||||
assert_eq!(layer_px(&s, 20, 25)[3], 0.0, "transparent pixels stay transparent");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stroke_path_with_the_eraser_paints_background_colour_on_locked_layers() {
|
||||
let path = json!({"path": {"subpaths": [{"knots": [[5, 10], [35, 10]]}]}});
|
||||
// The Background: background colour, still opaque.
|
||||
let mut s = doc("rgb", 8);
|
||||
s.execute("path.set", path.clone()).unwrap();
|
||||
s.execute("path.stroke", json!({"tool": "eraser", "size": 6, "hardness": 1.0, "opacity": 100})).unwrap();
|
||||
assert!(close(layer_px(&s, 20, 10), [1.0, 0.0, 0.0, 1.0]), "{:?}", layer_px(&s, 20, 10));
|
||||
// A transparency-locked layer: the same, and transparent pixels stay transparent.
|
||||
let mut s = doc("rgb", 16);
|
||||
s.execute("layer.new.layer", json!({})).unwrap();
|
||||
s.execute("paint.stroke", json!({"points": [[2, 10], [38, 10]], "size": 12, "hardness": 1.0, "color": "#00ff00"})).unwrap();
|
||||
s.execute("layer.lockLayers", json!({"transparency": true})).unwrap();
|
||||
s.execute("path.set", path.clone()).unwrap();
|
||||
s.execute("path.stroke", json!({"tool": "eraser", "size": 6, "hardness": 1.0, "opacity": 100})).unwrap();
|
||||
assert!(close(layer_px(&s, 20, 10), [1.0, 0.0, 0.0, 1.0]), "{:?}", layer_px(&s, 20, 10));
|
||||
assert_eq!(layer_px(&s, 20, 25)[3], 0.0);
|
||||
// Unlocked: erased to transparency.
|
||||
s.execute("layer.lockLayers", json!({"transparency": false})).unwrap();
|
||||
s.execute("paint.stroke", json!({"points": [[2, 10], [38, 10]], "size": 12, "hardness": 1.0, "color": "#00ff00"})).unwrap();
|
||||
s.execute("path.stroke", json!({"tool": "eraser", "size": 6, "hardness": 1.0, "opacity": 100})).unwrap();
|
||||
assert_eq!(layer_px(&s, 20, 10)[3], 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eraser_bad_params_fail_gracefully() {
|
||||
let mut s = doc("rgb", 8);
|
||||
|
||||
@@ -1104,6 +1104,21 @@ fn color_picker(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
|
||||
|
||||
// ----------------------------------------------------------------------------- layers
|
||||
|
||||
/// The simple themes' single lock button. On the Background it converts the layer to a normal
|
||||
/// one, as clicking the Background's lock does in Photoshop. Elsewhere it clears every lock when
|
||||
/// any is set, and locks all otherwise. Toggling only "lock all" would leave the Background's
|
||||
/// transparency lock (still showing as locked), and a second click would lock its pixels,
|
||||
/// shutting out the Eraser and every other paint tool (#76).
|
||||
fn simple_lock_toggle(background: bool, l: &Layer) -> (String, Value) {
|
||||
if background {
|
||||
return ("layer.new.layerFromBackground".into(), json!({}));
|
||||
}
|
||||
let k = &l.locks;
|
||||
let locked = k.transparency || k.pixels || k.position || k.artboard || k.all;
|
||||
let locks = if locked { json!({"transparency": false, "pixels": false, "position": false, "artboard": false, "all": false}) } else { json!({"all": true}) };
|
||||
("layer.setProps".into(), json!({"layer": l.id.0, "locks": locks}))
|
||||
}
|
||||
|
||||
fn blend_options(groups: bool) -> Vec<(BlendMode, &'static str)> {
|
||||
std::iter::once(BlendMode::PassThrough).filter(|_| groups).chain(BlendMode::LAYER_MODES).map(|m| (m, m.label())).collect()
|
||||
}
|
||||
@@ -1192,7 +1207,7 @@ fn layers(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
|
||||
if !t.pro {
|
||||
let locked = l.locks.transparency || l.locks.position || l.locks.all;
|
||||
if icons::button(ui, if locked { "lock" } else { "lock-open" }, 22.0, locked, "Lock layer").clicked() {
|
||||
actions.push(("layer.setProps".into(), json!({"layer": l.id.0, "locks": {"all": !l.locks.all}})));
|
||||
actions.push(simple_lock_toggle(bg, l));
|
||||
}
|
||||
}
|
||||
ui.add_enabled_ui(!bg, |ui| {
|
||||
@@ -2248,3 +2263,53 @@ mod color_tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod lock_tests {
|
||||
use super::*;
|
||||
use crate::canvas::{ToolEvent, tool_event};
|
||||
use egui::Modifiers;
|
||||
|
||||
fn click_lock(app: &mut PhotocraftApp) {
|
||||
let st = app.session.active().unwrap();
|
||||
let l = st.active_layer.and_then(|id| st.doc.layer(id)).unwrap();
|
||||
let (cmd, p) = simple_lock_toggle(crate::doc_props_ui::is_background(&st.doc, l), l);
|
||||
app.run(&cmd, p).unwrap();
|
||||
}
|
||||
|
||||
fn erase_line(app: &mut PhotocraftApp) {
|
||||
app.ui.tool = Tool::Eraser;
|
||||
let m = Modifiers::NONE;
|
||||
tool_event(app, ToolEvent::Down { x: 10.0, y: 40.0, pressure: 1.0 }, m);
|
||||
tool_event(app, ToolEvent::Move { x: 100.0, y: 40.0, pressure: 1.0 }, m);
|
||||
tool_event(app, ToolEvent::Up { x: 100.0, y: 40.0 }, m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_lock_button_never_locks_the_eraser_out_of_the_background() {
|
||||
// #76: the simple themes' lock button used to toggle "lock all" on the Background, which
|
||||
// kept showing as locked and then refused every paint tool.
|
||||
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), crate::Services::default());
|
||||
app.run("file.new", json!({"width": 120, "height": 80})).unwrap();
|
||||
app.run("tools.setColors", json!({"background": "#ff0000"})).unwrap();
|
||||
app.run("tools.setBrush", json!({"brush": {"size": 12, "hardness": 1.0}})).unwrap();
|
||||
// Clicking the Background's lock converts it to a normal layer (Photoshop).
|
||||
click_lock(&mut app);
|
||||
let l = &app.session.active().unwrap().doc.layers[0];
|
||||
assert_eq!(l.name, "Layer 0");
|
||||
assert!(!(l.locks.transparency || l.locks.pixels || l.locks.position || l.locks.all));
|
||||
erase_line(&mut app);
|
||||
assert!(!app.ui.status_error, "{}", app.ui.status);
|
||||
assert_eq!(app.session.active().unwrap().doc.layers[0].surface().unwrap().rgba(50, 40)[3], 0.0, "erased to transparency");
|
||||
// On a normal layer the button locks all, then unlocks everything.
|
||||
click_lock(&mut app);
|
||||
assert!(app.session.active().unwrap().doc.layers[0].locks.all);
|
||||
click_lock(&mut app);
|
||||
let k = app.session.active().unwrap().doc.layers[0].locks;
|
||||
assert!(!(k.transparency || k.pixels || k.position || k.artboard || k.all));
|
||||
// A layer with only transparency locked unlocks in one click.
|
||||
app.run("layer.setProps", json!({"locks": {"transparency": true}})).unwrap();
|
||||
click_lock(&mut app);
|
||||
assert!(!app.session.active().unwrap().doc.layers[0].locks.transparency);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user