mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-08 19:42:05 +00:00
Selection colors: Create style refuses a row whose fill or stroke list holds other paints
A created style holds the row's one paint, and linking it replaces the target's whole fill or stroke list, so a layer with other fills or strokes lost them. selection.createColorStyle now refuses such targets before any change, and the Styles menu leaves Create style out for those rows.
This commit is contained in:
1 parent
c7cd72b09e
commit
889983ffe2
3 files changed
+52
-4
No files matched your search
@@ -134,7 +134,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
spec!(
|
||||
"selection.createColorStyle",
|
||||
"Create selection color style",
|
||||
"{targets, name, description?} → {id}; makes a color style of the paint the targets share and applies it to them as selection.applyColorStyle does; a name another style has is refused",
|
||||
"{targets, name, description?} → {id}; makes a color style of the paint the targets share and applies it to them as selection.applyColorStyle does; a name another style has, or a target whose list holds other paints, is refused",
|
||||
true,
|
||||
create_style
|
||||
),
|
||||
@@ -252,6 +252,9 @@ fn create_style(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if targets.iter().any(|t| t.paint(doc).as_ref() != Some(&paint)) {
|
||||
return Err(bad(CMD, "the targets no longer share one paint"));
|
||||
}
|
||||
if let Some(why) = create_refusal(doc, &targets) {
|
||||
return Err(bad(CMD, why));
|
||||
}
|
||||
let name = crate::cmd::variables::clean_name(&req_string(CMD, p, "name")?).ok_or_else(|| bad(CMD, "name can't be empty"))?;
|
||||
if doc.styles.iter().any(|st| st.name == name) {
|
||||
return Err(bad(CMD, format!("a style named `{name}` already exists")));
|
||||
@@ -270,6 +273,16 @@ fn create_style(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"id": id}))
|
||||
}
|
||||
|
||||
/// Why a color style can't be made from `targets` (`selection.createColorStyle`), if it can't: a
|
||||
/// linked style replaces the whole fill or stroke list, so a target whose list holds other paints
|
||||
/// would lose them. The Selection colors menu leaves Create style out for such rows.
|
||||
pub fn create_refusal(doc: &Document, targets: &[PaintTarget]) -> Option<&'static str> {
|
||||
targets
|
||||
.iter()
|
||||
.any(|t| t.paints(doc).is_none_or(|paints| paints.len() != 1))
|
||||
.then_some("a style can only be made from a fill or stroke list that holds this one paint")
|
||||
}
|
||||
|
||||
/// Apply paint style `style` to every target: a fill or stroke list is linked to it; a text range
|
||||
/// takes its resolved paints and keeps its typography.
|
||||
fn link_style(s: &mut Session, targets: &[PaintTarget], style: &Style) -> Result<()> {
|
||||
|
||||
@@ -2,7 +2,7 @@ use figcraft_color::Paint;
|
||||
use figcraft_doc::NodeId;
|
||||
use figcraft_engine::{
|
||||
Session,
|
||||
selection_colors::{SelectionColor, collect},
|
||||
selection_colors::{self, SelectionColor, collect},
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
@@ -505,6 +505,30 @@ fn creating_a_style_refuses_taken_or_empty_names_and_stale_rows_and_changes_noth
|
||||
assert_eq!(fill(&s, a), "FF0000");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creating_a_style_refuses_a_row_whose_list_holds_other_paints() {
|
||||
let mut s = Session::new();
|
||||
let a = rectangle(&mut s, "#ff0000");
|
||||
let b = rectangle(&mut s, "#ff0000");
|
||||
run(&mut s, "fill.add", json!({"id":a,"paint":"#0000ff"}));
|
||||
let red = group(&s, &[a, b], "FF0000");
|
||||
let before = s.undo_depth();
|
||||
let styles = s.doc().unwrap().styles.len();
|
||||
let err = s.execute("selection.createColorStyle", &json!({"targets":red.targets,"name":"Red"})).unwrap_err();
|
||||
assert!(err.to_string().contains("holds this one paint"), "{err}");
|
||||
assert!(selection_colors::create_refusal(s.doc().unwrap(), &red.targets).is_some());
|
||||
assert_eq!(s.undo_depth(), before);
|
||||
let doc = s.doc().unwrap();
|
||||
assert_eq!(doc.styles.len(), styles);
|
||||
assert_eq!(doc.node(a).unwrap().fills.len(), 2, "the layer keeps its other fill");
|
||||
assert!(doc.node(b).unwrap().styles.fill.is_none());
|
||||
// The single-paint layer alone still makes a style.
|
||||
let b_only: Vec<_> = red.targets.iter().filter(|t| t.id == b).cloned().collect();
|
||||
assert!(selection_colors::create_refusal(s.doc().unwrap(), &b_only).is_none());
|
||||
run(&mut s, "selection.createColorStyle", json!({"targets":b_only,"name":"Red"}));
|
||||
assert!(s.doc().unwrap().node(b).unwrap().styles.fill.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_linked_row_switches_to_another_style() {
|
||||
let mut s = Session::new();
|
||||
|
||||
@@ -7,7 +7,7 @@ use egui::{Id, Ui};
|
||||
use figcraft_color::{Color, Paint};
|
||||
use figcraft_doc::{Document, NodeId, PublishedLibrary, Style, StyleKind, VariableType};
|
||||
use figcraft_engine::library;
|
||||
use figcraft_engine::selection_colors::{PaintTarget, SelectionColor, bind_refusal, collect, in_scope};
|
||||
use figcraft_engine::selection_colors::{PaintTarget, SelectionColor, bind_refusal, collect, create_refusal, in_scope};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::a11y::Tip;
|
||||
@@ -122,7 +122,7 @@ pub fn section(app: &mut FigApp, ui: &mut Ui, doc: &Arc<Document>, ids: &[NodeId
|
||||
style_items(app, ui, doc, group, &t);
|
||||
if linked_name.is_none() {
|
||||
ui.separator();
|
||||
if crate::right::menu_item(ui, tr("Create style"), "") {
|
||||
if create_refusal(doc, &group.targets).is_none() && crate::right::menu_item(ui, tr("Create style"), "") {
|
||||
open_create_form(app, ui, doc, group);
|
||||
ui.close();
|
||||
}
|
||||
@@ -536,6 +536,17 @@ mod tests {
|
||||
assert!(p.frame(vec![]).iter().any(|(s, _)| s == "Brand"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_style_is_left_out_for_a_row_whose_list_holds_other_paints() {
|
||||
let (mut p, a, _) = Panel::new();
|
||||
p.app.run("fill.add", json!({"id":a,"paint":"#00ff00"}));
|
||||
let red = text_at(&p.frame(vec![]), "FF0000");
|
||||
p.click(Pos2::new(228.0, red.y));
|
||||
let texts = p.frame(vec![]);
|
||||
assert!(texts.iter().any(|(s, _)| s == "No color styles"), "the Styles menu opens");
|
||||
assert!(!texts.iter().any(|(s, _)| s == "Create style"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_linked_rows_styles_menu_switches_its_style() {
|
||||
let (mut p, a, _) = Panel::new();
|
||||
|
||||
Reference in new issue
Block a user