egui/
introspection.rs

1//! Showing UI:s for egui/epaint types.
2use crate::{
3    epaint, memory, pos2, remap_clamp, vec2, Color32, CursorIcon, FontFamily, FontId, Label, Mesh,
4    NumExt, Rect, Response, Sense, Shape, Slider, TextStyle, TextWrapMode, Ui, Widget,
5};
6
7pub fn font_family_ui(ui: &mut Ui, font_family: &mut FontFamily) {
8    let families = ui.fonts(|f| f.families());
9    ui.horizontal(|ui| {
10        for alternative in families {
11            let text = alternative.to_string();
12            ui.radio_value(font_family, alternative, text);
13        }
14    });
15}
16
17pub fn font_id_ui(ui: &mut Ui, font_id: &mut FontId) {
18    let families = ui.fonts(|f| f.families());
19    ui.horizontal(|ui| {
20        ui.add(Slider::new(&mut font_id.size, 4.0..=40.0).max_decimals(1));
21        for alternative in families {
22            let text = alternative.to_string();
23            ui.radio_value(&mut font_id.family, alternative, text);
24        }
25    });
26}
27
28// Show font texture in demo Ui
29pub(crate) fn font_texture_ui(ui: &mut Ui, [width, height]: [usize; 2]) -> Response {
30    ui.vertical(|ui| {
31        let color = if ui.visuals().dark_mode {
32            Color32::WHITE
33        } else {
34            Color32::BLACK
35        };
36
37        ui.label(format!("Texture size: {width} x {height} (hover to zoom)"));
38        if width <= 1 || height <= 1 {
39            return;
40        }
41        let mut size = vec2(width as f32, height as f32);
42        if size.x > ui.available_width() {
43            size *= ui.available_width() / size.x;
44        }
45        let (rect, response) = ui.allocate_at_least(size, Sense::hover());
46        let mut mesh = Mesh::default();
47        mesh.add_rect_with_uv(rect, [pos2(0.0, 0.0), pos2(1.0, 1.0)].into(), color);
48        ui.painter().add(Shape::mesh(mesh));
49
50        let (tex_w, tex_h) = (width as f32, height as f32);
51
52        response
53            .on_hover_cursor(CursorIcon::ZoomIn)
54            .on_hover_ui_at_pointer(|ui| {
55                if let Some(pos) = ui.ctx().pointer_latest_pos() {
56                    let (_id, zoom_rect) = ui.allocate_space(vec2(128.0, 128.0));
57                    let u = remap_clamp(pos.x, rect.x_range(), 0.0..=tex_w);
58                    let v = remap_clamp(pos.y, rect.y_range(), 0.0..=tex_h);
59
60                    let texel_radius = 32.0;
61                    let u = u.at_least(texel_radius).at_most(tex_w - texel_radius);
62                    let v = v.at_least(texel_radius).at_most(tex_h - texel_radius);
63
64                    let uv_rect = Rect::from_min_max(
65                        pos2((u - texel_radius) / tex_w, (v - texel_radius) / tex_h),
66                        pos2((u + texel_radius) / tex_w, (v + texel_radius) / tex_h),
67                    );
68                    let mut mesh = Mesh::default();
69                    mesh.add_rect_with_uv(zoom_rect, uv_rect, color);
70                    ui.painter().add(Shape::mesh(mesh));
71                }
72            });
73    })
74    .response
75}
76
77impl Widget for &epaint::stats::PaintStats {
78    fn ui(self, ui: &mut Ui) -> Response {
79        ui.vertical(|ui| {
80            ui.label(
81                "egui generates intermediate level shapes like circles and text. \
82                These are later tessellated into triangles.",
83            );
84            ui.add_space(10.0);
85
86            ui.style_mut().override_text_style = Some(TextStyle::Monospace);
87
88            let epaint::stats::PaintStats {
89                shapes,
90                shape_text,
91                shape_path,
92                shape_mesh,
93                shape_vec,
94                num_callbacks,
95                text_shape_vertices,
96                text_shape_indices,
97                clipped_primitives,
98                vertices,
99                indices,
100            } = self;
101
102            ui.label("Intermediate:");
103            label(ui, shapes, "shapes").on_hover_text("Boxes, circles, etc");
104            ui.horizontal(|ui| {
105                label(ui, shape_text, "text");
106                ui.small("(mostly cached)");
107            });
108            label(ui, shape_path, "paths");
109            label(ui, shape_mesh, "nested meshes");
110            label(ui, shape_vec, "nested shapes");
111            ui.label(format!("{num_callbacks:6} callbacks"));
112            ui.add_space(10.0);
113
114            ui.label("Text shapes:");
115            label(ui, text_shape_vertices, "vertices");
116            label(ui, text_shape_indices, "indices")
117                .on_hover_text("Three 32-bit indices per triangles");
118            ui.add_space(10.0);
119
120            ui.label("Tessellated (and culled):");
121            label(ui, clipped_primitives, "primitives lists")
122                .on_hover_text("Number of separate clip rectangles");
123            label(ui, vertices, "vertices");
124            label(ui, indices, "indices").on_hover_text("Three 32-bit indices per triangles");
125            ui.add_space(10.0);
126
127            // ui.label("Total:");
128            // ui.label(self.total().format(""));
129        })
130        .response
131    }
132}
133
134fn label(ui: &mut Ui, alloc_info: &epaint::stats::AllocInfo, what: &str) -> Response {
135    ui.add(Label::new(alloc_info.format(what)).wrap_mode(TextWrapMode::Extend))
136}
137
138impl Widget for &mut epaint::TessellationOptions {
139    fn ui(self, ui: &mut Ui) -> Response {
140        ui.vertical(|ui| {
141            let epaint::TessellationOptions {
142                feathering,
143                feathering_size_in_pixels,
144                coarse_tessellation_culling,
145                prerasterized_discs,
146                round_text_to_pixels,
147                debug_paint_clip_rects,
148                debug_paint_text_rects,
149                debug_ignore_clip_rects,
150                bezier_tolerance,
151                epsilon: _,
152                parallel_tessellation,
153                validate_meshes,
154            } = self;
155
156            ui.horizontal(|ui| {
157                ui.checkbox(feathering, "Feathering (antialias)")
158                    .on_hover_text("Apply feathering to smooth out the edges of shapes. Turn off for small performance gain.");
159
160                if *feathering {
161                    ui.add(crate::DragValue::new(feathering_size_in_pixels).range(0.0..=10.0).speed(0.1).suffix(" px"));
162                }
163            });
164
165            ui.checkbox(prerasterized_discs, "Speed up filled circles with pre-rasterization");
166
167            ui.horizontal(|ui| {
168                ui.label("Spline tolerance");
169                let speed = 0.01 * *bezier_tolerance;
170                ui.add(
171                    crate::DragValue::new(bezier_tolerance).range(0.0001..=10.0)
172                        .speed(speed)
173                );
174            });
175
176            ui.add_enabled(epaint::HAS_RAYON, crate::Checkbox::new(parallel_tessellation, "Parallelize tessellation")
177                ).on_hover_text("Only available if epaint was compiled with the rayon feature")
178                .on_disabled_hover_text("epaint was not compiled with the rayon feature");
179
180            ui.checkbox(validate_meshes, "Validate meshes").on_hover_text("Check that incoming meshes are valid, i.e. that all indices are in range, etc.");
181
182            ui.collapsing("Debug", |ui| {
183                ui.checkbox(
184                    coarse_tessellation_culling,
185                    "Do coarse culling in the tessellator",
186                );
187                ui.checkbox(round_text_to_pixels, "Align text positions to pixel grid")
188                    .on_hover_text("Most text already is, so don't expect to see a large change.");
189
190                ui.checkbox(debug_ignore_clip_rects, "Ignore clip rectangles");
191                ui.checkbox(debug_paint_clip_rects, "Paint clip rectangles");
192                ui.checkbox(debug_paint_text_rects, "Paint text bounds");
193            });
194        })
195        .response
196    }
197}
198
199impl Widget for &memory::InteractionState {
200    fn ui(self, ui: &mut Ui) -> Response {
201        let memory::InteractionState {
202            potential_click_id,
203            potential_drag_id,
204        } = self;
205
206        ui.vertical(|ui| {
207            ui.label(format!("potential_click_id: {potential_click_id:?}"));
208            ui.label(format!("potential_drag_id: {potential_drag_id:?}"));
209        })
210        .response
211    }
212}