From db9704193697fc065a3672cb8e978dec4aee8975 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 22:32:52 +0900 Subject: [PATCH 01/28] window_draw_list.rs: Basic structure to wrap ImGui's draw API This patch makes the basic structure for a wrapper around Dear ImGui's drawing API. 1. Implement `with_window_draw_list` method on Ui. Call this method to get access to the `WindowDrawList` object. This object holds the methods to access ImGui's drawing API. 2. Dear ImGui uses the ImU32 (an unsigned c_int) to represent colors in the drawing API. This commit wraps this type with ImColor for convenience. Any color representation (3or4-tuples, 3or4-arrays, ImU32 or ImVec4) can be converted into ImColor for convenience. 3. Difference between WindowDrawList and ChannelsSplit: Most drawing functions can be called on WindowDrawList and ChannelsSplit objects. However for safety, some functions can only be called on WindowDrawList or ChannelsSplit instance. For example `channels_set_current` can only be called after channels have been split. To avoid code duplication, functions common to WindowDrawList and ChannelsSplit are implemented within the `impl_draw_list_methods` macro. 4. Implement drawing functions (in this commit, add_line only). Calling `add_line` returns a default representation of the line to be drawn, but does not draw it. Then parameters, such as thickness, can be set. You must call `build` to draw the line. All drawing functions will be implemented following this pattern. --- src/lib.rs | 27 ++++++ src/window_draw_list.rs | 179 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 src/window_draw_list.rs diff --git a/src/lib.rs b/src/lib.rs index 3c54b61..40e7833 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ pub use string::{ImStr, ImString}; pub use style::StyleVar; pub use trees::{CollapsingHeader, TreeNode}; pub use window::Window; +pub use window_draw_list::{ImColor, WindowDrawList}; mod child_frame; mod color_editors; @@ -41,6 +42,7 @@ mod string; mod style; mod trees; mod window; +mod window_draw_list; pub struct ImGui { // We need to keep ownership of the ImStr values to ensure the *const char pointer @@ -1211,3 +1213,28 @@ impl<'ui> Ui<'ui> { unsafe { sys::igEndGroup(); } } } + +/// # Draw list for custom drawing +impl<'ui> Ui<'ui> { + /// Get access to drawing API + /// + /// # Examples + /// + /// ``` + /// fn custom_draw(ui: &Ui) { + /// ui.with_window_draw_list(|draw_list| { + /// // Draw a line + /// const WHITE: [f32; 3] = [1.0, 1.0, 1.0]; + /// draw_list.add_line([100.0, 100.0], [200.0, 200.0], WHITE).build(); + /// // Continue drawing ... + /// }); + /// } + /// ``` + pub fn with_window_draw_list(&self, f: F) + where + F: FnOnce(&WindowDrawList), + { + let window_draw_list = WindowDrawList::new(self); + f(&window_draw_list); + } +} diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs new file mode 100644 index 0000000..6ea5841 --- /dev/null +++ b/src/window_draw_list.rs @@ -0,0 +1,179 @@ +use sys; +use sys::{ImDrawList, ImU32}; + +use super::{ImVec2, ImVec4, Ui}; + +use std::marker::PhantomData; + +/// Wrap `ImU32` (a type typically used by ImGui to store packed colors) +/// This type is used to represent the color of drawing primitives in ImGui's +/// custom drawing API. +/// +/// The type implements `From`, `From`, `From<[f32; 4]>`, +/// `From<[f32; 3]>`, `From<(f32, f32, f32, f32)>` and `From<(f32, f32, f32)>` +/// for convenience. If alpha is not provided, it is assumed to be 1.0 (255). +#[derive(Copy, Clone)] +pub struct ImColor(ImU32); + +impl From for ImU32 { + fn from(color: ImColor) -> Self { color.0 } +} + +impl From for ImColor { + fn from(color: ImU32) -> Self { ImColor(color) } +} + +impl From for ImColor { + fn from(v: ImVec4) -> Self { ImColor(unsafe { sys::igColorConvertFloat4ToU32(v) }) } +} + +impl From<[f32; 4]> for ImColor { + fn from(v: [f32; 4]) -> Self { ImColor(unsafe { sys::igColorConvertFloat4ToU32(v.into()) }) } +} + +impl From<(f32, f32, f32, f32)> for ImColor { + fn from(v: (f32, f32, f32, f32)) -> Self { + ImColor(unsafe { sys::igColorConvertFloat4ToU32(v.into()) }) + } +} + +impl From<[f32; 3]> for ImColor { + fn from(v: [f32; 3]) -> Self { [v[0], v[1], v[2], 1.0].into() } +} + +impl From<(f32, f32, f32)> for ImColor { + fn from(v: (f32, f32, f32)) -> Self { [v.0, v.1, v.2, 1.0].into() } +} + +/// All types from which ImGui's custom draw API can be used implement this +/// trait. This trait is internal to this library and implemented by +/// `WindowDrawList` and `ChannelsSplit`. +pub trait DrawAPI<'ui> { + /// Get draw_list object + fn draw_list(&self) -> *mut ImDrawList; +} + +/// Object implementing the custom draw API. +pub struct WindowDrawList<'ui> { + draw_list: *mut ImDrawList, + _phantom: PhantomData<&'ui Ui<'ui>>, +} + +impl<'ui> DrawAPI<'ui> for WindowDrawList<'ui> { + fn draw_list(&self) -> *mut ImDrawList { self.draw_list } +} + +impl<'ui> WindowDrawList<'ui> { + pub fn new(_: &Ui<'ui>) -> Self { + Self { + draw_list: unsafe { sys::igGetWindowDrawList() }, + _phantom: PhantomData, + } + } + + /// Split into *channels_count* drawing channels. + /// At the end of the closure, the channels are merged. The objects + /// are then drawn in the increasing order of their channel number, and not + /// in the all order they were called. + /// + /// # Example + /// + /// ``` + /// fn custom_drawing(ui: &Ui) { + /// ui.with_window_draw_list(|draw_list| { + /// draw_list.channels_split(2, |draw_list| { + /// draw_list.channels_set_current(1); + /// // ... Draw channel 1 + /// draw_list.channels_set_current(0); + /// // ... Draw channel 0 + /// }); + /// }); + /// } + /// ``` + pub fn channels_split(&self, channels_count: u32, f: F) { + unsafe { sys::ImDrawList_ChannelsSplit(self.draw_list, channels_count as i32) }; + f(&ChannelsSplit(self)); + unsafe { sys::ImDrawList_ChannelsMerge(self.draw_list) }; + } +} + +/// Represent the drawing interface within a call to `channels_split`. +pub struct ChannelsSplit<'ui>(&'ui WindowDrawList<'ui>); + +impl<'ui> DrawAPI<'ui> for ChannelsSplit<'ui> { + fn draw_list(&self) -> *mut ImDrawList { self.0.draw_list } +} + +impl<'ui> ChannelsSplit<'ui> { + /// Change current channel + pub fn channels_set_current(&self, channel_index: u32) { + unsafe { sys::ImDrawList_ChannelsSetCurrent(self.draw_list(), channel_index as i32) }; + } +} + +macro_rules! impl_draw_list_methods { + ($T: ident) => { + impl<'ui> $T<'ui> + where + $T<'ui>: DrawAPI<'ui>, + { + /// Returns a line from point `p1` to `p2` with color `c`. + pub fn add_line(&self, p1: P1, p2: P2, c: C) -> Line<'ui, $T> + where + P1: Into, + P2: Into, + C: Into, + { + Line::new(self, p1, p2, c) + } + } + }; +} + +impl_draw_list_methods!(WindowDrawList); +impl_draw_list_methods!(ChannelsSplit); + +/// Represents a line about to be drawn +pub struct Line<'ui, D: 'ui> { + p1: ImVec2, + p2: ImVec2, + color: ImColor, + thickness: f32, + draw_list: &'ui D, +} + +impl<'ui, D: DrawAPI<'ui>> Line<'ui, D> { + fn new(draw_list: &'ui D, p1: P1, p2: P2, c: C) -> Self + where + P1: Into, + P2: Into, + C: Into, + { + Self { + p1: p1.into(), + p2: p2.into(), + color: c.into(), + thickness: 1.0, + draw_list, + } + } + + /// Set line's thickness (default to 1.0 pixel) + pub fn thickness(mut self, thickness: f32) -> Self { + self.thickness = thickness; + self + } + + /// Draw the line on the window + pub fn build(self) { + unsafe { + sys::ImDrawList_AddLine( + self.draw_list.draw_list(), + self.p1, + self.p2, + self.color.into(), + self.thickness, + ) + } + } +} From 05b382e31715bd47d33cece0c700d82e4b53a09a Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 22:42:53 +0900 Subject: [PATCH 02/28] WindowDrawList: Wrap add_rect method This patch uses bitflags to set the whether the corners are rounded. Hence the `ImDrawCornerFlags` struct is defined, but only used internally. Externally, the valule of the flags can be changed with methods on the `Rect` structure such as `round_top_right` or `round_bot_left`. This patch wraps both ImDrawList_AddRectFilled and ImDrawList_AddRect. ImDrawList_AddRectFilled is seen as a particular case of `add_rect` where `filled` is set to `true`. --- imgui-sys/src/lib.rs | 24 ++++++++- src/window_draw_list.rs | 116 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/imgui-sys/src/lib.rs b/imgui-sys/src/lib.rs index 5908569..890fcf5 100644 --- a/imgui-sys/src/lib.rs +++ b/imgui-sys/src/lib.rs @@ -325,6 +325,26 @@ bitflags!( } ); +bitflags!( + /// Flags for indictating which corner of a rectangle should be rounded + #[repr(C)] + pub struct ImDrawCornerFlags: c_int { + const TopLeft = 1 << 0; + const TopRight = 1 << 1; + const BotRight = 1 << 2; + const BotLeft = 1 << 3; + const Top = ImDrawCornerFlags::TopLeft.bits + | ImDrawCornerFlags::TopRight.bits; + const Bot = ImDrawCornerFlags::BotLeft.bits + | ImDrawCornerFlags::BotRight.bits; + const Left = ImDrawCornerFlags::TopLeft.bits + | ImDrawCornerFlags::BotLeft.bits; + const Right = ImDrawCornerFlags::TopRight.bits + | ImDrawCornerFlags::BotRight.bits; + const All = 0xF; + } +); + pub type ImGuiTextEditCallback = Option< extern "C" fn(data: *mut ImGuiTextEditCallbackData) -> c_int, >; @@ -1840,7 +1860,7 @@ extern "C" { b: ImVec2, col: ImU32, rounding: c_float, - rounding_corners_flags: c_int, + rounding_corners_flags: ImDrawCornerFlags, thickness: c_float, ); pub fn ImDrawList_AddRectFilled( @@ -1849,7 +1869,7 @@ extern "C" { b: ImVec2, col: ImU32, rounding: c_float, - rounding_corners_flags: c_int, + rounding_corners_flags: ImDrawCornerFlags, ); pub fn ImDrawList_AddRectFilledMultiColor( list: *mut ImDrawList, diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 6ea5841..c2216a3 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -1,5 +1,5 @@ use sys; -use sys::{ImDrawList, ImU32}; +use sys::{ImDrawCornerFlags, ImDrawList, ImU32}; use super::{ImVec2, ImVec4, Ui}; @@ -126,6 +126,17 @@ macro_rules! impl_draw_list_methods { { Line::new(self, p1, p2, c) } + + /// Returns a rectangle whose upper-left corner is at point `p1` + /// and lower-right corner is at point `p2`, with color `c`. + pub fn add_rect(&self, p1: P1, p2: P2, c: C) -> Rect<'ui, $T> + where + P1: Into, + P2: Into, + C: Into, + { + Rect::new(self, p1, p2, c) + } } }; } @@ -177,3 +188,106 @@ impl<'ui, D: DrawAPI<'ui>> Line<'ui, D> { } } } + +/// Represents a rectangle about to be drawn +pub struct Rect<'ui, D: 'ui> { + p1: ImVec2, + p2: ImVec2, + color: ImColor, + rounding: f32, + flags: ImDrawCornerFlags, + thickness: f32, + filled: bool, + draw_list: &'ui D, +} + +impl<'ui, D: DrawAPI<'ui>> Rect<'ui, D> { + fn new(draw_list: &'ui D, p1: P1, p2: P2, c: C) -> Self + where + P1: Into, + P2: Into, + C: Into, + { + Self { + p1: p1.into(), + p2: p2.into(), + color: c.into(), + rounding: 0.0, + flags: ImDrawCornerFlags::All, + thickness: 1.0, + filled: false, + draw_list, + } + } + + /// Set rectangle's corner rounding (default to 0.0: no rounding). + /// By default all corners are rounded if this value is set. + pub fn rounding(mut self, rounding: f32) -> Self { + self.rounding = rounding; + self + } + + /// Set flag to indicate if rectangle's top-left corner will be rounded. + pub fn round_top_left(mut self, value: bool) -> Self { + self.flags.set(ImDrawCornerFlags::TopLeft, value); + self + } + + /// Set flag to indicate if rectangle's top-right corner will be rounded. + pub fn round_top_right(mut self, value: bool) -> Self { + self.flags.set(ImDrawCornerFlags::TopRight, value); + self + } + + /// Set flag to indicate if rectangle's bottom-left corner will be rounded. + pub fn round_bot_left(mut self, value: bool) -> Self { + self.flags.set(ImDrawCornerFlags::BotLeft, value); + self + } + + /// Set flag to indicate if rectangle's bottom-right corner will be rounded. + pub fn round_bot_right(mut self, value: bool) -> Self { + self.flags.set(ImDrawCornerFlags::BotRight, value); + self + } + + /// Set rectangle's thickness (default to 1.0 pixel). + pub fn thickness(mut self, thickness: f32) -> Self { + self.thickness = thickness; + self + } + + /// Set to `true` to make a filled rectangle (default to `false`). + pub fn filled(mut self, filled: bool) -> Self { + self.filled = filled; + self + } + + /// Draw the rectangle on the window. + pub fn build(self) { + if self.filled { + unsafe { + sys::ImDrawList_AddRectFilled( + self.draw_list.draw_list(), + self.p1, + self.p2, + self.color.into(), + self.rounding, + self.flags, + ); + } + } else { + unsafe { + sys::ImDrawList_AddRect( + self.draw_list.draw_list(), + self.p1, + self.p2, + self.color.into(), + self.rounding, + self.flags, + self.thickness, + ); + } + } + } +} From d1879b2a04c74b94463a91aba918053797b40b81 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 10:41:02 +0900 Subject: [PATCH 03/28] window_draw_list.rs: Add add_rect_filled_multicolor As `add_rect_filled_multicolor` does not have any option, the `build` pattern is not used and calling `add_rect_filled_multicolor` directly draws the rectangle on the window. --- src/window_draw_list.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index c2216a3..53ebb88 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -137,6 +137,40 @@ macro_rules! impl_draw_list_methods { { Rect::new(self, p1, p2, c) } + + /// Draw a rectangle whose upper-left corner is at point `p1` + /// and lower-right corner is at point `p2`. + /// The remains parameters are the respective color of the corners + /// in the counter-clockwise starting from the upper-left corner + /// first. + pub fn add_rect_filled_multicolor( + &self, + p1: P1, + p2: P2, + col_upr_left: C1, + col_upr_right: C2, + col_bot_right: C3, + col_bot_left: C4, + ) where + P1: Into, + P2: Into, + C1: Into, + C2: Into, + C3: Into, + C4: Into, + { + unsafe { + sys::ImDrawList_AddRectFilledMultiColor( + self.draw_list(), + p1.into(), + p2.into(), + col_upr_left.into().into(), + col_upr_right.into().into(), + col_bot_right.into().into(), + col_bot_left.into().into(), + ); + } + } } }; } From 878de089e842a9badc7ba283748a3709613bf5f5 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Fri, 30 Mar 2018 07:42:40 +0900 Subject: [PATCH 04/28] window_draw_list.rs: Wrap add_circle ImDrawList_AddCircle was missing an argument in the bindings, resulting in UB. This patches includes it and wrap the AddCircle API. --- imgui-sys/src/lib.rs | 1 + src/window_draw_list.rs | 83 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/imgui-sys/src/lib.rs b/imgui-sys/src/lib.rs index 890fcf5..3d0f6a1 100644 --- a/imgui-sys/src/lib.rs +++ b/imgui-sys/src/lib.rs @@ -1918,6 +1918,7 @@ extern "C" { radius: c_float, col: ImU32, num_segments: c_int, + thickness: c_float, ); pub fn ImDrawList_AddCircleFilled( list: *mut ImDrawList, diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 53ebb88..8b31fcb 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -171,6 +171,15 @@ macro_rules! impl_draw_list_methods { ); } } + + /// Returns a circle with the given `center`, `radius` and `color`. + pub fn add_circle(&self, center: P, radius: f32, color: C) -> Circle<'ui, $T> + where + P: Into, + C: Into, + { + Circle::new(self, center, radius, color) + } } }; } @@ -325,3 +334,77 @@ impl<'ui, D: DrawAPI<'ui>> Rect<'ui, D> { } } } + +/// Represents a circle about to be drawn +pub struct Circle<'ui, D: 'ui> { + center: ImVec2, + radius: f32, + color: ImColor, + num_segments: u32, + thickness: f32, + filled: bool, + draw_list: &'ui D, +} + +impl<'ui, D: DrawAPI<'ui>> Circle<'ui, D> { + pub fn new(draw_list: &'ui D, center: P, radius: f32, color: C) -> Self + where + P: Into, + C: Into, + { + Self { + center: center.into(), + radius, + color: color.into(), + num_segments: 12, + thickness: 1.0, + filled: false, + draw_list, + } + } + + /// Set number of segment used to draw the circle, default to 12. + /// Add more segments if you want a smoother circle. + pub fn num_segments(mut self, num_segments: u32) -> Self { + self.num_segments = num_segments; + self + } + + /// Set circle's thickness (default to 1.0 pixel) + pub fn thickness(mut self, thickness: f32) -> Self { + self.thickness = thickness; + self + } + + /// Set to `true` to make a filled circle (default to `false`). + pub fn filled(mut self, filled: bool) -> Self { + self.filled = filled; + self + } + + /// Draw the circle on the window. + pub fn build(self) { + if self.filled { + unsafe { + sys::ImDrawList_AddCircleFilled( + self.draw_list.draw_list(), + self.center, + self.radius, + self.color.into(), + self.num_segments as i32, + ) + } + } else { + unsafe { + sys::ImDrawList_AddCircle( + self.draw_list.draw_list(), + self.center, + self.radius, + self.color.into(), + self.num_segments as i32, + self.thickness, + ) + } + } + } +} From 8471bd40349cf466c9d5e9ed316ad8781084df7d Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 11:16:11 +0900 Subject: [PATCH 05/28] window_draw_list.rs: Wrap add_triangle --- src/window_draw_list.rs | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 8b31fcb..12cb00c 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -172,6 +172,24 @@ macro_rules! impl_draw_list_methods { } } + /// Returns a triangle with the given 3 vertices `p1`, `p2` and `p3` + /// and color `c`. + pub fn add_triangle( + &self, + p1: P1, + p2: P2, + p3: P3, + c: C, + ) -> Triangle<'ui, $T> + where + P1: Into, + P2: Into, + P3: Into, + C: Into, + { + Triangle::new(self, p1, p2, p3, c) + } + /// Returns a circle with the given `center`, `radius` and `color`. pub fn add_circle(&self, center: P, radius: f32, color: C) -> Circle<'ui, $T> where @@ -335,6 +353,75 @@ impl<'ui, D: DrawAPI<'ui>> Rect<'ui, D> { } } +/// Represents a circle about to be drawn on the window +pub struct Triangle<'ui, D: 'ui> { + p1: ImVec2, + p2: ImVec2, + p3: ImVec2, + color: ImColor, + thickness: f32, + filled: bool, + draw_list: &'ui D, +} + +impl<'ui, D: DrawAPI<'ui>> Triangle<'ui, D> { + fn new(draw_list: &'ui D, p1: P1, p2: P2, p3: P3, c: C) -> Self + where + P1: Into, + P2: Into, + P3: Into, + C: Into, + { + Self { + p1: p1.into(), + p2: p2.into(), + p3: p3.into(), + color: c.into(), + thickness: 1.0, + filled: false, + draw_list, + } + } + + /// Set triangle's thickness (default to 1.0 pixel) + pub fn thickness(mut self, thickness: f32) -> Self { + self.thickness = thickness; + self + } + + /// Set to `true` to make a filled triangle (default to `false`). + pub fn filled(mut self, filled: bool) -> Self { + self.filled = filled; + self + } + + /// Draw the triangle on the window. + pub fn build(self) { + if self.filled { + unsafe { + sys::ImDrawList_AddTriangleFilled( + self.draw_list.draw_list(), + self.p1, + self.p2, + self.p3, + self.color.into(), + ) + } + } else { + unsafe { + sys::ImDrawList_AddTriangle( + self.draw_list.draw_list(), + self.p1, + self.p2, + self.p3, + self.color.into(), + self.thickness, + ) + } + } + } +} + /// Represents a circle about to be drawn pub struct Circle<'ui, D: 'ui> { center: ImVec2, From 35db5fca7273eedf1e1ffd0d03bdc9bc8b4f6645 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 11:42:36 +0900 Subject: [PATCH 06/28] window_draw_list.rs: Wrap add_bezier_curve --- src/window_draw_list.rs | 84 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 12cb00c..7cd3e25 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -198,6 +198,26 @@ macro_rules! impl_draw_list_methods { { Circle::new(self, center, radius, color) } + + /// Returns a Bezier curve stretching from `pos0` to `pos1`, whose + /// curvature is defined by `cp0` and `cp1`. + pub fn add_bezier_curve( + &self, + pos0: P1, + cp0: P2, + cp1: P3, + pos1: P4, + color: C, + ) -> BezierCurve<'ui, $T> + where + P1: Into, + P2: Into, + P3: Into, + P4: Into, + C: Into, + { + BezierCurve::new(self, pos0, cp0, cp1, pos1, color) + } } }; } @@ -495,3 +515,67 @@ impl<'ui, D: DrawAPI<'ui>> Circle<'ui, D> { } } } + +/// Represents a Bezier curve about to be drawn +pub struct BezierCurve<'ui, D: 'ui> { + pos0: ImVec2, + cp0: ImVec2, + pos1: ImVec2, + cp1: ImVec2, + color: ImColor, + thickness: f32, + /// If num_segments is not set, the bezier curve is auto-tessalated. + num_segments: Option, + draw_list: &'ui D, +} + +impl<'ui, D: DrawAPI<'ui>> BezierCurve<'ui, D> { + fn new(draw_list: &'ui D, pos0: P1, cp0: P2, cp1: P3, pos1: P4, c: C) -> Self + where + P1: Into, + P2: Into, + P3: Into, + P4: Into, + C: Into, + { + Self { + pos0: pos0.into(), + cp0: cp0.into(), + pos1: pos1.into(), + cp1: cp1.into(), + color: c.into(), + thickness: 1.0, + num_segments: None, + draw_list, + } + } + + /// Set curve's thickness (default to 1.0 pixel) + pub fn thickness(mut self, thickness: f32) -> Self { + self.thickness = thickness; + self + } + + /// Set number of segments used to draw the Bezier curve. If not set, the + /// bezier curve is auto-tessalated. + pub fn num_segments(mut self, num_segments: u32) -> Self { + self.num_segments = Some(num_segments); + self + } + + /// Draw the curve on the window. + pub fn build(self) { + unsafe { + sys::ImDrawList_AddBezierCurve( + self.draw_list.draw_list(), + self.pos0, + self.cp0, + self.cp1, + self.pos1, + self.color.into(), + self.thickness, + self.num_segments.unwrap_or(0) as i32, + ) + } + } +} From 4358360d47e61953f6916311dfcffe76424838d1 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 13:47:14 +0900 Subject: [PATCH 07/28] window_draw_list.rs: Wrap ImDrawList_PushClipRect This commit adds two methods to the drawing APIs: with_clip_rect and with_clip_rect_intersect. Both wrap ImDrawList_PushClipRect and ImDrawList_PopClipRect. However, with_clip_rect_intersect sets the `intersect` argument of ImDrawList_PushClipRect to `true`. --- src/window_draw_list.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 7cd3e25..271df1e 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -218,6 +218,41 @@ macro_rules! impl_draw_list_methods { { BezierCurve::new(self, pos0, cp0, cp1, pos1, color) } + + /// Push a clipping rectangle on the stack, run `f` and pop it. + /// + /// Clip all drawings done within the closure `f` in the given + /// rectangle. + pub fn with_clip_rect(&self, min: P1, max: P2, f: F) + where + P1: Into, + P2: Into, + F: FnOnce(), + { + unsafe { + sys::ImDrawList_PushClipRect(self.draw_list(), min.into(), max.into(), false) + } + f(); + unsafe { sys::ImDrawList_PopClipRect(self.draw_list()) } + } + + /// Push a clipping rectangle on the stack, run `f` and pop it. + /// + /// Clip all drawings done within the closure `f` in the given + /// rectangle. Intersect with all clipping rectangle previously on + /// the stack. + pub fn with_clip_rect_intersect(&self, min: P1, max: P2, f: F) + where + P1: Into, + P2: Into, + F: FnOnce(), + { + unsafe { + sys::ImDrawList_PushClipRect(self.draw_list(), min.into(), max.into(), true) + } + f(); + unsafe { sys::ImDrawList_PopClipRect(self.draw_list()) } + } } }; } From a60465cd4093d7f4566fc111683c6dd9d11e37aa Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 11:53:18 +0900 Subject: [PATCH 08/28] ui: Wrap dummy --- src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 40e7833..1e3a119 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -469,6 +469,13 @@ impl<'ui> Ui<'ui> { pub fn get_columns_count(&self) -> i32 { unsafe { sys::igGetColumnsCount() } } + /// Fill a space of `size` in pixels with nothing on the current window. + /// Can be used to move the cursor on the window. + pub fn dummy>(&self, size: S) { + let size = size.into(); + unsafe { sys::igDummy(&size) } + } + /// Get cursor position on the screen, in screen coordinates. /// This sets the point on which the next widget will be drawn. /// From 15048ad6981fa5e696332e649a4d637e43f03891 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Mon, 26 Mar 2018 15:41:03 +0900 Subject: [PATCH 09/28] ui: Add mouse helper methods is_mouse_{dragging,down,clicked} This commit defines a new enum: `ImMouseButton`, which is used in the public-facing API of mouse-related methods. The discriminant values of the ImMouseButton enum are the value used internally by ImGui to represent the buttons of the mouse. --- src/lib.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1e3a119..73a675f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,15 @@ pub fn get_version() -> &'static str { } } +/// Represents one of the buttons of the mouse +pub enum ImMouseButton { + Left = 0, + Right = 1, + Middle = 2, + Extra1 = 3, + Extra2 = 4, +} + impl ImGui { pub fn init() -> ImGui { ImGui { @@ -231,6 +240,25 @@ impl ImGui { sys::igGetMouseCursor() } } + /// Returns `true` if mouse is currently dragging with the `button` provided + /// as argument. + pub fn is_mouse_dragging(&self, button: ImMouseButton) -> bool { + unsafe { + sys::igIsMouseDragging(button as c_int, -1.0) + } + } + /// Returns `true` if the `button` provided as argument is currently down. + pub fn is_mouse_down(&self, button: ImMouseButton) -> bool { + unsafe { + sys::igIsMouseDown(button as c_int) + } + } + /// Returns `true` if the `button` provided as argument is being clicked. + pub fn is_mouse_clicked(&self, button: ImMouseButton) -> bool { + unsafe { + sys::igIsMouseClicked(button as c_int, false) + } + } pub fn key_ctrl(&self) -> bool { let io = self.io(); io.key_ctrl From c58a93bfc13edda2f82d05efd3562ab8d6b23147 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Fri, 30 Mar 2018 09:53:27 +0900 Subject: [PATCH 10/28] ui: Wrap invisible_button --- src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 73a675f..5b0f224 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -610,6 +610,11 @@ impl<'ui> Ui<'ui> { pub fn small_button<'p>(&self, label: &'p ImStr) -> bool { unsafe { sys::igSmallButton(label.as_ptr()) } } + /// Make a invisible event. Can be used to conveniently catch events when + /// mouse hovers or click the area covered by this invisible button. + pub fn invisible_button<'p, S: Into>(&self, label: &'p ImStr, size: S) -> bool { + unsafe { sys::igInvisibleButton(label.as_ptr(), size.into()) } + } pub fn checkbox<'p>(&self, label: &'p ImStr, value: &'p mut bool) -> bool { unsafe { sys::igCheckbox(label.as_ptr(), value) } } From 738a9c978ea0cec1640d5e3d864b5e9b00bc0164 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 14:21:12 +0900 Subject: [PATCH 11/28] ui: Wrap get_content_region_avail --- src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5b0f224..82dfb1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -537,6 +537,16 @@ impl<'ui> Ui<'ui> { pub fn set_cursor_pos>(&self, pos: P) { unsafe { sys::igSetCursorPos(pos.into()) } } + + /// Get available space left between the cursor and the edges of the current + /// window. + pub fn get_content_region_avail(&self) -> (f32, f32) { + let mut out = ImVec2::new(0.0, 0.0); + unsafe { + sys::igGetContentRegionAvail(&mut out); + } + (out.x, out.y) + } } // ID scopes From ab908106d3d99d3f506ea1794359037ccae0cc9d Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Wed, 28 Mar 2018 16:23:37 +0900 Subject: [PATCH 12/28] test_window_impl.rs: Add first part half of show_example_app_custom_rendering show_example_app_custom_rendering is implemented exactly as it is in the original Dear ImGui in C++. The result should be the same. The only difference is that `DragFloat`, used to control the size of the drawings, is not implement as of now. This example demonstrates how the custom drawing API can be used. --- imgui-examples/examples/test_window_impl.rs | 165 ++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index c9cc5c8..71f33d4 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -47,6 +47,7 @@ struct State { file_menu: FileMenuState, radio_button: i32, color_edit: ColorEditState, + custom_rendering: CustomRenderingState, } impl Default for State { @@ -95,6 +96,7 @@ impl Default for State { file_menu: Default::default(), radio_button: 0, color_edit: ColorEditState::default(), + custom_rendering: Default::default(), } } } @@ -155,6 +157,20 @@ impl Default for AutoResizeState { fn default() -> Self { AutoResizeState { lines: 10 } } } +struct CustomRenderingState { + sz: f32, + col: [f32; 3], +} + +impl Default for CustomRenderingState { + fn default() -> Self { + CustomRenderingState { + sz: 36.0, + col: [1.0, 1.0, 0.4], + } + } +} + const CLEAR_COLOR: [f32; 4] = [114.0 / 255.0, 144.0 / 255.0, 154.0 / 255.0, 1.0]; fn main() { @@ -239,6 +255,14 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { }); } + if state.show_app_custom_rendering { + show_example_app_custom_rendering( + ui, + &mut state.custom_rendering, + &mut state.show_app_custom_rendering, + ); + } + ui.window(im_str!("ImGui Demo")) .title_bar(!state.no_titlebar) .show_borders(!state.no_border) @@ -742,3 +766,144 @@ My title is the same as window 1, but my identifier is unique.", .position((100.0, 300.0), ImGuiCond::FirstUseEver) .build(|| ui.text("This window has a changing title")); } + +fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, opened: &mut bool) { + ui.window(im_str!("Example: Custom rendering")) + .size((350.0, 560.0), ImGuiCond::FirstUseEver) + .opened(opened) + .build(|| { + ui.text("Primitives"); + // TODO: Add DragFloat to change value of sz + ui.color_edit(im_str!("Color"), &mut state.col).build(); + let p = ui.get_cursor_screen_pos(); + let spacing = 8.0; + let mut y = p.1 + 4.0; + for n in 0..2 { + let mut x = p.0 + 4.0; + let thickness = if n == 0 { 1.0 } else { 4.0 }; + ui.with_window_draw_list(|draw_list| { + draw_list + .add_circle( + (x + state.sz * 0.5, y + state.sz * 0.5), + state.sz * 0.5, + state.col, + ) + .num_segments(20) + .thickness(thickness) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .thickness(thickness) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .thickness(thickness) + .rounding(10.0) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .thickness(thickness) + .rounding(10.0) + .round_top_right(false) + .round_bot_left(false) + .build(); + x += state.sz + spacing; + draw_list + .add_triangle( + (x + state.sz * 0.5, y), + (x + state.sz, y + state.sz - 0.5), + (x, y + state.sz - 0.5), + state.col, + ) + .thickness(thickness) + .build(); + x += state.sz + spacing; + draw_list + .add_line((x, y), (x + state.sz, y), state.col) + .thickness(thickness) + .build(); + x += state.sz + spacing; + draw_list + .add_line((x, y), (x + state.sz, y + state.sz), state.col) + .thickness(thickness) + .build(); + x += state.sz + spacing; + draw_list + .add_line((x, y), (x, y + state.sz), state.col) + .thickness(thickness) + .build(); + x += spacing; + draw_list + .add_bezier_curve( + (x, y), + (x + state.sz * 1.3, y + state.sz * 0.3), + (x + state.sz - state.sz * 1.3, y + state.sz - state.sz * 0.3), + (x + state.sz, y + state.sz), + state.col, + ) + .thickness(thickness) + .build(); + }); + y += state.sz + spacing; + } + ui.with_window_draw_list(|draw_list| { + let mut x = p.0 + 4.0; + draw_list + .add_circle( + (x + state.sz * 0.5, y + state.sz * 0.5), + state.sz * 0.5, + state.col, + ) + .num_segments(32) + .filled(true) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .filled(true) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .filled(true) + .rounding(10.0) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .filled(true) + .rounding(10.0) + .round_top_right(false) + .round_bot_left(false) + .build(); + x += state.sz + spacing; + draw_list + .add_triangle( + (x + state.sz * 0.5, y), + (x + state.sz, y + state.sz - 0.5), + (x, y + state.sz - 0.5), + state.col, + ) + .filled(true) + .build(); + x += state.sz + spacing; + const MULTICOLOR_RECT_CORNER_COLOR1: [f32; 3] = [0.0, 0.0, 0.0]; + const MULTICOLOR_RECT_CORNER_COLOR2: [f32; 3] = [1.0, 0.0, 0.0]; + const MULTICOLOR_RECT_CORNER_COLOR3: [f32; 3] = [1.0, 1.0, 0.0]; + const MULTICOLOR_RECT_CORNER_COLOR4: [f32; 3] = [0.0, 1.0, 0.0]; + draw_list.add_rect_filled_multicolor( + (x, y), + (x + state.sz, y + state.sz), + MULTICOLOR_RECT_CORNER_COLOR1, + MULTICOLOR_RECT_CORNER_COLOR2, + MULTICOLOR_RECT_CORNER_COLOR3, + MULTICOLOR_RECT_CORNER_COLOR4, + ); + }); + ui.dummy(((state.sz + spacing) * 8.0, (state.sz + spacing) * 3.0)); + ui.separator(); + }); +} From c27748852d1926710e2b632771bcfdbe38a76ca3 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Thu, 29 Mar 2018 13:48:09 +0900 Subject: [PATCH 13/28] test_window_impl: show_example_app_custom_rendering: Include drawing canvas Include drawing canvas example into show_example_app_custom_rendering. The example contains now everything included in the original C++ example provided with dear imgui. --- imgui-examples/examples/test_window_impl.rs | 110 ++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 71f33d4..8cfb3c4 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -160,6 +160,8 @@ impl Default for AutoResizeState { struct CustomRenderingState { sz: f32, col: [f32; 3], + points: Vec<(f32, f32)>, + adding_line: bool, } impl Default for CustomRenderingState { @@ -167,6 +169,8 @@ impl Default for CustomRenderingState { CustomRenderingState { sz: 36.0, col: [1.0, 1.0, 0.4], + points: vec![], + adding_line: false, } } } @@ -905,5 +909,111 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, }); ui.dummy(((state.sz + spacing) * 8.0, (state.sz + spacing) * 3.0)); ui.separator(); + + ui.text(im_str!("Canvas example")); + if ui.button(im_str!("Clear"), (0.0, 0.0)) { + state.points.clear(); + } + if state.points.len() >= 2 { + ui.same_line(0.0); + if ui.button(im_str!("Undo"), (0.0, 0.0)) { + state.points.pop(); + state.points.pop(); + } + } + ui.text(im_str!( + "Left-click and drag to add lines,\nRight-click to undo" + )); + // Here we are using InvisibleButton() as a convenience to + // 1) advance the cursor, and + // 2) allows us to use IsItemHovered() + // However you can draw directly and poll mouse/keyboard by + // yourself. You can manipulate the cursor using GetCursorPos() and + // SetCursorPos(). If you only use the ImDrawList API, you can + // notify the owner window of its extends by using + // SetCursorPos(max). + + // ImDrawList API uses screen coordinates! + let canvas_pos = ui.get_cursor_screen_pos(); + // Resize canvas to what's available + let mut canvas_size = ui.get_content_region_avail(); + if canvas_size.0 < 50.0 { + canvas_size.0 = 50.0; + } + if canvas_size.1 < 50.0 { + canvas_size.1 = 50.0; + } + ui.with_window_draw_list(|draw_list| { + const CANVAS_CORNER_COLOR1: [f32; 3] = [0.2, 0.2, 0.2]; + const CANVAS_CORNER_COLOR2: [f32; 3] = [0.2, 0.2, 0.24]; + const CANVAS_CORNER_COLOR3: [f32; 3] = [0.24, 0.24, 0.27]; + const CANVAS_CORNER_COLOR4: [f32; 3] = [0.2, 0.2, 0.24]; + draw_list.add_rect_filled_multicolor( + canvas_pos, + (canvas_pos.0 + canvas_size.0, canvas_pos.1 + canvas_size.1), + CANVAS_CORNER_COLOR1, + CANVAS_CORNER_COLOR2, + CANVAS_CORNER_COLOR3, + CANVAS_CORNER_COLOR4, + ); + const CANVAS_BORDER_COLOR: [f32; 3] = [1.0, 1.0, 1.0]; + draw_list + .add_rect( + canvas_pos, + (canvas_pos.0 + canvas_size.0, canvas_pos.1 + canvas_size.1), + CANVAS_BORDER_COLOR, + ) + .build(); + + let mut adding_preview = false; + ui.invisible_button(im_str!("canvas"), canvas_size); + let mouse_pos = ui.imgui().mouse_pos(); + let mouse_pos_in_canvas = (mouse_pos.0 - canvas_pos.0, mouse_pos.1 - canvas_pos.1); + if state.adding_line { + adding_preview = true; + state.points.push(mouse_pos_in_canvas); + if !ui.imgui().is_mouse_down(ImMouseButton::Left) { + state.adding_line = false; + adding_preview = false; + } + } + if ui.is_item_hovered() { + if !state.adding_line && ui.imgui().is_mouse_clicked(ImMouseButton::Left) { + state.points.push(mouse_pos_in_canvas); + state.adding_line = true; + } + if ui.imgui().is_mouse_clicked(ImMouseButton::Right) && !state.points.is_empty() + { + state.adding_line = false; + adding_preview = false; + state.points.pop(); + state.points.pop(); + } + } + draw_list.with_clip_rect_intersect( + canvas_pos, + (canvas_pos.0 + canvas_size.0, canvas_pos.1 + canvas_size.1), + || { + const LINE_COLOR: [f32; 3] = [1.0, 1.0, 0.0]; + for line in state.points.chunks(2) { + if line.len() < 2 { + break; + } + let (p1, p2) = (line[0], line[1]); + draw_list + .add_line( + (canvas_pos.0 + p1.0, canvas_pos.1 + p1.1), + (canvas_pos.0 + p2.0, canvas_pos.1 + p2.1), + LINE_COLOR, + ) + .thickness(2.0) + .build(); + } + }, + ); + if adding_preview { + state.points.pop(); + } + }); }); } From 2312f4121a93c232a7f44668b7f0d56a11d87525 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Fri, 30 Mar 2018 09:06:50 +0900 Subject: [PATCH 14/28] test_drawing_channels_split: Add an example for channels_split As channels_split may be difficult to understand, this commit adds a simple example of its usage. --- .../examples/test_drawing_channels_split.rs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 imgui-examples/examples/test_drawing_channels_split.rs diff --git a/imgui-examples/examples/test_drawing_channels_split.rs b/imgui-examples/examples/test_drawing_channels_split.rs new file mode 100644 index 0000000..38954cd --- /dev/null +++ b/imgui-examples/examples/test_drawing_channels_split.rs @@ -0,0 +1,44 @@ +extern crate glium; +extern crate imgui; +extern crate imgui_glium_renderer; + +mod support; + +const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.0]; +const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0]; +const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; + +fn main() { + support::run("test_drawing_channels_split".to_owned(), CLEAR_COLOR, |ui| { + ui.with_window_draw_list(|draw_list| { + // Will draw channel 0 first, then channel 1, whatever the order of + // the calls in the code. + // + // Here, we draw a red line on channel 1 then a white circle on + // channel 0. As a result, the red line will always appear on top of + // the white circle. + draw_list.channels_split(2, |draw_list| { + const RADIUS: f32 = 100.0; + let canvas_pos = ui.get_cursor_screen_pos(); + draw_list.channels_set_current(1); + draw_list + .add_line( + canvas_pos, + [canvas_pos.0 + RADIUS, canvas_pos.1 + RADIUS], + RED, + ) + .thickness(5.0) + .build(); + + draw_list.channels_set_current(0); + let center = (canvas_pos.0 + RADIUS, canvas_pos.1 + RADIUS); + draw_list + .add_circle(center, RADIUS, WHITE) + .thickness(10.0) + .num_segments(50) + .build(); + }); + }); + true + }); +} From 664660a09095c7e2fc59ed5793ba090f84830639 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Fri, 30 Mar 2018 12:25:26 +0900 Subject: [PATCH 15/28] Add missing imports in example code --- src/lib.rs | 2 ++ src/window_draw_list.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 82dfb1d..2f7ada1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1271,6 +1271,8 @@ impl<'ui> Ui<'ui> { /// # Examples /// /// ``` + /// # extern crate imgui; + /// # use imgui::*; /// fn custom_draw(ui: &Ui) { /// ui.with_window_draw_list(|draw_list| { /// // Draw a line diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 271df1e..cd9e470 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -79,6 +79,8 @@ impl<'ui> WindowDrawList<'ui> { /// # Example /// /// ``` + /// # extern crate imgui; + /// # use imgui::*; /// fn custom_drawing(ui: &Ui) { /// ui.with_window_draw_list(|draw_list| { /// draw_list.channels_split(2, |draw_list| { From eab85ea8719ea2129ab97716aa0745bcd8cc229b Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 8 Apr 2018 09:08:02 +0900 Subject: [PATCH 16/28] with_window_draw_list: Fix example test The test was failing because no `main` function was defined, as explained in the rustdoc documentation [1]. Add the "no_run" flag. This way, there is no attempt to run the test code. Only compile checks are done. Thus defining a `main` function is unnecessary. Moreover "export crate imgui" is implicit, so removed. [1] https://doc.rust-lang.org/stable/rustdoc/documentation-tests.html#pre-processing-examples. --- src/lib.rs | 3 +-- src/window_draw_list.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2f7ada1..753d9db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1270,8 +1270,7 @@ impl<'ui> Ui<'ui> { /// /// # Examples /// - /// ``` - /// # extern crate imgui; + /// ```rust,no_run /// # use imgui::*; /// fn custom_draw(ui: &Ui) { /// ui.with_window_draw_list(|draw_list| { diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index cd9e470..2257fae 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -78,8 +78,7 @@ impl<'ui> WindowDrawList<'ui> { /// /// # Example /// - /// ``` - /// # extern crate imgui; + /// ```rust,no_run /// # use imgui::*; /// fn custom_drawing(ui: &Ui) { /// ui.with_window_draw_list(|draw_list| { From 43a2484c22f66b1f2e4fcfa9027e0dfe47d46fdc Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 11:36:14 +0900 Subject: [PATCH 17/28] ImMouseButton: Derive Copy, Clone, Eq, PartialEq, Debug For convenience. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 753d9db..8d0bbf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,6 +87,7 @@ pub fn get_version() -> &'static str { } /// Represents one of the buttons of the mouse +#[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum ImMouseButton { Left = 0, Right = 1, From 746bdc5ef3e03a1528a8144cca963b6709ed0f9c Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 11:38:53 +0900 Subject: [PATCH 18/28] ImColor: Derive Eq, PartialEq, Debug --- src/window_draw_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 2257fae..7bb4e5c 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -12,7 +12,7 @@ use std::marker::PhantomData; /// The type implements `From`, `From`, `From<[f32; 4]>`, /// `From<[f32; 3]>`, `From<(f32, f32, f32, f32)>` and `From<(f32, f32, f32)>` /// for convenience. If alpha is not provided, it is assumed to be 1.0 (255). -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] pub struct ImColor(ImU32); impl From for ImU32 { From 7be9f15acbe0b92d475922201a3cb72547cde0be Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 11:42:06 +0900 Subject: [PATCH 19/28] window_draw_list: Fix typo in doc --- src/window_draw_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 7bb4e5c..9e3688c 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -409,7 +409,7 @@ impl<'ui, D: DrawAPI<'ui>> Rect<'ui, D> { } } -/// Represents a circle about to be drawn on the window +/// Represents a triangle about to be drawn on the window pub struct Triangle<'ui, D: 'ui> { p1: ImVec2, p2: ImVec2, From 3aa807b173ba7ceff44cc0aad63735e2b71f79f0 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 11:54:54 +0900 Subject: [PATCH 20/28] DrawAPI: Remove unused lifetime --- src/window_draw_list.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 9e3688c..0897dbb 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -48,7 +48,7 @@ impl From<(f32, f32, f32)> for ImColor { /// All types from which ImGui's custom draw API can be used implement this /// trait. This trait is internal to this library and implemented by /// `WindowDrawList` and `ChannelsSplit`. -pub trait DrawAPI<'ui> { +pub trait DrawAPI { /// Get draw_list object fn draw_list(&self) -> *mut ImDrawList; } @@ -59,7 +59,7 @@ pub struct WindowDrawList<'ui> { _phantom: PhantomData<&'ui Ui<'ui>>, } -impl<'ui> DrawAPI<'ui> for WindowDrawList<'ui> { +impl<'ui> DrawAPI for WindowDrawList<'ui> { fn draw_list(&self) -> *mut ImDrawList { self.draw_list } } @@ -101,7 +101,7 @@ impl<'ui> WindowDrawList<'ui> { /// Represent the drawing interface within a call to `channels_split`. pub struct ChannelsSplit<'ui>(&'ui WindowDrawList<'ui>); -impl<'ui> DrawAPI<'ui> for ChannelsSplit<'ui> { +impl<'ui> DrawAPI for ChannelsSplit<'ui> { fn draw_list(&self) -> *mut ImDrawList { self.0.draw_list } } @@ -116,7 +116,7 @@ macro_rules! impl_draw_list_methods { ($T: ident) => { impl<'ui> $T<'ui> where - $T<'ui>: DrawAPI<'ui>, + $T<'ui>: DrawAPI, { /// Returns a line from point `p1` to `p2` with color `c`. pub fn add_line(&self, p1: P1, p2: P2, c: C) -> Line<'ui, $T> @@ -270,7 +270,7 @@ pub struct Line<'ui, D: 'ui> { draw_list: &'ui D, } -impl<'ui, D: DrawAPI<'ui>> Line<'ui, D> { +impl<'ui, D: DrawAPI> Line<'ui, D> { fn new(draw_list: &'ui D, p1: P1, p2: P2, c: C) -> Self where P1: Into, @@ -318,7 +318,7 @@ pub struct Rect<'ui, D: 'ui> { draw_list: &'ui D, } -impl<'ui, D: DrawAPI<'ui>> Rect<'ui, D> { +impl<'ui, D: DrawAPI> Rect<'ui, D> { fn new(draw_list: &'ui D, p1: P1, p2: P2, c: C) -> Self where P1: Into, @@ -420,7 +420,7 @@ pub struct Triangle<'ui, D: 'ui> { draw_list: &'ui D, } -impl<'ui, D: DrawAPI<'ui>> Triangle<'ui, D> { +impl<'ui, D: DrawAPI> Triangle<'ui, D> { fn new(draw_list: &'ui D, p1: P1, p2: P2, p3: P3, c: C) -> Self where P1: Into, @@ -489,7 +489,7 @@ pub struct Circle<'ui, D: 'ui> { draw_list: &'ui D, } -impl<'ui, D: DrawAPI<'ui>> Circle<'ui, D> { +impl<'ui, D: DrawAPI> Circle<'ui, D> { pub fn new(draw_list: &'ui D, center: P, radius: f32, color: C) -> Self where P: Into, @@ -565,7 +565,7 @@ pub struct BezierCurve<'ui, D: 'ui> { draw_list: &'ui D, } -impl<'ui, D: DrawAPI<'ui>> BezierCurve<'ui, D> { +impl<'ui, D: DrawAPI> BezierCurve<'ui, D> { fn new(draw_list: &'ui D, pos0: P1, cp0: P2, cp1: P3, pos1: P4, c: C) -> Self where P1: Into, From 9a65a64eca4554d295c41da1dc31af671c8f4051 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 15:21:42 +0900 Subject: [PATCH 21/28] WindowDrawList: Cannot use WindowDrawList::new outside of crate --- src/window_draw_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 0897dbb..bb3e1a3 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -64,7 +64,7 @@ impl<'ui> DrawAPI for WindowDrawList<'ui> { } impl<'ui> WindowDrawList<'ui> { - pub fn new(_: &Ui<'ui>) -> Self { + pub(crate) fn new(_: &Ui<'ui>) -> Self { Self { draw_list: unsafe { sys::igGetWindowDrawList() }, _phantom: PhantomData, From 95577a0d8c42371812e3393ce868b186508ffd18 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 16:19:35 +0900 Subject: [PATCH 22/28] ui: Rename to with_window_draw_list ot get_window_draw_list --- .../examples/test_drawing_channels_split.rs | 13 +- imgui-examples/examples/test_window_impl.rs | 302 +++++++++--------- src/lib.rs | 19 +- src/window_draw_list.rs | 13 +- 4 files changed, 169 insertions(+), 178 deletions(-) diff --git a/imgui-examples/examples/test_drawing_channels_split.rs b/imgui-examples/examples/test_drawing_channels_split.rs index 38954cd..6cccf8f 100644 --- a/imgui-examples/examples/test_drawing_channels_split.rs +++ b/imgui-examples/examples/test_drawing_channels_split.rs @@ -9,8 +9,11 @@ const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0]; const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; fn main() { - support::run("test_drawing_channels_split".to_owned(), CLEAR_COLOR, |ui| { - ui.with_window_draw_list(|draw_list| { + support::run( + "test_drawing_channels_split".to_owned(), + CLEAR_COLOR, + |ui| { + let draw_list = ui.get_window_draw_list(); // Will draw channel 0 first, then channel 1, whatever the order of // the calls in the code. // @@ -38,7 +41,7 @@ fn main() { .num_segments(50) .build(); }); - }); - true - }); + true + }, + ); } diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 8cfb3c4..2c589f7 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -779,106 +779,37 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, ui.text("Primitives"); // TODO: Add DragFloat to change value of sz ui.color_edit(im_str!("Color"), &mut state.col).build(); + let draw_list = ui.get_window_draw_list(); let p = ui.get_cursor_screen_pos(); let spacing = 8.0; let mut y = p.1 + 4.0; for n in 0..2 { let mut x = p.0 + 4.0; let thickness = if n == 0 { 1.0 } else { 4.0 }; - ui.with_window_draw_list(|draw_list| { - draw_list - .add_circle( - (x + state.sz * 0.5, y + state.sz * 0.5), - state.sz * 0.5, - state.col, - ) - .num_segments(20) - .thickness(thickness) - .build(); - x += state.sz + spacing; - draw_list - .add_rect((x, y), (x + state.sz, y + state.sz), state.col) - .thickness(thickness) - .build(); - x += state.sz + spacing; - draw_list - .add_rect((x, y), (x + state.sz, y + state.sz), state.col) - .thickness(thickness) - .rounding(10.0) - .build(); - x += state.sz + spacing; - draw_list - .add_rect((x, y), (x + state.sz, y + state.sz), state.col) - .thickness(thickness) - .rounding(10.0) - .round_top_right(false) - .round_bot_left(false) - .build(); - x += state.sz + spacing; - draw_list - .add_triangle( - (x + state.sz * 0.5, y), - (x + state.sz, y + state.sz - 0.5), - (x, y + state.sz - 0.5), - state.col, - ) - .thickness(thickness) - .build(); - x += state.sz + spacing; - draw_list - .add_line((x, y), (x + state.sz, y), state.col) - .thickness(thickness) - .build(); - x += state.sz + spacing; - draw_list - .add_line((x, y), (x + state.sz, y + state.sz), state.col) - .thickness(thickness) - .build(); - x += state.sz + spacing; - draw_list - .add_line((x, y), (x, y + state.sz), state.col) - .thickness(thickness) - .build(); - x += spacing; - draw_list - .add_bezier_curve( - (x, y), - (x + state.sz * 1.3, y + state.sz * 0.3), - (x + state.sz - state.sz * 1.3, y + state.sz - state.sz * 0.3), - (x + state.sz, y + state.sz), - state.col, - ) - .thickness(thickness) - .build(); - }); - y += state.sz + spacing; - } - ui.with_window_draw_list(|draw_list| { - let mut x = p.0 + 4.0; draw_list .add_circle( (x + state.sz * 0.5, y + state.sz * 0.5), state.sz * 0.5, state.col, ) - .num_segments(32) - .filled(true) + .num_segments(20) + .thickness(thickness) .build(); x += state.sz + spacing; draw_list .add_rect((x, y), (x + state.sz, y + state.sz), state.col) - .filled(true) + .thickness(thickness) .build(); x += state.sz + spacing; draw_list .add_rect((x, y), (x + state.sz, y + state.sz), state.col) - .filled(true) + .thickness(thickness) .rounding(10.0) .build(); x += state.sz + spacing; draw_list .add_rect((x, y), (x + state.sz, y + state.sz), state.col) - .filled(true) + .thickness(thickness) .rounding(10.0) .round_top_right(false) .round_bot_left(false) @@ -891,22 +822,88 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, (x, y + state.sz - 0.5), state.col, ) - .filled(true) + .thickness(thickness) .build(); x += state.sz + spacing; - const MULTICOLOR_RECT_CORNER_COLOR1: [f32; 3] = [0.0, 0.0, 0.0]; - const MULTICOLOR_RECT_CORNER_COLOR2: [f32; 3] = [1.0, 0.0, 0.0]; - const MULTICOLOR_RECT_CORNER_COLOR3: [f32; 3] = [1.0, 1.0, 0.0]; - const MULTICOLOR_RECT_CORNER_COLOR4: [f32; 3] = [0.0, 1.0, 0.0]; - draw_list.add_rect_filled_multicolor( - (x, y), - (x + state.sz, y + state.sz), - MULTICOLOR_RECT_CORNER_COLOR1, - MULTICOLOR_RECT_CORNER_COLOR2, - MULTICOLOR_RECT_CORNER_COLOR3, - MULTICOLOR_RECT_CORNER_COLOR4, - ); - }); + draw_list + .add_line((x, y), (x + state.sz, y), state.col) + .thickness(thickness) + .build(); + x += state.sz + spacing; + draw_list + .add_line((x, y), (x + state.sz, y + state.sz), state.col) + .thickness(thickness) + .build(); + x += state.sz + spacing; + draw_list + .add_line((x, y), (x, y + state.sz), state.col) + .thickness(thickness) + .build(); + x += spacing; + draw_list + .add_bezier_curve( + (x, y), + (x + state.sz * 1.3, y + state.sz * 0.3), + (x + state.sz - state.sz * 1.3, y + state.sz - state.sz * 0.3), + (x + state.sz, y + state.sz), + state.col, + ) + .thickness(thickness) + .build(); + y += state.sz + spacing; + } + let mut x = p.0 + 4.0; + draw_list + .add_circle( + (x + state.sz * 0.5, y + state.sz * 0.5), + state.sz * 0.5, + state.col, + ) + .num_segments(32) + .filled(true) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .filled(true) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .filled(true) + .rounding(10.0) + .build(); + x += state.sz + spacing; + draw_list + .add_rect((x, y), (x + state.sz, y + state.sz), state.col) + .filled(true) + .rounding(10.0) + .round_top_right(false) + .round_bot_left(false) + .build(); + x += state.sz + spacing; + draw_list + .add_triangle( + (x + state.sz * 0.5, y), + (x + state.sz, y + state.sz - 0.5), + (x, y + state.sz - 0.5), + state.col, + ) + .filled(true) + .build(); + x += state.sz + spacing; + const MULTICOLOR_RECT_CORNER_COLOR1: [f32; 3] = [0.0, 0.0, 0.0]; + const MULTICOLOR_RECT_CORNER_COLOR2: [f32; 3] = [1.0, 0.0, 0.0]; + const MULTICOLOR_RECT_CORNER_COLOR3: [f32; 3] = [1.0, 1.0, 0.0]; + const MULTICOLOR_RECT_CORNER_COLOR4: [f32; 3] = [0.0, 1.0, 0.0]; + draw_list.add_rect_filled_multicolor( + (x, y), + (x + state.sz, y + state.sz), + MULTICOLOR_RECT_CORNER_COLOR1, + MULTICOLOR_RECT_CORNER_COLOR2, + MULTICOLOR_RECT_CORNER_COLOR3, + MULTICOLOR_RECT_CORNER_COLOR4, + ); ui.dummy(((state.sz + spacing) * 8.0, (state.sz + spacing) * 3.0)); ui.separator(); @@ -943,77 +940,74 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, if canvas_size.1 < 50.0 { canvas_size.1 = 50.0; } - ui.with_window_draw_list(|draw_list| { - const CANVAS_CORNER_COLOR1: [f32; 3] = [0.2, 0.2, 0.2]; - const CANVAS_CORNER_COLOR2: [f32; 3] = [0.2, 0.2, 0.24]; - const CANVAS_CORNER_COLOR3: [f32; 3] = [0.24, 0.24, 0.27]; - const CANVAS_CORNER_COLOR4: [f32; 3] = [0.2, 0.2, 0.24]; - draw_list.add_rect_filled_multicolor( + const CANVAS_CORNER_COLOR1: [f32; 3] = [0.2, 0.2, 0.2]; + const CANVAS_CORNER_COLOR2: [f32; 3] = [0.2, 0.2, 0.24]; + const CANVAS_CORNER_COLOR3: [f32; 3] = [0.24, 0.24, 0.27]; + const CANVAS_CORNER_COLOR4: [f32; 3] = [0.2, 0.2, 0.24]; + draw_list.add_rect_filled_multicolor( + canvas_pos, + (canvas_pos.0 + canvas_size.0, canvas_pos.1 + canvas_size.1), + CANVAS_CORNER_COLOR1, + CANVAS_CORNER_COLOR2, + CANVAS_CORNER_COLOR3, + CANVAS_CORNER_COLOR4, + ); + const CANVAS_BORDER_COLOR: [f32; 3] = [1.0, 1.0, 1.0]; + draw_list + .add_rect( canvas_pos, (canvas_pos.0 + canvas_size.0, canvas_pos.1 + canvas_size.1), - CANVAS_CORNER_COLOR1, - CANVAS_CORNER_COLOR2, - CANVAS_CORNER_COLOR3, - CANVAS_CORNER_COLOR4, - ); - const CANVAS_BORDER_COLOR: [f32; 3] = [1.0, 1.0, 1.0]; - draw_list - .add_rect( - canvas_pos, - (canvas_pos.0 + canvas_size.0, canvas_pos.1 + canvas_size.1), - CANVAS_BORDER_COLOR, - ) - .build(); + CANVAS_BORDER_COLOR, + ) + .build(); - let mut adding_preview = false; - ui.invisible_button(im_str!("canvas"), canvas_size); - let mouse_pos = ui.imgui().mouse_pos(); - let mouse_pos_in_canvas = (mouse_pos.0 - canvas_pos.0, mouse_pos.1 - canvas_pos.1); - if state.adding_line { - adding_preview = true; + let mut adding_preview = false; + ui.invisible_button(im_str!("canvas"), canvas_size); + let mouse_pos = ui.imgui().mouse_pos(); + let mouse_pos_in_canvas = (mouse_pos.0 - canvas_pos.0, mouse_pos.1 - canvas_pos.1); + if state.adding_line { + adding_preview = true; + state.points.push(mouse_pos_in_canvas); + if !ui.imgui().is_mouse_down(ImMouseButton::Left) { + state.adding_line = false; + adding_preview = false; + } + } + if ui.is_item_hovered() { + if !state.adding_line && ui.imgui().is_mouse_clicked(ImMouseButton::Left) { state.points.push(mouse_pos_in_canvas); - if !ui.imgui().is_mouse_down(ImMouseButton::Left) { - state.adding_line = false; - adding_preview = false; - } + state.adding_line = true; } - if ui.is_item_hovered() { - if !state.adding_line && ui.imgui().is_mouse_clicked(ImMouseButton::Left) { - state.points.push(mouse_pos_in_canvas); - state.adding_line = true; - } - if ui.imgui().is_mouse_clicked(ImMouseButton::Right) && !state.points.is_empty() - { - state.adding_line = false; - adding_preview = false; - state.points.pop(); - state.points.pop(); - } - } - draw_list.with_clip_rect_intersect( - canvas_pos, - (canvas_pos.0 + canvas_size.0, canvas_pos.1 + canvas_size.1), - || { - const LINE_COLOR: [f32; 3] = [1.0, 1.0, 0.0]; - for line in state.points.chunks(2) { - if line.len() < 2 { - break; - } - let (p1, p2) = (line[0], line[1]); - draw_list - .add_line( - (canvas_pos.0 + p1.0, canvas_pos.1 + p1.1), - (canvas_pos.0 + p2.0, canvas_pos.1 + p2.1), - LINE_COLOR, - ) - .thickness(2.0) - .build(); - } - }, - ); - if adding_preview { + if ui.imgui().is_mouse_clicked(ImMouseButton::Right) && !state.points.is_empty() { + state.adding_line = false; + adding_preview = false; + state.points.pop(); state.points.pop(); } - }); + } + draw_list.with_clip_rect_intersect( + canvas_pos, + (canvas_pos.0 + canvas_size.0, canvas_pos.1 + canvas_size.1), + || { + const LINE_COLOR: [f32; 3] = [1.0, 1.0, 0.0]; + for line in state.points.chunks(2) { + if line.len() < 2 { + break; + } + let (p1, p2) = (line[0], line[1]); + draw_list + .add_line( + (canvas_pos.0 + p1.0, canvas_pos.1 + p1.1), + (canvas_pos.0 + p2.0, canvas_pos.1 + p2.1), + LINE_COLOR, + ) + .thickness(2.0) + .build(); + } + }, + ); + if adding_preview { + state.points.pop(); + } }); } diff --git a/src/lib.rs b/src/lib.rs index 8d0bbf3..d3d38ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1274,19 +1274,14 @@ impl<'ui> Ui<'ui> { /// ```rust,no_run /// # use imgui::*; /// fn custom_draw(ui: &Ui) { - /// ui.with_window_draw_list(|draw_list| { - /// // Draw a line - /// const WHITE: [f32; 3] = [1.0, 1.0, 1.0]; - /// draw_list.add_line([100.0, 100.0], [200.0, 200.0], WHITE).build(); - /// // Continue drawing ... - /// }); + /// let draw_list = ui.get_window_draw_list(); + /// // Draw a line + /// const WHITE: [f32; 3] = [1.0, 1.0, 1.0]; + /// draw_list.add_line([100.0, 100.0], [200.0, 200.0], WHITE).build(); + /// // Continue drawing ... /// } /// ``` - pub fn with_window_draw_list(&self, f: F) - where - F: FnOnce(&WindowDrawList), - { - let window_draw_list = WindowDrawList::new(self); - f(&window_draw_list); + pub fn get_window_draw_list(&'ui self) -> WindowDrawList<'ui> { + WindowDrawList::new(self) } } diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index bb3e1a3..ac6835c 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -81,13 +81,12 @@ impl<'ui> WindowDrawList<'ui> { /// ```rust,no_run /// # use imgui::*; /// fn custom_drawing(ui: &Ui) { - /// ui.with_window_draw_list(|draw_list| { - /// draw_list.channels_split(2, |draw_list| { - /// draw_list.channels_set_current(1); - /// // ... Draw channel 1 - /// draw_list.channels_set_current(0); - /// // ... Draw channel 0 - /// }); + /// let draw_list = ui.get_window_draw_list(); + /// draw_list.channels_split(2, |draw_list| { + /// draw_list.channels_set_current(1); + /// // ... Draw channel 1 + /// draw_list.channels_set_current(0); + /// // ... Draw channel 0 /// }); /// } /// ``` From 29b0d20fc179a3af7fb975ceae90c3e05f7bfeb2 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 17:39:01 +0900 Subject: [PATCH 23/28] ChannelsSplit: Panic when channel index overflows --- src/window_draw_list.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index ac6835c..4ca16ab 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -92,21 +92,35 @@ impl<'ui> WindowDrawList<'ui> { /// ``` pub fn channels_split(&self, channels_count: u32, f: F) { unsafe { sys::ImDrawList_ChannelsSplit(self.draw_list, channels_count as i32) }; - f(&ChannelsSplit(self)); + f(&ChannelsSplit { + draw_list: self, + channels_count, + }); unsafe { sys::ImDrawList_ChannelsMerge(self.draw_list) }; } } /// Represent the drawing interface within a call to `channels_split`. -pub struct ChannelsSplit<'ui>(&'ui WindowDrawList<'ui>); +pub struct ChannelsSplit<'ui> { + draw_list: &'ui WindowDrawList<'ui>, + channels_count: u32, +} impl<'ui> DrawAPI for ChannelsSplit<'ui> { - fn draw_list(&self) -> *mut ImDrawList { self.0.draw_list } + fn draw_list(&self) -> *mut ImDrawList { self.draw_list.draw_list } } impl<'ui> ChannelsSplit<'ui> { /// Change current channel + /// + /// Panic if channel_index overflows the number of channels. pub fn channels_set_current(&self, channel_index: u32) { + assert!( + channel_index < self.channels_count, + "Channel cannot be set! Provided channel index ({}) is higher than channel count ({}).", + channel_index, + self.channels_count + ); unsafe { sys::ImDrawList_ChannelsSetCurrent(self.draw_list(), channel_index as i32) }; } } From 734292fdab412fc091f123e696e9b68c751e5b8f Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 17:43:13 +0900 Subject: [PATCH 24/28] imgui: Make ChannelsSplit struct visible outside the crate --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d3d38ba..0626174 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ pub use string::{ImStr, ImString}; pub use style::StyleVar; pub use trees::{CollapsingHeader, TreeNode}; pub use window::Window; -pub use window_draw_list::{ImColor, WindowDrawList}; +pub use window_draw_list::{ImColor, WindowDrawList, ChannelsSplit}; mod child_frame; mod color_editors; From 082d5e47f94963e6a931250751e1a6b14e0f49d7 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 17:45:55 +0900 Subject: [PATCH 25/28] window_draw_list: Fix awkward wording in doc --- src/window_draw_list.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 4ca16ab..d7a6679 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -74,7 +74,7 @@ impl<'ui> WindowDrawList<'ui> { /// Split into *channels_count* drawing channels. /// At the end of the closure, the channels are merged. The objects /// are then drawn in the increasing order of their channel number, and not - /// in the all order they were called. + /// in the order they were called. /// /// # Example /// @@ -100,7 +100,9 @@ impl<'ui> WindowDrawList<'ui> { } } -/// Represent the drawing interface within a call to `channels_split`. +/// Represent the drawing interface within a call to [`channels_split`]. +/// +/// [`channels_split`]: WindowDrawList::channels_split pub struct ChannelsSplit<'ui> { draw_list: &'ui WindowDrawList<'ui>, channels_count: u32, @@ -111,7 +113,7 @@ impl<'ui> DrawAPI for ChannelsSplit<'ui> { } impl<'ui> ChannelsSplit<'ui> { - /// Change current channel + /// Change current channel. /// /// Panic if channel_index overflows the number of channels. pub fn channels_set_current(&self, channel_index: u32) { From 454e98037ec0b4083999d9a3f92f8f12863ff0c2 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Sun, 15 Apr 2018 19:08:12 +0900 Subject: [PATCH 26/28] Do not allow to create coexisting instances of WindowDrawList At run time, the environment checks that at most one instance of WindowDrawList exists using a static boolean: WINDOW_DRAW_LIST_LOADED. If two WindowDrawList could exist at the same time, there would be several instances of the same `*mut ImDrawList`, which could lead to unfathomable bad things. When a WindowDrawList is created, WINDOW_DRAW_LIST_LOADED is set to true. And when it is dropped, WINDOW_DRAW_LIST_LOADED is set to false. Creating a new WindowDrawList while WINDOW_DRAW_LIST_LOADED is true causes a panic. AtomicBool could have been used instead of a bool for WINDOW_DRAW_LIST_LOADED. Though it allows to avoid the use of `unsafe { }`, the construct we are doing is already inherently unsafe. WindowDrawList and Ui are !Send and !Sync, so they cannot anyway be shared among threads. So we'd better be explicit and use an unsafe block with a normal bool. --- src/lib.rs | 15 +++++++++++++++ src/window_draw_list.rs | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 0626174..f97a91b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1281,6 +1281,21 @@ impl<'ui> Ui<'ui> { /// // Continue drawing ... /// } /// ``` + /// + /// This function will panic if several instances of [`WindowDrawList`] + /// coexist. Before a new instance is got, a previous instance should be + /// dropped. + /// + /// ```rust + /// # use imgui::*; + /// fn custom_draw(ui: &Ui) { + /// let draw_list = ui.get_window_draw_list(); + /// // Draw something... + /// + /// // This second call will panic! + /// let draw_list = ui.get_window_draw_list(); + /// } + /// ``` pub fn get_window_draw_list(&'ui self) -> WindowDrawList<'ui> { WindowDrawList::new(self) } diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index d7a6679..6d871f6 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -54,6 +54,10 @@ pub trait DrawAPI { } /// Object implementing the custom draw API. +/// +/// Called from [`Ui::get_window_draw_list`]. No more than one instance of this +/// structure can live in a program at the same time. +/// The program will panic on creating a second instance. pub struct WindowDrawList<'ui> { draw_list: *mut ImDrawList, _phantom: PhantomData<&'ui Ui<'ui>>, @@ -63,8 +67,22 @@ impl<'ui> DrawAPI for WindowDrawList<'ui> { fn draw_list(&self) -> *mut ImDrawList { self.draw_list } } +static mut WINDOW_DRAW_LIST_LOADED: bool = false; + +impl<'ui> Drop for WindowDrawList<'ui> { + fn drop(&mut self) { + unsafe { WINDOW_DRAW_LIST_LOADED = false; } + } +} + impl<'ui> WindowDrawList<'ui> { pub(crate) fn new(_: &Ui<'ui>) -> Self { + unsafe { + if WINDOW_DRAW_LIST_LOADED { + panic!("WindowDrawList is already loaded! You can only load one instance of it!") + } + WINDOW_DRAW_LIST_LOADED = true; + } Self { draw_list: unsafe { sys::igGetWindowDrawList() }, _phantom: PhantomData, From 9a9484ff2146785f834994ac3c7a708fe35ec3e0 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Tue, 24 Apr 2018 12:17:04 +0900 Subject: [PATCH 27/28] ChannelsSplit: Rename channels_set_current to set_current Fancier API --- imgui-examples/examples/test_drawing_channels_split.rs | 6 +++--- src/window_draw_list.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui-examples/examples/test_drawing_channels_split.rs b/imgui-examples/examples/test_drawing_channels_split.rs index 6cccf8f..d38d8b8 100644 --- a/imgui-examples/examples/test_drawing_channels_split.rs +++ b/imgui-examples/examples/test_drawing_channels_split.rs @@ -20,10 +20,10 @@ fn main() { // Here, we draw a red line on channel 1 then a white circle on // channel 0. As a result, the red line will always appear on top of // the white circle. - draw_list.channels_split(2, |draw_list| { + draw_list.channels_split(2, |channels| { const RADIUS: f32 = 100.0; let canvas_pos = ui.get_cursor_screen_pos(); - draw_list.channels_set_current(1); + channels.set_current(1); draw_list .add_line( canvas_pos, @@ -33,7 +33,7 @@ fn main() { .thickness(5.0) .build(); - draw_list.channels_set_current(0); + channels.set_current(0); let center = (canvas_pos.0 + RADIUS, canvas_pos.1 + RADIUS); draw_list .add_circle(center, RADIUS, WHITE) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index 6d871f6..f61ad42 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -100,10 +100,10 @@ impl<'ui> WindowDrawList<'ui> { /// # use imgui::*; /// fn custom_drawing(ui: &Ui) { /// let draw_list = ui.get_window_draw_list(); - /// draw_list.channels_split(2, |draw_list| { - /// draw_list.channels_set_current(1); + /// draw_list.channels_split(2, |channels| { + /// channels.set_current(1); /// // ... Draw channel 1 - /// draw_list.channels_set_current(0); + /// channels.set_current(0); /// // ... Draw channel 0 /// }); /// } @@ -134,7 +134,7 @@ impl<'ui> ChannelsSplit<'ui> { /// Change current channel. /// /// Panic if channel_index overflows the number of channels. - pub fn channels_set_current(&self, channel_index: u32) { + pub fn set_current(&self, channel_index: u32) { assert!( channel_index < self.channels_count, "Channel cannot be set! Provided channel index ({}) is higher than channel count ({}).", From 892fe558a3cce8cfc97e3b6caec10b56e8f62721 Mon Sep 17 00:00:00 2001 From: Malik Olivier Boussejra Date: Tue, 24 Apr 2018 13:16:24 +0900 Subject: [PATCH 28/28] WindowDrawList: Get rid of DrawApi trait Since there are no mutable borrows in the WindowDrawList API, we can remove all draw functions from ChannelsSplit and just use the same `draw_list' all the time, as shown in the `WindowDrawList::channels_split` example. This approach makes the `DrawAPI` trait pointless so it can be removed, and ChannelsSplit is reduced to a type with just one function: `set_current`. --- src/window_draw_list.rs | 352 +++++++++++++++++++--------------------- 1 file changed, 163 insertions(+), 189 deletions(-) diff --git a/src/window_draw_list.rs b/src/window_draw_list.rs index f61ad42..abc6a53 100644 --- a/src/window_draw_list.rs +++ b/src/window_draw_list.rs @@ -45,14 +45,6 @@ impl From<(f32, f32, f32)> for ImColor { fn from(v: (f32, f32, f32)) -> Self { [v.0, v.1, v.2, 1.0].into() } } -/// All types from which ImGui's custom draw API can be used implement this -/// trait. This trait is internal to this library and implemented by -/// `WindowDrawList` and `ChannelsSplit`. -pub trait DrawAPI { - /// Get draw_list object - fn draw_list(&self) -> *mut ImDrawList; -} - /// Object implementing the custom draw API. /// /// Called from [`Ui::get_window_draw_list`]. No more than one instance of this @@ -63,10 +55,6 @@ pub struct WindowDrawList<'ui> { _phantom: PhantomData<&'ui Ui<'ui>>, } -impl<'ui> DrawAPI for WindowDrawList<'ui> { - fn draw_list(&self) -> *mut ImDrawList { self.draw_list } -} - static mut WINDOW_DRAW_LIST_LOADED: bool = false; impl<'ui> Drop for WindowDrawList<'ui> { @@ -126,10 +114,6 @@ pub struct ChannelsSplit<'ui> { channels_count: u32, } -impl<'ui> DrawAPI for ChannelsSplit<'ui> { - fn draw_list(&self) -> *mut ImDrawList { self.draw_list.draw_list } -} - impl<'ui> ChannelsSplit<'ui> { /// Change current channel. /// @@ -141,170 +125,153 @@ impl<'ui> ChannelsSplit<'ui> { channel_index, self.channels_count ); - unsafe { sys::ImDrawList_ChannelsSetCurrent(self.draw_list(), channel_index as i32) }; + unsafe { + sys::ImDrawList_ChannelsSetCurrent(self.draw_list.draw_list, channel_index as i32) + }; } } -macro_rules! impl_draw_list_methods { - ($T: ident) => { - impl<'ui> $T<'ui> - where - $T<'ui>: DrawAPI, - { - /// Returns a line from point `p1` to `p2` with color `c`. - pub fn add_line(&self, p1: P1, p2: P2, c: C) -> Line<'ui, $T> - where - P1: Into, - P2: Into, - C: Into, - { - Line::new(self, p1, p2, c) - } +/// Drawing functions +impl<'ui> WindowDrawList<'ui> { + /// Returns a line from point `p1` to `p2` with color `c`. + pub fn add_line(&'ui self, p1: P1, p2: P2, c: C) -> Line<'ui> + where + P1: Into, + P2: Into, + C: Into, + { + Line::new(self, p1, p2, c) + } - /// Returns a rectangle whose upper-left corner is at point `p1` - /// and lower-right corner is at point `p2`, with color `c`. - pub fn add_rect(&self, p1: P1, p2: P2, c: C) -> Rect<'ui, $T> - where - P1: Into, - P2: Into, - C: Into, - { - Rect::new(self, p1, p2, c) - } + /// Returns a rectangle whose upper-left corner is at point `p1` + /// and lower-right corner is at point `p2`, with color `c`. + pub fn add_rect(&'ui self, p1: P1, p2: P2, c: C) -> Rect<'ui> + where + P1: Into, + P2: Into, + C: Into, + { + Rect::new(self, p1, p2, c) + } - /// Draw a rectangle whose upper-left corner is at point `p1` - /// and lower-right corner is at point `p2`. - /// The remains parameters are the respective color of the corners - /// in the counter-clockwise starting from the upper-left corner - /// first. - pub fn add_rect_filled_multicolor( - &self, - p1: P1, - p2: P2, - col_upr_left: C1, - col_upr_right: C2, - col_bot_right: C3, - col_bot_left: C4, - ) where - P1: Into, - P2: Into, - C1: Into, - C2: Into, - C3: Into, - C4: Into, - { - unsafe { - sys::ImDrawList_AddRectFilledMultiColor( - self.draw_list(), - p1.into(), - p2.into(), - col_upr_left.into().into(), - col_upr_right.into().into(), - col_bot_right.into().into(), - col_bot_left.into().into(), - ); - } - } - - /// Returns a triangle with the given 3 vertices `p1`, `p2` and `p3` - /// and color `c`. - pub fn add_triangle( - &self, - p1: P1, - p2: P2, - p3: P3, - c: C, - ) -> Triangle<'ui, $T> - where - P1: Into, - P2: Into, - P3: Into, - C: Into, - { - Triangle::new(self, p1, p2, p3, c) - } - - /// Returns a circle with the given `center`, `radius` and `color`. - pub fn add_circle(&self, center: P, radius: f32, color: C) -> Circle<'ui, $T> - where - P: Into, - C: Into, - { - Circle::new(self, center, radius, color) - } - - /// Returns a Bezier curve stretching from `pos0` to `pos1`, whose - /// curvature is defined by `cp0` and `cp1`. - pub fn add_bezier_curve( - &self, - pos0: P1, - cp0: P2, - cp1: P3, - pos1: P4, - color: C, - ) -> BezierCurve<'ui, $T> - where - P1: Into, - P2: Into, - P3: Into, - P4: Into, - C: Into, - { - BezierCurve::new(self, pos0, cp0, cp1, pos1, color) - } - - /// Push a clipping rectangle on the stack, run `f` and pop it. - /// - /// Clip all drawings done within the closure `f` in the given - /// rectangle. - pub fn with_clip_rect(&self, min: P1, max: P2, f: F) - where - P1: Into, - P2: Into, - F: FnOnce(), - { - unsafe { - sys::ImDrawList_PushClipRect(self.draw_list(), min.into(), max.into(), false) - } - f(); - unsafe { sys::ImDrawList_PopClipRect(self.draw_list()) } - } - - /// Push a clipping rectangle on the stack, run `f` and pop it. - /// - /// Clip all drawings done within the closure `f` in the given - /// rectangle. Intersect with all clipping rectangle previously on - /// the stack. - pub fn with_clip_rect_intersect(&self, min: P1, max: P2, f: F) - where - P1: Into, - P2: Into, - F: FnOnce(), - { - unsafe { - sys::ImDrawList_PushClipRect(self.draw_list(), min.into(), max.into(), true) - } - f(); - unsafe { sys::ImDrawList_PopClipRect(self.draw_list()) } - } + /// Draw a rectangle whose upper-left corner is at point `p1` + /// and lower-right corner is at point `p2`. + /// The remains parameters are the respective color of the corners + /// in the counter-clockwise starting from the upper-left corner + /// first. + pub fn add_rect_filled_multicolor( + &self, + p1: P1, + p2: P2, + col_upr_left: C1, + col_upr_right: C2, + col_bot_right: C3, + col_bot_left: C4, + ) where + P1: Into, + P2: Into, + C1: Into, + C2: Into, + C3: Into, + C4: Into, + { + unsafe { + sys::ImDrawList_AddRectFilledMultiColor( + self.draw_list, + p1.into(), + p2.into(), + col_upr_left.into().into(), + col_upr_right.into().into(), + col_bot_right.into().into(), + col_bot_left.into().into(), + ); } - }; + } + + /// Returns a triangle with the given 3 vertices `p1`, `p2` and `p3` + /// and color `c`. + pub fn add_triangle(&'ui self, p1: P1, p2: P2, p3: P3, c: C) -> Triangle<'ui> + where + P1: Into, + P2: Into, + P3: Into, + C: Into, + { + Triangle::new(self, p1, p2, p3, c) + } + + /// Returns a circle with the given `center`, `radius` and `color`. + pub fn add_circle(&'ui self, center: P, radius: f32, color: C) -> Circle<'ui> + where + P: Into, + C: Into, + { + Circle::new(self, center, radius, color) + } + + /// Returns a Bezier curve stretching from `pos0` to `pos1`, whose + /// curvature is defined by `cp0` and `cp1`. + pub fn add_bezier_curve( + &'ui self, + pos0: P1, + cp0: P2, + cp1: P3, + pos1: P4, + color: C, + ) -> BezierCurve<'ui> + where + P1: Into, + P2: Into, + P3: Into, + P4: Into, + C: Into, + { + BezierCurve::new(self, pos0, cp0, cp1, pos1, color) + } + + /// Push a clipping rectangle on the stack, run `f` and pop it. + /// + /// Clip all drawings done within the closure `f` in the given + /// rectangle. + pub fn with_clip_rect(&self, min: P1, max: P2, f: F) + where + P1: Into, + P2: Into, + F: FnOnce(), + { + unsafe { sys::ImDrawList_PushClipRect(self.draw_list, min.into(), max.into(), false) } + f(); + unsafe { sys::ImDrawList_PopClipRect(self.draw_list) } + } + + /// Push a clipping rectangle on the stack, run `f` and pop it. + /// + /// Clip all drawings done within the closure `f` in the given + /// rectangle. Intersect with all clipping rectangle previously on + /// the stack. + pub fn with_clip_rect_intersect(&self, min: P1, max: P2, f: F) + where + P1: Into, + P2: Into, + F: FnOnce(), + { + unsafe { sys::ImDrawList_PushClipRect(self.draw_list, min.into(), max.into(), true) } + f(); + unsafe { sys::ImDrawList_PopClipRect(self.draw_list) } + } } -impl_draw_list_methods!(WindowDrawList); -impl_draw_list_methods!(ChannelsSplit); - /// Represents a line about to be drawn -pub struct Line<'ui, D: 'ui> { +pub struct Line<'ui> { p1: ImVec2, p2: ImVec2, color: ImColor, thickness: f32, - draw_list: &'ui D, + draw_list: &'ui WindowDrawList<'ui>, } -impl<'ui, D: DrawAPI> Line<'ui, D> { - fn new(draw_list: &'ui D, p1: P1, p2: P2, c: C) -> Self +impl<'ui> Line<'ui> { + fn new(draw_list: &'ui WindowDrawList, p1: P1, p2: P2, c: C) -> Self where P1: Into, P2: Into, @@ -329,7 +296,7 @@ impl<'ui, D: DrawAPI> Line<'ui, D> { pub fn build(self) { unsafe { sys::ImDrawList_AddLine( - self.draw_list.draw_list(), + self.draw_list.draw_list, self.p1, self.p2, self.color.into(), @@ -340,7 +307,7 @@ impl<'ui, D: DrawAPI> Line<'ui, D> { } /// Represents a rectangle about to be drawn -pub struct Rect<'ui, D: 'ui> { +pub struct Rect<'ui> { p1: ImVec2, p2: ImVec2, color: ImColor, @@ -348,11 +315,11 @@ pub struct Rect<'ui, D: 'ui> { flags: ImDrawCornerFlags, thickness: f32, filled: bool, - draw_list: &'ui D, + draw_list: &'ui WindowDrawList<'ui>, } -impl<'ui, D: DrawAPI> Rect<'ui, D> { - fn new(draw_list: &'ui D, p1: P1, p2: P2, c: C) -> Self +impl<'ui> Rect<'ui> { + fn new(draw_list: &'ui WindowDrawList, p1: P1, p2: P2, c: C) -> Self where P1: Into, P2: Into, @@ -418,7 +385,7 @@ impl<'ui, D: DrawAPI> Rect<'ui, D> { if self.filled { unsafe { sys::ImDrawList_AddRectFilled( - self.draw_list.draw_list(), + self.draw_list.draw_list, self.p1, self.p2, self.color.into(), @@ -429,7 +396,7 @@ impl<'ui, D: DrawAPI> Rect<'ui, D> { } else { unsafe { sys::ImDrawList_AddRect( - self.draw_list.draw_list(), + self.draw_list.draw_list, self.p1, self.p2, self.color.into(), @@ -443,18 +410,18 @@ impl<'ui, D: DrawAPI> Rect<'ui, D> { } /// Represents a triangle about to be drawn on the window -pub struct Triangle<'ui, D: 'ui> { +pub struct Triangle<'ui> { p1: ImVec2, p2: ImVec2, p3: ImVec2, color: ImColor, thickness: f32, filled: bool, - draw_list: &'ui D, + draw_list: &'ui WindowDrawList<'ui>, } -impl<'ui, D: DrawAPI> Triangle<'ui, D> { - fn new(draw_list: &'ui D, p1: P1, p2: P2, p3: P3, c: C) -> Self +impl<'ui> Triangle<'ui> { + fn new(draw_list: &'ui WindowDrawList, p1: P1, p2: P2, p3: P3, c: C) -> Self where P1: Into, P2: Into, @@ -489,7 +456,7 @@ impl<'ui, D: DrawAPI> Triangle<'ui, D> { if self.filled { unsafe { sys::ImDrawList_AddTriangleFilled( - self.draw_list.draw_list(), + self.draw_list.draw_list, self.p1, self.p2, self.p3, @@ -499,7 +466,7 @@ impl<'ui, D: DrawAPI> Triangle<'ui, D> { } else { unsafe { sys::ImDrawList_AddTriangle( - self.draw_list.draw_list(), + self.draw_list.draw_list, self.p1, self.p2, self.p3, @@ -512,18 +479,18 @@ impl<'ui, D: DrawAPI> Triangle<'ui, D> { } /// Represents a circle about to be drawn -pub struct Circle<'ui, D: 'ui> { +pub struct Circle<'ui> { center: ImVec2, radius: f32, color: ImColor, num_segments: u32, thickness: f32, filled: bool, - draw_list: &'ui D, + draw_list: &'ui WindowDrawList<'ui>, } -impl<'ui, D: DrawAPI> Circle<'ui, D> { - pub fn new(draw_list: &'ui D, center: P, radius: f32, color: C) -> Self +impl<'ui> Circle<'ui> { + pub fn new(draw_list: &'ui WindowDrawList, center: P, radius: f32, color: C) -> Self where P: Into, C: Into, @@ -563,7 +530,7 @@ impl<'ui, D: DrawAPI> Circle<'ui, D> { if self.filled { unsafe { sys::ImDrawList_AddCircleFilled( - self.draw_list.draw_list(), + self.draw_list.draw_list, self.center, self.radius, self.color.into(), @@ -573,7 +540,7 @@ impl<'ui, D: DrawAPI> Circle<'ui, D> { } else { unsafe { sys::ImDrawList_AddCircle( - self.draw_list.draw_list(), + self.draw_list.draw_list, self.center, self.radius, self.color.into(), @@ -586,7 +553,7 @@ impl<'ui, D: DrawAPI> Circle<'ui, D> { } /// Represents a Bezier curve about to be drawn -pub struct BezierCurve<'ui, D: 'ui> { +pub struct BezierCurve<'ui> { pos0: ImVec2, cp0: ImVec2, pos1: ImVec2, @@ -595,11 +562,18 @@ pub struct BezierCurve<'ui, D: 'ui> { thickness: f32, /// If num_segments is not set, the bezier curve is auto-tessalated. num_segments: Option, - draw_list: &'ui D, + draw_list: &'ui WindowDrawList<'ui>, } -impl<'ui, D: DrawAPI> BezierCurve<'ui, D> { - fn new(draw_list: &'ui D, pos0: P1, cp0: P2, cp1: P3, pos1: P4, c: C) -> Self +impl<'ui> BezierCurve<'ui> { + fn new( + draw_list: &'ui WindowDrawList, + pos0: P1, + cp0: P2, + cp1: P3, + pos1: P4, + c: C, + ) -> Self where P1: Into, P2: Into, @@ -636,7 +610,7 @@ impl<'ui, D: DrawAPI> BezierCurve<'ui, D> { pub fn build(self) { unsafe { sys::ImDrawList_AddBezierCurve( - self.draw_list.draw_list(), + self.draw_list.draw_list, self.pos0, self.cp0, self.cp1,