Revert "Merge PR #1262": the toast as a widget takes the clicks meant for what is under it (a dialog's button)

This commit is contained in:
bflatastic committed 2026-10-13 11:54:39 +07:00
1 parent 2e877ad476
commit 7693cbf3ae
5 files changed
+8 -89

No files matched your search

-5
View File
@@ -27,8 +27,3 @@ sync_github_name = true
[people.sanyamsingh001]
display_name = "sanyamsingh001"
[people.switch87]
real_name = "Gert Pellin"
display_name = "Gert Pellin"
sync_github_name = false
-2
View File
@@ -118,8 +118,6 @@ mod tests_temp_slider;
#[cfg(test)]
mod tests_titlebar;
#[cfg(test)]
mod tests_toast;
#[cfg(test)]
mod tests_unsaved;
#[cfg(test)]
mod tests_wb_selector;
+8 -22
View File
@@ -69,9 +69,6 @@ pub fn resizable_side(
}
/// The HUD toast at the bottom centre of the canvas.
///
/// Drawn as widgets rather than a painted galley so the message can be selected and copied: an
/// error here often carries a path or a command the user wants to paste somewhere else.
pub fn toast(app: &mut LightcraftApp, ctx: &egui::Context) {
let now = ctx.input(|i| i.time);
let Some((text, until, label)) = app.ui.toast.clone() else { return };
@@ -82,27 +79,16 @@ pub fn toast(app: &mut LightcraftApp, ctx: &egui::Context) {
let Some(canvas) = app.canvas_rect else { return };
let t = Tokens::get(ctx);
let fade = ((until - now) / 0.25).clamp(0.0, 1.0) as f32;
let (background, foreground) = label.map(crate::theme::label_toast_colors).unwrap_or((egui::Color32::from_black_alpha(200), t.text));
let background = background.gamma_multiply(fade);
let foreground = foreground.gamma_multiply(fade);
let painter = ctx.layer_painter(egui::LayerId::new(egui::Order::Foreground, egui::Id::new("toast")));
// long messages (a save warning, the web build's notices) wrap within the canvas
let wrap = (canvas.width() - 80.0).clamp(200.0, 720.0);
egui::Area::new(egui::Id::new("toast"))
.order(egui::Order::Foreground)
.movable(false)
// (the toast fades by its own clock)
.fade_in(false)
.pivot(Align2::CENTER_CENTER)
.fixed_pos(pos2(canvas.center().x, canvas.bottom() - 60.0))
.show(ctx, |ui| {
ui.set_max_width(wrap);
egui::Frame::NONE.fill(background).corner_radius(egui::CornerRadius::same(6)).inner_margin(egui::Margin::symmetric(20, 11)).show(
ui,
|ui| {
ui.add(egui::Label::new(egui::RichText::new(text).font(t.font(14.0)).color(foreground)).wrap());
},
);
});
let (background, foreground) = label.map(crate::theme::label_toast_colors).unwrap_or((egui::Color32::from_black_alpha(200), t.text));
let foreground = foreground.gamma_multiply(fade);
let galley = painter.layout(text, t.font(14.0), foreground, wrap);
let size = vec2(galley.size().x + 40.0, (galley.size().y + 22.0).max(40.0));
let r = Rect::from_center_size(pos2(canvas.center().x, canvas.bottom() - 60.0), size);
painter.rect_filled(r, 6.0, background.gamma_multiply(fade));
painter.galley(r.center() - galley.size() / 2.0, galley, foreground);
ctx.request_repaint_after(std::time::Duration::from_millis(30));
}
-2
View File
@@ -75,8 +75,6 @@ fn label_tints_grid_and_filmstrip_chrome_without_changing_photo_pixels() {
fn label_toast_has_pale_colour_and_rating_restores_neutral_style() {
let mut h = app(ViewMode::SquareGrid);
h.app.run("photo.label", json!({"label": "red"})).unwrap();
// (two frames: the toast is an egui Area, sized in its first frame and painted from the second, #1262)
h.step();
h.step();
let at = egui::pos2(h.app.canvas_rect.unwrap().center().x, h.app.canvas_rect.unwrap().bottom() - 45.0);
let red = pixel(&h.paint(), at);
-58
View File
@@ -1,58 +0,0 @@
//! The toast is drawn as widgets, not as paint. That is what makes its message selectable: a toast
//! often carries a path or a command the user wants somewhere else, and painted text cannot be
//! picked up by the pointer at all.
use std::time::Duration;
use crate::headless::Headless;
use crate::{LightcraftApp, Services};
const T: Duration = Duration::from_secs(20);
fn demo() -> Headless {
let app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Services { png: None, ..Default::default() });
let mut h = Headless::new(app, [1200.0, 800.0], 1.0);
// let the first renders finish: a render still running when the test binary exits can crash in the
// GPU driver's teardown (the preview pool doesn't join its workers)
h.settle(T);
h
}
/// One event per frame: [`LightcraftApp::raw_input_hook`] takes pointer events singly.
fn send(h: &mut Headless, e: egui::Event) {
h.app.synthetic.push(e);
h.step();
}
fn show_toast(h: &mut Headless, msg: &str) -> egui::Rect {
h.app.ui.toast = Some((msg.to_owned(), f64::MAX, None));
h.step();
h.step();
h.view.ctx.memory(|m| m.area_rect(egui::Id::new("toast"))).expect("the toast is laid out as a widget, not paint")
}
#[test]
fn the_toast_is_a_widget_the_pointer_reaches() {
let mut h = demo();
let msg = "Exported frame to /tmp/scene-0001.png";
let area = show_toast(&mut h, msg);
assert!(area.is_positive(), "the toast has a rect of its own: {area:?}");
assert!(h.painted_text().iter().any(|t| t.contains("Exported frame")), "the message is still painted");
// The pointer reaches the message: egui offers the text cursor over it. Painted text is never
// hit-tested, so this is the difference that makes selection possible at all.
send(&mut h, egui::Event::PointerMoved(area.center()));
assert_eq!(h.last_cursor, egui::CursorIcon::Text, "the toast text is a text target");
}
#[test]
fn the_toast_still_wraps_a_long_message_inside_the_canvas() {
let mut h = demo();
let msg = "The SAM 3 download failed: the file is 4.1 GB and the connection dropped at 98 %, so nothing was installed and the masks are still unavailable";
let area = show_toast(&mut h, msg);
let canvas = h.app.canvas_rect.expect("the demo has a canvas");
assert!(area.width() <= canvas.width() - 80.0, "the toast wraps within the canvas: {area:?}");
assert!(h.painted_text().iter().any(|t| t.contains("SAM 3 download failed")), "the whole message is painted");
}