mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Graphic Styles: Shift-click selects a range of styles
In the Graphic Styles panel, Shift-click and Cmd/Ctrl-click both added or removed the one style clicked. Shift-click now adds to the selection every style from the last one clicked without Shift through the clicked one, in the order the panel lists them, and Cmd/Ctrl-click still adds or removes one. A plain click applies the style and selects it alone, Alt-click still adds it on top, and the Control bar's Style picker still applies on every click. The panel uses swatches::click_selection, as the Swatches panel and the library panel do. Docs: docs/target-app-parity.md.
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
//! Graphic Styles panel: the document's styles as rendered thumbnails (Thumbnail View) or rows
|
||||
//! (Small and Large List View), shown on a square or on type. Clicking a style applies it to the
|
||||
//! selection (Alt-click adds it on top), Shift/Cmd-click selects several, double-click opens
|
||||
//! Graphic Style Options. Dragging a style moves it in the list, or applies it to the art it is
|
||||
//! dropped on (`canvas::panel_drop`); art dragged off the canvas or the Appearance panel's
|
||||
//! thumbnail dropped on the panel becomes a new style. Holding the right button on a style shows it
|
||||
//! large, on the selected object when there is one. The Control bar's Style picker lists the same
|
||||
//! tiles ([`picker`]). Graphic style libraries open in the library panel
|
||||
//! selection (Alt-click adds it on top); Shift-click adds to the panel's selection every style from
|
||||
//! the last one clicked without Shift through this one; Cmd/Ctrl-click adds or removes one;
|
||||
//! double-click opens Graphic Style Options. Dragging a style moves it in the list, or applies it
|
||||
//! to the art it is dropped on (`canvas::panel_drop`); art dragged off the canvas or the Appearance
|
||||
//! panel's thumbnail dropped on the panel becomes a new style. Holding the right button on a style
|
||||
//! shows it large, on the selected object when there is one. The Control bar's Style picker lists
|
||||
//! the same tiles ([`picker`]). Graphic style libraries open in the library panel
|
||||
//! ([`GraphicStyleLibraries`]).
|
||||
|
||||
use std::cell::RefCell;
|
||||
@@ -270,8 +271,8 @@ fn large_preview(app: &VectorcraftApp, ctx: &egui::Context, s: &Shown, i: usize)
|
||||
|
||||
/// What a click on a style does.
|
||||
enum Click {
|
||||
Apply(String),
|
||||
Toggle(String),
|
||||
/// A click with the modifiers held.
|
||||
Pick(String, egui::Modifiers),
|
||||
Options(String),
|
||||
}
|
||||
|
||||
@@ -294,8 +295,7 @@ fn clicked(ui: &Ui, resp: &Response, name: &str) -> Option<Click> {
|
||||
if resp.double_clicked() {
|
||||
Some(Click::Options(name.to_string()))
|
||||
} else if resp.clicked() {
|
||||
let m = ui.input(|i| i.modifiers);
|
||||
Some(if m.shift || m.command { Click::Toggle(name.to_string()) } else { Click::Apply(name.to_string()) })
|
||||
Some(Click::Pick(name.to_string(), ui.input(|i| i.modifiers)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -419,24 +419,19 @@ fn act(app: &mut VectorcraftApp, ui: &Ui, s: &Shown, ev: Events, picker: bool) {
|
||||
run(app, "graphicStyle.new", json!({ "id": id.0 }));
|
||||
}
|
||||
match ev.click {
|
||||
Some(Click::Apply(name) | Click::Toggle(name)) if picker => {
|
||||
Some(Click::Pick(name, _)) if picker => {
|
||||
app.run("graphicStyle.apply", json!({"name": name, "add": alt_held(ui)})).ok();
|
||||
ui.close();
|
||||
}
|
||||
Some(Click::Apply(name)) => {
|
||||
// With nothing selected the next object drawn takes the style.
|
||||
app.run("graphicStyle.apply", json!({"name": name, "add": alt_held(ui)})).ok();
|
||||
set_pstate(ui.ctx(), "gs-sel", vec![name]);
|
||||
}
|
||||
Some(Click::Toggle(name)) => {
|
||||
let mut sel = selected(ui.ctx(), s.d);
|
||||
match sel.iter().position(|n| *n == name) {
|
||||
Some(i) => {
|
||||
sel.remove(i);
|
||||
}
|
||||
None => sel.push(name),
|
||||
}
|
||||
Some(Click::Pick(name, m)) => {
|
||||
let order: Vec<&str> = s.d.graphic_styles.iter().map(|g| g.name.as_str()).collect();
|
||||
let sel = super::swatches::click_selection(ui, "gs-anchor", selected(ui.ctx(), s.d), &order, &name, m);
|
||||
set_pstate(ui.ctx(), "gs-sel", sel);
|
||||
// A plain click also applies the style (with nothing selected the next object drawn
|
||||
// takes it); Shift and Cmd/Ctrl clicks only select.
|
||||
if !(m.shift || m.command) {
|
||||
app.run("graphicStyle.apply", json!({"name": name, "add": alt_held(ui)})).ok();
|
||||
}
|
||||
}
|
||||
Some(Click::Options(name)) => {
|
||||
app.run("ui.graphicStyleOptions", json!({ "name": name })).ok();
|
||||
@@ -938,6 +933,53 @@ mod tests {
|
||||
assert_eq!(d.node(rect).unwrap().extras().graphic_style, d.graphic_styles.last().map(|g| g.id));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shift_click_selects_a_range_and_cmd_click_adds_or_removes_one_style() {
|
||||
let mut app = app();
|
||||
app.session.execute("graphicStyle.new", &json!({"name": "Mine"})).unwrap();
|
||||
let n = names(&app);
|
||||
assert_eq!(n.len(), 5);
|
||||
let ctx = egui::Context::default();
|
||||
set_pstate(&ctx, "gs-view", View::SmallList);
|
||||
frame(&ctx, |ui| show(&mut app, ui));
|
||||
// A click on row `i` (Small List View: rows of 18 points, 3 apart) with `modifiers` held,
|
||||
// a second after the last one so that two clicks on a row are not a double-click.
|
||||
let mut time = 0.0;
|
||||
let mut click = |app: &mut VectorcraftApp, i: usize, modifiers: egui::Modifiers| {
|
||||
let at = pos2(60.0, 10.0 + 21.0 * i as f32);
|
||||
time += 1.0;
|
||||
for pressed in [true, false] {
|
||||
let button = egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers };
|
||||
let raw = egui::RawInput {
|
||||
screen_rect: Some(Rect::from_min_size(pos2(0.0, 0.0), vec2(260.0, 600.0))),
|
||||
events: vec![egui::Event::ModifiersChanged(modifiers), egui::Event::PointerMoved(at), button],
|
||||
time: Some(time + if pressed { 0.0 } else { 0.05 }),
|
||||
..Default::default()
|
||||
};
|
||||
ctx.run_ui(raw, |ui| show(app, ui)).textures_delta.clear();
|
||||
}
|
||||
selected(&ctx, &app.session.active().unwrap().doc.clone())
|
||||
};
|
||||
let linked = |app: &VectorcraftApp| app.session.selection_graphic_style().map(|(g, _)| g.name.clone());
|
||||
// A plain click applies the style and selects it alone.
|
||||
assert_eq!(click(&mut app, 0, egui::Modifiers::NONE), [n[0].clone()]);
|
||||
assert_eq!(linked(&app).as_ref(), Some(&n[0]));
|
||||
// Shift-click selects from that style to the clicked one, and applies nothing.
|
||||
assert_eq!(click(&mut app, 3, egui::Modifiers::SHIFT), n[..4]);
|
||||
assert_eq!(linked(&app).as_ref(), Some(&n[0]));
|
||||
// Cmd/Ctrl-click removes a selected style and adds it back.
|
||||
assert_eq!(click(&mut app, 2, egui::Modifiers::COMMAND), [n[0].clone(), n[1].clone(), n[3].clone()]);
|
||||
assert_eq!(click(&mut app, 2, egui::Modifiers::COMMAND), [n[0].clone(), n[1].clone(), n[3].clone(), n[2].clone()]);
|
||||
// Shift-click extends from the style Cmd/Ctrl-clicked last.
|
||||
click(&mut app, 0, egui::Modifiers::NONE);
|
||||
click(&mut app, 2, egui::Modifiers::COMMAND);
|
||||
assert_eq!(click(&mut app, 4, egui::Modifiers::SHIFT), [n[0].clone(), n[2].clone(), n[3].clone(), n[4].clone()]);
|
||||
assert_eq!(linked(&app).as_ref(), Some(&n[0]));
|
||||
// A plain click applies the next style.
|
||||
assert_eq!(click(&mut app, 4, egui::Modifiers::NONE), [n[4].clone()]);
|
||||
assert_eq!(linked(&app).as_ref(), Some(&n[4]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_right_button_shows_a_large_preview_and_the_picker_applies_styles() {
|
||||
let mut app = app();
|
||||
|
||||
@@ -586,6 +586,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
On the canvas, the renderer no longer draws the picture of the art below back as an image each time it copies the backdrop of a non-isolated group that blends: it keeps that picture and draws the art that follows over it. At 2880 × 1800, 50 translucent groups with Multiply members take 65 ms a frame instead of 299 ms on 8 threads (`vectorcraft-cli bench`), and the frames have the same pixels.
|
||||
Type inside a group, layer, symbol instance, blend or repeat keeps its opacity, blend mode, opacity mask, Opacity & Mask Define Knockout Shape, own effects, perspective and visibility when the effects on the container outline it. Hidden type with a geometry effect of its own is left out of exports.
|
||||
- **Graphic styles:** styles keep opacity and blend mode, capture groups and type, apply on top with Alt, and link to the objects using them; Redefine, Break Link, Graphic Style Options, Select All Unused, Sort by Name, and Select > Same Graphic Style / Appearance Attribute. The panel has list and thumbnail views with rendered previews, merge, reorder and drag and drop, plus a Control bar Style picker; six generated style libraries save and load as `.vcstyles`.
|
||||
In the Graphic Styles panel, Shift-click selects a range of styles and Ctrl/Cmd-click adds or removes one.
|
||||
- **Neutral wording:** labels, MCP tool text, docs and packaging use neutral names, and `cargo xtask brands` (part of `cargo xtask ci`) fails on vendor names.
|
||||
- **Colour, type and file workflows:** Recolor Artwork (dialog with harmonies), Edit Colors, Find & Replace, Change Case, Smart Punctuation, Guides, Lock/Hide Above, Transform Each, Rasterize.
|
||||
Object › Lock › All Artwork Above and Object › Hide › All Artwork Above deselect the selected objects they lock or hide, the key object included.
|
||||
|
||||
Reference in New Issue
Block a user