Fix #577: the gradient stop popover stays open after the click that opens it
The Gradient panel's colour chip opens the stop popover on a click. The popover closes on a click elsewhere, and the opening click itself, which is released in the frame the popover first draws and lies outside it, counted as one: the popover closed at once (a double-click got through, as the popover lets the double-click that opens it from the canvas pass). The popover now records the frame it first drew in and only a click in a later frame closes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
28846a98a0
commit
ba93f62bcb
File diff suppressed because one or more lines are too long
|
|
@ -29,6 +29,9 @@ use crate::widgets::{self, Live};
|
|||
|
||||
pub(super) const SPEC: DialogSpec = DialogSpec::window(show, confirm);
|
||||
|
||||
/// The field holding the frame the popover was first drawn in.
|
||||
const OPENED: &str = "__opened";
|
||||
|
||||
/// What the popover edits: the selected stop of the gradient behind the active proxy (with all
|
||||
/// its stops), or the selected point of a freeform gradient.
|
||||
enum Target {
|
||||
|
|
@ -131,12 +134,18 @@ fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
|||
});
|
||||
})
|
||||
.response;
|
||||
if let (Some(tab), Some(d)) = (tab, app.ui.dialog.as_mut()) {
|
||||
d.fields.insert("tab".into(), json!(tab));
|
||||
// The frame it opened in (its first drawn).
|
||||
let frame = ctx.cumulative_frame_nr();
|
||||
let opened = d.fields.get(OPENED).and_then(Value::as_u64).unwrap_or(frame);
|
||||
if let Some(d) = app.ui.dialog.as_mut() {
|
||||
if let Some(tab) = tab {
|
||||
d.fields.insert("tab".into(), json!(tab));
|
||||
}
|
||||
d.fields.insert(OPENED.into(), json!(opened));
|
||||
}
|
||||
// The double-click that opened the popover lands on its chip, outside it: only a later click
|
||||
// elsewhere closes it.
|
||||
if resp.clicked_elsewhere() && !ctx.input(|i| i.pointer.button_double_clicked(egui::PointerButton::Primary)) {
|
||||
// The click that opened the popover (the Gradient panel's colour chip) or the double-click
|
||||
// (a stop's chip) lands outside it: only a later click elsewhere closes it (#577).
|
||||
if frame > opened && resp.clicked_elsewhere() && !ctx.input(|i| i.pointer.button_double_clicked(egui::PointerButton::Primary)) {
|
||||
app.ui.dialog = None;
|
||||
}
|
||||
}
|
||||
|
|
@ -246,10 +255,18 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
// One context per test, so frames follow one another as in the app.
|
||||
static CTX: egui::Context = {
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
ctx
|
||||
};
|
||||
}
|
||||
|
||||
/// One headless frame of the dialog layer and the shortcuts.
|
||||
fn frame(app: &mut VectorcraftApp, events: Vec<egui::Event>) {
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
let ctx = CTX.with(Clone::clone);
|
||||
let mut out = ctx.run_ui(egui::RawInput { events, ..Default::default() }, |ui| {
|
||||
crate::shortcuts::handle(app, ui.ctx());
|
||||
super::super::show(app, ui.ctx());
|
||||
|
|
@ -312,6 +329,25 @@ mod tests {
|
|||
assert!(app.ui.dialog.is_none(), "a click elsewhere closes it");
|
||||
}
|
||||
|
||||
/// #577: the Gradient panel's colour chip opens it with a click whose release lands in the
|
||||
/// frame it opens in, outside it: that click doesn't close it.
|
||||
#[test]
|
||||
fn the_click_that_opens_it_from_the_panel_keeps_it_open() {
|
||||
let mut app = app();
|
||||
let at = egui::pos2(5.0, 5.0);
|
||||
frame(&mut app, vec![egui::Event::PointerMoved(at), click(at, true)]);
|
||||
// The panel opens it while the click is released.
|
||||
app.ui.dialog = Some(Dialog::new("gradientStop", json!({"index": 1, "screen": [200.0, 200.0], "tab": "color"})));
|
||||
frame(&mut app, vec![click(at, false)]);
|
||||
for _ in 0..3 {
|
||||
frame(&mut app, vec![]);
|
||||
}
|
||||
assert!(app.ui.dialog.is_some(), "the opening click must not close it");
|
||||
let away = egui::pos2(5.0, 600.0);
|
||||
frame(&mut app, vec![egui::Event::PointerMoved(away), click(away, true), click(away, false)]);
|
||||
assert!(app.ui.dialog.is_none(), "a later click elsewhere closes it");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_popover_closes_when_its_stop_goes_away() {
|
||||
let mut app = app();
|
||||
|
|
|
|||
Loading…
Reference in New Issue