Skip to main content

dear_imgui_rs/
draw.rs

1//! Immediate drawing helpers (DrawList)
2//!
3//! Safe wrappers over Dear ImGui draw lists plus optional low-level primitives
4//! for custom geometry. Prefer high-level builders; resort to `prim_*` only
5//! when you need exact control and understand the safety requirements.
6//!
7//! Example (basic drawing):
8//! ```no_run
9//! # use dear_imgui_rs::*;
10//! # let mut ctx = Context::create();
11//! # let ui = ctx.frame();
12//! let dl = ui.get_window_draw_list();
13//! dl.add_line([10.0, 10.0], [100.0, 100.0], [1.0, 1.0, 1.0, 1.0])
14//!     .thickness(2.0)
15//!     .build();
16//! dl.add_text([12.0, 12.0], [1.0, 0.8, 0.2, 1.0], "Hello DrawList");
17//! ```
18//!
19#![allow(
20    clippy::cast_possible_truncation,
21    clippy::cast_sign_loss,
22    clippy::as_conversions,
23    clippy::unnecessary_cast
24)]
25use crate::texture::TextureId;
26use bitflags::bitflags;
27use std::marker::PhantomData;
28
29use crate::colors::Color;
30use crate::sys;
31
32// (MintVec2 legacy alias removed; draw APIs now accept Into<sys::ImVec2>)
33
34/// Packed RGBA color compatible with imgui-rs
35#[repr(transparent)]
36#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
37pub struct ImColor32(u32);
38
39impl ImColor32 {
40    /// Convenience constant for solid black.
41    pub const BLACK: Self = Self(0xff_00_00_00);
42    /// Convenience constant for solid white.
43    pub const WHITE: Self = Self(0xff_ff_ff_ff);
44    /// Convenience constant for full transparency.
45    pub const TRANSPARENT: Self = Self(0);
46
47    /// Construct a color from 4 single-byte `u8` channel values
48    #[inline]
49    pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
50        Self(((a as u32) << 24) | (r as u32) | ((g as u32) << 8) | ((b as u32) << 16))
51    }
52
53    /// Construct a fully opaque color from 3 single-byte `u8` channel values
54    #[inline]
55    pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
56        Self::from_rgba(r, g, b, 0xff)
57    }
58
59    /// Construct from f32 values in range 0.0..=1.0
60    pub fn from_rgba_f32s(r: f32, g: f32, b: f32, a: f32) -> Self {
61        Self::from_rgba(
62            (r.clamp(0.0, 1.0) * 255.0) as u8,
63            (g.clamp(0.0, 1.0) * 255.0) as u8,
64            (b.clamp(0.0, 1.0) * 255.0) as u8,
65            (a.clamp(0.0, 1.0) * 255.0) as u8,
66        )
67    }
68
69    /// Return the bits of the color as a u32
70    #[inline]
71    pub const fn to_bits(self) -> u32 {
72        self.0
73    }
74}
75
76impl From<Color> for ImColor32 {
77    fn from(color: Color) -> Self {
78        Self::from_rgba_f32s(color.r, color.g, color.b, color.a)
79    }
80}
81
82impl From<[f32; 4]> for ImColor32 {
83    fn from(arr: [f32; 4]) -> Self {
84        Self::from_rgba_f32s(arr[0], arr[1], arr[2], arr[3])
85    }
86}
87
88impl From<(f32, f32, f32, f32)> for ImColor32 {
89    fn from((r, g, b, a): (f32, f32, f32, f32)) -> Self {
90        Self::from_rgba_f32s(r, g, b, a)
91    }
92}
93
94impl From<[f32; 3]> for ImColor32 {
95    fn from(arr: [f32; 3]) -> Self {
96        Self::from_rgba_f32s(arr[0], arr[1], arr[2], 1.0)
97    }
98}
99
100impl From<(f32, f32, f32)> for ImColor32 {
101    fn from((r, g, b): (f32, f32, f32)) -> Self {
102        Self::from_rgba_f32s(r, g, b, 1.0)
103    }
104}
105
106impl From<ImColor32> for u32 {
107    fn from(color: ImColor32) -> Self {
108        color.0
109    }
110}
111
112impl From<u32> for ImColor32 {
113    fn from(color: u32) -> Self {
114        ImColor32(color)
115    }
116}
117
118// Removed legacy local Vec2 in favor of passing `impl Into<sys::ImVec2>` and using arrays/tuples.
119bitflags! {
120    /// Draw list flags
121    #[repr(transparent)]
122    pub struct DrawListFlags: i32 {
123        /// No flags
124        const NONE = sys::ImDrawListFlags_None as i32;
125        /// Enable anti-aliased lines/borders
126        const ANTI_ALIASED_LINES = sys::ImDrawListFlags_AntiAliasedLines as i32;
127        /// Enable anti-aliased lines/borders using textures where possible
128        const ANTI_ALIASED_LINES_USE_TEX = sys::ImDrawListFlags_AntiAliasedLinesUseTex as i32;
129        /// Enable anti-aliased edge around filled shapes
130        const ANTI_ALIASED_FILL = sys::ImDrawListFlags_AntiAliasedFill as i32;
131        /// Can emit 'VtxOffset > 0' to allow large meshes
132        const ALLOW_VTX_OFFSET = sys::ImDrawListFlags_AllowVtxOffset as i32;
133    }
134}
135
136bitflags! {
137    /// Flags accepted by `AddPolyline()` and `PathStroke()`.
138    #[repr(transparent)]
139    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
140    pub struct PolylineFlags: u32 {
141        const NONE = sys::ImDrawFlags_None as u32;
142        const CLOSED = sys::ImDrawFlags_Closed as u32;
143    }
144}
145
146impl Default for PolylineFlags {
147    fn default() -> Self {
148        Self::NONE
149    }
150}
151
152bitflags! {
153    /// Corner rounding flags accepted by rectangle and rounded-image drawing APIs.
154    ///
155    /// Dear ImGui uses zero as "default to all corners" when `rounding > 0`.
156    /// Use [`DrawCornerFlags::NO_ROUNDING`] to explicitly disable rounding.
157    #[repr(transparent)]
158    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
159    pub struct DrawCornerFlags: u32 {
160        const DEFAULT = sys::ImDrawFlags_None as u32;
161        const TOP_LEFT = sys::ImDrawFlags_RoundCornersTopLeft as u32;
162        const TOP_RIGHT = sys::ImDrawFlags_RoundCornersTopRight as u32;
163        const BOTTOM_LEFT = sys::ImDrawFlags_RoundCornersBottomLeft as u32;
164        const BOTTOM_RIGHT = sys::ImDrawFlags_RoundCornersBottomRight as u32;
165        const TOP = sys::ImDrawFlags_RoundCornersTop as u32;
166        const BOTTOM = sys::ImDrawFlags_RoundCornersBottom as u32;
167        const LEFT = sys::ImDrawFlags_RoundCornersLeft as u32;
168        const RIGHT = sys::ImDrawFlags_RoundCornersRight as u32;
169        const ALL = sys::ImDrawFlags_RoundCornersAll as u32;
170        const NO_ROUNDING = sys::ImDrawFlags_RoundCornersNone as u32;
171    }
172}
173
174impl Default for DrawCornerFlags {
175    fn default() -> Self {
176        Self::DEFAULT
177    }
178}
179
180// All draw types have been moved to crate::render module
181// Use crate::render::{DrawVert, DrawIdx, DrawData, DrawListIterator} instead
182
183/// Draw list wrapper
184#[repr(transparent)]
185pub struct DrawList(*mut sys::ImDrawList);
186
187impl DrawList {
188    /// Get command buffer as slice
189    unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] {
190        unsafe {
191            if (*self.0).CmdBuffer.Size <= 0 || (*self.0).CmdBuffer.Data.is_null() {
192                return &[];
193            }
194            let len = match usize::try_from((*self.0).CmdBuffer.Size) {
195                Ok(len) => len,
196                Err(_) => return &[],
197            };
198            std::slice::from_raw_parts((*self.0).CmdBuffer.Data as *const sys::ImDrawCmd, len)
199        }
200    }
201
202    /// Get vertex buffer
203    pub fn vtx_buffer(&self) -> &[crate::render::DrawVert] {
204        unsafe {
205            if (*self.0).VtxBuffer.Size <= 0 || (*self.0).VtxBuffer.Data.is_null() {
206                return &[];
207            }
208            let len = match usize::try_from((*self.0).VtxBuffer.Size) {
209                Ok(len) => len,
210                Err(_) => return &[],
211            };
212            std::slice::from_raw_parts(
213                (*self.0).VtxBuffer.Data as *const crate::render::DrawVert,
214                len,
215            )
216        }
217    }
218
219    /// Get index buffer
220    pub fn idx_buffer(&self) -> &[crate::render::DrawIdx] {
221        unsafe {
222            if (*self.0).IdxBuffer.Size <= 0 || (*self.0).IdxBuffer.Data.is_null() {
223                return &[];
224            }
225            let len = match usize::try_from((*self.0).IdxBuffer.Size) {
226                Ok(len) => len,
227                Err(_) => return &[],
228            };
229            std::slice::from_raw_parts((*self.0).IdxBuffer.Data, len)
230        }
231    }
232
233    /// Get draw commands iterator
234    pub fn commands(&self) -> DrawCmdIterator<'_> {
235        unsafe {
236            DrawCmdIterator {
237                iter: self.cmd_buffer().iter(),
238            }
239        }
240    }
241}
242
243/// Owned draw list returned by `CloneOutput`.
244///
245/// This owns an independent copy of a draw list and will free it on drop.
246pub struct OwnedDrawList(*mut sys::ImDrawList);
247
248impl Drop for OwnedDrawList {
249    fn drop(&mut self) {
250        unsafe { sys::ImDrawList_destroy(self.0) }
251    }
252}
253
254impl OwnedDrawList {
255    /// Create from raw pointer.
256    ///
257    /// Safety: `ptr` must be a valid pointer returned by `ImDrawList_CloneOutput` or `ImDrawList_ImDrawList`.
258    pub(crate) unsafe fn from_raw(ptr: *mut sys::ImDrawList) -> Self {
259        Self(ptr)
260    }
261
262    /// Borrow as a read-only draw list view.
263    pub fn as_view(&self) -> DrawList {
264        DrawList(self.0)
265    }
266
267    /// Clear free memory held by the draw list (release heap allocations).
268    pub fn clear_free_memory(&mut self) {
269        unsafe { sys::ImDrawList__ClearFreeMemory(self.0) }
270    }
271
272    /// Reset for new frame (not commonly needed for cloned lists).
273    pub fn reset_for_new_frame(&mut self) {
274        unsafe { sys::ImDrawList__ResetForNewFrame(self.0) }
275    }
276}
277
278/// Iterator over draw commands
279pub struct DrawCmdIterator<'a> {
280    iter: std::slice::Iter<'a, sys::ImDrawCmd>,
281}
282
283impl Iterator for DrawCmdIterator<'_> {
284    type Item = DrawCmd;
285
286    fn next(&mut self) -> Option<Self::Item> {
287        self.iter.next().map(|cmd| {
288            let cmd_params = DrawCmdParams {
289                clip_rect: [
290                    cmd.ClipRect.x,
291                    cmd.ClipRect.y,
292                    cmd.ClipRect.z,
293                    cmd.ClipRect.w,
294                ],
295                texture_id: TextureId::from(unsafe {
296                    let mut cmd_copy = *cmd;
297                    sys::ImDrawCmd_GetTexID(&mut cmd_copy)
298                }),
299                vtx_offset: cmd.VtxOffset as usize,
300                idx_offset: cmd.IdxOffset as usize,
301            };
302
303            match cmd.UserCallback {
304                Some(raw_callback) if raw_callback as usize == usize::MAX => {
305                    DrawCmd::ResetRenderState
306                }
307                Some(raw_callback) => DrawCmd::RawCallback {
308                    callback: raw_callback,
309                    raw_cmd: cmd,
310                },
311                None => DrawCmd::Elements {
312                    count: cmd.ElemCount as usize,
313                    cmd_params,
314                },
315            }
316        })
317    }
318}
319
320/// Draw command parameters
321#[derive(Copy, Clone, Debug, PartialEq)]
322pub struct DrawCmdParams {
323    /// Clipping rectangle (left, top, right, bottom)
324    pub clip_rect: [f32; 4],
325    /// Texture ID
326    pub texture_id: TextureId,
327    /// Vertex offset
328    pub vtx_offset: usize,
329    /// Index offset
330    pub idx_offset: usize,
331}
332
333/// Draw command
334#[derive(Debug, Clone)]
335pub enum DrawCmd {
336    /// Elements to draw
337    Elements {
338        /// Number of indices
339        count: usize,
340        /// Command parameters
341        cmd_params: DrawCmdParams,
342    },
343    /// Reset render state
344    ResetRenderState,
345    /// Raw callback
346    RawCallback {
347        /// Callback function
348        callback: unsafe extern "C" fn(*const sys::ImDrawList, cmd: *const sys::ImDrawCmd),
349        /// Raw command
350        raw_cmd: *const sys::ImDrawCmd,
351    },
352}
353
354enum DrawListType {
355    Window,
356    Background,
357    Foreground,
358}
359
360/// Object implementing the custom draw API.
361///
362/// Called from [`Ui::get_window_draw_list`], [`Ui::get_background_draw_list`] or [`Ui::get_foreground_draw_list`].
363/// No more than one instance of this structure can live in a program at the same time.
364/// The program will panic on creating a second instance.
365pub struct DrawListMut<'ui> {
366    draw_list_type: DrawListType,
367    draw_list: *mut sys::ImDrawList,
368    _phantom: PhantomData<&'ui ()>,
369}
370
371// Lock for each variant of draw list
372static DRAW_LIST_LOADED_WINDOW: std::sync::atomic::AtomicBool =
373    std::sync::atomic::AtomicBool::new(false);
374static DRAW_LIST_LOADED_BACKGROUND: std::sync::atomic::AtomicBool =
375    std::sync::atomic::AtomicBool::new(false);
376static DRAW_LIST_LOADED_FOREGROUND: std::sync::atomic::AtomicBool =
377    std::sync::atomic::AtomicBool::new(false);
378
379impl Drop for DrawListMut<'_> {
380    fn drop(&mut self) {
381        match self.draw_list_type {
382            DrawListType::Window => &DRAW_LIST_LOADED_WINDOW,
383            DrawListType::Background => &DRAW_LIST_LOADED_BACKGROUND,
384            DrawListType::Foreground => &DRAW_LIST_LOADED_FOREGROUND,
385        }
386        .store(false, std::sync::atomic::Ordering::Release);
387    }
388}
389
390impl DrawListMut<'_> {
391    fn lock_draw_list(t: DrawListType) {
392        let lock = match t {
393            DrawListType::Window => &DRAW_LIST_LOADED_WINDOW,
394            DrawListType::Background => &DRAW_LIST_LOADED_BACKGROUND,
395            DrawListType::Foreground => &DRAW_LIST_LOADED_FOREGROUND,
396        };
397
398        if lock
399            .compare_exchange(
400                false,
401                true,
402                std::sync::atomic::Ordering::Acquire,
403                std::sync::atomic::Ordering::Relaxed,
404            )
405            .is_err()
406        {
407            panic!(
408                "A DrawListMut is already in use! You can only have one DrawListMut in use at a time."
409            );
410        }
411    }
412
413    pub(crate) fn window(_ui: &crate::Ui) -> Self {
414        Self::lock_draw_list(DrawListType::Window);
415        Self {
416            draw_list: unsafe { sys::igGetWindowDrawList() },
417            draw_list_type: DrawListType::Window,
418            _phantom: PhantomData,
419        }
420    }
421
422    pub(crate) fn background(_ui: &crate::Ui) -> Self {
423        Self::lock_draw_list(DrawListType::Background);
424        Self {
425            draw_list: unsafe { sys::igGetBackgroundDrawList(std::ptr::null_mut()) },
426            draw_list_type: DrawListType::Background,
427            _phantom: PhantomData,
428        }
429    }
430
431    pub(crate) fn foreground(_ui: &crate::Ui) -> Self {
432        Self::lock_draw_list(DrawListType::Foreground);
433        Self {
434            draw_list: unsafe { sys::igGetForegroundDrawList_ViewportPtr(std::ptr::null_mut()) },
435            draw_list_type: DrawListType::Foreground,
436            _phantom: PhantomData,
437        }
438    }
439}
440
441/// Drawing functions
442impl<'ui> DrawListMut<'ui> {
443    /// Split draw into multiple channels and merge automatically at the end of the closure.
444    #[doc(alias = "ChannelsSplit")]
445    pub fn channels_split<F: FnOnce(&ChannelsSplit<'ui>)>(&'ui self, channels_count: u32, f: F) {
446        unsafe { sys::ImDrawList_ChannelsSplit(self.draw_list, channels_count as i32) };
447        f(&ChannelsSplit {
448            draw_list: self,
449            channels_count,
450        });
451        unsafe { sys::ImDrawList_ChannelsMerge(self.draw_list) };
452    }
453    /// Returns a line from point `p1` to `p2` with color `c`.
454    pub fn add_line<C>(
455        &'ui self,
456        p1: impl Into<sys::ImVec2>,
457        p2: impl Into<sys::ImVec2>,
458        c: C,
459    ) -> Line<'ui>
460    where
461        C: Into<ImColor32>,
462    {
463        Line::new(self, p1, p2, c)
464    }
465
466    /// Returns a rectangle whose upper-left corner is at point `p1`
467    /// and lower-right corner is at point `p2`, with color `c`.
468    pub fn add_rect<C>(
469        &'ui self,
470        p1: impl Into<sys::ImVec2>,
471        p2: impl Into<sys::ImVec2>,
472        c: C,
473    ) -> Rect<'ui>
474    where
475        C: Into<ImColor32>,
476    {
477        Rect::new(self, p1, p2, c)
478    }
479
480    /// Draw a filled rectangle with per-corner colors (counter-clockwise from upper-left).
481    #[doc(alias = "AddRectFilledMultiColor")]
482    pub fn add_rect_filled_multicolor<C1, C2, C3, C4>(
483        &self,
484        p1: impl Into<sys::ImVec2>,
485        p2: impl Into<sys::ImVec2>,
486        col_upr_left: C1,
487        col_upr_right: C2,
488        col_bot_right: C3,
489        col_bot_left: C4,
490    ) where
491        C1: Into<ImColor32>,
492        C2: Into<ImColor32>,
493        C3: Into<ImColor32>,
494        C4: Into<ImColor32>,
495    {
496        let p_min: sys::ImVec2 = p1.into();
497        let p_max: sys::ImVec2 = p2.into();
498        let c_ul: u32 = col_upr_left.into().into();
499        let c_ur: u32 = col_upr_right.into().into();
500        let c_br: u32 = col_bot_right.into().into();
501        let c_bl: u32 = col_bot_left.into().into();
502        unsafe {
503            sys::ImDrawList_AddRectFilledMultiColor(
504                self.draw_list,
505                p_min,
506                p_max,
507                c_ul,
508                c_ur,
509                c_br,
510                c_bl,
511            );
512        }
513    }
514
515    /// Returns a circle with the given `center`, `radius` and `color`.
516    pub fn add_circle<C>(
517        &'ui self,
518        center: impl Into<sys::ImVec2>,
519        radius: f32,
520        color: C,
521    ) -> Circle<'ui>
522    where
523        C: Into<ImColor32>,
524    {
525        Circle::new(self, center, radius, color)
526    }
527
528    /// Returns a Bezier curve stretching from `pos0` to `pos1`, whose
529    /// curvature is defined by `cp0` and `cp1`.
530    #[doc(alias = "AddBezier", alias = "AddBezierCubic")]
531    pub fn add_bezier_curve(
532        &'ui self,
533        pos0: impl Into<sys::ImVec2>,
534        cp0: impl Into<sys::ImVec2>,
535        cp1: impl Into<sys::ImVec2>,
536        pos1: impl Into<sys::ImVec2>,
537        color: impl Into<ImColor32>,
538    ) -> BezierCurve<'ui> {
539        BezierCurve::new(self, pos0, cp0, cp1, pos1, color)
540    }
541
542    /// Returns a triangle with the given 3 vertices `p1`, `p2` and `p3` and color `c`.
543    #[doc(alias = "AddTriangleFilled", alias = "AddTriangle")]
544    pub fn add_triangle<C>(
545        &'ui self,
546        p1: impl Into<sys::ImVec2>,
547        p2: impl Into<sys::ImVec2>,
548        p3: impl Into<sys::ImVec2>,
549        c: C,
550    ) -> Triangle<'ui>
551    where
552        C: Into<ImColor32>,
553    {
554        Triangle::new(self, p1, p2, p3, c)
555    }
556
557    /// Returns a polygonal line. If filled is rendered as a convex
558    /// polygon, if not filled is drawn as a line specified by
559    /// [`Polyline::thickness`] (default 1.0)
560    #[doc(alias = "AddPolyline", alias = "AddConvexPolyFilled")]
561    pub fn add_polyline<C, P>(&'ui self, points: Vec<P>, c: C) -> Polyline<'ui>
562    where
563        C: Into<ImColor32>,
564        P: Into<sys::ImVec2>,
565    {
566        Polyline::new(self, points, c)
567    }
568
569    // ========== Path Drawing Functions ==========
570
571    /// Clear the current path (i.e. start a new path).
572    #[doc(alias = "PathClear")]
573    pub fn path_clear(&self) {
574        unsafe {
575            // PathClear is inline: _Path.Size = 0;
576            let draw_list = self.draw_list;
577            (*draw_list)._Path.Size = 0;
578        }
579    }
580
581    /// Add a point to the current path.
582    #[doc(alias = "PathLineTo")]
583    pub fn path_line_to(&self, pos: impl Into<sys::ImVec2>) {
584        unsafe { sys::ImDrawList_PathLineTo(self.draw_list, pos.into()) }
585    }
586
587    /// Add a point to the current path, merging duplicate points.
588    #[doc(alias = "PathLineToMergeDuplicate")]
589    pub fn path_line_to_merge_duplicate(&self, pos: impl Into<sys::ImVec2>) {
590        unsafe { sys::ImDrawList_PathLineToMergeDuplicate(self.draw_list, pos.into()) }
591    }
592
593    /// Add an arc to the current path.
594    #[doc(alias = "PathArcTo")]
595    pub fn path_arc_to(
596        &self,
597        center: impl Into<sys::ImVec2>,
598        radius: f32,
599        a_min: f32,
600        a_max: f32,
601        num_segments: i32,
602    ) {
603        unsafe {
604            let center_vec: sys::ImVec2 = center.into();
605            sys::ImDrawList_PathArcTo(
606                self.draw_list,
607                center_vec,
608                radius,
609                a_min,
610                a_max,
611                num_segments,
612            );
613        }
614    }
615
616    /// Add an arc to the current path using fast precomputed angles.
617    #[doc(alias = "PathArcToFast")]
618    pub fn path_arc_to_fast(
619        &self,
620        center: impl Into<sys::ImVec2>,
621        radius: f32,
622        a_min_of_12: i32,
623        a_max_of_12: i32,
624    ) {
625        unsafe {
626            let center_vec: sys::ImVec2 = center.into();
627            sys::ImDrawList_PathArcToFast(
628                self.draw_list,
629                center_vec,
630                radius,
631                a_min_of_12,
632                a_max_of_12,
633            );
634        }
635    }
636
637    /// Add a rectangle to the current path.
638    #[doc(alias = "PathRect")]
639    pub fn path_rect(
640        &self,
641        rect_min: impl Into<sys::ImVec2>,
642        rect_max: impl Into<sys::ImVec2>,
643        rounding: f32,
644        flags: DrawCornerFlags,
645    ) {
646        unsafe {
647            let min_vec: sys::ImVec2 = rect_min.into();
648            let max_vec: sys::ImVec2 = rect_max.into();
649            sys::ImDrawList_PathRect(
650                self.draw_list,
651                min_vec,
652                max_vec,
653                rounding,
654                flags.bits() as sys::ImDrawFlags,
655            );
656        }
657    }
658
659    /// Add an elliptical arc to the current path.
660    #[doc(alias = "PathEllipticalArcTo")]
661    pub fn path_elliptical_arc_to(
662        &self,
663        center: impl Into<sys::ImVec2>,
664        radius: impl Into<sys::ImVec2>,
665        rot: f32,
666        a_min: f32,
667        a_max: f32,
668        num_segments: i32,
669    ) {
670        unsafe {
671            sys::ImDrawList_PathEllipticalArcTo(
672                self.draw_list,
673                center.into(),
674                radius.into(),
675                rot,
676                a_min,
677                a_max,
678                num_segments,
679            )
680        }
681    }
682
683    /// Add a quadratic bezier curve to the current path.
684    #[doc(alias = "PathBezierQuadraticCurveTo")]
685    pub fn path_bezier_quadratic_curve_to(
686        &self,
687        p2: impl Into<sys::ImVec2>,
688        p3: impl Into<sys::ImVec2>,
689        num_segments: i32,
690    ) {
691        unsafe {
692            sys::ImDrawList_PathBezierQuadraticCurveTo(
693                self.draw_list,
694                p2.into(),
695                p3.into(),
696                num_segments,
697            )
698        }
699    }
700
701    /// Add a cubic bezier curve to the current path.
702    #[doc(alias = "PathBezierCubicCurveTo")]
703    pub fn path_bezier_cubic_curve_to(
704        &self,
705        p2: impl Into<sys::ImVec2>,
706        p3: impl Into<sys::ImVec2>,
707        p4: impl Into<sys::ImVec2>,
708        num_segments: i32,
709    ) {
710        unsafe {
711            sys::ImDrawList_PathBezierCubicCurveTo(
712                self.draw_list,
713                p2.into(),
714                p3.into(),
715                p4.into(),
716                num_segments,
717            )
718        }
719    }
720
721    /// Stroke the current path with the specified color and thickness.
722    #[doc(alias = "PathStroke")]
723    pub fn path_stroke(&self, color: impl Into<ImColor32>, flags: PolylineFlags, thickness: f32) {
724        unsafe {
725            // PathStroke is inline: AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0;
726            let draw_list = self.draw_list;
727            let path = &mut (*draw_list)._Path;
728
729            if path.Size > 0 {
730                sys::ImDrawList_AddPolyline(
731                    self.draw_list,
732                    path.Data,
733                    path.Size,
734                    color.into().into(),
735                    flags.bits() as sys::ImDrawFlags,
736                    thickness,
737                );
738                path.Size = 0; // Clear path after stroking
739            }
740        }
741    }
742
743    /// Fill the current path as a convex polygon.
744    #[doc(alias = "PathFillConvex")]
745    pub fn path_fill_convex(&self, color: impl Into<ImColor32>) {
746        unsafe {
747            // PathFillConvex is inline: AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0;
748            let draw_list = self.draw_list;
749            let path = &mut (*draw_list)._Path;
750
751            if path.Size > 0 {
752                sys::ImDrawList_AddConvexPolyFilled(
753                    self.draw_list,
754                    path.Data,
755                    path.Size,
756                    color.into().into(),
757                );
758                path.Size = 0; // Clear path after filling
759            }
760        }
761    }
762
763    /// Draw a text whose upper-left corner is at point `pos`.
764    pub fn add_text(
765        &self,
766        pos: impl Into<sys::ImVec2>,
767        col: impl Into<ImColor32>,
768        text: impl AsRef<str>,
769    ) {
770        use std::os::raw::c_char;
771
772        let text = text.as_ref();
773        let pos: sys::ImVec2 = pos.into();
774        let col = col.into();
775
776        unsafe {
777            let start = text.as_ptr() as *const c_char;
778            let end = (start as usize + text.len()) as *const c_char;
779            sys::ImDrawList_AddText_Vec2(self.draw_list, pos, col.into(), start, end);
780        }
781    }
782
783    /// Draw text with an explicit font and optional fine CPU clip rectangle.
784    ///
785    /// This mirrors Dear ImGui's `ImDrawList::AddText(ImFont*, ...)` overload.
786    #[doc(alias = "AddText")]
787    pub fn add_text_with_font(
788        &self,
789        font: &crate::fonts::Font,
790        font_size: f32,
791        pos: impl Into<sys::ImVec2>,
792        col: impl Into<ImColor32>,
793        text: impl AsRef<str>,
794        wrap_width: f32,
795        cpu_fine_clip_rect: Option<[f32; 4]>,
796    ) {
797        use std::os::raw::c_char;
798        let text = text.as_ref();
799        let pos: sys::ImVec2 = pos.into();
800        let col = col.into();
801        let font_ptr = font.raw();
802
803        let clip_vec4 = cpu_fine_clip_rect.map(|r| sys::ImVec4 {
804            x: r[0],
805            y: r[1],
806            z: r[2],
807            w: r[3],
808        });
809        let clip_ptr = match clip_vec4.as_ref() {
810            Some(v) => v as *const sys::ImVec4,
811            None => std::ptr::null(),
812        };
813
814        unsafe {
815            let start = text.as_ptr() as *const c_char;
816            let end = (start as usize + text.len()) as *const c_char;
817            sys::ImDrawList_AddText_FontPtr(
818                self.draw_list,
819                font_ptr,
820                font_size,
821                pos,
822                col.into(),
823                start,
824                end,
825                wrap_width,
826                clip_ptr,
827            );
828        }
829    }
830
831    // channels_split is provided on DrawListMut
832
833    /// Push a texture on the drawlist texture stack (ImGui 1.92+)
834    ///
835    /// While pushed, image and primitives will use this texture unless otherwise specified.
836    ///
837    /// Example:
838    /// ```no_run
839    /// # use dear_imgui_rs::*;
840    /// # fn demo(ui: &Ui) {
841    /// let dl = ui.get_window_draw_list();
842    /// let tex = texture::TextureId::new(1);
843    /// dl.push_texture(tex);
844    /// dl.add_image(tex, [10.0,10.0], [110.0,110.0], [0.0,0.0], [1.0,1.0], Color::WHITE);
845    /// dl.pop_texture();
846    /// # }
847    /// ```
848    #[doc(alias = "PushTexture")]
849    pub fn push_texture(&self, texture: impl Into<crate::texture::TextureRef>) {
850        let tex_ref = texture.into().raw();
851        unsafe { sys::ImDrawList_PushTexture(self.draw_list, tex_ref) }
852    }
853
854    /// Pop the last texture from the drawlist texture stack (ImGui 1.92+)
855    #[doc(alias = "PopTexture")]
856    pub fn pop_texture(&self) {
857        unsafe {
858            sys::ImDrawList_PopTexture(self.draw_list);
859        }
860    }
861
862    /// Push a clip rectangle, optionally intersecting with the current clip rect.
863    #[doc(alias = "PushClipRect")]
864    pub fn push_clip_rect(
865        &self,
866        clip_rect_min: impl Into<sys::ImVec2>,
867        clip_rect_max: impl Into<sys::ImVec2>,
868        intersect_with_current: bool,
869    ) {
870        unsafe {
871            sys::ImDrawList_PushClipRect(
872                self.draw_list,
873                clip_rect_min.into(),
874                clip_rect_max.into(),
875                intersect_with_current,
876            )
877        }
878    }
879
880    /// Push a full-screen clip rectangle.
881    #[doc(alias = "PushClipRectFullScreen")]
882    pub fn push_clip_rect_full_screen(&self) {
883        unsafe { sys::ImDrawList_PushClipRectFullScreen(self.draw_list) }
884    }
885
886    /// Pop the last clip rectangle.
887    #[doc(alias = "PopClipRect")]
888    pub fn pop_clip_rect(&self) {
889        unsafe { sys::ImDrawList_PopClipRect(self.draw_list) }
890    }
891
892    /// Get current minimum clip rectangle point.
893    pub fn clip_rect_min(&self) -> [f32; 2] {
894        let out = unsafe { sys::ImDrawList_GetClipRectMin(self.draw_list) };
895        out.into()
896    }
897
898    /// Get current maximum clip rectangle point.
899    pub fn clip_rect_max(&self) -> [f32; 2] {
900        let out = unsafe { sys::ImDrawList_GetClipRectMax(self.draw_list) };
901        out.into()
902    }
903
904    /// Convenience: push a clip rect, run f, pop.
905    pub fn with_clip_rect<F>(
906        &self,
907        clip_rect_min: impl Into<sys::ImVec2>,
908        clip_rect_max: impl Into<sys::ImVec2>,
909        f: F,
910    ) where
911        F: FnOnce(),
912    {
913        self.push_clip_rect(clip_rect_min, clip_rect_max, false);
914        f();
915        self.pop_clip_rect();
916    }
917
918    /// Add an image quad (axis-aligned). Tint via `col`.
919    #[doc(alias = "AddImage")]
920    pub fn add_image(
921        &self,
922        texture: impl Into<crate::texture::TextureRef>,
923        p_min: impl Into<sys::ImVec2>,
924        p_max: impl Into<sys::ImVec2>,
925        uv_min: impl Into<sys::ImVec2>,
926        uv_max: impl Into<sys::ImVec2>,
927        col: impl Into<ImColor32>,
928    ) {
929        // Example:
930        // let tex = texture::TextureId::new(5);
931        // self.add_image(tex, [10.0,10.0], [110.0,110.0], [0.0,0.0], [1.0,1.0], Color::WHITE);
932        let p_min: sys::ImVec2 = p_min.into();
933        let p_max: sys::ImVec2 = p_max.into();
934        let uv_min: sys::ImVec2 = uv_min.into();
935        let uv_max: sys::ImVec2 = uv_max.into();
936        let col = col.into().to_bits();
937        let tex_ref = texture.into().raw();
938        unsafe {
939            sys::ImDrawList_AddImage(self.draw_list, tex_ref, p_min, p_max, uv_min, uv_max, col)
940        }
941    }
942
943    /// Add an image with 4 arbitrary corners.
944    #[doc(alias = "AddImageQuad")]
945    pub fn add_image_quad(
946        &self,
947        texture: impl Into<crate::texture::TextureRef>,
948        p1: impl Into<sys::ImVec2>,
949        p2: impl Into<sys::ImVec2>,
950        p3: impl Into<sys::ImVec2>,
951        p4: impl Into<sys::ImVec2>,
952        uv1: impl Into<sys::ImVec2>,
953        uv2: impl Into<sys::ImVec2>,
954        uv3: impl Into<sys::ImVec2>,
955        uv4: impl Into<sys::ImVec2>,
956        col: impl Into<ImColor32>,
957    ) {
958        // Example:
959        // let tex = texture::TextureId::new(5);
960        // self.add_image_quad(
961        //     tex,
962        //     [10.0,10.0], [110.0,20.0], [120.0,120.0], [5.0,100.0],
963        //     [0.0,0.0], [1.0,0.0], [1.0,1.0], [0.0,1.0],
964        //     Color::WHITE,
965        // );
966        let p1: sys::ImVec2 = p1.into();
967        let p2: sys::ImVec2 = p2.into();
968        let p3: sys::ImVec2 = p3.into();
969        let p4: sys::ImVec2 = p4.into();
970        let uv1: sys::ImVec2 = uv1.into();
971        let uv2: sys::ImVec2 = uv2.into();
972        let uv3: sys::ImVec2 = uv3.into();
973        let uv4: sys::ImVec2 = uv4.into();
974        let col = col.into().to_bits();
975        let tex_ref = texture.into().raw();
976        unsafe {
977            sys::ImDrawList_AddImageQuad(
978                self.draw_list,
979                tex_ref,
980                p1,
981                p2,
982                p3,
983                p4,
984                uv1,
985                uv2,
986                uv3,
987                uv4,
988                col,
989            )
990        }
991    }
992
993    /// Add an axis-aligned rounded image.
994    #[doc(alias = "AddImageRounded")]
995    pub fn add_image_rounded(
996        &self,
997        texture: impl Into<crate::texture::TextureRef>,
998        p_min: impl Into<sys::ImVec2>,
999        p_max: impl Into<sys::ImVec2>,
1000        uv_min: impl Into<sys::ImVec2>,
1001        uv_max: impl Into<sys::ImVec2>,
1002        col: impl Into<ImColor32>,
1003        rounding: f32,
1004        flags: DrawCornerFlags,
1005    ) {
1006        // Example:
1007        // let tex = texture::TextureId::new(5);
1008        // self.add_image_rounded(
1009        //     tex,
1010        //     [10.0,10.0], [110.0,110.0],
1011        //     [0.0,0.0], [1.0,1.0],
1012        //     Color::WHITE,
1013        //     8.0,
1014        //     DrawCornerFlags::ALL,
1015        // );
1016        let p_min: sys::ImVec2 = p_min.into();
1017        let p_max: sys::ImVec2 = p_max.into();
1018        let uv_min: sys::ImVec2 = uv_min.into();
1019        let uv_max: sys::ImVec2 = uv_max.into();
1020        let col = col.into().to_bits();
1021        let tex_ref = texture.into().raw();
1022        unsafe {
1023            sys::ImDrawList_AddImageRounded(
1024                self.draw_list,
1025                tex_ref,
1026                p_min,
1027                p_max,
1028                uv_min,
1029                uv_max,
1030                col,
1031                rounding,
1032                flags.bits() as sys::ImDrawFlags,
1033            )
1034        }
1035    }
1036
1037    /// Draw a quadrilateral outline given four points.
1038    #[doc(alias = "AddQuad")]
1039    pub fn add_quad<C>(
1040        &self,
1041        p1: impl Into<sys::ImVec2>,
1042        p2: impl Into<sys::ImVec2>,
1043        p3: impl Into<sys::ImVec2>,
1044        p4: impl Into<sys::ImVec2>,
1045        col: C,
1046        thickness: f32,
1047    ) where
1048        C: Into<ImColor32>,
1049    {
1050        unsafe {
1051            sys::ImDrawList_AddQuad(
1052                self.draw_list,
1053                p1.into(),
1054                p2.into(),
1055                p3.into(),
1056                p4.into(),
1057                col.into().into(),
1058                thickness,
1059            )
1060        }
1061    }
1062
1063    /// Draw a filled quadrilateral given four points.
1064    #[doc(alias = "AddQuadFilled")]
1065    pub fn add_quad_filled<C>(
1066        &self,
1067        p1: impl Into<sys::ImVec2>,
1068        p2: impl Into<sys::ImVec2>,
1069        p3: impl Into<sys::ImVec2>,
1070        p4: impl Into<sys::ImVec2>,
1071        col: C,
1072    ) where
1073        C: Into<ImColor32>,
1074    {
1075        unsafe {
1076            sys::ImDrawList_AddQuadFilled(
1077                self.draw_list,
1078                p1.into(),
1079                p2.into(),
1080                p3.into(),
1081                p4.into(),
1082                col.into().into(),
1083            )
1084        }
1085    }
1086
1087    /// Draw a regular n-gon outline.
1088    #[doc(alias = "AddNgon")]
1089    pub fn add_ngon<C>(
1090        &self,
1091        center: impl Into<sys::ImVec2>,
1092        radius: f32,
1093        col: C,
1094        num_segments: i32,
1095        thickness: f32,
1096    ) where
1097        C: Into<ImColor32>,
1098    {
1099        unsafe {
1100            sys::ImDrawList_AddNgon(
1101                self.draw_list,
1102                center.into(),
1103                radius,
1104                col.into().into(),
1105                num_segments,
1106                thickness,
1107            )
1108        }
1109    }
1110
1111    /// Draw a filled regular n-gon.
1112    #[doc(alias = "AddNgonFilled")]
1113    pub fn add_ngon_filled<C>(
1114        &self,
1115        center: impl Into<sys::ImVec2>,
1116        radius: f32,
1117        col: C,
1118        num_segments: i32,
1119    ) where
1120        C: Into<ImColor32>,
1121    {
1122        unsafe {
1123            sys::ImDrawList_AddNgonFilled(
1124                self.draw_list,
1125                center.into(),
1126                radius,
1127                col.into().into(),
1128                num_segments,
1129            )
1130        }
1131    }
1132
1133    /// Draw an ellipse outline.
1134    #[doc(alias = "AddEllipse")]
1135    pub fn add_ellipse<C>(
1136        &self,
1137        center: impl Into<sys::ImVec2>,
1138        radius: impl Into<sys::ImVec2>,
1139        col: C,
1140        rot: f32,
1141        num_segments: i32,
1142        thickness: f32,
1143    ) where
1144        C: Into<ImColor32>,
1145    {
1146        unsafe {
1147            sys::ImDrawList_AddEllipse(
1148                self.draw_list,
1149                center.into(),
1150                radius.into(),
1151                col.into().into(),
1152                rot,
1153                num_segments,
1154                thickness,
1155            )
1156        }
1157    }
1158
1159    /// Draw a filled ellipse.
1160    #[doc(alias = "AddEllipseFilled")]
1161    pub fn add_ellipse_filled<C>(
1162        &self,
1163        center: impl Into<sys::ImVec2>,
1164        radius: impl Into<sys::ImVec2>,
1165        col: C,
1166        rot: f32,
1167        num_segments: i32,
1168    ) where
1169        C: Into<ImColor32>,
1170    {
1171        unsafe {
1172            sys::ImDrawList_AddEllipseFilled(
1173                self.draw_list,
1174                center.into(),
1175                radius.into(),
1176                col.into().into(),
1177                rot,
1178                num_segments,
1179            )
1180        }
1181    }
1182
1183    /// Draw a quadratic Bezier curve directly.
1184    #[doc(alias = "AddBezierQuadratic")]
1185    pub fn add_bezier_quadratic<C>(
1186        &self,
1187        p1: impl Into<sys::ImVec2>,
1188        p2: impl Into<sys::ImVec2>,
1189        p3: impl Into<sys::ImVec2>,
1190        col: C,
1191        thickness: f32,
1192        num_segments: i32,
1193    ) where
1194        C: Into<ImColor32>,
1195    {
1196        unsafe {
1197            sys::ImDrawList_AddBezierQuadratic(
1198                self.draw_list,
1199                p1.into(),
1200                p2.into(),
1201                p3.into(),
1202                col.into().into(),
1203                thickness,
1204                num_segments,
1205            )
1206        }
1207    }
1208
1209    /// Fill a concave polygon (Dear ImGui 1.92+).
1210    #[doc(alias = "AddConcavePolyFilled")]
1211    pub fn add_concave_poly_filled<C, P>(&self, points: &[P], col: C)
1212    where
1213        C: Into<ImColor32>,
1214        P: Copy + Into<sys::ImVec2>,
1215    {
1216        let mut buf: Vec<sys::ImVec2> = Vec::with_capacity(points.len());
1217        for p in points.iter().copied() {
1218            buf.push(p.into());
1219        }
1220        let count = match i32::try_from(buf.len()) {
1221            Ok(n) => n,
1222            Err(_) => return,
1223        };
1224        unsafe {
1225            sys::ImDrawList_AddConcavePolyFilled(
1226                self.draw_list,
1227                buf.as_ptr(),
1228                count,
1229                col.into().into(),
1230            )
1231        }
1232    }
1233
1234    /// Fill the current path as a concave polygon (Dear ImGui 1.92+).
1235    #[doc(alias = "PathFillConcave")]
1236    pub fn path_fill_concave(&self, color: impl Into<ImColor32>) {
1237        unsafe { sys::ImDrawList_PathFillConcave(self.draw_list, color.into().into()) }
1238    }
1239
1240    /// Insert a raw draw callback.
1241    ///
1242    /// # Safety
1243    ///
1244    /// - `callback` must be an `extern "C"` function compatible with `ImDrawCallback` and must not unwind
1245    ///   across the FFI boundary.
1246    /// - `userdata` must remain valid until the draw list is executed by the renderer.
1247    /// - If you allocate memory and store its pointer in `userdata`, you are responsible for reclaiming it
1248    ///   from within the callback or otherwise ensuring no leaks occur. Note that callbacks are only invoked
1249    ///   if the draw list is actually rendered.
1250    #[doc(alias = "AddCallback")]
1251    pub unsafe fn add_callback(
1252        &self,
1253        callback: sys::ImDrawCallback,
1254        userdata: *mut std::os::raw::c_void,
1255        userdata_size: usize,
1256    ) {
1257        unsafe { sys::ImDrawList_AddCallback(self.draw_list, callback, userdata, userdata_size) }
1258    }
1259
1260    /// Insert a new draw command (forces a new draw call boundary).
1261    #[doc(alias = "AddDrawCmd")]
1262    pub fn add_draw_cmd(&self) {
1263        unsafe { sys::ImDrawList_AddDrawCmd(self.draw_list) }
1264    }
1265
1266    /// Clone the current draw list output into an owned, independent copy.
1267    ///
1268    /// The returned draw list is heap-allocated by Dear ImGui and will be destroyed on drop.
1269    #[doc(alias = "CloneOutput")]
1270    pub fn clone_output(&self) -> OwnedDrawList {
1271        unsafe { OwnedDrawList::from_raw(sys::ImDrawList_CloneOutput(self.draw_list)) }
1272    }
1273}
1274
1275/// Represent the drawing interface within a call to `channels_split`.
1276pub struct ChannelsSplit<'ui> {
1277    draw_list: &'ui DrawListMut<'ui>,
1278    channels_count: u32,
1279}
1280
1281impl ChannelsSplit<'_> {
1282    /// Change current channel. Panics if `channel_index >= channels_count`.
1283    #[doc(alias = "ChannelsSetCurrent")]
1284    pub fn set_current(&self, channel_index: u32) {
1285        assert!(
1286            channel_index < self.channels_count,
1287            "Channel index {} out of range {}",
1288            channel_index,
1289            self.channels_count
1290        );
1291        unsafe {
1292            sys::ImDrawList_ChannelsSetCurrent(self.draw_list.draw_list, channel_index as i32)
1293        };
1294    }
1295}
1296
1297/// A safe builder for registering a Rust callback to be executed during draw.
1298#[must_use = "call .build() to register the callback"]
1299pub struct Callback<'ui, F> {
1300    draw_list: &'ui DrawListMut<'ui>,
1301    callback: F,
1302}
1303
1304impl<'ui, F: FnOnce() + 'static> Callback<'ui, F> {
1305    /// Construct a new callback builder. Typically created via `DrawListMut::add_callback_safe`.
1306    pub fn new(draw_list: &'ui DrawListMut<'_>, callback: F) -> Self {
1307        Self {
1308            draw_list,
1309            callback,
1310        }
1311    }
1312
1313    /// Register the callback with the draw list.
1314    pub fn build(self) {
1315        use std::os::raw::c_void;
1316        // Box the closure so we can pass an owning pointer to C.
1317        //
1318        // Note: Dear ImGui's `ImDrawList::AddCallback()` optionally copies `userdata` bytes into an
1319        // internal unaligned byte buffer when `userdata_size != 0`. That mode is suitable only for
1320        // plain-old-data payloads; it must not be used for Rust closures.
1321        let ptr: *mut F = Box::into_raw(Box::new(self.callback));
1322        unsafe {
1323            sys::ImDrawList_AddCallback(
1324                self.draw_list.draw_list,
1325                Some(Self::run_callback),
1326                ptr as *mut c_void,
1327                0,
1328            );
1329        }
1330    }
1331
1332    unsafe extern "C" fn run_callback(
1333        _parent_list: *const sys::ImDrawList,
1334        cmd: *const sys::ImDrawCmd,
1335    ) {
1336        if cmd.is_null() {
1337            return;
1338        }
1339        let cmd_ptr = cmd as *mut sys::ImDrawCmd;
1340        if unsafe { (*cmd_ptr).UserCallbackData.is_null() } {
1341            return;
1342        }
1343        if unsafe { (*cmd_ptr).UserCallbackDataOffset } != -1 {
1344            eprintln!("dear-imgui-rs: unexpected UserCallbackDataOffset (expected -1)");
1345            std::process::abort();
1346        }
1347        if unsafe { (*cmd_ptr).UserCallbackDataSize } != 0 {
1348            eprintln!("dear-imgui-rs: unexpected UserCallbackDataSize (expected 0)");
1349            std::process::abort();
1350        }
1351        // Compute pointer to our boxed closure (respect offset if ever used)
1352        let data_ptr = unsafe { (*cmd_ptr).UserCallbackData as *mut F };
1353        if data_ptr.is_null() {
1354            return;
1355        }
1356        // Take ownership and clear the pointer/size to avoid double-free or re-entry
1357        unsafe {
1358            (*cmd_ptr).UserCallbackData = std::ptr::null_mut();
1359            (*cmd_ptr).UserCallbackDataSize = 0;
1360            (*cmd_ptr).UserCallbackDataOffset = 0;
1361        }
1362        let cb = unsafe { Box::from_raw(data_ptr) };
1363        let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
1364            cb();
1365        }));
1366        if res.is_err() {
1367            eprintln!("dear-imgui-rs: panic in DrawList callback");
1368            std::process::abort();
1369        }
1370    }
1371}
1372
1373#[cfg(test)]
1374mod callback_tests {
1375    use super::*;
1376
1377    #[test]
1378    fn safe_draw_callback_uses_direct_user_data_pointer() {
1379        fn noop() {}
1380
1381        let shared = unsafe { sys::ImDrawListSharedData_ImDrawListSharedData() };
1382        assert!(!shared.is_null());
1383        let raw_draw_list = unsafe { sys::ImDrawList_ImDrawList(shared) };
1384        assert!(!raw_draw_list.is_null());
1385
1386        // Ensure CmdBuffer.Size > 0 (required by AddCallback).
1387        unsafe { sys::ImDrawList_AddDrawCmd(raw_draw_list) };
1388
1389        let draw_list = DrawListMut {
1390            draw_list_type: DrawListType::Window,
1391            draw_list: raw_draw_list,
1392            _phantom: PhantomData,
1393        };
1394        draw_list.add_callback_safe(noop).build();
1395
1396        let cmd_buffer = unsafe { &(*draw_list.draw_list).CmdBuffer };
1397        assert!(cmd_buffer.Size > 0);
1398        assert!(!cmd_buffer.Data.is_null());
1399
1400        let (cmd_ptr, cmd_copy) = {
1401            let cmds = unsafe {
1402                let len = usize::try_from(cmd_buffer.Size)
1403                    .expect("expected non-negative CmdBuffer.Size in test");
1404                std::slice::from_raw_parts(cmd_buffer.Data, len)
1405            };
1406            let (i, cmd) = cmds
1407                .iter()
1408                .enumerate()
1409                .find(|(_, cmd)| cmd.UserCallback.is_some() && !cmd.UserCallbackData.is_null())
1410                .expect("expected callback command to be present");
1411
1412            let cmd_ptr = unsafe { cmd_buffer.Data.add(i) as *const sys::ImDrawCmd };
1413            (cmd_ptr, *cmd)
1414        };
1415
1416        assert!(cmd_copy.UserCallback.is_some());
1417        assert_eq!(cmd_copy.UserCallbackDataOffset, -1);
1418        assert_eq!(cmd_copy.UserCallbackDataSize, 0);
1419        assert!(!cmd_copy.UserCallbackData.is_null());
1420
1421        // Run the callback once to reclaim the boxed closure and avoid leaking in the test.
1422        unsafe { cmd_copy.UserCallback.unwrap()(draw_list.draw_list as *const _, cmd_ptr) }
1423
1424        let cmd_after = unsafe { *cmd_ptr };
1425        assert!(cmd_after.UserCallbackData.is_null());
1426
1427        unsafe {
1428            sys::ImDrawList_destroy(raw_draw_list);
1429            sys::ImDrawListSharedData_destroy(shared);
1430        }
1431    }
1432}
1433
1434impl<'ui> DrawListMut<'ui> {
1435    /// Safe variant: add a Rust callback (executed when the draw list is rendered).
1436    /// Note: if the draw list is never rendered, the callback will not run and its resources won't be reclaimed.
1437    #[cfg(not(target_arch = "wasm32"))]
1438    pub fn add_callback_safe<F: FnOnce() + 'static>(&'ui self, callback: F) -> Callback<'ui, F> {
1439        Callback::new(self, callback)
1440    }
1441
1442    /// Safe variant: add a Rust callback (executed when the draw list is rendered).
1443    ///
1444    /// On wasm32 targets using the import-style Dear ImGui provider, C code cannot
1445    /// safely invoke Rust function pointers across module boundaries. For now this
1446    /// API is disabled on wasm to avoid undefined behaviour; use other mechanisms
1447    /// (e.g. higher-level rendering hooks) instead.
1448    #[cfg(target_arch = "wasm32")]
1449    pub fn add_callback_safe<F: FnOnce() + 'static>(&'ui self, _callback: F) -> Callback<'ui, F> {
1450        panic!(
1451            "DrawListMut::add_callback_safe is not supported on wasm32 targets; \
1452             C->Rust callbacks are not available in the import-style web build."
1453        );
1454    }
1455}
1456
1457impl<'ui> DrawListMut<'ui> {
1458    /// Unsafe low-level geometry API: reserve index and vertex space.
1459    ///
1460    /// # Safety
1461    /// Caller must write exactly the reserved amount using `prim_write_*` and ensure valid topology.
1462    pub unsafe fn prim_reserve(&self, idx_count: i32, vtx_count: i32) {
1463        unsafe { sys::ImDrawList_PrimReserve(self.draw_list, idx_count, vtx_count) }
1464    }
1465
1466    /// Unsafe low-level geometry API: unreserve previously reserved space.
1467    ///
1468    /// # Safety
1469    /// Must match a prior call to `prim_reserve` which hasn't been fully written.
1470    pub unsafe fn prim_unreserve(&self, idx_count: i32, vtx_count: i32) {
1471        unsafe { sys::ImDrawList_PrimUnreserve(self.draw_list, idx_count, vtx_count) }
1472    }
1473
1474    /// Unsafe low-level geometry API: append a rectangle primitive with a single color.
1475    ///
1476    /// # Safety
1477    /// Only use between `prim_reserve` and completing the reserved writes.
1478    pub unsafe fn prim_rect(
1479        &self,
1480        a: impl Into<sys::ImVec2>,
1481        b: impl Into<sys::ImVec2>,
1482        col: impl Into<ImColor32>,
1483    ) {
1484        unsafe { sys::ImDrawList_PrimRect(self.draw_list, a.into(), b.into(), col.into().into()) }
1485    }
1486
1487    /// Unsafe low-level geometry API: append a rectangle primitive with UVs and color.
1488    ///
1489    /// # Safety
1490    /// Only use between `prim_reserve` and completing the reserved writes.
1491    pub unsafe fn prim_rect_uv(
1492        &self,
1493        a: impl Into<sys::ImVec2>,
1494        b: impl Into<sys::ImVec2>,
1495        uv_a: impl Into<sys::ImVec2>,
1496        uv_b: impl Into<sys::ImVec2>,
1497        col: impl Into<ImColor32>,
1498    ) {
1499        unsafe {
1500            sys::ImDrawList_PrimRectUV(
1501                self.draw_list,
1502                a.into(),
1503                b.into(),
1504                uv_a.into(),
1505                uv_b.into(),
1506                col.into().into(),
1507            )
1508        }
1509    }
1510
1511    /// Unsafe low-level geometry API: append a quad primitive with UVs and color.
1512    ///
1513    /// # Safety
1514    /// Only use between `prim_reserve` and completing the reserved writes.
1515    pub unsafe fn prim_quad_uv(
1516        &self,
1517        a: impl Into<sys::ImVec2>,
1518        b: impl Into<sys::ImVec2>,
1519        c: impl Into<sys::ImVec2>,
1520        d: impl Into<sys::ImVec2>,
1521        uv_a: impl Into<sys::ImVec2>,
1522        uv_b: impl Into<sys::ImVec2>,
1523        uv_c: impl Into<sys::ImVec2>,
1524        uv_d: impl Into<sys::ImVec2>,
1525        col: impl Into<ImColor32>,
1526    ) {
1527        unsafe {
1528            sys::ImDrawList_PrimQuadUV(
1529                self.draw_list,
1530                a.into(),
1531                b.into(),
1532                c.into(),
1533                d.into(),
1534                uv_a.into(),
1535                uv_b.into(),
1536                uv_c.into(),
1537                uv_d.into(),
1538                col.into().into(),
1539            )
1540        }
1541    }
1542
1543    /// Unsafe low-level geometry API: write a vertex.
1544    ///
1545    /// # Safety
1546    /// Only use to fill space reserved by `prim_reserve`.
1547    pub unsafe fn prim_write_vtx(
1548        &self,
1549        pos: impl Into<sys::ImVec2>,
1550        uv: impl Into<sys::ImVec2>,
1551        col: impl Into<ImColor32>,
1552    ) {
1553        unsafe {
1554            sys::ImDrawList_PrimWriteVtx(self.draw_list, pos.into(), uv.into(), col.into().into())
1555        }
1556    }
1557
1558    /// Unsafe low-level geometry API: write an index.
1559    ///
1560    /// # Safety
1561    /// Only use to fill space reserved by `prim_reserve`.
1562    pub unsafe fn prim_write_idx(&self, idx: sys::ImDrawIdx) {
1563        unsafe { sys::ImDrawList_PrimWriteIdx(self.draw_list, idx) }
1564    }
1565
1566    /// Unsafe low-level geometry API: convenience to append one vertex (pos+uv+col).
1567    ///
1568    /// # Safety
1569    /// Only use between `prim_reserve` and completing the reserved writes.
1570    pub unsafe fn prim_vtx(
1571        &self,
1572        pos: impl Into<sys::ImVec2>,
1573        uv: impl Into<sys::ImVec2>,
1574        col: impl Into<ImColor32>,
1575    ) {
1576        unsafe { sys::ImDrawList_PrimVtx(self.draw_list, pos.into(), uv.into(), col.into().into()) }
1577    }
1578}
1579
1580/// Represents a line about to be drawn
1581#[must_use = "should call .build() to draw the object"]
1582pub struct Line<'ui> {
1583    p1: [f32; 2],
1584    p2: [f32; 2],
1585    color: ImColor32,
1586    thickness: f32,
1587    draw_list: &'ui DrawListMut<'ui>,
1588}
1589
1590impl<'ui> Line<'ui> {
1591    fn new<C>(
1592        draw_list: &'ui DrawListMut<'_>,
1593        p1: impl Into<sys::ImVec2>,
1594        p2: impl Into<sys::ImVec2>,
1595        c: C,
1596    ) -> Self
1597    where
1598        C: Into<ImColor32>,
1599    {
1600        Self {
1601            p1: {
1602                let v: sys::ImVec2 = p1.into();
1603                v.into()
1604            },
1605            p2: {
1606                let v: sys::ImVec2 = p2.into();
1607                v.into()
1608            },
1609            color: c.into(),
1610            thickness: 1.0,
1611            draw_list,
1612        }
1613    }
1614
1615    /// Set line's thickness (default to 1.0 pixel)
1616    pub fn thickness(mut self, thickness: f32) -> Self {
1617        self.thickness = thickness;
1618        self
1619    }
1620
1621    /// Draw the line on the window
1622    pub fn build(self) {
1623        unsafe {
1624            let p1 = sys::ImVec2 {
1625                x: self.p1[0],
1626                y: self.p1[1],
1627            };
1628            let p2 = sys::ImVec2 {
1629                x: self.p2[0],
1630                y: self.p2[1],
1631            };
1632            sys::ImDrawList_AddLine(
1633                self.draw_list.draw_list,
1634                p1,
1635                p2,
1636                self.color.into(),
1637                self.thickness,
1638            )
1639        }
1640    }
1641}
1642
1643/// Represents a rectangle about to be drawn
1644#[must_use = "should call .build() to draw the object"]
1645pub struct Rect<'ui> {
1646    p1: [f32; 2],
1647    p2: [f32; 2],
1648    color: ImColor32,
1649    rounding: f32,
1650    flags: DrawCornerFlags,
1651    thickness: f32,
1652    filled: bool,
1653    draw_list: &'ui DrawListMut<'ui>,
1654}
1655
1656impl<'ui> Rect<'ui> {
1657    fn new<C>(
1658        draw_list: &'ui DrawListMut<'_>,
1659        p1: impl Into<sys::ImVec2>,
1660        p2: impl Into<sys::ImVec2>,
1661        c: C,
1662    ) -> Self
1663    where
1664        C: Into<ImColor32>,
1665    {
1666        Self {
1667            p1: {
1668                let v: sys::ImVec2 = p1.into();
1669                v.into()
1670            },
1671            p2: {
1672                let v: sys::ImVec2 = p2.into();
1673                v.into()
1674            },
1675            color: c.into(),
1676            rounding: 0.0,
1677            flags: DrawCornerFlags::ALL,
1678            thickness: 1.0,
1679            filled: false,
1680            draw_list,
1681        }
1682    }
1683
1684    /// Set rectangle's corner rounding (default to 0.0 = no rounding)
1685    pub fn rounding(mut self, rounding: f32) -> Self {
1686        self.rounding = rounding;
1687        self
1688    }
1689
1690    /// Set rectangle's thickness (default to 1.0 pixel). Has no effect if filled
1691    pub fn thickness(mut self, thickness: f32) -> Self {
1692        self.thickness = thickness;
1693        self
1694    }
1695
1696    /// Draw rectangle as filled
1697    pub fn filled(mut self, filled: bool) -> Self {
1698        self.filled = filled;
1699        self
1700    }
1701
1702    /// Set rectangle's corner rounding flags
1703    pub fn flags(mut self, flags: DrawCornerFlags) -> Self {
1704        self.flags = flags;
1705        self
1706    }
1707
1708    /// Draw the rectangle on the window
1709    pub fn build(self) {
1710        let p1 = sys::ImVec2 {
1711            x: self.p1[0],
1712            y: self.p1[1],
1713        };
1714        let p2 = sys::ImVec2 {
1715            x: self.p2[0],
1716            y: self.p2[1],
1717        };
1718
1719        if self.filled {
1720            unsafe {
1721                sys::ImDrawList_AddRectFilled(
1722                    self.draw_list.draw_list,
1723                    p1,
1724                    p2,
1725                    self.color.into(),
1726                    self.rounding,
1727                    self.flags.bits() as sys::ImDrawFlags,
1728                )
1729            }
1730        } else {
1731            unsafe {
1732                sys::ImDrawList_AddRect(
1733                    self.draw_list.draw_list,
1734                    p1,
1735                    p2,
1736                    self.color.into(),
1737                    self.rounding,
1738                    self.flags.bits() as sys::ImDrawFlags,
1739                    self.thickness,
1740                )
1741            }
1742        }
1743    }
1744}
1745
1746/// Represents a circle about to be drawn
1747#[must_use = "should call .build() to draw the object"]
1748pub struct Circle<'ui> {
1749    center: [f32; 2],
1750    radius: f32,
1751    color: ImColor32,
1752    num_segments: i32,
1753    thickness: f32,
1754    filled: bool,
1755    draw_list: &'ui DrawListMut<'ui>,
1756}
1757
1758impl<'ui> Circle<'ui> {
1759    fn new<C>(
1760        draw_list: &'ui DrawListMut<'_>,
1761        center: impl Into<sys::ImVec2>,
1762        radius: f32,
1763        color: C,
1764    ) -> Self
1765    where
1766        C: Into<ImColor32>,
1767    {
1768        Self {
1769            center: {
1770                let v: sys::ImVec2 = center.into();
1771                v.into()
1772            },
1773            radius,
1774            color: color.into(),
1775            num_segments: 0, // 0 = auto
1776            thickness: 1.0,
1777            filled: false,
1778            draw_list,
1779        }
1780    }
1781
1782    /// Set circle's thickness (default to 1.0 pixel). Has no effect if filled
1783    pub fn thickness(mut self, thickness: f32) -> Self {
1784        self.thickness = thickness;
1785        self
1786    }
1787
1788    /// Draw circle as filled
1789    pub fn filled(mut self, filled: bool) -> Self {
1790        self.filled = filled;
1791        self
1792    }
1793
1794    /// Set number of segments (default to 0 = auto)
1795    pub fn num_segments(mut self, num_segments: i32) -> Self {
1796        self.num_segments = num_segments;
1797        self
1798    }
1799
1800    /// Draw the circle on the window
1801    pub fn build(self) {
1802        let center = sys::ImVec2 {
1803            x: self.center[0],
1804            y: self.center[1],
1805        };
1806
1807        if self.filled {
1808            unsafe {
1809                sys::ImDrawList_AddCircleFilled(
1810                    self.draw_list.draw_list,
1811                    center,
1812                    self.radius,
1813                    self.color.into(),
1814                    self.num_segments,
1815                )
1816            }
1817        } else {
1818            unsafe {
1819                sys::ImDrawList_AddCircle(
1820                    self.draw_list.draw_list,
1821                    center,
1822                    self.radius,
1823                    self.color.into(),
1824                    self.num_segments,
1825                    self.thickness,
1826                )
1827            }
1828        }
1829    }
1830}
1831
1832/// Represents a Bezier curve about to be drawn
1833#[must_use = "should call .build() to draw the object"]
1834pub struct BezierCurve<'ui> {
1835    pos0: [f32; 2],
1836    cp0: [f32; 2],
1837    pos1: [f32; 2],
1838    cp1: [f32; 2],
1839    color: ImColor32,
1840    thickness: f32,
1841    /// If num_segments is not set, the bezier curve is auto-tessalated.
1842    num_segments: Option<u32>,
1843    draw_list: &'ui DrawListMut<'ui>,
1844}
1845
1846impl<'ui> BezierCurve<'ui> {
1847    /// Typically constructed by [`DrawListMut::add_bezier_curve`]
1848    pub fn new<C>(
1849        draw_list: &'ui DrawListMut<'_>,
1850        pos0: impl Into<sys::ImVec2>,
1851        cp0: impl Into<sys::ImVec2>,
1852        cp1: impl Into<sys::ImVec2>,
1853        pos1: impl Into<sys::ImVec2>,
1854        c: C,
1855    ) -> Self
1856    where
1857        C: Into<ImColor32>,
1858    {
1859        Self {
1860            pos0: {
1861                let v: sys::ImVec2 = pos0.into();
1862                v.into()
1863            },
1864            cp0: {
1865                let v: sys::ImVec2 = cp0.into();
1866                v.into()
1867            },
1868            cp1: {
1869                let v: sys::ImVec2 = cp1.into();
1870                v.into()
1871            },
1872            pos1: {
1873                let v: sys::ImVec2 = pos1.into();
1874                v.into()
1875            },
1876            color: c.into(),
1877            thickness: 1.0,
1878            num_segments: None,
1879            draw_list,
1880        }
1881    }
1882
1883    /// Set curve's thickness (default to 1.0 pixel)
1884    pub fn thickness(mut self, thickness: f32) -> Self {
1885        self.thickness = thickness;
1886        self
1887    }
1888
1889    /// Set number of segments used to draw the Bezier curve. If not set, the
1890    /// bezier curve is auto-tessalated.
1891    pub fn num_segments(mut self, num_segments: u32) -> Self {
1892        self.num_segments = Some(num_segments);
1893        self
1894    }
1895
1896    /// Draw the curve on the window.
1897    pub fn build(self) {
1898        unsafe {
1899            let pos0: sys::ImVec2 = self.pos0.into();
1900            let cp0: sys::ImVec2 = self.cp0.into();
1901            let cp1: sys::ImVec2 = self.cp1.into();
1902            let pos1: sys::ImVec2 = self.pos1.into();
1903
1904            sys::ImDrawList_AddBezierCubic(
1905                self.draw_list.draw_list,
1906                pos0,
1907                cp0,
1908                cp1,
1909                pos1,
1910                self.color.into(),
1911                self.thickness,
1912                self.num_segments.unwrap_or(0) as i32,
1913            )
1914        }
1915    }
1916}
1917
1918/// Represents a poly line about to be drawn
1919#[must_use = "should call .build() to draw the object"]
1920pub struct Polyline<'ui> {
1921    points: Vec<sys::ImVec2>,
1922    thickness: f32,
1923    flags: PolylineFlags,
1924    filled: bool,
1925    color: ImColor32,
1926    draw_list: &'ui DrawListMut<'ui>,
1927}
1928
1929impl<'ui> Polyline<'ui> {
1930    fn new<C, P>(draw_list: &'ui DrawListMut<'_>, points: Vec<P>, c: C) -> Self
1931    where
1932        C: Into<ImColor32>,
1933        P: Into<sys::ImVec2>,
1934    {
1935        Self {
1936            points: points.into_iter().map(Into::into).collect(),
1937            color: c.into(),
1938            thickness: 1.0,
1939            flags: PolylineFlags::NONE,
1940            filled: false,
1941            draw_list,
1942        }
1943    }
1944
1945    /// Set line's thickness (default to 1.0 pixel). Has no effect if
1946    /// shape is filled
1947    pub fn thickness(mut self, thickness: f32) -> Self {
1948        self.thickness = thickness;
1949        self
1950    }
1951
1952    /// Set polyline flags. Has no effect if shape is filled.
1953    pub fn flags(mut self, flags: PolylineFlags) -> Self {
1954        self.flags = flags;
1955        self
1956    }
1957
1958    /// Draw the polyline as a closed shape. Has no effect if shape is filled.
1959    pub fn closed(mut self, closed: bool) -> Self {
1960        self.flags.set(PolylineFlags::CLOSED, closed);
1961        self
1962    }
1963
1964    /// Draw shape as filled convex polygon
1965    pub fn filled(mut self, filled: bool) -> Self {
1966        self.filled = filled;
1967        self
1968    }
1969
1970    /// Draw the line on the window
1971    pub fn build(self) {
1972        let count = match i32::try_from(self.points.len()) {
1973            Ok(n) => n,
1974            Err(_) => return,
1975        };
1976        if self.filled {
1977            unsafe {
1978                sys::ImDrawList_AddConvexPolyFilled(
1979                    self.draw_list.draw_list,
1980                    self.points.as_ptr(),
1981                    count,
1982                    self.color.into(),
1983                )
1984            }
1985        } else {
1986            unsafe {
1987                sys::ImDrawList_AddPolyline(
1988                    self.draw_list.draw_list,
1989                    self.points.as_ptr(),
1990                    count,
1991                    self.color.into(),
1992                    self.flags.bits() as sys::ImDrawFlags,
1993                    self.thickness,
1994                )
1995            }
1996        }
1997    }
1998}
1999
2000/// Represents a triangle about to be drawn on the window
2001#[must_use = "should call .build() to draw the object"]
2002pub struct Triangle<'ui> {
2003    p1: [f32; 2],
2004    p2: [f32; 2],
2005    p3: [f32; 2],
2006    color: ImColor32,
2007    thickness: f32,
2008    filled: bool,
2009    draw_list: &'ui DrawListMut<'ui>,
2010}
2011
2012impl<'ui> Triangle<'ui> {
2013    fn new<C>(
2014        draw_list: &'ui DrawListMut<'_>,
2015        p1: impl Into<sys::ImVec2>,
2016        p2: impl Into<sys::ImVec2>,
2017        p3: impl Into<sys::ImVec2>,
2018        c: C,
2019    ) -> Self
2020    where
2021        C: Into<ImColor32>,
2022    {
2023        Self {
2024            p1: {
2025                let v: sys::ImVec2 = p1.into();
2026                v.into()
2027            },
2028            p2: {
2029                let v: sys::ImVec2 = p2.into();
2030                v.into()
2031            },
2032            p3: {
2033                let v: sys::ImVec2 = p3.into();
2034                v.into()
2035            },
2036            color: c.into(),
2037            thickness: 1.0,
2038            filled: false,
2039            draw_list,
2040        }
2041    }
2042
2043    /// Set triangle's thickness (default to 1.0 pixel)
2044    pub fn thickness(mut self, thickness: f32) -> Self {
2045        self.thickness = thickness;
2046        self
2047    }
2048
2049    /// Set to `true` to make a filled triangle (default to `false`).
2050    pub fn filled(mut self, filled: bool) -> Self {
2051        self.filled = filled;
2052        self
2053    }
2054
2055    /// Draw the triangle on the window.
2056    pub fn build(self) {
2057        let p1 = sys::ImVec2 {
2058            x: self.p1[0],
2059            y: self.p1[1],
2060        };
2061        let p2 = sys::ImVec2 {
2062            x: self.p2[0],
2063            y: self.p2[1],
2064        };
2065        let p3 = sys::ImVec2 {
2066            x: self.p3[0],
2067            y: self.p3[1],
2068        };
2069
2070        if self.filled {
2071            unsafe {
2072                sys::ImDrawList_AddTriangleFilled(
2073                    self.draw_list.draw_list,
2074                    p1,
2075                    p2,
2076                    p3,
2077                    self.color.into(),
2078                )
2079            }
2080        } else {
2081            unsafe {
2082                sys::ImDrawList_AddTriangle(
2083                    self.draw_list.draw_list,
2084                    p1,
2085                    p2,
2086                    p3,
2087                    self.color.into(),
2088                    self.thickness,
2089                )
2090            }
2091        }
2092    }
2093}