Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading