mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 02:46:51 -06:00
File tabs: stop the close button from flickering (#1914)
* File tabs: the close button no longer flickers under the pointer An inactive drawing tab drew its X only while the tab was hovered. Once the pointer reached the X, egui gave the hover to the X, so the next frame left the X out, the tab got the hover back, and the frame after drew the X again. The tab background and its X flickered for as long as the pointer stayed there, and a click could land on the tab instead of the X. The tab now asks whether the pointer is inside its rectangle (`Ui::rect_contains_pointer`), which the X does not change, both for the hover fill and for showing the X. Regression test: tests/file_tab_close.rs points at an inactive tab's X, checks the X is there on each of the next frames, and clicks it to close that drawing. It failed on the first frame before the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * File tabs: plainer comment on why the tab checks the pointer 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
78c1653f50
commit
6842b8774b
@@ -198,12 +198,15 @@ pub fn file_tabs(app: &mut CadApp, ui: &mut egui::Ui) {
|
||||
|ui: &mut egui::Ui, label: &str, dirty: bool, active: bool, closable: bool, w: f32, id: egui::Id| -> (bool, bool, egui::Response) {
|
||||
let tr = Rect::from_min_size(pos2(x, r.top()), vec2(w, r.height()));
|
||||
let resp = ui.interact(tr, id, Sense::click());
|
||||
// The close button takes the tab's hover, so `resp.hovered()` would make the tab and
|
||||
// its X flicker while the pointer is on the X.
|
||||
let hovered = ui.rect_contains_pointer(tr);
|
||||
ui.painter().rect_filled(
|
||||
tr,
|
||||
0.0,
|
||||
if active {
|
||||
t.tab_active
|
||||
} else if resp.hovered() {
|
||||
} else if hovered {
|
||||
t.chrome
|
||||
} else {
|
||||
t.chrome_dark
|
||||
@@ -223,7 +226,7 @@ pub fn file_tabs(app: &mut CadApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
let resp = if cut { resp.on_hover_text(format!("{label}{mark}")) } else { resp };
|
||||
let mut close = false;
|
||||
if closable && (active || resp.hovered()) {
|
||||
if closable && (active || hovered) {
|
||||
let cr = Rect::from_center_size(pos2(tr.right() - 11.0, tr.center().y), vec2(12.0, 12.0));
|
||||
let cresp = ui.interact(cr, id.with("x"), Sense::click());
|
||||
icons::paint(ui.painter(), cr, Icon::Close, false);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//! The close button of an inactive file tab stays while the pointer is on it, and a click on it
|
||||
//! closes that drawing. It used to take the tab's hover, so the next frame dropped it and the one
|
||||
//! after drew it again: the tab and its X flickered.
|
||||
|
||||
use cadcraft_engine::Session;
|
||||
use cadcraft_ui_egui::{CadApp, Services};
|
||||
use egui::accesskit::Role;
|
||||
use egui::vec2;
|
||||
use egui_kittest::Harness;
|
||||
use egui_kittest::kittest::Queryable;
|
||||
|
||||
#[test]
|
||||
fn an_inactive_tabs_close_button_stays_under_the_pointer_and_closes_it() {
|
||||
let mut app = CadApp::new(Session::default(), Services::default());
|
||||
app.run("new", serde_json::json!({})).unwrap();
|
||||
app.run("new", serde_json::json!({})).unwrap();
|
||||
app.ui.start_tab = false;
|
||||
assert_ne!(app.session.active, 0, "the first drawing's tab is inactive");
|
||||
let first = app.session.docs[0].title.clone();
|
||||
let open = app.session.docs.len();
|
||||
let mut h = Harness::builder().with_size(vec2(1200.0, 760.0)).build_ui_state(
|
||||
|ui, app: &mut CadApp| {
|
||||
app.logic(&ui.ctx().clone());
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(5);
|
||||
// Pointing at the tab shows its close button.
|
||||
h.get_by_role_and_label(Role::Tab, &first).hover();
|
||||
h.run_steps(3);
|
||||
let close = format!("Close {first}");
|
||||
h.get_by_label(&close).hover();
|
||||
for frame in 0..6 {
|
||||
h.step();
|
||||
assert!(h.query_by_label(&close).is_some(), "the close button is gone on frame {frame} under the pointer");
|
||||
}
|
||||
h.get_by_label(&close).click();
|
||||
h.run_steps(3);
|
||||
let titles: Vec<&str> = h.state().session.docs.iter().map(|d| d.title.as_str()).collect();
|
||||
assert_eq!(titles.len(), open - 1, "one drawing closed: {titles:?}");
|
||||
assert!(!titles.contains(&first.as_str()), "the clicked tab's drawing is the one closed: {titles:?}");
|
||||
}
|
||||
Reference in New Issue
Block a user