Skip to main content

gpui/elements/
surface.rs

1use crate::{
2    App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
3    ObjectFit, Pixels, Style, StyleRefinement, Styled, Window,
4};
5#[cfg(any(target_os = "linux", target_os = "freebsd"))]
6use crate::{DevicePixels, Size};
7#[cfg(target_os = "macos")]
8use core_video::pixel_buffer::CVPixelBuffer;
9use refineable::Refineable;
10#[cfg(any(target_os = "linux", target_os = "freebsd"))]
11use std::sync::Arc;
12
13/// A source of a surface's content.
14pub enum SurfaceSource {
15    /// A macOS image buffer from CoreVideo
16    #[cfg(target_os = "macos")]
17    Surface(CVPixelBuffer),
18    /// A GPU texture handle (type-erased to avoid depending on wgpu)
19    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
20    Texture {
21        /// The GPU texture, type-erased (expected to be `Arc<wgpu::Texture>`)
22        texture: Arc<dyn std::any::Any + Send + Sync>,
23        /// Dimensions of the texture in device pixels
24        size: Size<DevicePixels>,
25    },
26}
27
28impl Clone for SurfaceSource {
29    fn clone(&self) -> Self {
30        match *self {
31            #[cfg(target_os = "macos")]
32            SurfaceSource::Surface(ref buf) => SurfaceSource::Surface(buf.clone()),
33            #[cfg(any(target_os = "linux", target_os = "freebsd"))]
34            SurfaceSource::Texture { ref texture, size } => SurfaceSource::Texture {
35                texture: Arc::clone(texture),
36                size,
37            },
38        }
39    }
40}
41
42impl std::fmt::Debug for SurfaceSource {
43    fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        match *self {
45            #[cfg(target_os = "macos")]
46            SurfaceSource::Surface(ref buf) => _f.debug_tuple("Surface").field(buf).finish(),
47            #[cfg(any(target_os = "linux", target_os = "freebsd"))]
48            SurfaceSource::Texture { size, .. } => _f
49                .debug_struct("Texture")
50                .field("size", &size)
51                .finish_non_exhaustive(),
52        }
53    }
54}
55
56#[cfg(target_os = "macos")]
57impl From<CVPixelBuffer> for SurfaceSource {
58    fn from(value: CVPixelBuffer) -> Self {
59        SurfaceSource::Surface(value)
60    }
61}
62
63/// A surface element.
64pub struct Surface {
65    source: SurfaceSource,
66    object_fit: ObjectFit,
67    style: StyleRefinement,
68}
69
70/// Create a new surface element.
71pub fn surface(source: impl Into<SurfaceSource>) -> Surface {
72    Surface {
73        source: source.into(),
74        object_fit: ObjectFit::Contain,
75        style: Default::default(),
76    }
77}
78
79impl Surface {
80    /// Set the object fit for the image.
81    pub fn object_fit(mut self, object_fit: ObjectFit) -> Self {
82        self.object_fit = object_fit;
83        self
84    }
85}
86
87impl Element for Surface {
88    type RequestLayoutState = ();
89    type PrepaintState = ();
90
91    fn id(&self) -> Option<ElementId> {
92        None
93    }
94
95    fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
96        None
97    }
98
99    fn request_layout(
100        &mut self,
101        _global_id: Option<&GlobalElementId>,
102        _inspector_id: Option<&InspectorElementId>,
103        window: &mut Window,
104        cx: &mut App,
105    ) -> (LayoutId, Self::RequestLayoutState) {
106        let mut style = Style::default();
107        style.refine(&self.style);
108        let layout_id = window.request_layout(style, [], cx);
109        (layout_id, ())
110    }
111
112    fn prepaint(
113        &mut self,
114        _global_id: Option<&GlobalElementId>,
115        _inspector_id: Option<&InspectorElementId>,
116        _bounds: Bounds<Pixels>,
117        _request_layout: &mut Self::RequestLayoutState,
118        _window: &mut Window,
119        _cx: &mut App,
120    ) -> Self::PrepaintState {
121    }
122
123    fn paint(
124        &mut self,
125        _global_id: Option<&GlobalElementId>,
126        _inspector_id: Option<&InspectorElementId>,
127        _bounds: Bounds<Pixels>,
128        _: &mut Self::RequestLayoutState,
129        _: &mut Self::PrepaintState,
130        _window: &mut Window,
131        _: &mut App,
132    ) {
133        match self.source {
134            #[cfg(target_os = "macos")]
135            SurfaceSource::Surface(ref surface) => {
136                let size = crate::size(surface.get_width().into(), surface.get_height().into());
137                let new_bounds = self.object_fit.get_bounds(_bounds, size);
138                // TODO: Add support for corner_radii
139                _window.paint_surface(new_bounds, surface.clone());
140            }
141            #[cfg(any(target_os = "linux", target_os = "freebsd"))]
142            SurfaceSource::Texture {
143                ref texture,
144                ref size,
145            } => {
146                let new_bounds = self.object_fit.get_bounds(_bounds, *size);
147                _window.paint_surface(new_bounds, Arc::clone(texture), *size);
148            }
149        }
150    }
151}
152
153impl IntoElement for Surface {
154    type Element = Self;
155
156    fn into_element(self) -> Self::Element {
157        self
158    }
159}
160
161impl Styled for Surface {
162    fn style(&mut self) -> &mut StyleRefinement {
163        &mut self.style
164    }
165}