#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::as_conversions,
clippy::unnecessary_cast
)]
use crate::texture::TextureId;
use bitflags::bitflags;
use std::marker::PhantomData;
use crate::colors::Color;
use crate::sys;
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ImColor32(u32);
impl ImColor32 {
pub const BLACK: Self = Self(0xff_00_00_00);
pub const WHITE: Self = Self(0xff_ff_ff_ff);
pub const TRANSPARENT: Self = Self(0);
#[inline]
pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self(((a as u32) << 24) | (r as u32) | ((g as u32) << 8) | ((b as u32) << 16))
}
#[inline]
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self::from_rgba(r, g, b, 0xff)
}
pub fn from_rgba_f32s(r: f32, g: f32, b: f32, a: f32) -> Self {
Self::from_rgba(
(r.clamp(0.0, 1.0) * 255.0) as u8,
(g.clamp(0.0, 1.0) * 255.0) as u8,
(b.clamp(0.0, 1.0) * 255.0) as u8,
(a.clamp(0.0, 1.0) * 255.0) as u8,
)
}
#[inline]
pub const fn to_bits(self) -> u32 {
self.0
}
}
impl From<Color> for ImColor32 {
fn from(color: Color) -> Self {
Self::from_rgba_f32s(color.r, color.g, color.b, color.a)
}
}
impl From<[f32; 4]> for ImColor32 {
fn from(arr: [f32; 4]) -> Self {
Self::from_rgba_f32s(arr[0], arr[1], arr[2], arr[3])
}
}
impl From<(f32, f32, f32, f32)> for ImColor32 {
fn from((r, g, b, a): (f32, f32, f32, f32)) -> Self {
Self::from_rgba_f32s(r, g, b, a)
}
}
impl From<[f32; 3]> for ImColor32 {
fn from(arr: [f32; 3]) -> Self {
Self::from_rgba_f32s(arr[0], arr[1], arr[2], 1.0)
}
}
impl From<(f32, f32, f32)> for ImColor32 {
fn from((r, g, b): (f32, f32, f32)) -> Self {
Self::from_rgba_f32s(r, g, b, 1.0)
}
}
impl From<ImColor32> for u32 {
fn from(color: ImColor32) -> Self {
color.0
}
}
impl From<u32> for ImColor32 {
fn from(color: u32) -> Self {
ImColor32(color)
}
}
bitflags! {
#[repr(transparent)]
pub struct DrawListFlags: i32 {
const NONE = sys::ImDrawListFlags_None as i32;
const ANTI_ALIASED_LINES = sys::ImDrawListFlags_AntiAliasedLines as i32;
const ANTI_ALIASED_LINES_USE_TEX = sys::ImDrawListFlags_AntiAliasedLinesUseTex as i32;
const ANTI_ALIASED_FILL = sys::ImDrawListFlags_AntiAliasedFill as i32;
const ALLOW_VTX_OFFSET = sys::ImDrawListFlags_AllowVtxOffset as i32;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PolylineFlags: u32 {
const NONE = sys::ImDrawFlags_None as u32;
const CLOSED = sys::ImDrawFlags_Closed as u32;
}
}
impl Default for PolylineFlags {
fn default() -> Self {
Self::NONE
}
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DrawCornerFlags: u32 {
const DEFAULT = sys::ImDrawFlags_None as u32;
const TOP_LEFT = sys::ImDrawFlags_RoundCornersTopLeft as u32;
const TOP_RIGHT = sys::ImDrawFlags_RoundCornersTopRight as u32;
const BOTTOM_LEFT = sys::ImDrawFlags_RoundCornersBottomLeft as u32;
const BOTTOM_RIGHT = sys::ImDrawFlags_RoundCornersBottomRight as u32;
const TOP = sys::ImDrawFlags_RoundCornersTop as u32;
const BOTTOM = sys::ImDrawFlags_RoundCornersBottom as u32;
const LEFT = sys::ImDrawFlags_RoundCornersLeft as u32;
const RIGHT = sys::ImDrawFlags_RoundCornersRight as u32;
const ALL = sys::ImDrawFlags_RoundCornersAll as u32;
const NO_ROUNDING = sys::ImDrawFlags_RoundCornersNone as u32;
}
}
impl Default for DrawCornerFlags {
fn default() -> Self {
Self::DEFAULT
}
}
#[repr(transparent)]
pub struct DrawList(*mut sys::ImDrawList);
impl DrawList {
unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] {
unsafe {
if (*self.0).CmdBuffer.Size <= 0 || (*self.0).CmdBuffer.Data.is_null() {
return &[];
}
let len = match usize::try_from((*self.0).CmdBuffer.Size) {
Ok(len) => len,
Err(_) => return &[],
};
std::slice::from_raw_parts((*self.0).CmdBuffer.Data as *const sys::ImDrawCmd, len)
}
}
pub fn vtx_buffer(&self) -> &[crate::render::DrawVert] {
unsafe {
if (*self.0).VtxBuffer.Size <= 0 || (*self.0).VtxBuffer.Data.is_null() {
return &[];
}
let len = match usize::try_from((*self.0).VtxBuffer.Size) {
Ok(len) => len,
Err(_) => return &[],
};
std::slice::from_raw_parts(
(*self.0).VtxBuffer.Data as *const crate::render::DrawVert,
len,
)
}
}
pub fn idx_buffer(&self) -> &[crate::render::DrawIdx] {
unsafe {
if (*self.0).IdxBuffer.Size <= 0 || (*self.0).IdxBuffer.Data.is_null() {
return &[];
}
let len = match usize::try_from((*self.0).IdxBuffer.Size) {
Ok(len) => len,
Err(_) => return &[],
};
std::slice::from_raw_parts((*self.0).IdxBuffer.Data, len)
}
}
pub fn commands(&self) -> DrawCmdIterator<'_> {
unsafe {
DrawCmdIterator {
iter: self.cmd_buffer().iter(),
}
}
}
}
pub struct OwnedDrawList(*mut sys::ImDrawList);
impl Drop for OwnedDrawList {
fn drop(&mut self) {
unsafe { sys::ImDrawList_destroy(self.0) }
}
}
impl OwnedDrawList {
pub(crate) unsafe fn from_raw(ptr: *mut sys::ImDrawList) -> Self {
Self(ptr)
}
pub fn as_view(&self) -> DrawList {
DrawList(self.0)
}
pub fn clear_free_memory(&mut self) {
unsafe { sys::ImDrawList__ClearFreeMemory(self.0) }
}
pub fn reset_for_new_frame(&mut self) {
unsafe { sys::ImDrawList__ResetForNewFrame(self.0) }
}
}
pub struct DrawCmdIterator<'a> {
iter: std::slice::Iter<'a, sys::ImDrawCmd>,
}
impl Iterator for DrawCmdIterator<'_> {
type Item = DrawCmd;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|cmd| {
let cmd_params = DrawCmdParams {
clip_rect: [
cmd.ClipRect.x,
cmd.ClipRect.y,
cmd.ClipRect.z,
cmd.ClipRect.w,
],
texture_id: TextureId::from(unsafe {
let mut cmd_copy = *cmd;
sys::ImDrawCmd_GetTexID(&mut cmd_copy)
}),
vtx_offset: cmd.VtxOffset as usize,
idx_offset: cmd.IdxOffset as usize,
};
match cmd.UserCallback {
Some(raw_callback) if raw_callback as usize == usize::MAX => {
DrawCmd::ResetRenderState
}
Some(raw_callback) => DrawCmd::RawCallback {
callback: raw_callback,
raw_cmd: cmd,
},
None => DrawCmd::Elements {
count: cmd.ElemCount as usize,
cmd_params,
},
}
})
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DrawCmdParams {
pub clip_rect: [f32; 4],
pub texture_id: TextureId,
pub vtx_offset: usize,
pub idx_offset: usize,
}
#[derive(Debug, Clone)]
pub enum DrawCmd {
Elements {
count: usize,
cmd_params: DrawCmdParams,
},
ResetRenderState,
RawCallback {
callback: unsafe extern "C" fn(*const sys::ImDrawList, cmd: *const sys::ImDrawCmd),
raw_cmd: *const sys::ImDrawCmd,
},
}
enum DrawListType {
Window,
Background,
Foreground,
}
pub struct DrawListMut<'ui> {
draw_list_type: DrawListType,
draw_list: *mut sys::ImDrawList,
_phantom: PhantomData<&'ui ()>,
}
static DRAW_LIST_LOADED_WINDOW: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
static DRAW_LIST_LOADED_BACKGROUND: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
static DRAW_LIST_LOADED_FOREGROUND: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
impl Drop for DrawListMut<'_> {
fn drop(&mut self) {
match self.draw_list_type {
DrawListType::Window => &DRAW_LIST_LOADED_WINDOW,
DrawListType::Background => &DRAW_LIST_LOADED_BACKGROUND,
DrawListType::Foreground => &DRAW_LIST_LOADED_FOREGROUND,
}
.store(false, std::sync::atomic::Ordering::Release);
}
}
impl DrawListMut<'_> {
fn lock_draw_list(t: DrawListType) {
let lock = match t {
DrawListType::Window => &DRAW_LIST_LOADED_WINDOW,
DrawListType::Background => &DRAW_LIST_LOADED_BACKGROUND,
DrawListType::Foreground => &DRAW_LIST_LOADED_FOREGROUND,
};
if lock
.compare_exchange(
false,
true,
std::sync::atomic::Ordering::Acquire,
std::sync::atomic::Ordering::Relaxed,
)
.is_err()
{
panic!(
"A DrawListMut is already in use! You can only have one DrawListMut in use at a time."
);
}
}
pub(crate) fn window(_ui: &crate::Ui) -> Self {
Self::lock_draw_list(DrawListType::Window);
Self {
draw_list: unsafe { sys::igGetWindowDrawList() },
draw_list_type: DrawListType::Window,
_phantom: PhantomData,
}
}
pub(crate) fn background(_ui: &crate::Ui) -> Self {
Self::lock_draw_list(DrawListType::Background);
Self {
draw_list: unsafe { sys::igGetBackgroundDrawList(std::ptr::null_mut()) },
draw_list_type: DrawListType::Background,
_phantom: PhantomData,
}
}
pub(crate) fn foreground(_ui: &crate::Ui) -> Self {
Self::lock_draw_list(DrawListType::Foreground);
Self {
draw_list: unsafe { sys::igGetForegroundDrawList_ViewportPtr(std::ptr::null_mut()) },
draw_list_type: DrawListType::Foreground,
_phantom: PhantomData,
}
}
}
impl<'ui> DrawListMut<'ui> {
#[doc(alias = "ChannelsSplit")]
pub fn channels_split<F: FnOnce(&ChannelsSplit<'ui>)>(&'ui self, channels_count: u32, f: F) {
unsafe { sys::ImDrawList_ChannelsSplit(self.draw_list, channels_count as i32) };
f(&ChannelsSplit {
draw_list: self,
channels_count,
});
unsafe { sys::ImDrawList_ChannelsMerge(self.draw_list) };
}
pub fn add_line<C>(
&'ui self,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
c: C,
) -> Line<'ui>
where
C: Into<ImColor32>,
{
Line::new(self, p1, p2, c)
}
pub fn add_rect<C>(
&'ui self,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
c: C,
) -> Rect<'ui>
where
C: Into<ImColor32>,
{
Rect::new(self, p1, p2, c)
}
#[doc(alias = "AddRectFilledMultiColor")]
pub fn add_rect_filled_multicolor<C1, C2, C3, C4>(
&self,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
col_upr_left: C1,
col_upr_right: C2,
col_bot_right: C3,
col_bot_left: C4,
) where
C1: Into<ImColor32>,
C2: Into<ImColor32>,
C3: Into<ImColor32>,
C4: Into<ImColor32>,
{
let p_min: sys::ImVec2 = p1.into();
let p_max: sys::ImVec2 = p2.into();
let c_ul: u32 = col_upr_left.into().into();
let c_ur: u32 = col_upr_right.into().into();
let c_br: u32 = col_bot_right.into().into();
let c_bl: u32 = col_bot_left.into().into();
unsafe {
sys::ImDrawList_AddRectFilledMultiColor(
self.draw_list,
p_min,
p_max,
c_ul,
c_ur,
c_br,
c_bl,
);
}
}
pub fn add_circle<C>(
&'ui self,
center: impl Into<sys::ImVec2>,
radius: f32,
color: C,
) -> Circle<'ui>
where
C: Into<ImColor32>,
{
Circle::new(self, center, radius, color)
}
#[doc(alias = "AddBezier", alias = "AddBezierCubic")]
pub fn add_bezier_curve(
&'ui self,
pos0: impl Into<sys::ImVec2>,
cp0: impl Into<sys::ImVec2>,
cp1: impl Into<sys::ImVec2>,
pos1: impl Into<sys::ImVec2>,
color: impl Into<ImColor32>,
) -> BezierCurve<'ui> {
BezierCurve::new(self, pos0, cp0, cp1, pos1, color)
}
#[doc(alias = "AddTriangleFilled", alias = "AddTriangle")]
pub fn add_triangle<C>(
&'ui self,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
p3: impl Into<sys::ImVec2>,
c: C,
) -> Triangle<'ui>
where
C: Into<ImColor32>,
{
Triangle::new(self, p1, p2, p3, c)
}
#[doc(alias = "AddPolyline", alias = "AddConvexPolyFilled")]
pub fn add_polyline<C, P>(&'ui self, points: Vec<P>, c: C) -> Polyline<'ui>
where
C: Into<ImColor32>,
P: Into<sys::ImVec2>,
{
Polyline::new(self, points, c)
}
#[doc(alias = "PathClear")]
pub fn path_clear(&self) {
unsafe {
let draw_list = self.draw_list;
(*draw_list)._Path.Size = 0;
}
}
#[doc(alias = "PathLineTo")]
pub fn path_line_to(&self, pos: impl Into<sys::ImVec2>) {
unsafe { sys::ImDrawList_PathLineTo(self.draw_list, pos.into()) }
}
#[doc(alias = "PathLineToMergeDuplicate")]
pub fn path_line_to_merge_duplicate(&self, pos: impl Into<sys::ImVec2>) {
unsafe { sys::ImDrawList_PathLineToMergeDuplicate(self.draw_list, pos.into()) }
}
#[doc(alias = "PathArcTo")]
pub fn path_arc_to(
&self,
center: impl Into<sys::ImVec2>,
radius: f32,
a_min: f32,
a_max: f32,
num_segments: i32,
) {
unsafe {
let center_vec: sys::ImVec2 = center.into();
sys::ImDrawList_PathArcTo(
self.draw_list,
center_vec,
radius,
a_min,
a_max,
num_segments,
);
}
}
#[doc(alias = "PathArcToFast")]
pub fn path_arc_to_fast(
&self,
center: impl Into<sys::ImVec2>,
radius: f32,
a_min_of_12: i32,
a_max_of_12: i32,
) {
unsafe {
let center_vec: sys::ImVec2 = center.into();
sys::ImDrawList_PathArcToFast(
self.draw_list,
center_vec,
radius,
a_min_of_12,
a_max_of_12,
);
}
}
#[doc(alias = "PathRect")]
pub fn path_rect(
&self,
rect_min: impl Into<sys::ImVec2>,
rect_max: impl Into<sys::ImVec2>,
rounding: f32,
flags: DrawCornerFlags,
) {
unsafe {
let min_vec: sys::ImVec2 = rect_min.into();
let max_vec: sys::ImVec2 = rect_max.into();
sys::ImDrawList_PathRect(
self.draw_list,
min_vec,
max_vec,
rounding,
flags.bits() as sys::ImDrawFlags,
);
}
}
#[doc(alias = "PathEllipticalArcTo")]
pub fn path_elliptical_arc_to(
&self,
center: impl Into<sys::ImVec2>,
radius: impl Into<sys::ImVec2>,
rot: f32,
a_min: f32,
a_max: f32,
num_segments: i32,
) {
unsafe {
sys::ImDrawList_PathEllipticalArcTo(
self.draw_list,
center.into(),
radius.into(),
rot,
a_min,
a_max,
num_segments,
)
}
}
#[doc(alias = "PathBezierQuadraticCurveTo")]
pub fn path_bezier_quadratic_curve_to(
&self,
p2: impl Into<sys::ImVec2>,
p3: impl Into<sys::ImVec2>,
num_segments: i32,
) {
unsafe {
sys::ImDrawList_PathBezierQuadraticCurveTo(
self.draw_list,
p2.into(),
p3.into(),
num_segments,
)
}
}
#[doc(alias = "PathBezierCubicCurveTo")]
pub fn path_bezier_cubic_curve_to(
&self,
p2: impl Into<sys::ImVec2>,
p3: impl Into<sys::ImVec2>,
p4: impl Into<sys::ImVec2>,
num_segments: i32,
) {
unsafe {
sys::ImDrawList_PathBezierCubicCurveTo(
self.draw_list,
p2.into(),
p3.into(),
p4.into(),
num_segments,
)
}
}
#[doc(alias = "PathStroke")]
pub fn path_stroke(&self, color: impl Into<ImColor32>, flags: PolylineFlags, thickness: f32) {
unsafe {
let draw_list = self.draw_list;
let path = &mut (*draw_list)._Path;
if path.Size > 0 {
sys::ImDrawList_AddPolyline(
self.draw_list,
path.Data,
path.Size,
color.into().into(),
flags.bits() as sys::ImDrawFlags,
thickness,
);
path.Size = 0; }
}
}
#[doc(alias = "PathFillConvex")]
pub fn path_fill_convex(&self, color: impl Into<ImColor32>) {
unsafe {
let draw_list = self.draw_list;
let path = &mut (*draw_list)._Path;
if path.Size > 0 {
sys::ImDrawList_AddConvexPolyFilled(
self.draw_list,
path.Data,
path.Size,
color.into().into(),
);
path.Size = 0; }
}
}
pub fn add_text(
&self,
pos: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
text: impl AsRef<str>,
) {
use std::os::raw::c_char;
let text = text.as_ref();
let pos: sys::ImVec2 = pos.into();
let col = col.into();
unsafe {
let start = text.as_ptr() as *const c_char;
let end = (start as usize + text.len()) as *const c_char;
sys::ImDrawList_AddText_Vec2(self.draw_list, pos, col.into(), start, end);
}
}
#[doc(alias = "AddText")]
pub fn add_text_with_font(
&self,
font: &crate::fonts::Font,
font_size: f32,
pos: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
text: impl AsRef<str>,
wrap_width: f32,
cpu_fine_clip_rect: Option<[f32; 4]>,
) {
use std::os::raw::c_char;
let text = text.as_ref();
let pos: sys::ImVec2 = pos.into();
let col = col.into();
let font_ptr = font.raw();
let clip_vec4 = cpu_fine_clip_rect.map(|r| sys::ImVec4 {
x: r[0],
y: r[1],
z: r[2],
w: r[3],
});
let clip_ptr = match clip_vec4.as_ref() {
Some(v) => v as *const sys::ImVec4,
None => std::ptr::null(),
};
unsafe {
let start = text.as_ptr() as *const c_char;
let end = (start as usize + text.len()) as *const c_char;
sys::ImDrawList_AddText_FontPtr(
self.draw_list,
font_ptr,
font_size,
pos,
col.into(),
start,
end,
wrap_width,
clip_ptr,
);
}
}
#[doc(alias = "PushTexture")]
pub fn push_texture(&self, texture: impl Into<crate::texture::TextureRef>) {
let tex_ref = texture.into().raw();
unsafe { sys::ImDrawList_PushTexture(self.draw_list, tex_ref) }
}
#[doc(alias = "PopTexture")]
pub fn pop_texture(&self) {
unsafe {
sys::ImDrawList_PopTexture(self.draw_list);
}
}
#[doc(alias = "PushClipRect")]
pub fn push_clip_rect(
&self,
clip_rect_min: impl Into<sys::ImVec2>,
clip_rect_max: impl Into<sys::ImVec2>,
intersect_with_current: bool,
) {
unsafe {
sys::ImDrawList_PushClipRect(
self.draw_list,
clip_rect_min.into(),
clip_rect_max.into(),
intersect_with_current,
)
}
}
#[doc(alias = "PushClipRectFullScreen")]
pub fn push_clip_rect_full_screen(&self) {
unsafe { sys::ImDrawList_PushClipRectFullScreen(self.draw_list) }
}
#[doc(alias = "PopClipRect")]
pub fn pop_clip_rect(&self) {
unsafe { sys::ImDrawList_PopClipRect(self.draw_list) }
}
pub fn clip_rect_min(&self) -> [f32; 2] {
let out = unsafe { sys::ImDrawList_GetClipRectMin(self.draw_list) };
out.into()
}
pub fn clip_rect_max(&self) -> [f32; 2] {
let out = unsafe { sys::ImDrawList_GetClipRectMax(self.draw_list) };
out.into()
}
pub fn with_clip_rect<F>(
&self,
clip_rect_min: impl Into<sys::ImVec2>,
clip_rect_max: impl Into<sys::ImVec2>,
f: F,
) where
F: FnOnce(),
{
self.push_clip_rect(clip_rect_min, clip_rect_max, false);
f();
self.pop_clip_rect();
}
#[doc(alias = "AddImage")]
pub fn add_image(
&self,
texture: impl Into<crate::texture::TextureRef>,
p_min: impl Into<sys::ImVec2>,
p_max: impl Into<sys::ImVec2>,
uv_min: impl Into<sys::ImVec2>,
uv_max: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
) {
let p_min: sys::ImVec2 = p_min.into();
let p_max: sys::ImVec2 = p_max.into();
let uv_min: sys::ImVec2 = uv_min.into();
let uv_max: sys::ImVec2 = uv_max.into();
let col = col.into().to_bits();
let tex_ref = texture.into().raw();
unsafe {
sys::ImDrawList_AddImage(self.draw_list, tex_ref, p_min, p_max, uv_min, uv_max, col)
}
}
#[doc(alias = "AddImageQuad")]
pub fn add_image_quad(
&self,
texture: impl Into<crate::texture::TextureRef>,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
p3: impl Into<sys::ImVec2>,
p4: impl Into<sys::ImVec2>,
uv1: impl Into<sys::ImVec2>,
uv2: impl Into<sys::ImVec2>,
uv3: impl Into<sys::ImVec2>,
uv4: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
) {
let p1: sys::ImVec2 = p1.into();
let p2: sys::ImVec2 = p2.into();
let p3: sys::ImVec2 = p3.into();
let p4: sys::ImVec2 = p4.into();
let uv1: sys::ImVec2 = uv1.into();
let uv2: sys::ImVec2 = uv2.into();
let uv3: sys::ImVec2 = uv3.into();
let uv4: sys::ImVec2 = uv4.into();
let col = col.into().to_bits();
let tex_ref = texture.into().raw();
unsafe {
sys::ImDrawList_AddImageQuad(
self.draw_list,
tex_ref,
p1,
p2,
p3,
p4,
uv1,
uv2,
uv3,
uv4,
col,
)
}
}
#[doc(alias = "AddImageRounded")]
pub fn add_image_rounded(
&self,
texture: impl Into<crate::texture::TextureRef>,
p_min: impl Into<sys::ImVec2>,
p_max: impl Into<sys::ImVec2>,
uv_min: impl Into<sys::ImVec2>,
uv_max: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
rounding: f32,
flags: DrawCornerFlags,
) {
let p_min: sys::ImVec2 = p_min.into();
let p_max: sys::ImVec2 = p_max.into();
let uv_min: sys::ImVec2 = uv_min.into();
let uv_max: sys::ImVec2 = uv_max.into();
let col = col.into().to_bits();
let tex_ref = texture.into().raw();
unsafe {
sys::ImDrawList_AddImageRounded(
self.draw_list,
tex_ref,
p_min,
p_max,
uv_min,
uv_max,
col,
rounding,
flags.bits() as sys::ImDrawFlags,
)
}
}
#[doc(alias = "AddQuad")]
pub fn add_quad<C>(
&self,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
p3: impl Into<sys::ImVec2>,
p4: impl Into<sys::ImVec2>,
col: C,
thickness: f32,
) where
C: Into<ImColor32>,
{
unsafe {
sys::ImDrawList_AddQuad(
self.draw_list,
p1.into(),
p2.into(),
p3.into(),
p4.into(),
col.into().into(),
thickness,
)
}
}
#[doc(alias = "AddQuadFilled")]
pub fn add_quad_filled<C>(
&self,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
p3: impl Into<sys::ImVec2>,
p4: impl Into<sys::ImVec2>,
col: C,
) where
C: Into<ImColor32>,
{
unsafe {
sys::ImDrawList_AddQuadFilled(
self.draw_list,
p1.into(),
p2.into(),
p3.into(),
p4.into(),
col.into().into(),
)
}
}
#[doc(alias = "AddNgon")]
pub fn add_ngon<C>(
&self,
center: impl Into<sys::ImVec2>,
radius: f32,
col: C,
num_segments: i32,
thickness: f32,
) where
C: Into<ImColor32>,
{
unsafe {
sys::ImDrawList_AddNgon(
self.draw_list,
center.into(),
radius,
col.into().into(),
num_segments,
thickness,
)
}
}
#[doc(alias = "AddNgonFilled")]
pub fn add_ngon_filled<C>(
&self,
center: impl Into<sys::ImVec2>,
radius: f32,
col: C,
num_segments: i32,
) where
C: Into<ImColor32>,
{
unsafe {
sys::ImDrawList_AddNgonFilled(
self.draw_list,
center.into(),
radius,
col.into().into(),
num_segments,
)
}
}
#[doc(alias = "AddEllipse")]
pub fn add_ellipse<C>(
&self,
center: impl Into<sys::ImVec2>,
radius: impl Into<sys::ImVec2>,
col: C,
rot: f32,
num_segments: i32,
thickness: f32,
) where
C: Into<ImColor32>,
{
unsafe {
sys::ImDrawList_AddEllipse(
self.draw_list,
center.into(),
radius.into(),
col.into().into(),
rot,
num_segments,
thickness,
)
}
}
#[doc(alias = "AddEllipseFilled")]
pub fn add_ellipse_filled<C>(
&self,
center: impl Into<sys::ImVec2>,
radius: impl Into<sys::ImVec2>,
col: C,
rot: f32,
num_segments: i32,
) where
C: Into<ImColor32>,
{
unsafe {
sys::ImDrawList_AddEllipseFilled(
self.draw_list,
center.into(),
radius.into(),
col.into().into(),
rot,
num_segments,
)
}
}
#[doc(alias = "AddBezierQuadratic")]
pub fn add_bezier_quadratic<C>(
&self,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
p3: impl Into<sys::ImVec2>,
col: C,
thickness: f32,
num_segments: i32,
) where
C: Into<ImColor32>,
{
unsafe {
sys::ImDrawList_AddBezierQuadratic(
self.draw_list,
p1.into(),
p2.into(),
p3.into(),
col.into().into(),
thickness,
num_segments,
)
}
}
#[doc(alias = "AddConcavePolyFilled")]
pub fn add_concave_poly_filled<C, P>(&self, points: &[P], col: C)
where
C: Into<ImColor32>,
P: Copy + Into<sys::ImVec2>,
{
let mut buf: Vec<sys::ImVec2> = Vec::with_capacity(points.len());
for p in points.iter().copied() {
buf.push(p.into());
}
let count = match i32::try_from(buf.len()) {
Ok(n) => n,
Err(_) => return,
};
unsafe {
sys::ImDrawList_AddConcavePolyFilled(
self.draw_list,
buf.as_ptr(),
count,
col.into().into(),
)
}
}
#[doc(alias = "PathFillConcave")]
pub fn path_fill_concave(&self, color: impl Into<ImColor32>) {
unsafe { sys::ImDrawList_PathFillConcave(self.draw_list, color.into().into()) }
}
#[doc(alias = "AddCallback")]
pub unsafe fn add_callback(
&self,
callback: sys::ImDrawCallback,
userdata: *mut std::os::raw::c_void,
userdata_size: usize,
) {
unsafe { sys::ImDrawList_AddCallback(self.draw_list, callback, userdata, userdata_size) }
}
#[doc(alias = "AddDrawCmd")]
pub fn add_draw_cmd(&self) {
unsafe { sys::ImDrawList_AddDrawCmd(self.draw_list) }
}
#[doc(alias = "CloneOutput")]
pub fn clone_output(&self) -> OwnedDrawList {
unsafe { OwnedDrawList::from_raw(sys::ImDrawList_CloneOutput(self.draw_list)) }
}
}
pub struct ChannelsSplit<'ui> {
draw_list: &'ui DrawListMut<'ui>,
channels_count: u32,
}
impl ChannelsSplit<'_> {
#[doc(alias = "ChannelsSetCurrent")]
pub fn set_current(&self, channel_index: u32) {
assert!(
channel_index < self.channels_count,
"Channel index {} out of range {}",
channel_index,
self.channels_count
);
unsafe {
sys::ImDrawList_ChannelsSetCurrent(self.draw_list.draw_list, channel_index as i32)
};
}
}
#[must_use = "call .build() to register the callback"]
pub struct Callback<'ui, F> {
draw_list: &'ui DrawListMut<'ui>,
callback: F,
}
impl<'ui, F: FnOnce() + 'static> Callback<'ui, F> {
pub fn new(draw_list: &'ui DrawListMut<'_>, callback: F) -> Self {
Self {
draw_list,
callback,
}
}
pub fn build(self) {
use std::os::raw::c_void;
let ptr: *mut F = Box::into_raw(Box::new(self.callback));
unsafe {
sys::ImDrawList_AddCallback(
self.draw_list.draw_list,
Some(Self::run_callback),
ptr as *mut c_void,
0,
);
}
}
unsafe extern "C" fn run_callback(
_parent_list: *const sys::ImDrawList,
cmd: *const sys::ImDrawCmd,
) {
if cmd.is_null() {
return;
}
let cmd_ptr = cmd as *mut sys::ImDrawCmd;
if unsafe { (*cmd_ptr).UserCallbackData.is_null() } {
return;
}
if unsafe { (*cmd_ptr).UserCallbackDataOffset } != -1 {
eprintln!("dear-imgui-rs: unexpected UserCallbackDataOffset (expected -1)");
std::process::abort();
}
if unsafe { (*cmd_ptr).UserCallbackDataSize } != 0 {
eprintln!("dear-imgui-rs: unexpected UserCallbackDataSize (expected 0)");
std::process::abort();
}
let data_ptr = unsafe { (*cmd_ptr).UserCallbackData as *mut F };
if data_ptr.is_null() {
return;
}
unsafe {
(*cmd_ptr).UserCallbackData = std::ptr::null_mut();
(*cmd_ptr).UserCallbackDataSize = 0;
(*cmd_ptr).UserCallbackDataOffset = 0;
}
let cb = unsafe { Box::from_raw(data_ptr) };
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
cb();
}));
if res.is_err() {
eprintln!("dear-imgui-rs: panic in DrawList callback");
std::process::abort();
}
}
}
#[cfg(test)]
mod callback_tests {
use super::*;
#[test]
fn safe_draw_callback_uses_direct_user_data_pointer() {
fn noop() {}
let shared = unsafe { sys::ImDrawListSharedData_ImDrawListSharedData() };
assert!(!shared.is_null());
let raw_draw_list = unsafe { sys::ImDrawList_ImDrawList(shared) };
assert!(!raw_draw_list.is_null());
unsafe { sys::ImDrawList_AddDrawCmd(raw_draw_list) };
let draw_list = DrawListMut {
draw_list_type: DrawListType::Window,
draw_list: raw_draw_list,
_phantom: PhantomData,
};
draw_list.add_callback_safe(noop).build();
let cmd_buffer = unsafe { &(*draw_list.draw_list).CmdBuffer };
assert!(cmd_buffer.Size > 0);
assert!(!cmd_buffer.Data.is_null());
let (cmd_ptr, cmd_copy) = {
let cmds = unsafe {
let len = usize::try_from(cmd_buffer.Size)
.expect("expected non-negative CmdBuffer.Size in test");
std::slice::from_raw_parts(cmd_buffer.Data, len)
};
let (i, cmd) = cmds
.iter()
.enumerate()
.find(|(_, cmd)| cmd.UserCallback.is_some() && !cmd.UserCallbackData.is_null())
.expect("expected callback command to be present");
let cmd_ptr = unsafe { cmd_buffer.Data.add(i) as *const sys::ImDrawCmd };
(cmd_ptr, *cmd)
};
assert!(cmd_copy.UserCallback.is_some());
assert_eq!(cmd_copy.UserCallbackDataOffset, -1);
assert_eq!(cmd_copy.UserCallbackDataSize, 0);
assert!(!cmd_copy.UserCallbackData.is_null());
unsafe { cmd_copy.UserCallback.unwrap()(draw_list.draw_list as *const _, cmd_ptr) }
let cmd_after = unsafe { *cmd_ptr };
assert!(cmd_after.UserCallbackData.is_null());
unsafe {
sys::ImDrawList_destroy(raw_draw_list);
sys::ImDrawListSharedData_destroy(shared);
}
}
}
impl<'ui> DrawListMut<'ui> {
#[cfg(not(target_arch = "wasm32"))]
pub fn add_callback_safe<F: FnOnce() + 'static>(&'ui self, callback: F) -> Callback<'ui, F> {
Callback::new(self, callback)
}
#[cfg(target_arch = "wasm32")]
pub fn add_callback_safe<F: FnOnce() + 'static>(&'ui self, _callback: F) -> Callback<'ui, F> {
panic!(
"DrawListMut::add_callback_safe is not supported on wasm32 targets; \
C->Rust callbacks are not available in the import-style web build."
);
}
}
impl<'ui> DrawListMut<'ui> {
pub unsafe fn prim_reserve(&self, idx_count: i32, vtx_count: i32) {
unsafe { sys::ImDrawList_PrimReserve(self.draw_list, idx_count, vtx_count) }
}
pub unsafe fn prim_unreserve(&self, idx_count: i32, vtx_count: i32) {
unsafe { sys::ImDrawList_PrimUnreserve(self.draw_list, idx_count, vtx_count) }
}
pub unsafe fn prim_rect(
&self,
a: impl Into<sys::ImVec2>,
b: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
) {
unsafe { sys::ImDrawList_PrimRect(self.draw_list, a.into(), b.into(), col.into().into()) }
}
pub unsafe fn prim_rect_uv(
&self,
a: impl Into<sys::ImVec2>,
b: impl Into<sys::ImVec2>,
uv_a: impl Into<sys::ImVec2>,
uv_b: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
) {
unsafe {
sys::ImDrawList_PrimRectUV(
self.draw_list,
a.into(),
b.into(),
uv_a.into(),
uv_b.into(),
col.into().into(),
)
}
}
pub unsafe fn prim_quad_uv(
&self,
a: impl Into<sys::ImVec2>,
b: impl Into<sys::ImVec2>,
c: impl Into<sys::ImVec2>,
d: impl Into<sys::ImVec2>,
uv_a: impl Into<sys::ImVec2>,
uv_b: impl Into<sys::ImVec2>,
uv_c: impl Into<sys::ImVec2>,
uv_d: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
) {
unsafe {
sys::ImDrawList_PrimQuadUV(
self.draw_list,
a.into(),
b.into(),
c.into(),
d.into(),
uv_a.into(),
uv_b.into(),
uv_c.into(),
uv_d.into(),
col.into().into(),
)
}
}
pub unsafe fn prim_write_vtx(
&self,
pos: impl Into<sys::ImVec2>,
uv: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
) {
unsafe {
sys::ImDrawList_PrimWriteVtx(self.draw_list, pos.into(), uv.into(), col.into().into())
}
}
pub unsafe fn prim_write_idx(&self, idx: sys::ImDrawIdx) {
unsafe { sys::ImDrawList_PrimWriteIdx(self.draw_list, idx) }
}
pub unsafe fn prim_vtx(
&self,
pos: impl Into<sys::ImVec2>,
uv: impl Into<sys::ImVec2>,
col: impl Into<ImColor32>,
) {
unsafe { sys::ImDrawList_PrimVtx(self.draw_list, pos.into(), uv.into(), col.into().into()) }
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Line<'ui> {
p1: [f32; 2],
p2: [f32; 2],
color: ImColor32,
thickness: f32,
draw_list: &'ui DrawListMut<'ui>,
}
impl<'ui> Line<'ui> {
fn new<C>(
draw_list: &'ui DrawListMut<'_>,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
c: C,
) -> Self
where
C: Into<ImColor32>,
{
Self {
p1: {
let v: sys::ImVec2 = p1.into();
v.into()
},
p2: {
let v: sys::ImVec2 = p2.into();
v.into()
},
color: c.into(),
thickness: 1.0,
draw_list,
}
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn build(self) {
unsafe {
let p1 = sys::ImVec2 {
x: self.p1[0],
y: self.p1[1],
};
let p2 = sys::ImVec2 {
x: self.p2[0],
y: self.p2[1],
};
sys::ImDrawList_AddLine(
self.draw_list.draw_list,
p1,
p2,
self.color.into(),
self.thickness,
)
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Rect<'ui> {
p1: [f32; 2],
p2: [f32; 2],
color: ImColor32,
rounding: f32,
flags: DrawCornerFlags,
thickness: f32,
filled: bool,
draw_list: &'ui DrawListMut<'ui>,
}
impl<'ui> Rect<'ui> {
fn new<C>(
draw_list: &'ui DrawListMut<'_>,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
c: C,
) -> Self
where
C: Into<ImColor32>,
{
Self {
p1: {
let v: sys::ImVec2 = p1.into();
v.into()
},
p2: {
let v: sys::ImVec2 = p2.into();
v.into()
},
color: c.into(),
rounding: 0.0,
flags: DrawCornerFlags::ALL,
thickness: 1.0,
filled: false,
draw_list,
}
}
pub fn rounding(mut self, rounding: f32) -> Self {
self.rounding = rounding;
self
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn filled(mut self, filled: bool) -> Self {
self.filled = filled;
self
}
pub fn flags(mut self, flags: DrawCornerFlags) -> Self {
self.flags = flags;
self
}
pub fn build(self) {
let p1 = sys::ImVec2 {
x: self.p1[0],
y: self.p1[1],
};
let p2 = sys::ImVec2 {
x: self.p2[0],
y: self.p2[1],
};
if self.filled {
unsafe {
sys::ImDrawList_AddRectFilled(
self.draw_list.draw_list,
p1,
p2,
self.color.into(),
self.rounding,
self.flags.bits() as sys::ImDrawFlags,
)
}
} else {
unsafe {
sys::ImDrawList_AddRect(
self.draw_list.draw_list,
p1,
p2,
self.color.into(),
self.rounding,
self.flags.bits() as sys::ImDrawFlags,
self.thickness,
)
}
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Circle<'ui> {
center: [f32; 2],
radius: f32,
color: ImColor32,
num_segments: i32,
thickness: f32,
filled: bool,
draw_list: &'ui DrawListMut<'ui>,
}
impl<'ui> Circle<'ui> {
fn new<C>(
draw_list: &'ui DrawListMut<'_>,
center: impl Into<sys::ImVec2>,
radius: f32,
color: C,
) -> Self
where
C: Into<ImColor32>,
{
Self {
center: {
let v: sys::ImVec2 = center.into();
v.into()
},
radius,
color: color.into(),
num_segments: 0, thickness: 1.0,
filled: false,
draw_list,
}
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn filled(mut self, filled: bool) -> Self {
self.filled = filled;
self
}
pub fn num_segments(mut self, num_segments: i32) -> Self {
self.num_segments = num_segments;
self
}
pub fn build(self) {
let center = sys::ImVec2 {
x: self.center[0],
y: self.center[1],
};
if self.filled {
unsafe {
sys::ImDrawList_AddCircleFilled(
self.draw_list.draw_list,
center,
self.radius,
self.color.into(),
self.num_segments,
)
}
} else {
unsafe {
sys::ImDrawList_AddCircle(
self.draw_list.draw_list,
center,
self.radius,
self.color.into(),
self.num_segments,
self.thickness,
)
}
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct BezierCurve<'ui> {
pos0: [f32; 2],
cp0: [f32; 2],
pos1: [f32; 2],
cp1: [f32; 2],
color: ImColor32,
thickness: f32,
num_segments: Option<u32>,
draw_list: &'ui DrawListMut<'ui>,
}
impl<'ui> BezierCurve<'ui> {
pub fn new<C>(
draw_list: &'ui DrawListMut<'_>,
pos0: impl Into<sys::ImVec2>,
cp0: impl Into<sys::ImVec2>,
cp1: impl Into<sys::ImVec2>,
pos1: impl Into<sys::ImVec2>,
c: C,
) -> Self
where
C: Into<ImColor32>,
{
Self {
pos0: {
let v: sys::ImVec2 = pos0.into();
v.into()
},
cp0: {
let v: sys::ImVec2 = cp0.into();
v.into()
},
cp1: {
let v: sys::ImVec2 = cp1.into();
v.into()
},
pos1: {
let v: sys::ImVec2 = pos1.into();
v.into()
},
color: c.into(),
thickness: 1.0,
num_segments: None,
draw_list,
}
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn num_segments(mut self, num_segments: u32) -> Self {
self.num_segments = Some(num_segments);
self
}
pub fn build(self) {
unsafe {
let pos0: sys::ImVec2 = self.pos0.into();
let cp0: sys::ImVec2 = self.cp0.into();
let cp1: sys::ImVec2 = self.cp1.into();
let pos1: sys::ImVec2 = self.pos1.into();
sys::ImDrawList_AddBezierCubic(
self.draw_list.draw_list,
pos0,
cp0,
cp1,
pos1,
self.color.into(),
self.thickness,
self.num_segments.unwrap_or(0) as i32,
)
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Polyline<'ui> {
points: Vec<sys::ImVec2>,
thickness: f32,
flags: PolylineFlags,
filled: bool,
color: ImColor32,
draw_list: &'ui DrawListMut<'ui>,
}
impl<'ui> Polyline<'ui> {
fn new<C, P>(draw_list: &'ui DrawListMut<'_>, points: Vec<P>, c: C) -> Self
where
C: Into<ImColor32>,
P: Into<sys::ImVec2>,
{
Self {
points: points.into_iter().map(Into::into).collect(),
color: c.into(),
thickness: 1.0,
flags: PolylineFlags::NONE,
filled: false,
draw_list,
}
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn flags(mut self, flags: PolylineFlags) -> Self {
self.flags = flags;
self
}
pub fn closed(mut self, closed: bool) -> Self {
self.flags.set(PolylineFlags::CLOSED, closed);
self
}
pub fn filled(mut self, filled: bool) -> Self {
self.filled = filled;
self
}
pub fn build(self) {
let count = match i32::try_from(self.points.len()) {
Ok(n) => n,
Err(_) => return,
};
if self.filled {
unsafe {
sys::ImDrawList_AddConvexPolyFilled(
self.draw_list.draw_list,
self.points.as_ptr(),
count,
self.color.into(),
)
}
} else {
unsafe {
sys::ImDrawList_AddPolyline(
self.draw_list.draw_list,
self.points.as_ptr(),
count,
self.color.into(),
self.flags.bits() as sys::ImDrawFlags,
self.thickness,
)
}
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Triangle<'ui> {
p1: [f32; 2],
p2: [f32; 2],
p3: [f32; 2],
color: ImColor32,
thickness: f32,
filled: bool,
draw_list: &'ui DrawListMut<'ui>,
}
impl<'ui> Triangle<'ui> {
fn new<C>(
draw_list: &'ui DrawListMut<'_>,
p1: impl Into<sys::ImVec2>,
p2: impl Into<sys::ImVec2>,
p3: impl Into<sys::ImVec2>,
c: C,
) -> Self
where
C: Into<ImColor32>,
{
Self {
p1: {
let v: sys::ImVec2 = p1.into();
v.into()
},
p2: {
let v: sys::ImVec2 = p2.into();
v.into()
},
p3: {
let v: sys::ImVec2 = p3.into();
v.into()
},
color: c.into(),
thickness: 1.0,
filled: false,
draw_list,
}
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn filled(mut self, filled: bool) -> Self {
self.filled = filled;
self
}
pub fn build(self) {
let p1 = sys::ImVec2 {
x: self.p1[0],
y: self.p1[1],
};
let p2 = sys::ImVec2 {
x: self.p2[0],
y: self.p2[1],
};
let p3 = sys::ImVec2 {
x: self.p3[0],
y: self.p3[1],
};
if self.filled {
unsafe {
sys::ImDrawList_AddTriangleFilled(
self.draw_list.draw_list,
p1,
p2,
p3,
self.color.into(),
)
}
} else {
unsafe {
sys::ImDrawList_AddTriangle(
self.draw_list.draw_list,
p1,
p2,
p3,
self.color.into(),
self.thickness,
)
}
}
}
}