Offer a mask that is nothing but a hand
Every route to a layer began with a selection — a gradient, a band, a subject, a category — and painting was reachable only by making one of those and joining a painted part to it. So the answer to "brush a correction onto this corner of the sky" was "add a radial gradient you do not want, then paint into that", which is not an answer. Paint sits beside Linear and Radial and makes a layer whose base is a brush. It covers nothing until a stroke lands in it, so pressing it arms the brush and shows the mask as well: a row that appeared and changed no pixel, with the pointer still in "select", is indistinguishable from a button that did nothing.
This commit is contained in:
@@ -3434,6 +3434,37 @@ impl DevelopSession {
|
|||||||
Some(id)
|
Some(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TRACES: FR-DEV-19b
|
||||||
|
/// Add a mask that is nothing but hand-painted, and select it.
|
||||||
|
///
|
||||||
|
/// **The one route to a brush that starts from nothing.** Everything else
|
||||||
|
/// in this file makes a layer out of a selection — a gradient, a band, a
|
||||||
|
/// subject, a category — and painting was reachable only by making one of
|
||||||
|
/// those first and then joining a painted part to it. So the answer to
|
||||||
|
/// "brush a correction onto this corner of the sky" was "add a radial
|
||||||
|
/// gradient you do not want, then paint into it", which is not an answer.
|
||||||
|
///
|
||||||
|
/// The layer covers nothing until a stroke lands in it, which is exactly
|
||||||
|
/// what [`dr_pipeline::mask::MaskPart::covers`] is about: it is not active,
|
||||||
|
/// it costs no slice, and an invert on it would not take the adjustment
|
||||||
|
/// global. What the panel shows meanwhile is a row with the tools armed
|
||||||
|
/// over it — see `masks_ui`, which arms them.
|
||||||
|
pub fn add_brush_mask(&mut self) -> Option<String> {
|
||||||
|
let id = self.graph.masks().next_id();
|
||||||
|
if !self
|
||||||
|
.graph
|
||||||
|
.masks_mut()
|
||||||
|
.push(MaskLayer::new(id.clone(), MaskSource::brush()))
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.active_masks = vec![id.clone()];
|
||||||
|
self.active_part = 0;
|
||||||
|
self.history
|
||||||
|
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
||||||
|
Some(id)
|
||||||
|
}
|
||||||
|
|
||||||
/// TRACES: FR-DEV-10
|
/// TRACES: FR-DEV-10
|
||||||
/// Add a mask that selects by a range of the photograph's own values.
|
/// Add a mask that selects by a range of the photograph's own values.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1010,6 +1010,37 @@ pub(crate) fn wire(
|
|||||||
redraw(&w);
|
redraw(&w);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// TRACES: FR-DEV-19b
|
||||||
|
{
|
||||||
|
let weak = window.as_weak();
|
||||||
|
let session = session.clone();
|
||||||
|
let redraw = redraw.clone();
|
||||||
|
let rows = rows.clone();
|
||||||
|
window.global::<Masking>().on_add_brush(move || {
|
||||||
|
let Some(w) = weak.upgrade() else { return };
|
||||||
|
let made = session
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|s| s.add_brush_mask())
|
||||||
|
.is_some();
|
||||||
|
// Armed, and this is not a convenience: the layer covers no pixel
|
||||||
|
// until something is painted into it, so a press that made a row
|
||||||
|
// and left the pointer in "select" would be indistinguishable from
|
||||||
|
// a button that did nothing. The same reasoning as `on_part_added`
|
||||||
|
// below, and the tool's own handler turns the mask view on.
|
||||||
|
if made {
|
||||||
|
w.global::<Masking>().set_tool(1);
|
||||||
|
if let Some(s) = session.borrow_mut().as_mut() {
|
||||||
|
if s.mask_view() == 0 {
|
||||||
|
s.set_mask_view(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync(&w, &session);
|
||||||
|
sync_rows(&w, &rows, &session);
|
||||||
|
redraw(&w);
|
||||||
|
});
|
||||||
|
}
|
||||||
// TRACES: FR-DEV-10
|
// TRACES: FR-DEV-10
|
||||||
{
|
{
|
||||||
let weak = window.as_weak();
|
let weak = window.as_weak();
|
||||||
|
|||||||
@@ -970,6 +970,22 @@ export component MaskPanel inherits Rectangle {
|
|||||||
enabled: Develop.enabled;
|
enabled: Develop.enabled;
|
||||||
clicked => { Masking.add-gradient(true); }
|
clicked => { Masking.add-gradient(true); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TRACES: FR-DEV-19b
|
||||||
|
// **A mask that is nothing but a hand.** Beside the gradients
|
||||||
|
// because it belongs to the same answer to "where" — and because
|
||||||
|
// without it the only way to reach the brush was to make a
|
||||||
|
// gradient nobody wanted and paint into that.
|
||||||
|
//
|
||||||
|
// It covers nothing until a stroke lands, so pressing this arms
|
||||||
|
// the brush as well as making the layer: a row that appeared and
|
||||||
|
// changed no pixel, with the pointer still in "select", would look
|
||||||
|
// exactly like a button that did nothing.
|
||||||
|
Button {
|
||||||
|
text: "Paint";
|
||||||
|
enabled: Develop.enabled;
|
||||||
|
clicked => { Masking.add-brush(); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranges, on their own row rather than beside the gradients. A
|
// Ranges, on their own row rather than beside the gradients. A
|
||||||
|
|||||||
Reference in New Issue
Block a user