mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-27 03:06:57 -06:00
M3.28: Color panel behaviour — the mode follows the colour's own model (the last mode picked returns for colours in its model), Alt-clicking the spectrum, None/Black/White, swatches, recent colours or Color Guide chips paints the inactive proxy, Hide Options leaves the proxy and spectrum, RGB/HSB show the out-of-gamut warning (click to correct), Shift-clicking the spectrum cycles modes, Shift-dragging a slider moves the others in tandem, and a mixed selection draws "?" proxies (paint.proxies reports fillMixed/strokeMixed)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f21161ce8c
commit
6d2dc7cbbc
@@ -5,7 +5,7 @@ use vectorcraft_color::{Color, Gradient, GradientKind, GradientPaint, Paint};
|
||||
use vectorcraft_doc::{Appearance, CharStyle, Node, NodeId, NodeKind};
|
||||
|
||||
use super::*;
|
||||
use crate::EngineError;
|
||||
use crate::{DocState, EngineError};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![
|
||||
@@ -118,6 +118,15 @@ pub fn proxy_specs() -> Vec<CommandSpec> {
|
||||
}))
|
||||
}
|
||||
),
|
||||
cmd!(
|
||||
query "paint.proxies",
|
||||
"Fill and Stroke",
|
||||
[],
|
||||
None,
|
||||
"{} → {fill, stroke: the paints the Fill/Stroke proxies show (the first selected object's, type: its first run's; else the defaults for new art), fillActive, fillMixed, strokeMixed: the selected objects' fills (strokes) differ, shown as a \"?\" proxy}",
|
||||
always,
|
||||
proxies
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -161,6 +170,53 @@ pub(crate) fn proxy_paint(n: &Node, stroke: bool) -> Paint {
|
||||
}
|
||||
}
|
||||
|
||||
/// Do two paints look the same in a proxy? Solid colours compare without their swatch link and
|
||||
/// gradients without their per-object geometry.
|
||||
fn same_in_proxy(a: &Paint, b: &Paint) -> bool {
|
||||
match (a, b) {
|
||||
(Paint::Solid { color: x, .. }, Paint::Solid { color: y, .. }) => x == y,
|
||||
(Paint::Gradient(x), Paint::Gradient(y)) => x.gradient == y.gradient,
|
||||
_ => a == b,
|
||||
}
|
||||
}
|
||||
|
||||
impl DocState {
|
||||
/// Whether the selected objects' fills and strokes differ as the proxies show them (a "?"
|
||||
/// proxy). One walk of the document, so callers that ask every frame cache it by revision.
|
||||
pub fn proxy_mixed(&self) -> (bool, bool) {
|
||||
if self.selection.len() < 2 {
|
||||
return (false, false);
|
||||
}
|
||||
let ids: std::collections::HashSet<NodeId> = self.selection.objects.iter().copied().collect();
|
||||
let mut first: Option<(Paint, Paint)> = None;
|
||||
let mut mixed = (false, false);
|
||||
self.doc.walk(|n| {
|
||||
if (mixed.0 && mixed.1) || !ids.contains(&n.id) {
|
||||
return;
|
||||
}
|
||||
let (f, s) = (proxy_paint(n, false), proxy_paint(n, true));
|
||||
match &first {
|
||||
Some((f0, s0)) => {
|
||||
mixed.0 |= !same_in_proxy(f0, &f);
|
||||
mixed.1 |= !same_in_proxy(s0, &s);
|
||||
}
|
||||
None => first = Some((f, s)),
|
||||
}
|
||||
});
|
||||
mixed
|
||||
}
|
||||
}
|
||||
|
||||
fn proxies(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
let st = s.doc().ok();
|
||||
let (fill, stroke) = match st.and_then(|st| st.selection.objects.first().and_then(|id| st.doc.node(*id))) {
|
||||
Some(n) => (proxy_paint(n, false), proxy_paint(n, true)),
|
||||
None => (s.paint.fill.clone(), s.paint.stroke.clone()),
|
||||
};
|
||||
let (fill_mixed, stroke_mixed) = st.map_or((false, false), DocState::proxy_mixed);
|
||||
Ok(json!({"fill": fill, "stroke": stroke, "fillActive": s.fill_active, "fillMixed": fill_mixed, "strokeMixed": stroke_mixed}))
|
||||
}
|
||||
|
||||
/// `stroke` param, defaulting to the proxy that is in front.
|
||||
fn stroke_param(s: &Session, p: &Value) -> bool {
|
||||
p.get("stroke").and_then(Value::as_bool).unwrap_or(!s.fill_active)
|
||||
|
||||
Reference in New Issue
Block a user