#1284: right-clicking a Graph Editor keyframe opens its key menu

The Graph Editor had no menu for keyframes. Right-click now opens the same key menu as the
Timeline, for the commands that apply from the graph: Edit Value, Copy and Paste, selecting
equal or neighbouring keys, Hold, Rove Across Time (spatial properties only), Easy Ease,
interpolation and Delete. The items run the existing keys.* commands, so undo and the
automation ids are unchanged.

A right-click on an unselected key selects it first, as a click does.

Not covered by an automated test; checked by compiling and by reading the menu against the
Timeline's.

Claude-Session: https://claude.ai/code/session_0136kZtXrYo5VUWEFxWduwUD
This commit is contained in:
Claude committed 2026-10-13 19:28:22 +00:00
1 parent 32296d6f65
commit a7cad22c13
1 file changed
+43 -1
+43 -1
View File
@@ -453,7 +453,7 @@ pub(crate) fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painte
}
} else if kresp.clicked() && mods.shift {
actions.push(("keys.select".into(), json!({"keys": [kjson.clone()], "toggle": true})));
} else if (kresp.clicked() || kresp.drag_started()) && !k.sel {
} else if (kresp.clicked() || kresp.drag_started() || kresp.secondary_clicked()) && !k.sel {
actions.push(("keys.select".into(), json!({"keys": [kjson.clone()], "add": mods.shift})));
}
// A drag moves every selected key, by how far the pointer went from where the button
@@ -485,6 +485,48 @@ pub(crate) fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painte
ctx.data_mut(|dd| dd.insert_temp(drag_id, false));
actions.push(("__endMerge".into(), json!({})));
}
// Right-click: the Timeline's key menu, for the commands that apply from the Graph Editor.
kresp.context_menu(|ui| {
let editable_value = matches!(key.value, Value::Scalar(_) | Value::Vec2(_) | Value::Vec3(_));
for (lbl, cmd, params) in [
("Edit Value…", "keys.set", kjson.clone()),
("-", "", json!({})),
("Copy", "keys.copy", json!({})),
("Paste", "keys.paste", json!({})),
("-", "", json!({})),
("Select Equal Keyframes", "keys.selectEqual", json!({})),
("Select Previous Keyframes", "keys.selectPrevious", json!({})),
("Select Following Keyframes", "keys.selectFollowing", json!({})),
("-", "", json!({})),
("Toggle Hold Keyframe", "keys.toggleHold", json!({})),
("Rove Across Time", "keys.interpolation", json!({"roving": !key.roving})),
("-", "", json!({})),
("Easy Ease", "keys.easyEase", json!({})),
("Easy Ease In", "keys.easyEaseIn", json!({})),
("Easy Ease Out", "keys.easyEaseOut", json!({})),
("-", "", json!({})),
("Linear", "keys.interpolation", json!({"interpolation": "linear"})),
("Bezier", "keys.interpolation", json!({"interpolation": "bezier"})),
("Auto Bezier", "keys.interpolation", json!({"interpolation": "autoBezier"})),
("-", "", json!({})),
("Delete", "keys.delete", json!({})),
] {
if lbl == "-" {
ui.separator();
continue;
}
if lbl == "Rove Across Time" && !c.prop.spatial {
continue;
}
if lbl == "Edit Value…" && !editable_value {
continue;
}
if ui.button(lbl).clicked() {
actions.push((cmd.into(), params));
ui.close();
}
}
});
}
}