Skip to main content

dear_imgui_rs/widget/
image.rs

1//! Image widgets
2//!
3//! Draw images from a legacy `TextureId` or from modern `TextureData` handled
4//! via `DrawData::textures()`. See crate-level docs for texture management.
5//!
6//! Quick example (image button):
7//! ```no_run
8//! # use dear_imgui_rs::*;
9//! # let mut ctx = Context::create();
10//! # let ui = ctx.frame();
11//! let tex_id = texture::TextureId::new(42);
12//! if ui.image_button("btn", tex_id, [32.0, 32.0]) {
13//!     // clicked
14//! }
15//! ```
16//!
17use crate::sys;
18use crate::texture::TextureRef;
19use crate::ui::Ui;
20use crate::{StyleColor, StyleVar};
21use std::borrow::Cow;
22
23fn assert_non_negative_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
24    assert!(
25        value[0].is_finite() && value[1].is_finite(),
26        "{caller} {name} must contain finite values"
27    );
28    assert!(
29        value[0] >= 0.0 && value[1] >= 0.0,
30        "{caller} {name} must contain non-negative values"
31    );
32}
33
34fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
35    assert!(
36        value[0].is_finite() && value[1].is_finite(),
37        "{caller} {name} must contain finite values"
38    );
39}
40
41fn assert_finite_vec4(caller: &str, name: &str, value: [f32; 4]) {
42    assert!(
43        value.iter().all(|component| component.is_finite()),
44        "{caller} {name} must contain finite values"
45    );
46}
47
48fn is_default_tint_color(color: [f32; 4]) -> bool {
49    color == [1.0, 1.0, 1.0, 1.0]
50}
51
52fn is_transparent_color(color: [f32; 4]) -> bool {
53    color == [0.0, 0.0, 0.0, 0.0]
54}
55
56fn im_vec4(value: [f32; 4]) -> sys::ImVec4 {
57    sys::ImVec4 {
58        x: value[0],
59        y: value[1],
60        z: value[2],
61        w: value[3],
62    }
63}
64
65/// # Image Widgets
66///
67/// Examples
68/// - Using a plain texture id:
69/// ```no_run
70/// # use dear_imgui_rs::*;
71/// # fn demo(ui: &Ui) {
72/// let tex_id = texture::TextureId::new(0xDEAD_BEEF);
73/// ui.image(tex_id, [128.0, 128.0]);
74/// # }
75/// ```
76/// - Using an ImGui-managed texture:
77/// ```no_run
78/// # use dear_imgui_rs::*;
79/// # fn demo(ui: &Ui) {
80/// let mut tex = texture::TextureData::new();
81/// tex.create(texture::TextureFormat::RGBA32, 64, 64);
82/// ui.image(&mut *tex, [64.0, 64.0]);
83/// # }
84/// ```
85impl Ui {
86    /// Creates an image widget
87    #[doc(alias = "Image")]
88    pub fn image(&self, texture: impl Into<TextureRef>, size: [f32; 2]) {
89        self.image_config(texture, size).build()
90    }
91
92    /// Creates an image button widget
93    #[doc(alias = "ImageButton")]
94    pub fn image_button(
95        &self,
96        str_id: impl AsRef<str>,
97        texture: impl Into<TextureRef>,
98        size: [f32; 2],
99    ) -> bool {
100        self.image_button_config(str_id.as_ref(), texture, size)
101            .build()
102    }
103
104    /// Creates an image builder
105    pub fn image_config(&self, texture: impl Into<TextureRef>, size: [f32; 2]) -> Image<'_> {
106        Image::new(self, texture, size)
107    }
108
109    /// Creates an image button builder
110    pub fn image_button_config<'ui>(
111        &'ui self,
112        str_id: impl Into<Cow<'ui, str>>,
113        texture: impl Into<TextureRef>,
114        size: [f32; 2],
115    ) -> ImageButton<'ui> {
116        ImageButton::new(self, str_id, texture, size)
117    }
118}
119
120/// Builder for an image widget
121#[derive(Debug)]
122#[must_use]
123pub struct Image<'ui> {
124    _ui: &'ui Ui,
125    texture: TextureRef,
126    size: [f32; 2],
127    uv0: [f32; 2],
128    uv1: [f32; 2],
129    tint_color: [f32; 4],
130    border_color: [f32; 4],
131}
132
133impl<'ui> Image<'ui> {
134    /// Creates a new image builder
135    pub fn new(ui: &'ui Ui, texture: impl Into<TextureRef>, size: [f32; 2]) -> Self {
136        Self {
137            _ui: ui,
138            texture: texture.into(),
139            size,
140            uv0: [0.0, 0.0],
141            uv1: [1.0, 1.0],
142            tint_color: [1.0, 1.0, 1.0, 1.0],
143            border_color: [0.0, 0.0, 0.0, 0.0],
144        }
145    }
146
147    /// Sets the UV coordinates for the top-left corner (default: [0.0, 0.0])
148    pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
149        self.uv0 = uv0;
150        self
151    }
152
153    /// Sets the UV coordinates for the bottom-right corner (default: [1.0, 1.0])
154    pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
155        self.uv1 = uv1;
156        self
157    }
158
159    /// Sets the tint color (default: white, no tint)
160    ///
161    /// Dear ImGui 1.91.9 moved image tinting from `Image()` to `ImageWithBg()`.
162    /// If this is set, [`build`](Self::build) will call the tinted path while
163    /// keeping a transparent background.
164    pub fn tint_color(mut self, tint_color: [f32; 4]) -> Self {
165        self.tint_color = tint_color;
166        self
167    }
168
169    /// Sets the border color (default: transparent, no border)
170    ///
171    /// Dear ImGui 1.91.9 moved image border thickness to `Style::ImageBorderSize`
172    /// and border color to `StyleColor::Border`; this builder applies matching
173    /// temporary style overrides around [`build`](Self::build).
174    pub fn border_color(mut self, border_color: [f32; 4]) -> Self {
175        self.border_color = border_color;
176        self
177    }
178
179    /// Builds the image widget
180    pub fn build(self) {
181        assert_non_negative_finite_vec2("Image::build()", "size", self.size);
182        assert_finite_vec2("Image::build()", "uv0", self.uv0);
183        assert_finite_vec2("Image::build()", "uv1", self.uv1);
184        assert_finite_vec4("Image::build()", "tint_color", self.tint_color);
185        assert_finite_vec4("Image::build()", "border_color", self.border_color);
186
187        let size_vec: sys::ImVec2 = self.size.into();
188        let uv0_vec: sys::ImVec2 = self.uv0.into();
189        let uv1_vec: sys::ImVec2 = self.uv1.into();
190
191        let _border_size_token = (self.border_color[3] > 0.0).then(|| {
192            let current_size = unsafe { self._ui.style().image_border_size() };
193            self._ui
194                .push_style_var(StyleVar::ImageBorderSize(current_size.max(1.0)))
195        });
196        let _border_color_token = (self.border_color[3] > 0.0).then(|| {
197            self._ui
198                .push_style_color(StyleColor::Border, self.border_color)
199        });
200
201        if is_default_tint_color(self.tint_color) && is_transparent_color(self.border_color) {
202            unsafe { sys::igImage(self.texture.raw(), size_vec, uv0_vec, uv1_vec) }
203        } else {
204            unsafe {
205                sys::igImageWithBg(
206                    self.texture.raw(),
207                    size_vec,
208                    uv0_vec,
209                    uv1_vec,
210                    im_vec4([0.0, 0.0, 0.0, 0.0]),
211                    im_vec4(self.tint_color),
212                )
213            }
214        }
215    }
216
217    /// Builds the image widget with background color and tint (v1.92+)
218    pub fn build_with_bg(self, bg_color: [f32; 4], tint_color: [f32; 4]) {
219        assert_non_negative_finite_vec2("Image::build_with_bg()", "size", self.size);
220        assert_finite_vec2("Image::build_with_bg()", "uv0", self.uv0);
221        assert_finite_vec2("Image::build_with_bg()", "uv1", self.uv1);
222        assert_finite_vec4("Image::build_with_bg()", "bg_color", bg_color);
223        assert_finite_vec4("Image::build_with_bg()", "tint_color", tint_color);
224        assert_finite_vec4("Image::build_with_bg()", "border_color", self.border_color);
225
226        let size_vec: sys::ImVec2 = self.size.into();
227        let uv0_vec: sys::ImVec2 = self.uv0.into();
228        let uv1_vec: sys::ImVec2 = self.uv1.into();
229
230        let _border_size_token = (self.border_color[3] > 0.0).then(|| {
231            let current_size = unsafe { self._ui.style().image_border_size() };
232            self._ui
233                .push_style_var(StyleVar::ImageBorderSize(current_size.max(1.0)))
234        });
235        let _border_color_token = (self.border_color[3] > 0.0).then(|| {
236            self._ui
237                .push_style_color(StyleColor::Border, self.border_color)
238        });
239
240        unsafe {
241            sys::igImageWithBg(
242                self.texture.raw(),
243                size_vec,
244                uv0_vec,
245                uv1_vec,
246                im_vec4(bg_color),
247                im_vec4(tint_color),
248            )
249        }
250    }
251}
252
253/// Builder for an image button widget
254#[derive(Debug)]
255#[must_use]
256pub struct ImageButton<'ui> {
257    ui: &'ui Ui,
258    str_id: Cow<'ui, str>,
259    texture: TextureRef,
260    size: [f32; 2],
261    uv0: [f32; 2],
262    uv1: [f32; 2],
263    bg_color: [f32; 4],
264    tint_color: [f32; 4],
265}
266
267impl<'ui> ImageButton<'ui> {
268    /// Creates a new image button builder
269    pub fn new(
270        ui: &'ui Ui,
271        str_id: impl Into<Cow<'ui, str>>,
272        texture: impl Into<TextureRef>,
273        size: [f32; 2],
274    ) -> Self {
275        Self {
276            ui,
277            str_id: str_id.into(),
278            texture: texture.into(),
279            size,
280            uv0: [0.0, 0.0],
281            uv1: [1.0, 1.0],
282            bg_color: [0.0, 0.0, 0.0, 0.0],
283            tint_color: [1.0, 1.0, 1.0, 1.0],
284        }
285    }
286
287    /// Sets the UV coordinates for the top-left corner (default: [0.0, 0.0])
288    pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
289        self.uv0 = uv0;
290        self
291    }
292
293    /// Sets the UV coordinates for the bottom-right corner (default: [1.0, 1.0])
294    pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
295        self.uv1 = uv1;
296        self
297    }
298
299    /// Sets the background color (default: transparent)
300    pub fn bg_color(mut self, bg_color: [f32; 4]) -> Self {
301        self.bg_color = bg_color;
302        self
303    }
304
305    /// Sets the tint color (default: white, no tint)
306    pub fn tint_color(mut self, tint_color: [f32; 4]) -> Self {
307        self.tint_color = tint_color;
308        self
309    }
310
311    /// Builds the image button widget
312    pub fn build(self) -> bool {
313        assert_non_negative_finite_vec2("ImageButton::build()", "size", self.size);
314        assert_finite_vec2("ImageButton::build()", "uv0", self.uv0);
315        assert_finite_vec2("ImageButton::build()", "uv1", self.uv1);
316        assert_finite_vec4("ImageButton::build()", "bg_color", self.bg_color);
317        assert_finite_vec4("ImageButton::build()", "tint_color", self.tint_color);
318
319        let str_id_ptr = self.ui.scratch_txt(self.str_id.as_ref());
320        let size_vec: sys::ImVec2 = self.size.into();
321        let uv0_vec: sys::ImVec2 = self.uv0.into();
322        let uv1_vec: sys::ImVec2 = self.uv1.into();
323
324        unsafe {
325            sys::igImageButton(
326                str_id_ptr,
327                self.texture.raw(),
328                size_vec,
329                uv0_vec,
330                uv1_vec,
331                im_vec4(self.bg_color),
332                im_vec4(self.tint_color),
333            )
334        }
335    }
336}