Merge pull request #12164 from ElNeMeR70/fix/patch-sample-all-layers-default-on-12125

Patch tool: start Content-Aware Sample All Layers on (#12125)
This commit is contained in:
David Mann authored and GitHub committed 2026-11-08 00:41:59 +01:00
commit 3aa1d9f432
3 files changed
+97 -8

No files matched your search

@@ -0,0 +1,64 @@
//! The Patch tool's Content-Aware Sample All Layers starts on (#12125): a fresh start and Reset
//! Tool give the Patch tool Sample All Layers on while other tools stay off, and saved settings
//! keep their stored value.
use serde_json::json;
use crate::PhotocraftApp;
use crate::state::{SampleAllLayers, Tool, ToolOptions};
fn app() -> PhotocraftApp {
let mut s = photocraft_engine::Session::new();
s.execute("file.new", json!({"width": 16, "height": 16})).unwrap();
PhotocraftApp::new(s, crate::Services::default())
}
#[test]
fn patch_sample_all_layers_starts_on_and_other_tools_start_off() {
let defaults = SampleAllLayers::default();
assert!(defaults.patch);
assert!(defaults.get(Tool::Patch));
for tool in Tool::ALL {
if tool != Tool::Patch {
assert!(!defaults.get(tool), "{tool:?} should default to Sample All Layers off");
}
}
let app = app();
assert!(app.ui.tool_options.sample_all.patch);
}
#[test]
fn reset_tool_restores_patch_sample_all_layers_to_on() {
let mut app = app();
let ctx = egui::Context::default();
app.ui.tool = Tool::Patch;
app.ui.tool_options.sample_all.patch = false;
app.ui.tool_options.sample_all.magic_wand = true;
crate::menus::invoke(&mut app, &ctx, super::RESET, json!({})).unwrap();
assert!(app.ui.tool_options.sample_all.patch, "Reset Tool restores Patch Sample All Layers to on");
assert!(app.ui.tool_options.sample_all.magic_wand, "other tools keep their Sample All Layers setting");
}
#[test]
fn saved_settings_keep_stored_patch_sample_all_layers_value() {
let saved_off: ToolOptions = serde_json::from_value(json!({
"sample_all": {
"patch": false,
"magic_wand": true
}
}))
.unwrap();
assert!(!saved_off.sample_all.patch);
assert!(saved_off.sample_all.magic_wand);
let missing_patch: ToolOptions = serde_json::from_value(json!({
"sample_all": {
"magic_wand": true
}
}))
.unwrap();
assert!(missing_patch.sample_all.patch);
assert!(missing_patch.sample_all.magic_wand);
}
+23 -4
View File
@@ -353,10 +353,10 @@ fn default_eyedropper_sample() -> String {
}
/// Each tool's Sample All Layers (the Paint Bucket calls it All Layers). Photoshop keeps one per
/// tool, all off by default: turning it on for the Magic Wand must not leave Blur or Smudge
/// copying the composite onto the active layer (#5524). The Mixer Brush keeps its own with the
/// brush (`MixerSettings`).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
/// tool, all off by default except the Patch tool's Content-Aware Sample All Layers (#5524,
/// #12125): turning it on for the Magic Wand must not leave Blur or Smudge copying the composite
/// onto the active layer. The Mixer Brush keeps its own with the brush (`MixerSettings`).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default)]
pub struct SampleAllLayers {
pub magic_wand: bool,
@@ -373,6 +373,25 @@ pub struct SampleAllLayers {
pub smudge: bool,
}
impl Default for SampleAllLayers {
fn default() -> Self {
Self {
magic_wand: false,
quick_selection: false,
object_selection: false,
paint_bucket: false,
magic_eraser: false,
spot_healing: false,
patch: true,
content_aware_move: false,
remove: false,
blur: false,
sharpen: false,
smudge: false,
}
}
}
impl SampleAllLayers {
/// `tool`'s setting; false for a tool without the option.
pub fn get(mut self, tool: Tool) -> bool {
+10 -4
View File
@@ -159,10 +159,12 @@ pub fn reset_options(o: &mut ToolOptions, tool: Tool) {
/// The serialized name of `tool`'s own Sample All Layers inside `sample_all`, if it has one: a
/// tool preset stores that one alone, not the other tools' (#5524, #7356).
pub fn sample_all_key(tool: Tool) -> Option<String> {
let mut probe = crate::state::SampleAllLayers::default();
*probe.slot(tool)? = true;
let Ok(Value::Object(m)) = serde_json::to_value(probe) else { return None };
m.into_iter().find(|(_, v)| v.as_bool() == Some(true)).map(|(k, _)| k)
let base = crate::state::SampleAllLayers::default();
let mut probe = base;
let slot = probe.slot(tool)?;
*slot = !*slot;
let (Ok(Value::Object(b)), Ok(Value::Object(m))) = (serde_json::to_value(base), serde_json::to_value(probe)) else { return None };
m.into_iter().find(|(k, v)| b.get(k) != Some(v)).map(|(k, _)| k)
}
/// The [`ToolOptions`] fields `tool` shows, by their serialized names: what Reset Tool resets and
@@ -370,3 +372,7 @@ mod tests {
assert_eq!((keys.len(), distinct.len()), (12, 12), "one key per tool with the option");
}
}
#[cfg(test)]
#[path = "../patch_sample_all_default_tests.rs"]
mod patch_sample_all_default_tests;