Layers panel: right-click opens the fx and adjustment footer menus (#1680)
As in Photoshop, a secondary click on the Layers footer's Add a layer style and Create new fill or adjustment layer buttons opens their menus too (a primary click still toggles them), and a small arrow marks them as menu buttons. Fixes #1579. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
efa62efa0c
commit
681e7f3b89
|
|
@ -1606,8 +1606,8 @@ fn layers(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
|
|||
actions.push(("layer.new.group".into(), json!({})));
|
||||
}
|
||||
actions.extend(footer_drop(ui, &group, footer_drag, "layer.groupLayers"));
|
||||
let adj = icons::button(ui, "contrast", 26.0, false, tl!("Create new fill or adjustment layer"));
|
||||
egui::Popup::menu(&adj).show(|ui| {
|
||||
let adj = footer_menu_button(ui, "contrast", 26.0, tl!("Create new fill or adjustment layer"));
|
||||
egui::Popup::menu(&adj).open_memory(footer_menu_right_click(&adj)).show(|ui| {
|
||||
ui.set_min_width(190.0);
|
||||
for c in photocraft_engine::command_specs().iter().filter(|c| c.id.starts_with("layer.newAdjustmentLayer.")) {
|
||||
if ui.button(tl!(c.label).trim_end_matches('…')).clicked() {
|
||||
|
|
@ -1638,7 +1638,7 @@ fn layers(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
|
|||
actions.push((crate::layer_menu_ui::add_mask_command(doc.selection.is_some(), alt).into(), json!({})));
|
||||
}
|
||||
let fx = fx_button(ui, 26.0, tl!("Add a layer style"));
|
||||
egui::Popup::menu(&fx).show(|ui| {
|
||||
egui::Popup::menu(&fx).open_memory(footer_menu_right_click(&fx)).show(|ui| {
|
||||
ui.set_min_width(180.0);
|
||||
if ui.button(tl!("Blending Options…")).clicked() {
|
||||
crate::layer_style::open(app, None);
|
||||
|
|
@ -1711,6 +1711,31 @@ fn footer_label(command: &str) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
/// A secondary click opens a Layers footer popup, as in Photoshop; a primary click still toggles
|
||||
/// it (the `Popup::menu` default this replaces).
|
||||
fn footer_menu_right_click(response: &egui::Response) -> Option<egui::SetOpenCommand> {
|
||||
if response.secondary_clicked() {
|
||||
Some(egui::SetOpenCommand::Bool(true))
|
||||
} else if response.clicked() {
|
||||
Some(egui::SetOpenCommand::Toggle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The small down-arrow makes it clear that the footer icon expands into a menu.
|
||||
fn footer_menu_arrow(ui: &egui::Ui, rect: Rect) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let arrow = Rect::from_min_size(rect.right_bottom() - vec2(11.0, 10.0), vec2(9.0, 9.0));
|
||||
icons::paint(ui, arrow, "chevron-down", 8.0, t.text_dim);
|
||||
}
|
||||
|
||||
fn footer_menu_button(ui: &mut egui::Ui, icon: &str, size: f32, tip: &str) -> egui::Response {
|
||||
let response = icons::button(ui, icon, size, false, tip);
|
||||
footer_menu_arrow(ui, response.rect);
|
||||
response
|
||||
}
|
||||
|
||||
/// Photoshop's italic "fx" footer button (no icon-font equivalent).
|
||||
fn fx_button(ui: &mut egui::Ui, size: f32, tip: &str) -> egui::Response {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
|
|
@ -1722,6 +1747,7 @@ fn fx_button(ui: &mut egui::Ui, size: f32, tip: &str) -> egui::Response {
|
|||
job.append("fx", 0.0, egui::TextFormat { font_id: egui::FontId::proportional(15.0), color: t.icon, italics: true, ..Default::default() });
|
||||
let g = ui.painter().layout_job(job);
|
||||
ui.painter().galley(r.center() - g.size() / 2.0, g, t.icon);
|
||||
footer_menu_arrow(ui, r);
|
||||
resp.on_hover_text(tip)
|
||||
}
|
||||
|
||||
|
|
@ -3433,6 +3459,48 @@ mod layer_drag_edge_scroll_tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod footer_menu_tests {
|
||||
use super::*;
|
||||
|
||||
// Exercise the actual egui pointer path, not just a mocked click flag.
|
||||
#[test]
|
||||
fn right_click_opens_footer_menu_and_primary_click_still_works() {
|
||||
let ctx = egui::Context::default();
|
||||
let rect = Rect::from_min_size(Pos2::ZERO, vec2(300.0, 100.0));
|
||||
let pos = pos2(35.0, 20.0);
|
||||
let frame = |events: Vec<egui::Event>| {
|
||||
let mut opened = false;
|
||||
let mut output = ctx.run_ui(egui::RawInput { screen_rect: Some(rect), events, ..Default::default() }, |ui| {
|
||||
let response = ui.add_sized([100.0, 28.0], egui::Button::new("Footer menu"));
|
||||
egui::Popup::menu(&response).open_memory(footer_menu_right_click(&response)).show(|ui| {
|
||||
opened = true;
|
||||
ui.label("Menu entry");
|
||||
});
|
||||
});
|
||||
output.textures_delta.clear();
|
||||
opened
|
||||
};
|
||||
frame(Vec::new());
|
||||
frame(vec![
|
||||
egui::Event::PointerMoved(pos),
|
||||
egui::Event::PointerButton { pos, button: egui::PointerButton::Secondary, pressed: true, modifiers: egui::Modifiers::NONE },
|
||||
]);
|
||||
assert!(
|
||||
frame(vec![egui::Event::PointerButton { pos, button: egui::PointerButton::Secondary, pressed: false, modifiers: egui::Modifiers::NONE }]),
|
||||
"secondary release opens the popup"
|
||||
);
|
||||
|
||||
// A primary click also remains supported by egui::Popup::menu.
|
||||
frame(vec![egui::Event::Key { key: egui::Key::Escape, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }]);
|
||||
frame(vec![egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed: true, modifiers: egui::Modifiers::NONE }]);
|
||||
assert!(
|
||||
frame(vec![egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed: false, modifiers: egui::Modifiers::NONE }]),
|
||||
"primary release still opens the popup"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod group_drag_selection_tests {
|
||||
use super::*;
|
||||
|
|
|
|||
Loading…
Reference in New Issue