Add option to show a callstack to the widget under the mouse (#3391)
This commit is contained in:
parent
e8986b1e59
commit
23ce4e70ca
|
|
@ -81,6 +81,9 @@ jobs:
|
||||||
- name: Cranky
|
- name: Cranky
|
||||||
run: cargo cranky --all-targets --all-features -- -D warnings
|
run: cargo cranky --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
- name: Cranky release
|
||||||
|
run: cargo cranky --all-targets --all-features --release -- -D warnings
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
check_wasm:
|
check_wasm:
|
||||||
|
|
|
||||||
|
|
@ -1164,6 +1164,7 @@ version = "0.22.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"ahash 0.8.3",
|
"ahash 0.8.3",
|
||||||
|
"backtrace",
|
||||||
"document-features",
|
"document-features",
|
||||||
"epaint",
|
"epaint",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ default = ["default_fonts"]
|
||||||
## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`epaint::Vertex`], [`emath::Vec2`] etc to `&[u8]`.
|
## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`epaint::Vertex`], [`emath::Vec2`] etc to `&[u8]`.
|
||||||
bytemuck = ["epaint/bytemuck"]
|
bytemuck = ["epaint/bytemuck"]
|
||||||
|
|
||||||
|
## Show a debug-ui on hover including the stacktrace to the hovered item.
|
||||||
|
## This is very useful in finding the code that creates a part of the UI.
|
||||||
|
## Does not work on web.
|
||||||
|
callstack = ["dep:backtrace"]
|
||||||
|
|
||||||
## [`cint`](https://docs.rs/cint) enables interoperability with other color libraries.
|
## [`cint`](https://docs.rs/cint) enables interoperability with other color libraries.
|
||||||
cint = ["epaint/cint"]
|
cint = ["epaint/cint"]
|
||||||
|
|
||||||
|
|
@ -80,6 +85,8 @@ nohash-hasher = "0.2"
|
||||||
## accessibility APIs. Also requires support in the egui integration.
|
## accessibility APIs. Also requires support in the egui integration.
|
||||||
accesskit = { version = "0.11", optional = true }
|
accesskit = { version = "0.11", optional = true }
|
||||||
|
|
||||||
|
backtrace = { version = "0.3", optional = true }
|
||||||
|
|
||||||
## Enable this when generating docs.
|
## Enable this when generating docs.
|
||||||
document-features = { version = "0.2", optional = true }
|
document-features = { version = "0.2", optional = true }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Frame {
|
||||||
|
/// `_main` is usually as the deepest depth.
|
||||||
|
depth: usize,
|
||||||
|
name: String,
|
||||||
|
file_and_line: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Capture a callstack, skipping the frames that are not interesting.
|
||||||
|
///
|
||||||
|
/// In particular: slips everything before `egui::Context::run`,
|
||||||
|
/// and skipping all frames in the `egui::` namespace.
|
||||||
|
pub fn capture() -> String {
|
||||||
|
let mut frames = vec![];
|
||||||
|
let mut depth = 0;
|
||||||
|
|
||||||
|
backtrace::trace(|frame| {
|
||||||
|
// Resolve this instruction pointer to a symbol name
|
||||||
|
backtrace::resolve_frame(frame, |symbol| {
|
||||||
|
let mut file_and_line = symbol.filename().map(shorten_source_file_path);
|
||||||
|
|
||||||
|
if let Some(file_and_line) = &mut file_and_line {
|
||||||
|
if let Some(line_nr) = symbol.lineno() {
|
||||||
|
file_and_line.push_str(&format!(":{line_nr}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let file_and_line = file_and_line.unwrap_or_default();
|
||||||
|
|
||||||
|
let name = symbol
|
||||||
|
.name()
|
||||||
|
.map(|name| name.to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
frames.push(Frame {
|
||||||
|
depth,
|
||||||
|
name,
|
||||||
|
file_and_line,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
depth += 1; // note: we can resolve multiple symbols on the same frame.
|
||||||
|
|
||||||
|
true // keep going to the next frame
|
||||||
|
});
|
||||||
|
|
||||||
|
if frames.is_empty() {
|
||||||
|
return Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inclusive:
|
||||||
|
let mut min_depth = 0;
|
||||||
|
let mut max_depth = frames.len() - 1;
|
||||||
|
|
||||||
|
for frame in &frames {
|
||||||
|
if frame.name.starts_with("egui::callstack::capture") {
|
||||||
|
min_depth = frame.depth + 1;
|
||||||
|
}
|
||||||
|
if frame.name.starts_with("egui::context::Context::run") {
|
||||||
|
max_depth = frame.depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove frames that are uninteresting:
|
||||||
|
frames.retain(|frame| {
|
||||||
|
// Keep some special frames to give the user a sense of chronology:
|
||||||
|
if frame.name == "main"
|
||||||
|
|| frame.name == "_main"
|
||||||
|
|| frame.name.starts_with("egui::context::Context::run")
|
||||||
|
|| frame.name.starts_with("eframe::run_native")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if frame.depth < min_depth || max_depth < frame.depth {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stuff that isn't user calls:
|
||||||
|
let skip_prefixes = [
|
||||||
|
// "backtrace::", // not needed, since we cut at at egui::callstack::capture
|
||||||
|
"egui::",
|
||||||
|
"<egui::",
|
||||||
|
"<F as egui::widgets::Widget>",
|
||||||
|
"egui_plot::",
|
||||||
|
"egui_extras::",
|
||||||
|
"core::ptr::drop_in_place<egui::ui::Ui>::",
|
||||||
|
"eframe::",
|
||||||
|
"core::ops::function::FnOnce::call_once",
|
||||||
|
"<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once",
|
||||||
|
];
|
||||||
|
for prefix in skip_prefixes {
|
||||||
|
if frame.name.starts_with(prefix) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
|
frames.reverse(); // main on top, i.e. chronological order. Same as Python.
|
||||||
|
|
||||||
|
let mut deepest_depth = 0;
|
||||||
|
let mut widest_file_line = 0;
|
||||||
|
for frame in &frames {
|
||||||
|
deepest_depth = frame.depth.max(deepest_depth);
|
||||||
|
widest_file_line = frame.file_and_line.len().max(widest_file_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
let widest_depth = deepest_depth.to_string().len();
|
||||||
|
|
||||||
|
let mut formatted = String::new();
|
||||||
|
|
||||||
|
if !frames.is_empty() {
|
||||||
|
let mut last_depth = frames[0].depth;
|
||||||
|
|
||||||
|
for frame in &frames {
|
||||||
|
let Frame {
|
||||||
|
depth,
|
||||||
|
name,
|
||||||
|
file_and_line,
|
||||||
|
} = frame;
|
||||||
|
|
||||||
|
if frame.depth + 1 < last_depth || last_depth + 1 < frame.depth {
|
||||||
|
// Show that some frames were elided
|
||||||
|
formatted.push_str(&format!("{:widest_depth$} …\n", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
formatted.push_str(&format!(
|
||||||
|
"{depth:widest_depth$}: {file_and_line:widest_file_line$} {name}\n"
|
||||||
|
));
|
||||||
|
|
||||||
|
last_depth = frame.depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatted
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shorten a path to a Rust source file from a callstack.
|
||||||
|
///
|
||||||
|
/// Example input:
|
||||||
|
/// * `/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs`
|
||||||
|
/// * `crates/rerun/src/main.rs`
|
||||||
|
/// * `/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs`
|
||||||
|
fn shorten_source_file_path(path: &std::path::Path) -> String {
|
||||||
|
// Look for `src` and strip everything up to it.
|
||||||
|
|
||||||
|
let components: Vec<_> = path.iter().map(|path| path.to_string_lossy()).collect();
|
||||||
|
|
||||||
|
let mut src_idx = None;
|
||||||
|
for (i, c) in components.iter().enumerate() {
|
||||||
|
if c == "src" {
|
||||||
|
src_idx = Some(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the last `src`:
|
||||||
|
if let Some(src_idx) = src_idx {
|
||||||
|
// Before `src` comes the name of the crate - let's include that:
|
||||||
|
let first_index = src_idx.saturating_sub(1);
|
||||||
|
|
||||||
|
let mut output = components[first_index].to_string();
|
||||||
|
for component in &components[first_index + 1..] {
|
||||||
|
output.push('/');
|
||||||
|
output.push_str(component);
|
||||||
|
}
|
||||||
|
output
|
||||||
|
} else {
|
||||||
|
// No `src` directory found - weird!
|
||||||
|
path.display().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shorten_path() {
|
||||||
|
for (before, after) in [
|
||||||
|
("/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs", "tokio-1.24.1/src/runtime/runtime.rs"),
|
||||||
|
("crates/rerun/src/main.rs", "rerun/src/main.rs"),
|
||||||
|
("/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs", "core/src/ops/function.rs"),
|
||||||
|
("/weird/path/file.rs", "/weird/path/file.rs"),
|
||||||
|
]
|
||||||
|
{
|
||||||
|
use std::str::FromStr as _;
|
||||||
|
let before = std::path::PathBuf::from_str(before).unwrap();
|
||||||
|
assert_eq!(shorten_source_file_path(&before), after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -314,6 +314,7 @@ impl Resize {
|
||||||
|
|
||||||
state.store(ui.ctx(), id);
|
state.store(ui.ctx(), id);
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
if ui.ctx().style().debug.show_resize {
|
if ui.ctx().style().debug.show_resize {
|
||||||
ui.ctx().debug_painter().debug_rect(
|
ui.ctx().debug_painter().debug_rect(
|
||||||
Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size),
|
Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size),
|
||||||
|
|
|
||||||
|
|
@ -662,6 +662,7 @@ impl Context {
|
||||||
// This solves the problem of overlapping widgets.
|
// This solves the problem of overlapping widgets.
|
||||||
// Whichever widget is added LAST (=on top) gets the input:
|
// Whichever widget is added LAST (=on top) gets the input:
|
||||||
if interact_rect.is_positive() && sense.interactive() {
|
if interact_rect.is_positive() && sense.interactive() {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
if self.style().debug.show_interactive_widgets {
|
if self.style().debug.show_interactive_widgets {
|
||||||
Self::layer_painter(self, LayerId::debug()).rect(
|
Self::layer_painter(self, LayerId::debug()).rect(
|
||||||
interact_rect,
|
interact_rect,
|
||||||
|
|
@ -670,6 +671,8 @@ impl Context {
|
||||||
Stroke::new(1.0, Color32::YELLOW.additive().linear_multiply(0.05)),
|
Stroke::new(1.0, Color32::YELLOW.additive().linear_multiply(0.05)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
let mut show_blocking_widget = None;
|
let mut show_blocking_widget = None;
|
||||||
|
|
||||||
self.write(|ctx| {
|
self.write(|ctx| {
|
||||||
|
|
@ -690,6 +693,7 @@ impl Context {
|
||||||
// Another interactive widget is covering us at the pointer position,
|
// Another interactive widget is covering us at the pointer position,
|
||||||
// so we aren't hovered.
|
// so we aren't hovered.
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
if ctx.memory.options.style.debug.show_blocking_widget {
|
if ctx.memory.options.style.debug.show_blocking_widget {
|
||||||
// Store the rects to use them outside the write() call to
|
// Store the rects to use them outside the write() call to
|
||||||
// avoid deadlock
|
// avoid deadlock
|
||||||
|
|
@ -705,6 +709,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
if let Some((interact_rect, prev_rect)) = show_blocking_widget {
|
if let Some((interact_rect, prev_rect)) = show_blocking_widget {
|
||||||
Self::layer_painter(self, LayerId::debug()).debug_rect(
|
Self::layer_painter(self, LayerId::debug()).debug_rect(
|
||||||
interact_rect,
|
interact_rect,
|
||||||
|
|
@ -1528,15 +1533,15 @@ impl Context {
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
/// Whether or not to debug widget layout on hover.
|
/// Whether or not to debug widget layout on hover.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub fn debug_on_hover(&self) -> bool {
|
pub fn debug_on_hover(&self) -> bool {
|
||||||
self.options(|opt| opt.style.debug.debug_on_hover)
|
self.options(|opt| opt.style.debug.debug_on_hover)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn on/off whether or not to debug widget layout on hover.
|
/// Turn on/off whether or not to debug widget layout on hover.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
|
pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
|
||||||
let mut style = self.options(|opt| (*opt.style).clone());
|
self.style_mut(|style| style.debug.debug_on_hover = debug_on_hover);
|
||||||
style.debug.debug_on_hover = debug_on_hover;
|
|
||||||
self.set_style(style);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1619,7 +1624,6 @@ impl Context {
|
||||||
/// Show the state of egui, including its input and output.
|
/// Show the state of egui, including its input and output.
|
||||||
pub fn inspection_ui(&self, ui: &mut Ui) {
|
pub fn inspection_ui(&self, ui: &mut Ui) {
|
||||||
use crate::containers::*;
|
use crate::containers::*;
|
||||||
crate::trace!(ui);
|
|
||||||
|
|
||||||
ui.label(format!("Is using pointer: {}", self.is_using_pointer()))
|
ui.label(format!("Is using pointer: {}", self.is_using_pointer()))
|
||||||
.on_hover_text(
|
.on_hover_text(
|
||||||
|
|
|
||||||
|
|
@ -455,6 +455,11 @@ impl Modifiers {
|
||||||
!self.is_none()
|
!self.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn all(&self) -> bool {
|
||||||
|
self.alt && self.ctrl && self.shift && self.command
|
||||||
|
}
|
||||||
|
|
||||||
/// Is shift the only pressed button?
|
/// Is shift the only pressed button?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_only(&self) -> bool {
|
pub fn shift_only(&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ pub(crate) struct FrameState {
|
||||||
|
|
||||||
/// Highlight these widgets the next frame. Write to this.
|
/// Highlight these widgets the next frame. Write to this.
|
||||||
pub(crate) highlight_next_frame: IdSet,
|
pub(crate) highlight_next_frame: IdSet,
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub(crate) has_debug_viewed_this_frame: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FrameState {
|
impl Default for FrameState {
|
||||||
|
|
@ -70,6 +73,9 @@ impl Default for FrameState {
|
||||||
accesskit_state: None,
|
accesskit_state: None,
|
||||||
highlight_this_frame: Default::default(),
|
highlight_this_frame: Default::default(),
|
||||||
highlight_next_frame: Default::default(),
|
highlight_next_frame: Default::default(),
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
has_debug_viewed_this_frame: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +95,9 @@ impl FrameState {
|
||||||
accesskit_state,
|
accesskit_state,
|
||||||
highlight_this_frame,
|
highlight_this_frame,
|
||||||
highlight_next_frame,
|
highlight_next_frame,
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
has_debug_viewed_this_frame,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
used_ids.clear();
|
used_ids.clear();
|
||||||
|
|
@ -99,6 +108,11 @@ impl FrameState {
|
||||||
*scroll_delta = input.scroll_delta;
|
*scroll_delta = input.scroll_delta;
|
||||||
*scroll_target = [None, None];
|
*scroll_target = [None, None];
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
*has_debug_viewed_this_frame = false;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
{
|
{
|
||||||
*accesskit_state = None;
|
*accesskit_state = None;
|
||||||
|
|
|
||||||
|
|
@ -187,24 +187,27 @@ impl GridLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn advance(&mut self, cursor: &mut Rect, _frame_rect: Rect, widget_rect: Rect) {
|
pub(crate) fn advance(&mut self, cursor: &mut Rect, _frame_rect: Rect, widget_rect: Rect) {
|
||||||
let debug_expand_width = self.style.debug.show_expand_width;
|
#[cfg(debug_assertions)]
|
||||||
let debug_expand_height = self.style.debug.show_expand_height;
|
{
|
||||||
if debug_expand_width || debug_expand_height {
|
let debug_expand_width = self.style.debug.show_expand_width;
|
||||||
let rect = widget_rect;
|
let debug_expand_height = self.style.debug.show_expand_height;
|
||||||
let too_wide = rect.width() > self.prev_col_width(self.col);
|
if debug_expand_width || debug_expand_height {
|
||||||
let too_high = rect.height() > self.prev_row_height(self.row);
|
let rect = widget_rect;
|
||||||
|
let too_wide = rect.width() > self.prev_col_width(self.col);
|
||||||
|
let too_high = rect.height() > self.prev_row_height(self.row);
|
||||||
|
|
||||||
if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
|
if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
|
||||||
let painter = self.ctx.debug_painter();
|
let painter = self.ctx.debug_painter();
|
||||||
painter.rect_stroke(rect, 0.0, (1.0, Color32::LIGHT_BLUE));
|
painter.rect_stroke(rect, 0.0, (1.0, Color32::LIGHT_BLUE));
|
||||||
|
|
||||||
let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
|
let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
|
||||||
let paint_line_seg = |a, b| painter.line_segment([a, b], stroke);
|
let paint_line_seg = |a, b| painter.line_segment([a, b], stroke);
|
||||||
|
|
||||||
if debug_expand_width && too_wide {
|
if debug_expand_width && too_wide {
|
||||||
paint_line_seg(rect.left_top(), rect.left_bottom());
|
paint_line_seg(rect.left_top(), rect.left_bottom());
|
||||||
paint_line_seg(rect.left_center(), rect.right_center());
|
paint_line_seg(rect.left_center(), rect.right_center());
|
||||||
paint_line_seg(rect.right_top(), rect.right_bottom());
|
paint_line_seg(rect.right_top(), rect.right_bottom());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -801,6 +801,7 @@ impl Layout {
|
||||||
/// ## Debug stuff
|
/// ## Debug stuff
|
||||||
impl Layout {
|
impl Layout {
|
||||||
/// Shows where the next widget is going to be placed
|
/// Shows where the next widget is going to be placed
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub(crate) fn paint_text_at_cursor(
|
pub(crate) fn paint_text_at_cursor(
|
||||||
&self,
|
&self,
|
||||||
painter: &crate::Painter,
|
painter: &crate::Painter,
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,10 @@ pub mod util;
|
||||||
pub mod widget_text;
|
pub mod widget_text;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
|
#[cfg(feature = "callstack")]
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
mod callstack;
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
pub use accesskit;
|
pub use accesskit;
|
||||||
|
|
||||||
|
|
@ -486,32 +490,6 @@ macro_rules! github_link_file {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Show debug info on hover when [`Context::set_debug_on_hover`] has been turned on.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # egui::__run_test_ui(|ui| {
|
|
||||||
/// // Turn on tracing of widgets
|
|
||||||
/// ui.ctx().set_debug_on_hover(true);
|
|
||||||
///
|
|
||||||
/// /// Show [`std::file`], [`std::line`] and argument on hover
|
|
||||||
/// egui::trace!(ui, "MyWindow");
|
|
||||||
///
|
|
||||||
/// /// Show [`std::file`] and [`std::line`] on hover
|
|
||||||
/// egui::trace!(ui);
|
|
||||||
/// # });
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! trace {
|
|
||||||
($ui: expr) => {{
|
|
||||||
$ui.trace_location(format!("{}:{}", file!(), line!()))
|
|
||||||
}};
|
|
||||||
($ui: expr, $label: expr) => {{
|
|
||||||
$ui.trace_location(format!("{} - {}:{}", $label, file!(), line!()))
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// An assert that is only active when `egui` is compiled with the `extra_asserts` feature
|
/// An assert that is only active when `egui` is compiled with the `extra_asserts` feature
|
||||||
/// or with the `extra_debug_asserts` feature in debug builds.
|
/// or with the `extra_debug_asserts` feature in debug builds.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,7 @@ impl Placer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Placer {
|
impl Placer {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub(crate) fn debug_paint_cursor(&self, painter: &crate::Painter, text: impl ToString) {
|
pub(crate) fn debug_paint_cursor(&self, painter: &crate::Painter, text: impl ToString) {
|
||||||
let stroke = Stroke::new(1.0, Color32::DEBUG_COLOR);
|
let stroke = Stroke::new(1.0, Color32::DEBUG_COLOR);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,9 @@ pub struct Style {
|
||||||
pub animation_time: f32,
|
pub animation_time: f32,
|
||||||
|
|
||||||
/// Options to help debug why egui behaves strangely.
|
/// Options to help debug why egui behaves strangely.
|
||||||
|
///
|
||||||
|
/// Only available in debug builds.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub debug: DebugOptions,
|
pub debug: DebugOptions,
|
||||||
|
|
||||||
/// Show tooltips explaining [`DragValue`]:s etc when hovered.
|
/// Show tooltips explaining [`DragValue`]:s etc when hovered.
|
||||||
|
|
@ -690,12 +693,36 @@ impl WidgetVisuals {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for help debug egui by adding extra visualization
|
/// Options for help debug egui by adding extra visualization
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub struct DebugOptions {
|
pub struct DebugOptions {
|
||||||
/// However over widgets to see their rectangles
|
/// Always show callstack to ui on hover.
|
||||||
|
///
|
||||||
|
/// Useful for figuring out where in the code some UI is being created.
|
||||||
|
///
|
||||||
|
/// Only works in debug builds.
|
||||||
|
/// Requires the `callstack` feature.
|
||||||
|
/// Does not work on web.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub debug_on_hover: bool,
|
pub debug_on_hover: bool,
|
||||||
|
|
||||||
|
/// Show callstack for the current widget on hover if all modifier keys are pressed down.
|
||||||
|
///
|
||||||
|
/// Useful for figuring out where in the code some UI is being created.
|
||||||
|
///
|
||||||
|
/// Only works in debug builds.
|
||||||
|
/// Requires the `callstack` feature.
|
||||||
|
/// Does not work on web.
|
||||||
|
///
|
||||||
|
/// Default is `true` in debug builds, on native, if the `callstack` feature is enabled.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub debug_on_hover_with_all_modifiers: bool,
|
||||||
|
|
||||||
|
/// If we show the hover ui, include where the next widget is placed.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub hover_shows_next: bool,
|
||||||
|
|
||||||
/// Show which widgets make their parent wider
|
/// Show which widgets make their parent wider
|
||||||
pub show_expand_width: bool,
|
pub show_expand_width: bool,
|
||||||
|
|
||||||
|
|
@ -711,6 +738,23 @@ pub struct DebugOptions {
|
||||||
pub show_blocking_widget: bool,
|
pub show_blocking_widget: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
impl Default for DebugOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
debug_on_hover: false,
|
||||||
|
debug_on_hover_with_all_modifiers: cfg!(feature = "callstack")
|
||||||
|
&& !cfg!(target_arch = "wasm32"),
|
||||||
|
hover_shows_next: false,
|
||||||
|
show_expand_width: false,
|
||||||
|
show_expand_height: false,
|
||||||
|
show_resize: false,
|
||||||
|
show_interactive_widgets: false,
|
||||||
|
show_blocking_widget: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// The default text styles of the default egui theme.
|
/// The default text styles of the default egui theme.
|
||||||
|
|
@ -739,6 +783,7 @@ impl Default for Style {
|
||||||
interaction: Interaction::default(),
|
interaction: Interaction::default(),
|
||||||
visuals: Visuals::default(),
|
visuals: Visuals::default(),
|
||||||
animation_time: 1.0 / 12.0,
|
animation_time: 1.0 / 12.0,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
debug: Default::default(),
|
debug: Default::default(),
|
||||||
explanation_tooltips: false,
|
explanation_tooltips: false,
|
||||||
}
|
}
|
||||||
|
|
@ -993,6 +1038,7 @@ impl Style {
|
||||||
interaction,
|
interaction,
|
||||||
visuals,
|
visuals,
|
||||||
animation_time,
|
animation_time,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
debug,
|
debug,
|
||||||
explanation_tooltips,
|
explanation_tooltips,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
@ -1055,6 +1101,8 @@ impl Style {
|
||||||
ui.collapsing("📏 Spacing", |ui| spacing.ui(ui));
|
ui.collapsing("📏 Spacing", |ui| spacing.ui(ui));
|
||||||
ui.collapsing("☝ Interaction", |ui| interaction.ui(ui));
|
ui.collapsing("☝ Interaction", |ui| interaction.ui(ui));
|
||||||
ui.collapsing("🎨 Visuals", |ui| visuals.ui(ui));
|
ui.collapsing("🎨 Visuals", |ui| visuals.ui(ui));
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
ui.collapsing("🐛 Debug", |ui| debug.ui(ui));
|
ui.collapsing("🐛 Debug", |ui| debug.ui(ui));
|
||||||
|
|
||||||
ui.checkbox(explanation_tooltips, "Explanation tooltips")
|
ui.checkbox(explanation_tooltips, "Explanation tooltips")
|
||||||
|
|
@ -1477,10 +1525,13 @@ impl Visuals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
impl DebugOptions {
|
impl DebugOptions {
|
||||||
pub fn ui(&mut self, ui: &mut crate::Ui) {
|
pub fn ui(&mut self, ui: &mut crate::Ui) {
|
||||||
let Self {
|
let Self {
|
||||||
debug_on_hover,
|
debug_on_hover,
|
||||||
|
debug_on_hover_with_all_modifiers,
|
||||||
|
hover_shows_next,
|
||||||
show_expand_width,
|
show_expand_width,
|
||||||
show_expand_height,
|
show_expand_height,
|
||||||
show_resize,
|
show_resize,
|
||||||
|
|
@ -1488,7 +1539,16 @@ impl DebugOptions {
|
||||||
show_blocking_widget,
|
show_blocking_widget,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.checkbox(debug_on_hover, "Show debug info on hover");
|
{
|
||||||
|
ui.checkbox(debug_on_hover, "Show widget info on hover.");
|
||||||
|
ui.checkbox(
|
||||||
|
debug_on_hover_with_all_modifiers,
|
||||||
|
"Show widget info on hover if holding all modifier keys",
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.checkbox(hover_shows_next, "Show next widget placement on hover");
|
||||||
|
}
|
||||||
|
|
||||||
ui.checkbox(
|
ui.checkbox(
|
||||||
show_expand_width,
|
show_expand_width,
|
||||||
"Show which widgets make their parent wider",
|
"Show which widgets make their parent wider",
|
||||||
|
|
|
||||||
|
|
@ -738,43 +738,41 @@ impl Ui {
|
||||||
/// # });
|
/// # });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn allocate_space(&mut self, desired_size: Vec2) -> (Id, Rect) {
|
pub fn allocate_space(&mut self, desired_size: Vec2) -> (Id, Rect) {
|
||||||
// For debug rendering
|
#[cfg(debug_assertions)]
|
||||||
let original_available = self.available_size_before_wrap();
|
let original_available = self.available_size_before_wrap();
|
||||||
let too_wide = desired_size.x > original_available.x;
|
|
||||||
let too_high = desired_size.y > original_available.y;
|
|
||||||
|
|
||||||
let rect = self.allocate_space_impl(desired_size);
|
let rect = self.allocate_space_impl(desired_size);
|
||||||
|
|
||||||
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
|
#[cfg(debug_assertions)]
|
||||||
let painter = self.ctx().debug_painter();
|
{
|
||||||
painter.rect_stroke(rect, 4.0, (1.0, Color32::LIGHT_BLUE));
|
let too_wide = desired_size.x > original_available.x;
|
||||||
self.placer.debug_paint_cursor(&painter, "next");
|
let too_high = desired_size.y > original_available.y;
|
||||||
}
|
|
||||||
|
|
||||||
let debug_expand_width = self.style().debug.show_expand_width;
|
let debug_expand_width = self.style().debug.show_expand_width;
|
||||||
let debug_expand_height = self.style().debug.show_expand_height;
|
let debug_expand_height = self.style().debug.show_expand_height;
|
||||||
|
|
||||||
if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
|
if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
|
||||||
self.painter
|
self.painter
|
||||||
.rect_stroke(rect, 0.0, (1.0, Color32::LIGHT_BLUE));
|
.rect_stroke(rect, 0.0, (1.0, Color32::LIGHT_BLUE));
|
||||||
|
|
||||||
let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
|
let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
|
||||||
let paint_line_seg = |a, b| self.painter().line_segment([a, b], stroke);
|
let paint_line_seg = |a, b| self.painter().line_segment([a, b], stroke);
|
||||||
|
|
||||||
if debug_expand_width && too_wide {
|
if debug_expand_width && too_wide {
|
||||||
paint_line_seg(rect.left_top(), rect.left_bottom());
|
paint_line_seg(rect.left_top(), rect.left_bottom());
|
||||||
paint_line_seg(rect.left_center(), rect.right_center());
|
paint_line_seg(rect.left_center(), rect.right_center());
|
||||||
paint_line_seg(
|
paint_line_seg(
|
||||||
pos2(rect.left() + original_available.x, rect.top()),
|
pos2(rect.left() + original_available.x, rect.top()),
|
||||||
pos2(rect.left() + original_available.x, rect.bottom()),
|
pos2(rect.left() + original_available.x, rect.bottom()),
|
||||||
);
|
);
|
||||||
paint_line_seg(rect.right_top(), rect.right_bottom());
|
paint_line_seg(rect.right_top(), rect.right_bottom());
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug_expand_height && too_high {
|
if debug_expand_height && too_high {
|
||||||
paint_line_seg(rect.left_top(), rect.right_top());
|
paint_line_seg(rect.left_top(), rect.right_top());
|
||||||
paint_line_seg(rect.center_top(), rect.center_bottom());
|
paint_line_seg(rect.center_top(), rect.center_bottom());
|
||||||
paint_line_seg(rect.left_bottom(), rect.right_bottom());
|
paint_line_seg(rect.left_bottom(), rect.right_bottom());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -795,6 +793,8 @@ impl Ui {
|
||||||
self.placer
|
self.placer
|
||||||
.advance_after_rects(frame_rect, widget_rect, item_spacing);
|
.advance_after_rects(frame_rect, widget_rect, item_spacing);
|
||||||
|
|
||||||
|
register_rect(self, widget_rect);
|
||||||
|
|
||||||
widget_rect
|
widget_rect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -803,6 +803,7 @@ impl Ui {
|
||||||
/// Ignore the layout of the [`Ui`]: just put my widget here!
|
/// Ignore the layout of the [`Ui`]: just put my widget here!
|
||||||
/// The layout cursor will advance to past this `rect`.
|
/// The layout cursor will advance to past this `rect`.
|
||||||
pub fn allocate_rect(&mut self, rect: Rect, sense: Sense) -> Response {
|
pub fn allocate_rect(&mut self, rect: Rect, sense: Sense) -> Response {
|
||||||
|
register_rect(self, rect);
|
||||||
let id = self.advance_cursor_after_rect(rect);
|
let id = self.advance_cursor_after_rect(rect);
|
||||||
self.interact(rect, id, sense)
|
self.interact(rect, id, sense)
|
||||||
}
|
}
|
||||||
|
|
@ -813,12 +814,6 @@ impl Ui {
|
||||||
let item_spacing = self.spacing().item_spacing;
|
let item_spacing = self.spacing().item_spacing;
|
||||||
self.placer.advance_after_rects(rect, rect, item_spacing);
|
self.placer.advance_after_rects(rect, rect, item_spacing);
|
||||||
|
|
||||||
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
|
|
||||||
let painter = self.ctx().debug_painter();
|
|
||||||
painter.rect_stroke(rect, 4.0, (1.0, Color32::LIGHT_BLUE));
|
|
||||||
self.placer.debug_paint_cursor(&painter, "next");
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = Id::new(self.next_auto_id_source);
|
let id = Id::new(self.next_auto_id_source);
|
||||||
self.next_auto_id_source = self.next_auto_id_source.wrapping_add(1);
|
self.next_auto_id_source = self.next_auto_id_source.wrapping_add(1);
|
||||||
id
|
id
|
||||||
|
|
@ -896,13 +891,6 @@ impl Ui {
|
||||||
self.placer
|
self.placer
|
||||||
.advance_after_rects(final_child_rect, final_child_rect, item_spacing);
|
.advance_after_rects(final_child_rect, final_child_rect, item_spacing);
|
||||||
|
|
||||||
if self.style().debug.debug_on_hover && self.rect_contains_pointer(final_child_rect) {
|
|
||||||
let painter = self.ctx().debug_painter();
|
|
||||||
painter.rect_stroke(frame_rect, 4.0, (1.0, Color32::LIGHT_BLUE));
|
|
||||||
painter.rect_stroke(final_child_rect, 4.0, (1.0, Color32::LIGHT_BLUE));
|
|
||||||
self.placer.debug_paint_cursor(&painter, "next");
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = self.interact(final_child_rect, child_ui.id, Sense::hover());
|
let response = self.interact(final_child_rect, child_ui.id, Sense::hover());
|
||||||
InnerResponse::new(ret, response)
|
InnerResponse::new(ret, response)
|
||||||
}
|
}
|
||||||
|
|
@ -1793,10 +1781,7 @@ impl Ui {
|
||||||
let mut child_rect = self.placer.available_rect_before_wrap();
|
let mut child_rect = self.placer.available_rect_before_wrap();
|
||||||
child_rect.min.x += indent;
|
child_rect.min.x += indent;
|
||||||
|
|
||||||
let mut child_ui = Self {
|
let mut child_ui = self.child_ui_with_id_source(child_rect, *self.layout(), id_source);
|
||||||
id: self.id.with(id_source),
|
|
||||||
..self.child_ui(child_rect, *self.layout())
|
|
||||||
};
|
|
||||||
let ret = add_contents(&mut child_ui);
|
let ret = add_contents(&mut child_ui);
|
||||||
|
|
||||||
let left_vline = self.visuals().indent_has_left_vline;
|
let left_vline = self.visuals().indent_has_left_vline;
|
||||||
|
|
@ -2024,12 +2009,6 @@ impl Ui {
|
||||||
let item_spacing = self.spacing().item_spacing;
|
let item_spacing = self.spacing().item_spacing;
|
||||||
self.placer.advance_after_rects(rect, rect, item_spacing);
|
self.placer.advance_after_rects(rect, rect, item_spacing);
|
||||||
|
|
||||||
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
|
|
||||||
let painter = self.ctx().debug_painter();
|
|
||||||
painter.rect_stroke(rect, 4.0, (1.0, Color32::LIGHT_BLUE));
|
|
||||||
self.placer.debug_paint_cursor(&painter, "next");
|
|
||||||
}
|
|
||||||
|
|
||||||
InnerResponse::new(inner, self.interact(rect, child_ui.id, Sense::hover()))
|
InnerResponse::new(inner, self.interact(rect, child_ui.id, Sense::hover()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2215,21 +2194,121 @@ impl Ui {
|
||||||
/// # Debug stuff
|
/// # Debug stuff
|
||||||
impl Ui {
|
impl Ui {
|
||||||
/// Shows where the next widget is going to be placed
|
/// Shows where the next widget is going to be placed
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub fn debug_paint_cursor(&self) {
|
pub fn debug_paint_cursor(&self) {
|
||||||
self.placer.debug_paint_cursor(&self.painter, "next");
|
self.placer.debug_paint_cursor(&self.painter, "next");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Shows the given text where the next widget is to be placed
|
#[cfg(debug_assertions)]
|
||||||
/// if when [`Context::set_debug_on_hover`] has been turned on and the mouse is hovering the Ui.
|
impl Drop for Ui {
|
||||||
pub fn trace_location(&self, text: impl ToString) {
|
fn drop(&mut self) {
|
||||||
let rect = self.max_rect();
|
register_rect(self, self.min_rect());
|
||||||
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
|
}
|
||||||
self.placer
|
}
|
||||||
.debug_paint_cursor(&self.ctx().debug_painter(), text);
|
|
||||||
|
/// Show this rectangle to the user if certain debug options are set.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
fn register_rect(ui: &Ui, rect: Rect) {
|
||||||
|
let debug = ui.style().debug;
|
||||||
|
|
||||||
|
let show_callstacks = debug.debug_on_hover
|
||||||
|
|| debug.debug_on_hover_with_all_modifiers && ui.input(|i| i.modifiers.all());
|
||||||
|
|
||||||
|
if !show_callstacks {
|
||||||
|
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:
|
||||||
|
{
|
||||||
|
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 galley = painter.layout_no_wrap(text, font_id, Color32::WHITE);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
if ui.input(|i| i.pointer.any_click()) {
|
||||||
|
ui.ctx().copy_text(callstack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
fn register_rect(_ui: &Ui, _rect: Rect) {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ui_impl_send_sync() {
|
fn ui_impl_send_sync() {
|
||||||
fn assert_send_sync<T: Send + Sync>() {}
|
fn assert_send_sync<T: Send + Sync>() {}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ chrono = { version = "0.4", default-features = false, features = [
|
||||||
] }
|
] }
|
||||||
eframe = { version = "0.22.0", path = "../eframe", default-features = false }
|
eframe = { version = "0.22.0", path = "../eframe", default-features = false }
|
||||||
egui = { version = "0.22.0", path = "../egui", features = [
|
egui = { version = "0.22.0", path = "../egui", features = [
|
||||||
|
"callstack",
|
||||||
"extra_debug_asserts",
|
"extra_debug_asserts",
|
||||||
"log",
|
"log",
|
||||||
] }
|
] }
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,6 @@ impl BackendPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||||
egui::trace!(ui);
|
|
||||||
|
|
||||||
self.integration_ui(ui, frame);
|
self.integration_ui(ui, frame);
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
@ -101,11 +99,9 @@ impl BackendPanel {
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
{
|
#[cfg(debug_assertions)]
|
||||||
let mut debug_on_hover = ui.ctx().debug_on_hover();
|
if ui.ctx().style().debug.debug_on_hover_with_all_modifiers {
|
||||||
ui.checkbox(&mut debug_on_hover, "🐛 Debug on hover")
|
ui.label("Press down all modifiers and hover a widget to see a callstack for it");
|
||||||
.on_hover_text("Show structure of the ui when you hover with the mouse");
|
|
||||||
ui.ctx().set_debug_on_hover(debug_on_hover);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
|
|
||||||
|
|
@ -164,21 +164,21 @@ pub struct WrapApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WrapApp {
|
impl WrapApp {
|
||||||
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
egui_extras::install_image_loaders(&_cc.egui_ctx);
|
egui_extras::install_image_loaders(&cc.egui_ctx);
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut slf = Self {
|
let mut slf = Self {
|
||||||
state: State::default(),
|
state: State::default(),
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
||||||
custom3d: crate::apps::Custom3d::new(_cc),
|
custom3d: crate::apps::Custom3d::new(cc),
|
||||||
|
|
||||||
dropped_files: Default::default(),
|
dropped_files: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
if let Some(storage) = _cc.storage {
|
if let Some(storage) = cc.storage {
|
||||||
if let Some(state) = eframe::get_value(storage, eframe::APP_KEY) {
|
if let Some(state) = eframe::get_value(storage, eframe::APP_KEY) {
|
||||||
slf.state = state;
|
slf.state = state;
|
||||||
}
|
}
|
||||||
|
|
@ -263,7 +263,6 @@ impl eframe::App for WrapApp {
|
||||||
|
|
||||||
let mut cmd = Command::Nothing;
|
let mut cmd = Command::Nothing;
|
||||||
egui::TopBottomPanel::top("wrap_app_top_bar").show(ctx, |ui| {
|
egui::TopBottomPanel::top("wrap_app_top_bar").show(ctx, |ui| {
|
||||||
egui::trace!(ui);
|
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
ui.visuals_mut().button_frame = false;
|
ui.visuals_mut().button_frame = false;
|
||||||
self.bar_contents(ui, frame, &mut cmd);
|
self.bar_contents(ui, frame, &mut cmd);
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,6 @@ impl DemoWindows {
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.default_width(150.0)
|
.default_width(150.0)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
egui::trace!(ui);
|
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("✒ egui demos");
|
ui.heading("✒ egui demos");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue