epaint/shapes/
paint_callback.rs

1use std::{any::Any, sync::Arc};
2
3use crate::*;
4
5/// Information passed along with [`PaintCallback`] ([`Shape::Callback`]).
6pub struct PaintCallbackInfo {
7    /// Viewport in points.
8    ///
9    /// This specifies where on the screen to paint, and the borders of this
10    /// Rect is the [-1, +1] of the Normalized Device Coordinates.
11    ///
12    /// Note than only a portion of this may be visible due to [`Self::clip_rect`].
13    ///
14    /// This comes from [`PaintCallback::rect`].
15    pub viewport: Rect,
16
17    /// Clip rectangle in points.
18    pub clip_rect: Rect,
19
20    /// Pixels per point.
21    pub pixels_per_point: f32,
22
23    /// Full size of the screen, in pixels.
24    pub screen_size_px: [u32; 2],
25}
26
27#[test]
28fn test_viewport_rounding() {
29    for i in 0..=10_000 {
30        // Two adjacent viewports should never overlap:
31        let x = i as f32 / 97.0;
32        let left = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_max_x(x);
33        let right = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_min_x(x);
34
35        for pixels_per_point in [0.618, 1.0, std::f32::consts::PI] {
36            let left = ViewportInPixels::from_points(&left, pixels_per_point, [100, 100]);
37            let right = ViewportInPixels::from_points(&right, pixels_per_point, [100, 100]);
38            assert_eq!(left.left_px + left.width_px, right.left_px);
39        }
40    }
41}
42
43impl PaintCallbackInfo {
44    /// The viewport rectangle. This is what you would use in e.g. `glViewport`.
45    pub fn viewport_in_pixels(&self) -> ViewportInPixels {
46        ViewportInPixels::from_points(&self.viewport, self.pixels_per_point, self.screen_size_px)
47    }
48
49    /// The "scissor" or "clip" rectangle. This is what you would use in e.g. `glScissor`.
50    pub fn clip_rect_in_pixels(&self) -> ViewportInPixels {
51        ViewportInPixels::from_points(&self.clip_rect, self.pixels_per_point, self.screen_size_px)
52    }
53}
54
55/// If you want to paint some 3D shapes inside an egui region, you can use this.
56///
57/// This is advanced usage, and is backend specific.
58#[derive(Clone)]
59pub struct PaintCallback {
60    /// Where to paint.
61    ///
62    /// This will become [`PaintCallbackInfo::viewport`].
63    pub rect: Rect,
64
65    /// Paint something custom (e.g. 3D stuff).
66    ///
67    /// The concrete value of `callback` depends on the rendering backend used. For instance, the
68    /// `glow` backend requires that callback be an `egui_glow::CallbackFn` while the `wgpu`
69    /// backend requires a `egui_wgpu::Callback`.
70    ///
71    /// If the type cannot be downcast to the type expected by the current backend the callback
72    /// will not be drawn.
73    ///
74    /// The rendering backend is responsible for first setting the active viewport to
75    /// [`Self::rect`].
76    ///
77    /// The rendering backend is also responsible for restoring any state, such as the bound shader
78    /// program, vertex array, etc.
79    ///
80    /// Shape has to be clone, therefore this has to be an `Arc` instead of a `Box`.
81    pub callback: Arc<dyn Any + Send + Sync>,
82}
83
84impl std::fmt::Debug for PaintCallback {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        f.debug_struct("CustomShape")
87            .field("rect", &self.rect)
88            .finish_non_exhaustive()
89    }
90}
91
92impl std::cmp::PartialEq for PaintCallback {
93    fn eq(&self, other: &Self) -> bool {
94        self.rect.eq(&other.rect) && Arc::ptr_eq(&self.callback, &other.callback)
95    }
96}
97
98impl From<PaintCallback> for Shape {
99    #[inline(always)]
100    fn from(shape: PaintCallback) -> Self {
101        Self::Callback(shape)
102    }
103}