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                round_line_segments_to_pixels,
148                round_rects_to_pixels,
149                debug_paint_clip_rects,
150                debug_paint_text_rects,
151                debug_ignore_clip_rects,
152                bezier_tolerance,
153                epsilon: _,
154                parallel_tessellation,
155                validate_meshes,
156            } = self;
157
158            ui.horizontal(|ui| {
159                ui.checkbox(feathering, "Feathering (antialias)")
160                    .on_hover_text("Apply feathering to smooth out the edges of shapes. Turn off for small performance gain.");
161
162                if *feathering {
163                    ui.add(crate::DragValue::new(feathering_size_in_pixels).range(0.0..=10.0).speed(0.025).suffix(" px"));
164                }
165            });
166
167            ui.checkbox(prerasterized_discs, "Speed up filled circles with pre-rasterization");
168
169            ui.horizontal(|ui| {
170                ui.label("Spline tolerance");
171                let speed = 0.01 * *bezier_tolerance;
172                ui.add(
173                    crate::DragValue::new(bezier_tolerance).range(0.0001..=10.0)
174                        .speed(speed)
175                );
176            });
177
178            ui.add_enabled(epaint::HAS_RAYON, crate::Checkbox::new(parallel_tessellation, "Parallelize tessellation")
179                ).on_hover_text("Only available if epaint was compiled with the rayon feature")
180                .on_disabled_hover_text("epaint was not compiled with the rayon feature");
181
182            ui.checkbox(validate_meshes, "Validate meshes").on_hover_text("Check that incoming meshes are valid, i.e. that all indices are in range, etc.");
183
184            ui.collapsing("Align to pixel grid", |ui| {
185                ui.checkbox(round_text_to_pixels, "Text")
186                    .on_hover_text("Most text already is, so don't expect to see a large change.");
187
188                ui.checkbox(round_line_segments_to_pixels, "Line segments")
189                    .on_hover_text("Makes line segments appear crisp on any display.");
190
191                ui.checkbox(round_rects_to_pixels, "Rectangles")
192                    .on_hover_text("Makes line segments appear crisp on any display.");
193            });
194
195            ui.collapsing("Debug", |ui| {
196                ui.checkbox(
197                    coarse_tessellation_culling,
198                    "Do coarse culling in the tessellator",
199                );
200
201                ui.checkbox(debug_ignore_clip_rects, "Ignore clip rectangles");
202                ui.checkbox(debug_paint_clip_rects, "Paint clip rectangles");
203                ui.checkbox(debug_paint_text_rects, "Paint text bounds");
204            });
205        })
206        .response
207    }
208}
209
210impl Widget for &memory::InteractionState {
211    fn ui(self, ui: &mut Ui) -> Response {
212        let memory::InteractionState {
213            potential_click_id,
214            potential_drag_id,
215        } = self;
216
217        ui.vertical(|ui| {
218            ui.label(format!("potential_click_id: {potential_click_id:?}"));
219            ui.label(format!("potential_drag_id: {potential_drag_id:?}"));
220        })
221        .response
222    }
223}