Show the innermost debug rectangle when pressing all modifier keys (#4782)

This is usually what the user is interested in.
This commit is contained in:
Emil Ernerfeldt 2024-07-05 08:36:56 +02:00 committed by GitHub
parent ad597a8491
commit 143119943d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 135 additions and 98 deletions

View File

@ -1942,6 +1942,10 @@ impl Context {
paint_widget(widget, "drag", Color32::GREEN);
}
}
if let Some(debug_rect) = self.frame_state_mut(|fs| fs.debug_rect.take()) {
debug_rect.paint(&self.debug_painter());
}
}
}

View File

@ -47,6 +47,99 @@ pub struct AccessKitFrameState {
pub parent_stack: Vec<Id>,
}
#[cfg(debug_assertions)]
#[derive(Clone)]
pub struct DebugRect {
pub rect: Rect,
pub callstack: String,
pub is_clicking: bool,
}
#[cfg(debug_assertions)]
impl DebugRect {
pub fn paint(self, painter: &Painter) {
let Self {
rect,
callstack,
is_clicking,
} = self;
let ctx = painter.ctx();
// Paint rectangle around widget:
{
// Print width and height:
let text_color = if ctx.style().visuals.dark_mode {
Color32::WHITE
} else {
Color32::BLACK
};
painter.debug_text(
rect.left_center() + 2.0 * Vec2::LEFT,
Align2::RIGHT_CENTER,
text_color,
format!("H: {:.1}", rect.height()),
);
painter.debug_text(
rect.center_top(),
Align2::CENTER_BOTTOM,
text_color,
format!("W: {:.1}", rect.width()),
);
// Paint rect:
let rect_fg_color = if is_clicking {
Color32::WHITE
} else {
Color32::LIGHT_BLUE
};
let rect_bg_color = Color32::BLUE.gamma_multiply(0.5);
painter.rect(rect, 0.0, rect_bg_color, (1.0, rect_fg_color));
}
if !callstack.is_empty() {
let font_id = FontId::monospace(12.0);
let text = format!("{callstack}\n\n(click to copy)");
let text_color = Color32::WHITE;
let galley = painter.layout_no_wrap(text, font_id, text_color);
// Position the text either under or above:
let screen_rect = ctx.screen_rect();
let y = if galley.size().y <= rect.top() {
// Above
rect.top() - galley.size().y - 16.0
} else {
// Below
rect.bottom()
};
let y = y
.at_most(screen_rect.bottom() - galley.size().y)
.at_least(0.0);
let x = rect
.left()
.at_most(screen_rect.right() - galley.size().x)
.at_least(0.0);
let text_pos = pos2(x, y);
let text_bg_color = Color32::from_black_alpha(180);
let text_rect_stroke_color = if is_clicking {
Color32::WHITE
} else {
text_bg_color
};
let text_rect = Rect::from_min_size(text_pos, galley.size());
painter.rect(text_rect, 0.0, text_bg_color, (1.0, text_rect_stroke_color));
painter.galley(text_pos, galley, text_color);
if is_clicking {
ctx.copy_text(callstack);
}
}
}
}
/// State that is collected during a frame, then saved for the next frame,
/// and then cleared.
///
@ -99,7 +192,7 @@ pub struct FrameState {
pub highlight_next_frame: IdSet,
#[cfg(debug_assertions)]
pub has_debug_viewed_this_frame: bool,
pub debug_rect: Option<DebugRect>,
}
impl Default for FrameState {
@ -119,7 +212,7 @@ impl Default for FrameState {
highlight_next_frame: Default::default(),
#[cfg(debug_assertions)]
has_debug_viewed_this_frame: false,
debug_rect: None,
}
}
}
@ -142,7 +235,7 @@ impl FrameState {
highlight_next_frame,
#[cfg(debug_assertions)]
has_debug_viewed_this_frame,
debug_rect,
} = self;
used_ids.clear();
@ -157,7 +250,7 @@ impl FrameState {
#[cfg(debug_assertions)]
{
*has_debug_viewed_this_frame = false;
*debug_rect = None;
}
#[cfg(feature = "accesskit")]

View File

@ -106,7 +106,7 @@
//!
//! `egui` uses logical _points_ as its coordinate system.
//! Those related to physical _pixels_ by the `pixels_per_point` scale factor.
//! For example, a high-dpi screeen can have `pixels_per_point = 2.0`,
//! For example, a high-dpi screen can have `pixels_per_point = 2.0`,
//! meaning there are two physical screen pixels for each logical point.
//!
//! Angles are in radians, and are measured clockwise from the X-axis, which has angle=0.

View File

@ -727,7 +727,7 @@ pub struct Interaction {
/// Can the user select text that span multiple labels?
///
/// The default is `true`, but text seelction can be slightly glitchy,
/// The default is `true`, but text selection can be slightly glitchy,
/// so you may want to disable it.
pub multi_widget_text_select: bool,
}

View File

@ -2599,110 +2599,50 @@ fn register_rect(ui: &Ui, rect: Rect) {
return;
}
if ui.ctx().frame_state(|o| o.has_debug_viewed_this_frame) {
return;
}
if !ui.rect_contains_pointer(rect) {
return;
}
// We only show one debug rectangle, or things get confusing:
ui.ctx()
.frame_state_mut(|o| o.has_debug_viewed_this_frame = true);
// ----------------------------------------------
let is_clicking = ui.input(|i| i.pointer.could_any_button_be_click());
// Use the debug-painter to avoid clip rect,
// otherwise the content of the widget may cover what we paint here!
let painter = ui.ctx().debug_painter();
// Paint rectangle around widget:
{
// Print width and height:
let text_color = if ui.visuals().dark_mode {
Color32::WHITE
} else {
Color32::BLACK
};
painter.debug_text(
rect.left_center() + 2.0 * Vec2::LEFT,
Align2::RIGHT_CENTER,
text_color,
format!("H: {:.1}", rect.height()),
);
painter.debug_text(
rect.center_top(),
Align2::CENTER_BOTTOM,
text_color,
format!("W: {:.1}", rect.width()),
);
// Paint rect:
let rect_fg_color = if is_clicking {
Color32::WHITE
} else {
Color32::LIGHT_BLUE
};
let rect_bg_color = Color32::BLUE.gamma_multiply(0.5);
painter.rect(rect, 0.0, rect_bg_color, (1.0, rect_fg_color));
}
// ----------------------------------------------
if debug.hover_shows_next {
ui.placer.debug_paint_cursor(&painter, "next");
}
// ----------------------------------------------
#[cfg(feature = "callstack")]
let callstack = crate::callstack::capture();
#[cfg(not(feature = "callstack"))]
let callstack = String::default();
if !callstack.is_empty() {
let font_id = FontId::monospace(12.0);
let text = format!("{callstack}\n\n(click to copy)");
let text_color = Color32::WHITE;
let galley = painter.layout_no_wrap(text, font_id, text_color);
// We only show one debug rectangle, or things get confusing:
let debug_rect = frame_state::DebugRect {
rect,
callstack,
is_clicking,
};
// Position the text either under or above:
let screen_rect = ui.ctx().screen_rect();
let y = if galley.size().y <= rect.top() {
// Above
rect.top() - galley.size().y - 16.0
let mut kept = false;
ui.ctx().frame_state_mut(|fs| {
if let Some(final_debug_rect) = &mut fs.debug_rect {
// or maybe pick the one with deepest callstack?
if final_debug_rect.rect.contains_rect(rect) {
*final_debug_rect = debug_rect;
kept = true;
}
} else {
// Below
rect.bottom()
};
let y = y
.at_most(screen_rect.bottom() - galley.size().y)
.at_least(0.0);
let x = rect
.left()
.at_most(screen_rect.right() - galley.size().x)
.at_least(0.0);
let text_pos = pos2(x, y);
let text_bg_color = Color32::from_black_alpha(180);
let text_rect_stroke_color = if is_clicking {
Color32::WHITE
} else {
text_bg_color
};
let text_rect = Rect::from_min_size(text_pos, galley.size());
painter.rect(text_rect, 0.0, text_bg_color, (1.0, text_rect_stroke_color));
painter.galley(text_pos, galley, text_color);
if ui.input(|i| i.pointer.any_click()) {
ui.ctx().copy_text(callstack);
fs.debug_rect = Some(debug_rect);
kept = true;
}
});
if !kept {
return;
}
// ----------------------------------------------
// Use the debug-painter to avoid clip rect,
// otherwise the content of the widget may cover what we paint here!
let painter = ui.ctx().debug_painter();
if debug.hover_shows_next {
ui.placer.debug_paint_cursor(&painter, "next");
}
}

View File

@ -713,7 +713,7 @@ fn quadratic_for_each_local_extremum<F: FnMut(f32)>(p0: f32, p1: f32, p2: f32, c
fn cubic_for_each_local_extremum<F: FnMut(f32)>(p0: f32, p1: f32, p2: f32, p3: f32, cb: &mut F) {
// See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation
// A cubic Bézier curve can be derivated by the following equation:
// A cubic Bézier curve can be derived by the following equation:
// B'(t) = 3(1-t)^2(p1-p0) + 6(1-t)t(p2-p1) + 3t^2(p3-p2) or
// f(x) = a * x² + b * x + c
let a = 3.0 * (p3 + 3.0 * (p1 - p2) - p0);

View File

@ -11,7 +11,7 @@
//!
//! `epaint` uses logical _points_ as its coordinate system.
//! Those related to physical _pixels_ by the `pixels_per_point` scale factor.
//! For example, a high-dpi screeen can have `pixels_per_point = 2.0`,
//! For example, a high-dpi screen can have `pixels_per_point = 2.0`,
//! meaning there are two physical screen pixels for each logical point.
//!
//! Angles are in radians, and are measured clockwise from the X-axis, which has angle=0.

View File

@ -42,7 +42,7 @@ disallowed-types = [
# { path = "std::path::PathBuf", reason = "Can't read/write files on web" }, // TODO(emilk): consider banning Path on wasm
]
# Allow-list of words for markdown in dosctrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
# Allow-list of words for markdown in docstrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
doc-valid-idents = [
# You must also update the same list in the root `clippy.toml`!
"AccessKit",