Skip to content

Commit

Permalink
Show the innermost debug rectangle when pressing all modifier keys (e…
Browse files Browse the repository at this point in the history
…milk#4782)

This is usually what the user is interested in.
  • Loading branch information
emilk authored Jul 5, 2024
1 parent ad597a8 commit 1431199
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 97 deletions.
4 changes: 4 additions & 0 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
}

Expand Down
101 changes: 97 additions & 4 deletions crates/egui/src/frame_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
}
}
}
Expand All @@ -142,7 +235,7 @@ impl FrameState {
highlight_next_frame,

#[cfg(debug_assertions)]
has_debug_viewed_this_frame,
debug_rect,
} = self;

used_ids.clear();
Expand All @@ -157,7 +250,7 @@ impl FrameState {

#[cfg(debug_assertions)]
{
*has_debug_viewed_this_frame = false;
*debug_rect = None;
}

#[cfg(feature = "accesskit")]
Expand Down
2 changes: 1 addition & 1 deletion crates/egui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion crates/egui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
116 changes: 28 additions & 88 deletions crates/egui/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

// 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
// We only show one debug rectangle, or things get confusing:
let debug_rect = frame_state::DebugRect {
rect,
callstack,
is_clicking,
};

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);
fs.debug_rect = Some(debug_rect);
kept = true;
}
});
if !kept {
return;
}

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);
// 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 ui.input(|i| i.pointer.any_click()) {
ui.ctx().copy_text(callstack);
}
if debug.hover_shows_next {
ui.placer.debug_paint_cursor(&painter, "next");
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/epaint/src/bezier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion crates/epaint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion scripts/clippy_wasm/clippy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 1431199

Please sign in to comment.