mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-13 02:34:33 +00:00
Merge main into #2192
This commit is contained in:
@@ -254,6 +254,8 @@ mod save_as_tests;
|
||||
#[cfg(test)]
|
||||
mod select_all_tests;
|
||||
#[cfg(test)]
|
||||
mod selection_colors_tests;
|
||||
#[cfg(test)]
|
||||
mod shortcut_keys_tests;
|
||||
#[cfg(test)]
|
||||
mod shortcuts_tests;
|
||||
|
||||
@@ -65,7 +65,7 @@ pub(crate) fn toggle(app: &mut FigApp, targets: Vec<PaintTarget>, response: Opti
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the selection-colour groups (at least two): swatch opens the picker, hex and opacity
|
||||
/// Draw the selection-colour groups (two, or one when a selected layer has children): swatch opens the picker, hex and opacity
|
||||
/// edits, style/variable detach/apply/bind, a style from this file or an enabled library, Create
|
||||
/// style, and “select layers with this color”; disabled during an interaction.
|
||||
pub fn section(app: &mut FigApp, ui: &mut Ui, doc: &Arc<Document>, ids: &[NodeId]) {
|
||||
@@ -73,7 +73,10 @@ pub fn section(app: &mut FigApp, ui: &mut Ui, doc: &Arc<Document>, ids: &[NodeId
|
||||
let interacting = ds.interaction.is_some();
|
||||
let uid = ds.uid;
|
||||
let colors = colors(ui, uid, doc, ids, interacting);
|
||||
if colors.len() < 2 {
|
||||
// Measured in Figma: two distinct colours show the section; one colour does too when a selected
|
||||
// layer has children (frame, group, component, instance).
|
||||
let has_children = ids.iter().any(|id| doc.node(*id).is_some_and(|n| !n.children.is_empty()));
|
||||
if colors.len() < 2 && !(colors.len() == 1 && has_children) {
|
||||
ui.data_mut(|d| d.remove::<CreateForm>(create_form_id()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
//! When the Design panel shows Selection colors (#2188): measured in Figma's web app, a layer with
|
||||
//! children shows the section from one distinct colour, while layers without children need two.
|
||||
//! Driven through egui headless; each row has a "Select layers with this color" button.
|
||||
|
||||
use egui::accesskit::Role;
|
||||
use figcraft_doc::NodeId;
|
||||
use figcraft_engine::Session;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::layers_drag_tests::Ui;
|
||||
|
||||
const ROW_BUTTON: &str = "Select layers with this color";
|
||||
|
||||
fn id(v: Value) -> NodeId {
|
||||
NodeId(v["id"].as_u64().unwrap())
|
||||
}
|
||||
|
||||
fn rect(s: &mut Session, x: i32, color: &str, parent: Option<NodeId>) -> NodeId {
|
||||
let mut p = json!({"x": x, "y": 0, "width": 40, "height": 40});
|
||||
if let Some(parent) = parent {
|
||||
p["parent"] = json!(parent);
|
||||
}
|
||||
let r = id(s.execute("shape.rectangle", &p).unwrap());
|
||||
s.execute("fill.set", &json!({"ids": [r], "color": color})).unwrap();
|
||||
r
|
||||
}
|
||||
|
||||
fn frame(s: &mut Session, fill: Option<&str>) -> NodeId {
|
||||
let f = id(s.execute("frame.create", &json!({"x": 0, "y": 0, "width": 200, "height": 200})).unwrap());
|
||||
match fill {
|
||||
Some(c) => s.execute("fill.set", &json!({"ids": [f], "color": c})).unwrap(),
|
||||
None => s.execute("fill.remove", &json!({"ids": [f], "index": 0})).unwrap(),
|
||||
};
|
||||
f
|
||||
}
|
||||
|
||||
/// How many Selection colors rows the Design panel shows with `ids` selected.
|
||||
fn rows(mut s: Session, ids: &[NodeId]) -> usize {
|
||||
s.execute("select.set", &json!({"ids": ids})).unwrap();
|
||||
let mut u = Ui::new(s);
|
||||
u.ctx.enable_accesskit();
|
||||
u.frame(vec![]);
|
||||
let out = u.frame_output(vec![]);
|
||||
let nodes = out.platform_output.accesskit_update.expect("an AccessKit update").nodes;
|
||||
nodes.iter().filter(|(_, n)| n.role() == Role::Button && n.label() == Some(ROW_BUTTON)).count()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_frame_holding_one_red_layer_shows_selection_colors_with_one_row() {
|
||||
let mut s = Session::new();
|
||||
let f = frame(&mut s, None);
|
||||
rect(&mut s, 10, "#FF0000", Some(f));
|
||||
assert_eq!(rows(s, &[f]), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_group_of_two_red_layers_shows_one_row() {
|
||||
let mut s = Session::new();
|
||||
let a = rect(&mut s, 0, "#FF0000", None);
|
||||
let b = rect(&mut s, 60, "#FF0000", None);
|
||||
s.execute("select.set", &json!({"ids": [a, b]})).unwrap();
|
||||
let g = id(s.execute("object.group", &json!({"ids": [a, b]})).unwrap());
|
||||
assert_eq!(rows(s, &[g]), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_instance_whose_child_has_one_fill_shows_selection_colors() {
|
||||
let mut s = Session::new();
|
||||
let f = frame(&mut s, None);
|
||||
rect(&mut s, 10, "#FF0000", Some(f));
|
||||
let c = id(s.execute("component.create", &json!({"ids": [f]})).unwrap());
|
||||
let i = id(s.execute("instance.create", &json!({"component": c, "x": 400, "y": 0})).unwrap());
|
||||
assert_eq!(rows(s, &[i]), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_layer_or_two_of_the_same_colour_without_children_hide_it() {
|
||||
let mut s = Session::new();
|
||||
let a = rect(&mut s, 0, "#FF0000", None);
|
||||
assert_eq!(rows(s, &[a]), 0, "one red rectangle");
|
||||
let mut s = Session::new();
|
||||
let a = rect(&mut s, 0, "#FF0000", None);
|
||||
let b = rect(&mut s, 60, "#FF0000", None);
|
||||
assert_eq!(rows(s, &[a, b]), 0, "two red rectangles");
|
||||
let mut s = Session::new();
|
||||
let g = frame(&mut s, Some("#00FF00"));
|
||||
assert_eq!(rows(s, &[g]), 0, "a childless green frame");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_loose_layers_of_different_colours_show_two_rows() {
|
||||
let mut s = Session::new();
|
||||
let a = rect(&mut s, 0, "#FF0000", None);
|
||||
let b = rect(&mut s, 60, "#0000FF", None);
|
||||
assert_eq!(rows(s, &[a, b]), 2);
|
||||
}
|
||||
Reference in New Issue
Block a user