diff --git a/imgui-examples/examples/custom_textures.rs b/imgui-examples/examples/custom_textures.rs index 5080ffa..2593a9d 100644 --- a/imgui-examples/examples/custom_textures.rs +++ b/imgui-examples/examples/custom_textures.rs @@ -79,7 +79,7 @@ impl CustomTexturesApp { fn show_textures(&self, ui: &Ui) { Window::new(im_str!("Hello textures")) - .size([400.0, 600.0], Condition::FirstUseEver) + .size([400.0, 400.0], Condition::FirstUseEver) .build(ui, || { ui.text(im_str!("Hello textures!")); if let Some(my_texture_id) = self.my_texture_id { @@ -91,6 +91,89 @@ impl CustomTexturesApp { ui.text("Say hello to Lenna.jpg"); lenna.show(ui); } + + // Example of using custom textures on a button + if let Some(lenna) = &self.lenna { + ui.text("The Lenna buttons"); + + { + let _is_clicked = + ui.invisible_button(im_str!("Boring Button"), [100.0, 100.0]); + // See also `imgui::Ui::style_color` + let tint_none = [1.0, 1.0, 1.0, 1.0]; + let tint_green = [0.5, 1.0, 0.5, 1.0]; + let tint_red = [1.0, 0.5, 0.5, 1.0]; + + let tint = match ( + ui.is_item_hovered(), + ui.is_mouse_down(imgui::MouseButton::Left), + ) { + (false, false) => tint_none, + (false, true) => tint_none, + (true, false) => tint_green, + (true, true) => tint_red, + }; + + let draw_list = ui.get_window_draw_list(); + draw_list + .add_image(lenna.texture_id, ui.item_rect_min(), ui.item_rect_max()) + .col(tint) + .build(); + } + + { + ui.same_line(0.0); + + // Button using quad positioned image + let is_clicked = + ui.invisible_button(im_str!("Exciting Button"), [100.0, 100.0]); + + // Button bounds + let min = ui.item_rect_min(); + let max = ui.item_rect_max(); + + // get corner coordinates + let tl = [ + min[0], + min[1] + (ui.frame_count() as f32 / 10.0).cos() * 10.0, + ]; + let tr = [ + max[0], + min[1] + (ui.frame_count() as f32 / 10.0).sin() * 10.0, + ]; + let bl = [min[0], max[1]]; + let br = max; + + let draw_list = ui.get_window_draw_list(); + draw_list + .add_image_quad(lenna.texture_id, tl, tr, br, bl) + .build(); + } + + // Rounded image + { + ui.same_line(0.0); + let is_clicked = + ui.invisible_button(im_str!("Smooth Button"), [100.0, 100.0]); + + let draw_list = ui.get_window_draw_list(); + draw_list + .add_image_rounded( + lenna.texture_id, + ui.item_rect_min(), + ui.item_rect_max(), + 16.0, + ) + // Tint brighter for visiblity of corners + .col([2.0, 0.5, 0.5, 1.0]) + // Rounding on each corner can be changed separately + .round_top_left((ui.frame_count()+0) / 60 % 4 == 0) + .round_top_right((ui.frame_count()+1) / 60 % 4 == 1) + .round_bot_right((ui.frame_count()+3) / 60 % 4 == 2) + .round_bot_left((ui.frame_count()+2) / 60 % 4 == 3) + .build(); + } + } }); } } diff --git a/imgui/src/draw_list.rs b/imgui/src/draw_list.rs index b117dc7..8626fcb 100644 --- a/imgui/src/draw_list.rs +++ b/imgui/src/draw_list.rs @@ -3,6 +3,7 @@ use sys::ImDrawList; use super::Ui; use crate::legacy::ImDrawCornerFlags; +use crate::render::renderer::TextureId; use std::marker::PhantomData; @@ -251,6 +252,66 @@ impl<'ui> DrawListMut<'ui> { } } +/// # Images +impl<'ui> DrawListMut<'ui> { + /// Draw the specified image in the rect specified by `p_min` to + /// `p_max`. + /// + /// # Examples + /// + /// ``` + /// # use imgui::*; + /// fn custom_button(ui: &Ui, img_id: TextureId) { + /// // Tint image red + /// + /// // Invisible button is good widget to customise with custom image + /// ui.invisible_button(im_str!("custom_button"), [100.0, 20.0]); + /// + /// // Red tint when button is hovered, no tint otherwise + /// let overlay_color = if ui.is_item_hovered() { + /// [1.0, 0.6, 0.6, 1.0] + /// } else { + /// [1.0, 1.0, 1.0, 1.0] + /// }; + /// + /// // Get draw list and draw image over invisible button + /// let draw_list = ui.get_window_draw_list(); + /// draw_list + /// .add_image(img_id, ui.item_rect_min(), ui.item_rect_max()) + /// .col(overlay_color) + /// .build(); + /// } + /// ``` + pub fn add_image(&'ui self, texture_id: TextureId, p_min: [f32; 2], p_max: [f32; 2]) -> Image { + Image::new(self, texture_id, p_min, p_max) + } + + /// Draw the specified image to a quad with the specified + /// coordinates. Similar to [`DrawListMut::add_image`] but this + /// method is able to draw non-rectangle images. + pub fn add_image_quad( + &'ui self, + texture_id: TextureId, + p1: [f32; 2], + p2: [f32; 2], + p3: [f32; 2], + p4: [f32; 2], + ) -> ImageQuad { + ImageQuad::new(self, texture_id, p1, p2, p3, p4) + } + + /// Draw the speciied image, with rounded corners + pub fn add_image_rounded( + &'ui self, + texture_id: TextureId, + p_min: [f32; 2], + p_max: [f32; 2], + rounding: f32, + ) -> ImageRounded { + ImageRounded::new(self, texture_id, p_min, p_max, rounding) + } +} + /// Represents a line about to be drawn #[must_use = "should call .build() to draw the object"] pub struct Line<'ui> { @@ -605,3 +666,265 @@ impl<'ui> BezierCurve<'ui> { } } } + +/// Represents a image about to be drawn +#[must_use = "should call .build() to draw the object"] +pub struct Image<'ui> { + texture_id: TextureId, + p_min: [f32; 2], + p_max: [f32; 2], + uv_min: [f32; 2], + uv_max: [f32; 2], + col: ImColor32, + draw_list: &'ui DrawListMut<'ui>, +} + +impl<'ui> Image<'ui> { + fn new( + draw_list: &'ui DrawListMut, + texture_id: TextureId, + p_min: [f32; 2], + p_max: [f32; 2], + ) -> Self { + Self { + texture_id, + p_min, + p_max, + uv_min: [0.0, 0.0], + uv_max: [1.0, 1.0], + col: [1.0, 1.0, 1.0, 1.0].into(), + draw_list, + } + } + + /// Set uv_min (default `[0.0, 0.0]`) + pub fn uv_min(mut self, uv_min: [f32; 2]) -> Self { + self.uv_min = uv_min; + self + } + /// Set uv_max (default `[1.0, 1.0]`) + pub fn uv_max(mut self, uv_max: [f32; 2]) -> Self { + self.uv_max = uv_max; + self + } + + /// Set color tint (default: no tint/white `[1.0, 1.0, 1.0, 1.0]`) + pub fn col(mut self, col: C) -> Self + where + C: Into, + { + self.col = col.into(); + self + } + + /// Draw the image on the window. + pub fn build(self) { + use std::os::raw::c_void; + + unsafe { + sys::ImDrawList_AddImage( + self.draw_list.draw_list, + self.texture_id.id() as *mut c_void, + self.p_min.into(), + self.p_max.into(), + self.uv_min.into(), + self.uv_max.into(), + self.col.into(), + ); + } + } +} + +/// Represents a image about to be drawn +#[must_use = "should call .build() to draw the object"] +pub struct ImageQuad<'ui> { + texture_id: TextureId, + p1: [f32; 2], + p2: [f32; 2], + p3: [f32; 2], + p4: [f32; 2], + uv1: [f32; 2], + uv2: [f32; 2], + uv3: [f32; 2], + uv4: [f32; 2], + col: ImColor32, + draw_list: &'ui DrawListMut<'ui>, +} + +impl<'ui> ImageQuad<'ui> { + fn new( + draw_list: &'ui DrawListMut, + texture_id: TextureId, + p1: [f32; 2], + p2: [f32; 2], + p3: [f32; 2], + p4: [f32; 2], + ) -> Self { + Self { + texture_id, + p1, + p2, + p3, + p4, + uv1: [0.0, 0.0], + uv2: [1.0, 0.0], + uv3: [1.0, 1.0], + uv4: [0.0, 1.0], + col: [1.0, 1.0, 1.0, 1.0].into(), + draw_list, + } + } + + /// Set uv coordinates of each point of the quad. If not called, defaults are: + /// + /// ``` + /// uv1: [0, 0], + /// uv2: [1, 0], + /// uv3: [1, 1], + /// uv4: [0, 1], + /// ``` + pub fn uv(mut self, uv1: [f32; 2], uv2: [f32; 2], uv3: [f32; 2], uv4: [f32; 2]) -> Self { + self.uv1 = uv1; + self.uv2 = uv2; + self.uv3 = uv3; + self.uv4 = uv4; + self + } + + /// Set color tint (default: no tint/white `[1.0, 1.0, 1.0, 1.0]`) + pub fn col(mut self, col: C) -> Self + where + C: Into, + { + self.col = col.into(); + self + } + + /// Draw the image on the window. + pub fn build(self) { + use std::os::raw::c_void; + + unsafe { + sys::ImDrawList_AddImageQuad( + self.draw_list.draw_list, + self.texture_id.id() as *mut c_void, + self.p1.into(), + self.p2.into(), + self.p3.into(), + self.p4.into(), + self.uv1.into(), + self.uv2.into(), + self.uv3.into(), + self.uv4.into(), + self.col.into(), + ); + } + } +} + +/// Represents a image about to be drawn +#[must_use = "should call .build() to draw the object"] +pub struct ImageRounded<'ui> { + texture_id: TextureId, + p_min: [f32; 2], + p_max: [f32; 2], + uv_min: [f32; 2], + uv_max: [f32; 2], + col: ImColor32, + rounding: f32, + rounding_corners: ImDrawCornerFlags, + draw_list: &'ui DrawListMut<'ui>, +} + +impl<'ui> ImageRounded<'ui> { + fn new( + draw_list: &'ui DrawListMut, + texture_id: TextureId, + p_min: [f32; 2], + p_max: [f32; 2], + rounding: f32, + ) -> Self { + Self { + texture_id, + p_min, + p_max, + uv_min: [0.0, 0.0], + uv_max: [1.0, 1.0], + col: [1.0, 1.0, 1.0, 1.0].into(), + rounding, + rounding_corners: ImDrawCornerFlags::All, + draw_list, + } + } + + /// Set uv_min (default `[0.0, 0.0]`) + pub fn uv_min(mut self, uv_min: [f32; 2]) -> Self { + self.uv_min = uv_min; + self + } + /// Set uv_max (default `[1.0, 1.0]`) + pub fn uv_max(mut self, uv_max: [f32; 2]) -> Self { + self.uv_max = uv_max; + self + } + + /// Set color tint (default: no tint/white `[1.0, 1.0, 1.0, 1.0]`) + pub fn col(mut self, col: C) -> Self + where + C: Into, + { + self.col = col.into(); + self + } + + /// Set flag to indicate rounding on all all corners. + pub fn round_all(mut self, value: bool) -> Self { + self.rounding_corners.set(ImDrawCornerFlags::All, value); + self + } + + /// Set flag to indicate if image's top-left corner will be rounded. + pub fn round_top_left(mut self, value: bool) -> Self { + self.rounding_corners.set(ImDrawCornerFlags::TopLeft, value); + self + } + + /// Set flag to indicate if image's top-right corner will be rounded. + pub fn round_top_right(mut self, value: bool) -> Self { + self.rounding_corners + .set(ImDrawCornerFlags::TopRight, value); + self + } + + /// Set flag to indicate if image's bottom-left corner will be rounded. + pub fn round_bot_left(mut self, value: bool) -> Self { + self.rounding_corners.set(ImDrawCornerFlags::BotLeft, value); + self + } + + /// Set flag to indicate if image's bottom-right corner will be rounded. + pub fn round_bot_right(mut self, value: bool) -> Self { + self.rounding_corners + .set(ImDrawCornerFlags::BotRight, value); + self + } + + /// Draw the image on the window. + pub fn build(self) { + use std::os::raw::c_void; + + unsafe { + sys::ImDrawList_AddImageRounded( + self.draw_list.draw_list, + self.texture_id.id() as *mut c_void, + self.p_min.into(), + self.p_max.into(), + self.uv_min.into(), + self.uv_max.into(), + self.col.into(), + self.rounding.into(), + self.rounding_corners.bits(), + ); + } + } +}