Merge pull request #5827 from mockydevs/fix/appearance-opacity-default-5742

Appearance: Delete Selected Item on an Opacity row sets it back to Default (#5742)
This commit is contained in:
BFlat
2026-11-06 00:54:49 +07:00
committed by GitHub
3 changed files with 71 additions and 9 deletions
+28 -8
View File
@@ -13,7 +13,8 @@
//! (dropped on its row) or onto the object; Alt copies instead. Dropped on Duplicate Selected Item
//! or Delete Selected Item, a fill/stroke row (with the other selected rows, when it is one of
//! them) or an effect row is copied or removed. Cmd-click and Shift-click select several
//! fills/strokes for Duplicate and Delete.
//! fills/strokes for Duplicate and Delete. Delete on a selected Opacity row sets it back to
//! Default (100%, Normal).
use std::sync::OnceLock;
@@ -59,11 +60,16 @@ impl Sel {
Sel::None | Sel::Effect(_) | Sel::Contents | Sel::Opacity => None,
}
}
/// Is a fill/stroke or effect row selected (what Duplicate and Delete act on)? An Opacity row
/// is not an item: it can't be copied or removed.
/// Is a fill/stroke or effect row selected (what Duplicate acts on)? An Opacity row is not an
/// item: it can't be copied.
fn editable(self) -> bool {
matches!(self, Sel::Item(_) | Sel::Effect(_) | Sel::ItemEffect(..))
}
/// Does Delete act on the selected row? A fill/stroke or effect row is removed; deleting an
/// Opacity row sets it back to Default (100%, Normal) (#5742).
fn deletable(self) -> bool {
self.editable() || matches!(self, Sel::Opacity | Sel::ItemOpacity(_))
}
}
/// The selected row of `node` (the first selected object): a selected effect row while that
@@ -490,7 +496,7 @@ fn delete_key(app: &mut VectorcraftApp, ui: &mut Ui) {
let node = first_subject(app);
let sel = current_sel(app, &ctx, node.as_ref());
if keys
&& sel.editable()
&& sel.deletable()
&& ctx.memory(|m| m.focused().is_none())
&& ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Delete) || i.consume_key(egui::Modifiers::NONE, egui::Key::Backspace))
{
@@ -1088,7 +1094,8 @@ fn opacity_row(app: &mut VectorcraftApp, ui: &mut Ui, item: Option<usize>, opaci
let link = name_link(ui, pos2(lx, r.center().y), ("ap-op-link", item), tl!("Opacity:")).clicked();
if link || resp.clicked() {
select_row(app, ui.ctx(), this);
// The panel has the keyboard (Delete then removes nothing, rather than the object).
// The panel has the keyboard (Delete then sets the row back to Default, rather than
// deleting the object).
super::set_row_keys(ui.ctx(), ROW_KEYS, true);
}
if link {
@@ -1415,7 +1422,8 @@ fn bottom(app: &mut VectorcraftApp, ui: &mut Ui, node: Option<&Node>, sel: Sel)
if duplicate.clicked() {
duplicate_selected(app, ui, node, sel);
}
let delete = widgets::icon_button_enabled(ui, "trash-2", tl!("Delete Selected Item"), false, can, 24.0);
let can_delete = can || (has && sel.deletable());
let delete = widgets::icon_button_enabled(ui, "trash-2", tl!("Delete Selected Item"), false, can_delete, 24.0);
if delete.clicked() {
delete_selected(app, ui, node, sel);
}
@@ -1558,8 +1566,20 @@ fn highlight_copies(app: &mut VectorcraftApp, ctx: &egui::Context, n: &Node, sel
set_pstate(ctx, "ap-multi", Highlight { owner: Some(n.id), rows, order, anchor, active });
}
/// Remove the highlighted fills/strokes or effects; a removed item effect leaves its fill/stroke selected.
/// Remove the highlighted fills/strokes or effects; a removed item effect leaves its fill/stroke
/// selected. A selected Opacity row is set back to Default (100%, Normal) and stays selected.
fn delete_selected(app: &mut VectorcraftApp, ui: &Ui, node: Option<&Node>, sel: Sel) {
match sel {
Sel::Opacity => {
app.run("transparency.set", json!({"item": null, "opacity": 100, "blend": "Normal"})).ok();
return;
}
Sel::ItemOpacity(i) => {
app.run("transparency.set", aligned(app, json!({"item": i, "opacity": 100, "blend": "Normal"}))).ok();
return;
}
_ => {}
}
let ok = match selected_effects(ui.ctx(), node, sel) {
Some((item, ks)) => app.run("effect.remove", aligned(app, json!({"item": item, "indices": ks}))).is_ok(),
None if matches!(sel, Sel::Item(_)) => {
@@ -1616,7 +1636,7 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
if menu_item(ui, tl!("Duplicate Item"), has && sel.editable(), false) {
duplicate_selected(app, ui, node.as_ref(), sel);
}
if menu_item(ui, tl!("Remove Item"), has && sel.editable(), false) {
if menu_item(ui, tl!("Remove Item"), has && sel.deletable(), false) {
delete_selected(app, ui, node.as_ref(), sel);
}
if menu_item(ui, tl!("Clear Appearance"), has, false) {
@@ -85,6 +85,47 @@ fn removing_items_keeps_a_surviving_item_opacity_row_selected() {
assert_eq!(app.session.appearance_item(), None);
}
/// #5742: Delete Selected Item on an Opacity row sets it back to Default (100%, Normal): the
/// object's own, or a fill's, leaving the other untouched. Duplicate stays off for it.
#[test]
fn deleting_an_opacity_row_sets_it_back_to_default() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
app.run("file.new", json!({"width": 100, "height": 100})).unwrap();
let id = app.run("shape.rectangle", json!({"x": 0, "y": 0, "width": 20, "height": 20})).unwrap()["id"].as_u64().unwrap();
app.run("select.set", json!({ "ids": [id] })).unwrap();
app.run("transparency.set", json!({"item": null, "opacity": 50, "blend": "Multiply"})).unwrap();
app.run("transparency.set", json!({"item": 0, "opacity": 40, "blend": "Screen"})).unwrap();
let owner = Some(NodeId(id));
let ctx = egui::Context::default();
let delete = |app: &mut VectorcraftApp| {
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| {
let node = first_subject(app);
let sel = current_sel(app, ui.ctx(), node.as_ref());
assert!(sel.deletable() && !sel.editable(), "{sel:?}");
delete_selected(app, ui, node.as_ref(), sel);
});
out.textures_delta.clear();
};
let node = |app: &VectorcraftApp| first_subject(app).unwrap();
// The fill's Opacity row: only the fill goes back to Default.
app.run("appearance.setActiveItem", json!({ "index": 0 })).unwrap();
set_pstate(&ctx, "ap-sel", (owner, Sel::ItemOpacity(0)));
delete(&mut app);
let n = node(&app);
assert_eq!((n.appearance.items[0].opacity(), n.appearance.items[0].blend()), (1.0, BlendMode::Normal));
assert_eq!((n.opacity, n.blend), (0.5, BlendMode::Multiply));
assert_eq!(pstate::<(Option<NodeId>, Sel)>(&ctx, "ap-sel").1, Sel::ItemOpacity(0));
// The object's Opacity row.
app.run("appearance.setActiveItem", json!({ "index": null })).unwrap();
set_pstate(&ctx, "ap-sel", (owner, Sel::Opacity));
delete(&mut app);
let n = node(&app);
assert_eq!(opacity_text(n.opacity, n.blend), "Default");
// One step, so Undo brings the object's opacity back.
app.run("edit.undo", json!({})).unwrap();
assert_eq!(node(&app).opacity, 0.5);
}
#[test]
fn opacity_labels() {
assert_eq!(opacity_text(1.0, BlendMode::Normal), "Default");
+2 -1
View File
@@ -1,6 +1,6 @@
# UI and interaction parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Pencil and Paintbrush: a new stroke with Keep selected off leaves nothing selected, #5752; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Appearance panel: Delete Selected Item on an Opacity row sets it back to Default, #5742; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
## Feature submission evidence
@@ -38,6 +38,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard
| Date | Change | Summary |
|---|---|---|
| 2026-11-05 | minor | Appearance panel: Delete Selected Item (and Delete/Backspace, and Remove Item in the panel menu) on a selected Opacity row, the object's or a fill's or stroke's, sets it back to Default (100%, Normal) as one undo step instead of being dimmed (#5742); scores and estimates unchanged |
| 2026-11-05 | minor | Selection Tools add-on: Select Linked, Embedded, Missing and Modified select the images and placed documents with that link status (Object › Plug-ins › Selection Tools and the Links panel menu; Shift adds), and Select by Link Status… lists them by name, status and format to pick from, optionally zooming to them; what is hidden, locked, on a template layer or outside the isolated group is left out and the status bar says how many (#5526, a VectorCraft addition); scores and estimates unchanged (estimated). |
| 2026-11-05 | minor | Pencil and Paintbrush: a new stroke with Keep selected off leaves nothing selected, the earlier selection included (#5752); scores and estimates unchanged |
| 2026-11-05 | minor | Layers panel: a layer whose Layer Options › Print is off shows its name italic, as the reference application shows non-printing layers, and keeps it while the row is dragged (#5739); scores and estimates unchanged |