Effect menu: Glowing Edges moves to a Stylize submenu under Raster Effects

On main, Glowing Edges… is listed in the Effect menu's vector Stylize
submenu, after Drop Shadow, Inner Glow, Outer Glow and Feather. The
reference app lists it in the Stylize submenu of its Photoshop effects,
below the vector effects.

The Effect menu now has a Stylize submenu under Raster Effects, between
Sharpen and Texture, and Glowing Edges is listed there. The menu sends
the effects at the Stylize path that PIXEL_EFFECTS names to that
submenu, and the vector Stylize submenu keeps its six effects. The
effect's id, its catalog entry, its effect.list entry and Apply Last
Effect do not change.
This commit is contained in:
dotCooCoo
2026-10-16 04:04:49 +07:00
committed by bflatastic
parent 427cd65f30
commit ea59b066d4
2 changed files with 49 additions and 8 deletions
+15 -8
View File
@@ -3504,13 +3504,16 @@ fn font_items(english_names: bool) -> Vec<Item> {
/// The raster effects' submenus of the Effect menu (the Photoshop-style effects, below the
/// vector effects): (submenu, the catalogue's menu path of its effects).
const RASTER_MENUS: [(&str, &[&str]); 8] = [
/// The raster Stylize submenu shares its path with the vector one and lists only the effects at
/// that path that [`vectorcraft_effects::PIXEL_EFFECTS`] names.
const RASTER_MENUS: [(&str, &[&str]); 9] = [
("Artistic", &["Effect", "Artistic"]),
("Blur", &["Effect", "Blur"]),
("Brush Strokes", &["Effect", "Brush Strokes"]),
("Distort", &["Effect", "Distort"]),
("Pixelate", &["Effect", "Pixelate"]),
("Sharpen", &["Effect", "Sharpen"]),
("Stylize", &["Effect", "Stylize"]),
("Texture", &["Effect", "Texture"]),
("Video", &["Effect", "Video"]),
];
@@ -3540,10 +3543,16 @@ fn effect_menu() -> Vec<Item> {
"Warp",
];
let top_level = |e: &vectorcraft_effects::EffectInfo| e.menu == ["Effect"] && order.contains(&e.label);
// An effect goes in the vector submenu its path names unless it is a Photoshop-style effect,
// which goes in the raster submenu of the same name when `RASTER_MENUS` has one (Stylize ›
// Glowing Edges).
let in_vector_menu =
|e: &vectorcraft_effects::EffectInfo| order.iter().any(|s| e.menu == ["Effect", *s]) && !vectorcraft_effects::PIXEL_EFFECTS.contains(&e.id);
// The effects whose catalogue menu path is `path`.
let items_at = |path: &[&str]| -> Vec<Item> {
// `vector` picks the ones for the vector submenu or the ones for the raster submenu.
let items_at = |path: &[&str], vector: bool| -> Vec<Item> {
cat.iter()
.filter(|e| e.menu == path)
.filter(|e| e.menu == path && in_vector_menu(e) == vector)
.map(|e| match e.defaults.as_object().is_some_and(|o| o.is_empty()) {
// No options (Effect → Pathfinder): apply directly, like Illustrator.
true => Item::Cmd(e.label, "effect.apply", json!({ "effect": e.id })),
@@ -3556,7 +3565,7 @@ fn effect_menu() -> Vec<Item> {
out.push(Item::Cmd(e.label, "effect.apply", json!({ "effect": e.id })));
continue;
}
let items = items_at(&["Effect", sub_name]);
let items = items_at(&["Effect", sub_name], true);
if items.is_empty() {
let placeholder = match sub_name {
"3D and Materials" => vec![todo("Extrude & Bevel…"), todo("Revolve…"), todo("Inflate…"), todo("Rotate…"), todo("Materials…")],
@@ -3583,7 +3592,7 @@ fn effect_menu() -> Vec<Item> {
let raster: Vec<Item> = RASTER_MENUS
.iter()
.filter_map(|(name, path)| {
let items = items_at(path);
let items = items_at(path, false);
(!items.is_empty()).then(|| sub(name, items))
})
.collect();
@@ -3593,9 +3602,7 @@ fn effect_menu() -> Vec<Item> {
out.extend(raster);
}
// Anything not placed above (future effects) still shows up.
let placed = |e: &vectorcraft_effects::EffectInfo| {
top_level(e) || order.iter().any(|s| e.menu == ["Effect", *s]) || RASTER_MENUS.iter().any(|(_, path)| e.menu == *path)
};
let placed = |e: &vectorcraft_effects::EffectInfo| top_level(e) || in_vector_menu(e) || RASTER_MENUS.iter().any(|(_, path)| e.menu == *path);
for e in cat.iter().filter(|e| !placed(e)) {
out.push(Item::Cmd(e.label, "effect.dialog", json!({ "effect": e.id })));
}
+34
View File
@@ -140,6 +140,40 @@ fn effect_menu_lists_the_texture_filters_between_sharpen_and_video() {
assert!(at("Sharpen") < at("Texture") && at("Texture") < at("Video"));
}
/// Effect › Stylize under Raster Effects, between Sharpen and Texture: Glowing Edges opens its
/// dialog.
#[test]
fn effect_menu_lists_glowing_edges_in_the_raster_stylize_between_sharpen_and_texture() {
let app = app();
let entries = menus::menu_entries(&app);
let at = |label: &str| entries.iter().position(|e| e.path[0] == "Effect" && e.label == label).unwrap();
let glowing = &entries[at("Glowing Edges…")];
assert_eq!(glowing.path, ["Effect", "Stylize"]);
assert_eq!(glowing.command.as_deref(), Some("effect.dialog"));
assert_eq!(glowing.params, json!({"effect": "stylize.glowingEdges"}));
assert!(at("Unsharp Mask…") < at("Glowing Edges…") && at("Glowing Edges…") < at("Craquelure…"));
}
/// The vector effects' Stylize keeps its six effects and leaves Glowing Edges out.
#[test]
fn effect_menu_keeps_glowing_edges_out_of_the_vector_stylize() {
let tree = menus::menu_tree();
let (_, effect) = tree.iter().find(|(t, _)| *t == "Effect").expect("Effect menu");
let raster = effect.iter().position(|i| matches!(i, Item::Header("Raster Effects"))).expect("Raster Effects");
let stylize: Vec<(usize, Vec<&str>)> = effect
.iter()
.enumerate()
.filter_map(|(i, it)| match it {
Item::Sub("Stylize", items) => Some((i, items.iter().filter_map(|c| if let Item::Cmd(l, ..) = c { Some(*l) } else { None }).collect())),
_ => None,
})
.collect();
assert_eq!(stylize.len(), 2);
assert!(stylize[0].0 < raster && raster < stylize[1].0);
assert_eq!(stylize[0].1, ["Round Corners…", "Scribble…", "Drop Shadow…", "Inner Glow…", "Outer Glow…", "Feather…"]);
assert_eq!(stylize[1].1, ["Glowing Edges…"]);
}
#[test]
fn library_submenus_are_disabled_placeholders() {
let app = app();