1use gpui::prelude::*;
21use gpui::{img, px, AnyElement, App, ImageSource, IntoElement, Window};
22
23pub use gpui::ObjectFit;
24
25use crate::devtools::Probed;
26use crate::theme::{theme, Size};
27
28#[derive(IntoElement)]
34pub struct Image {
35 source: ImageSource,
36 width: Option<f32>,
37 height: Option<f32>,
38 radius: Option<Size>,
39 circle: bool,
40 fit: ObjectFit,
41 fallback: Option<Box<dyn Fn() -> AnyElement + 'static>>,
42}
43
44impl Image {
45 pub fn new(source: impl Into<ImageSource>) -> Self {
46 Image {
47 source: source.into(),
48 width: None,
49 height: None,
50 radius: None,
51 circle: false,
52 fit: ObjectFit::Cover,
53 fallback: None,
54 }
55 }
56
57 pub fn width(mut self, width: f32) -> Self {
60 self.width = Some(width);
61 self
62 }
63
64 pub fn height(mut self, height: f32) -> Self {
66 self.height = Some(height);
67 self
68 }
69
70 pub fn radius(mut self, radius: Size) -> Self {
73 self.radius = Some(radius);
74 self
75 }
76
77 pub fn circle(mut self) -> Self {
79 self.circle = true;
80 self
81 }
82
83 pub fn fit(mut self, fit: ObjectFit) -> Self {
85 self.fit = fit;
86 self
87 }
88
89 pub fn fallback<E: IntoElement>(mut self, fallback: impl Fn() -> E + 'static) -> Self {
91 self.fallback = Some(Box::new(move || fallback().into_any_element()));
92 self
93 }
94}
95
96impl RenderOnce for Image {
97 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
98 let t = theme(cx);
99 let mut el = img(self.source).object_fit(self.fit);
100 if let Some(fallback) = self.fallback {
101 el = el.with_fallback(fallback);
102 }
103 if let Some(width) = self.width {
104 el = el.w(px(width));
105 }
106 if let Some(height) = self.height {
107 el = el.h(px(height));
108 }
109 if self.circle {
110 el = el.rounded_full();
111 } else if let Some(radius) = self.radius {
112 el = el.rounded(px(t.radius(radius)));
113 }
114 el.probe("Image")
115 }
116}