Skip to main content

epaint/shapes/
paint_callback.rs

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