mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 01:57:12 -06:00
fix(ui): keep colour popups open when "+" opens the custom colour picker (#4066)
* fix(ui): keep colour popups open when "+" opens the custom colour picker The colour popups used egui's default menu close behaviour (CloseOnClick), which closes on any click, including the "+" swatch inside the popup. The picker's open state was remembered, so it appeared only on the next open. Close these popups only on clicks outside them: the quick bar's colour and fill popups, the comment context menu's Colour submenu and Format text's colour popup. Picking a swatch or pressing Apply still closes them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * contributors: credit agent-reed by GitHub profile name * test(ui): pin the fill popup and context-menu Colour submenu keeping their picker open The quick bar's Fill colour popup and the comment context menu's Colour submenu each get a kittest test: "+" shows the picker without closing the popup, and choosing a colour still closes it. Both fail with the fix reverted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
30bafb4992
commit
7169160df9
@@ -173,3 +173,6 @@ sync_github_name = true
|
|||||||
|
|
||||||
[people.amoisttowe1]
|
[people.amoisttowe1]
|
||||||
real_name = "Nick Nyssen"
|
real_name = "Nick Nyssen"
|
||||||
|
|
||||||
|
[people.agent-reed]
|
||||||
|
sync_github_name = true
|
||||||
|
|||||||
@@ -1873,7 +1873,8 @@ pub(crate) fn context_menu(ui: &mut egui::Ui, view: &mut DocView, info: &DocInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.menu_button(tl!("Colour"), |ui| {
|
let keep_open = egui::containers::menu::MenuConfig::new().close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside);
|
||||||
|
let _ = egui::containers::menu::SubMenuButton::new(tl!("Colour")).config(keep_open).ui(ui, |ui| {
|
||||||
if let Some(c) = swatch_grid(ui, a.color.map(|c| c.map(f64::from))) {
|
if let Some(c) = swatch_grid(ui, a.color.map(|c| c.map(f64::from))) {
|
||||||
action = Some(CanvasAction::Edit(Box::new(Edit::StyleAnnotation {
|
action = Some(CanvasAction::Edit(Box::new(Edit::StyleAnnotation {
|
||||||
page,
|
page,
|
||||||
@@ -2092,7 +2093,7 @@ pub(crate) fn quick_bar_controls(ui: &mut egui::Ui, tool: CommentTool, prefs: &m
|
|||||||
ui.painter().circle_stroke(r.center(), 9.0, Stroke::new(1.0, Color32::from_black_alpha(50)));
|
ui.painter().circle_stroke(r.center(), 9.0, Stroke::new(1.0, Color32::from_black_alpha(50)));
|
||||||
resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, true, tl!("Colour")));
|
resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, true, tl!("Colour")));
|
||||||
let resp = resp.on_hover_text(tl!("Colour"));
|
let resp = resp.on_hover_text(tl!("Colour"));
|
||||||
egui::Popup::menu(&resp).align(egui::RectAlign::RIGHT_START).show(|ui| {
|
egui::Popup::menu(&resp).align(egui::RectAlign::RIGHT_START).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| {
|
||||||
if let Some(c) = swatch_grid(ui, Some(style.color)) {
|
if let Some(c) = swatch_grid(ui, Some(style.color)) {
|
||||||
prefs.set_color(tool, c);
|
prefs.set_color(tool, c);
|
||||||
selected_color = Some(c);
|
selected_color = Some(c);
|
||||||
@@ -2101,7 +2102,7 @@ pub(crate) fn quick_bar_controls(ui: &mut egui::Ui, tool: CommentTool, prefs: &m
|
|||||||
});
|
});
|
||||||
if tool.has_fill() {
|
if tool.has_fill() {
|
||||||
let resp = fill_button(ui, style.fill);
|
let resp = fill_button(ui, style.fill);
|
||||||
egui::Popup::menu(&resp).align(egui::RectAlign::RIGHT_START).show(|ui| {
|
egui::Popup::menu(&resp).align(egui::RectAlign::RIGHT_START).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| {
|
||||||
ui.label(egui::RichText::new(tl!("Fill colour")).font(theme::semibold(12.0)));
|
ui.label(egui::RichText::new(tl!("Fill colour")).font(theme::semibold(12.0)));
|
||||||
if let Some(f) = fill_picker(ui, style.fill) {
|
if let Some(f) = fill_picker(ui, style.fill) {
|
||||||
prefs.set_fill(tool, f);
|
prefs.set_fill(tool, f);
|
||||||
@@ -2339,6 +2340,106 @@ mod tests {
|
|||||||
assert_eq!(h.query_all_by_label("Apply").count(), 1, "only the first grid's picker opens");
|
assert_eq!(h.query_all_by_label("Apply").count(), 1, "only the first grid's picker opens");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn other_colour_opens_the_picker_inside_the_quick_bar_popup() {
|
||||||
|
use egui_kittest::kittest::Queryable;
|
||||||
|
let mut h = egui_kittest::Harness::new_ui_state(
|
||||||
|
|ui, prefs: &mut CommentPrefs| {
|
||||||
|
let _ = quick_bar_controls(ui, CommentTool::Highlight, prefs);
|
||||||
|
},
|
||||||
|
CommentPrefs::default(),
|
||||||
|
);
|
||||||
|
h.run();
|
||||||
|
h.get_by_label("Colour").click();
|
||||||
|
h.run();
|
||||||
|
h.get_by_label("Other colour…").click();
|
||||||
|
h.run();
|
||||||
|
assert!(h.query_by_label("Apply").is_some(), "the popup stays open with the picker showing");
|
||||||
|
h.get_by_label("Apply").click();
|
||||||
|
h.run();
|
||||||
|
assert!(h.query_by_label("Other colour…").is_none(), "applying a colour closes the popup");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn other_colour_opens_the_picker_inside_the_quick_bar_fill_popup() {
|
||||||
|
use egui_kittest::kittest::Queryable;
|
||||||
|
let prefs = CommentPrefs::default();
|
||||||
|
let fill_label = if prefs.style(CommentTool::Rectangle).fill.is_some() { "Fill colour" } else { "Fill colour: none" };
|
||||||
|
let mut h = egui_kittest::Harness::new_ui_state(
|
||||||
|
|ui, prefs: &mut CommentPrefs| {
|
||||||
|
let _ = quick_bar_controls(ui, CommentTool::Rectangle, prefs);
|
||||||
|
},
|
||||||
|
prefs,
|
||||||
|
);
|
||||||
|
h.ctx.set_fonts(theme::font_definitions());
|
||||||
|
h.run();
|
||||||
|
h.get_by_label(fill_label).click();
|
||||||
|
h.run();
|
||||||
|
h.get_by_label("Other colour…").click();
|
||||||
|
h.run();
|
||||||
|
assert!(h.query_by_label("Apply").is_some(), "the popup stays open with the picker showing");
|
||||||
|
h.get_by_label("Apply").click();
|
||||||
|
h.run();
|
||||||
|
assert!(h.query_by_label("No fill").is_none(), "applying a colour closes the popup");
|
||||||
|
assert!(h.state().style(CommentTool::Rectangle).fill.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn context_menu_colour_submenu_keeps_its_picker_open_and_a_swatch_closes_the_menu() {
|
||||||
|
use egui_kittest::kittest::Queryable;
|
||||||
|
let info = DocInfo {
|
||||||
|
pages: vec![PageInfo { width: 300.0, height: 400.0, label: String::new(), crop: [0.0, 0.0, 300.0, 400.0], rotation: 0 }],
|
||||||
|
annotations: vec![Annotation {
|
||||||
|
page: 0,
|
||||||
|
subtype: "Square".into(),
|
||||||
|
author: None,
|
||||||
|
contents: None,
|
||||||
|
modified: None,
|
||||||
|
name: None,
|
||||||
|
in_reply_to: None,
|
||||||
|
rect: [10.0, 10.0, 50.0, 50.0],
|
||||||
|
color: None,
|
||||||
|
index: 0,
|
||||||
|
state: None,
|
||||||
|
quads: Vec::new(),
|
||||||
|
locked: false,
|
||||||
|
intent: None,
|
||||||
|
fill_sign: false,
|
||||||
|
points: Vec::new(),
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut view = DocView::new(pdfcraft_engine::DocId(1), &info, crate::canvas::ViewDefaults::default());
|
||||||
|
view.comments.selected = Some((0, 0));
|
||||||
|
let prefs = CommentPrefs::default();
|
||||||
|
let mut h = egui_kittest::Harness::new_ui_state(
|
||||||
|
|ui, (view, actions): &mut (DocView, Vec<CanvasAction>)| {
|
||||||
|
ui.button("Page").context_menu(|ui| {
|
||||||
|
if let Some(a) = context_menu(ui, view, &info, &prefs, true) {
|
||||||
|
actions.push(a);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(view, Vec::new()),
|
||||||
|
);
|
||||||
|
h.run();
|
||||||
|
h.get_by_label("Page").click_secondary();
|
||||||
|
h.run();
|
||||||
|
h.get_by_label("Colour ⏵").click();
|
||||||
|
h.run();
|
||||||
|
h.get_by_label("Other colour…").click();
|
||||||
|
h.run();
|
||||||
|
assert!(h.query_by_label("Apply").is_some(), "the submenu stays open with the picker showing");
|
||||||
|
h.get_by_label("Red").click();
|
||||||
|
h.run();
|
||||||
|
assert!(h.query_by_label("Edit text…").is_none(), "picking a swatch closes the whole menu");
|
||||||
|
let actions = &h.state().1;
|
||||||
|
assert!(
|
||||||
|
matches!(actions.as_slice(), [CanvasAction::Edit(e)] if matches!(**e, Edit::StyleAnnotation { page: 0, index: 0, color: Some(_), .. })),
|
||||||
|
"{actions:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shift_keeps_the_current_proportions_unless_the_box_has_its_own() {
|
fn shift_keeps_the_current_proportions_unless_the_box_has_its_own() {
|
||||||
let r = Rect::from_min_max(pos2(100.0, 100.0), pos2(196.0, 132.0));
|
let r = Rect::from_min_max(pos2(100.0, 100.0), pos2(196.0, 132.0));
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ pub(crate) fn panel(ui: &mut egui::Ui, t: &Tokens, look: &mut AddedText, extras:
|
|||||||
.response
|
.response
|
||||||
.on_hover_text(tl!("Font size"));
|
.on_hover_text(tl!("Font size"));
|
||||||
let swatch = colour_button(ui, t, look.color);
|
let swatch = colour_button(ui, t, look.color);
|
||||||
egui::Popup::menu(&swatch).show(|ui| {
|
egui::Popup::menu(&swatch).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| {
|
||||||
if let Some(picked) = crate::comments::swatch_grid(ui, Some(look.color)) {
|
if let Some(picked) = crate::comments::swatch_grid(ui, Some(look.color)) {
|
||||||
look.color = picked;
|
look.color = picked;
|
||||||
ui.close();
|
ui.close();
|
||||||
|
|||||||
Reference in New Issue
Block a user