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 bitflags::bitflags;
26use std::cell::RefCell;
27use std::marker::PhantomData;
28
29use crate::colors::Color;
30use crate::internal::len_i32;
31use crate::sys;
32
33thread_local! {
34    static BORROWED_DRAW_LISTS: RefCell<Vec<usize>> = RefCell::new(Vec::new());
35}
36
37// (MintVec2 legacy alias removed; draw APIs now accept Into<sys::ImVec2>)
38
39fn assert_finite_f32(caller: &str, name: &str, value: f32) {
40    assert!(value.is_finite(), "{caller} {name} must be finite");
41}
42
43fn assert_non_negative_f32(caller: &str, name: &str, value: f32) {
44    assert_finite_f32(caller, name, value);
45    assert!(value >= 0.0, "{caller} {name} must be non-negative");
46}
47
48fn assert_positive_f32(caller: &str, name: &str, value: f32) {
49    assert_finite_f32(caller, name, value);
50    assert!(value > 0.0, "{caller} {name} must be positive");
51}
52
53fn assert_non_negative_i32(caller: &str, name: &str, value: i32) {
54    assert!(value >= 0, "{caller} {name} must be non-negative");
55}
56
57fn assert_finite_vec2(caller: &str, name: &str, value: sys::ImVec2) {
58    assert!(
59        value.x.is_finite() && value.y.is_finite(),
60        "{caller} {name} must contain finite values"
61    );
62}
63
64fn assert_non_negative_vec2(caller: &str, name: &str, value: sys::ImVec2) {
65    assert_finite_vec2(caller, name, value);
66    assert!(
67        value.x >= 0.0 && value.y >= 0.0,
68        "{caller} {name} must contain non-negative values"
69    );
70}
71
72fn assert_finite_vec4(caller: &str, name: &str, value: sys::ImVec4) {
73    assert!(
74        value.x.is_finite() && value.y.is_finite() && value.z.is_finite() && value.w.is_finite(),
75        "{caller} {name} must contain finite values"
76    );
77}
78
79fn finite_vec2(caller: &str, name: &str, value: impl Into<sys::ImVec2>) -> sys::ImVec2 {
80    let value = value.into();
81    assert_finite_vec2(caller, name, value);
82    value
83}
84
85fn non_negative_vec2(caller: &str, name: &str, value: impl Into<sys::ImVec2>) -> sys::ImVec2 {
86    let value = value.into();
87    assert_non_negative_vec2(caller, name, value);
88    value
89}
90
91fn finite_vec4(caller: &str, name: &str, value: impl Into<sys::ImVec4>) -> sys::ImVec4 {
92    let value = value.into();
93    assert_finite_vec4(caller, name, value);
94    value
95}
96
97fn assert_path_not_empty(draw_list: *mut sys::ImDrawList, caller: &str) {
98    let path_size = unsafe { (*draw_list)._Path.Size };
99    assert!(
100        path_size > 0,
101        "{caller} requires a current path point; call path_line_to() first"
102    );
103}
104
105fn assert_arc_fast_steps(caller: &str, a_min_of_12: i32, a_max_of_12: i32) {
106    assert!(
107        (0..=12).contains(&a_min_of_12),
108        "{caller} a_min_of_12 must be in 0..=12"
109    );
110    assert!(
111        (0..=12).contains(&a_max_of_12),
112        "{caller} a_max_of_12 must be in 0..=12"
113    );
114}
115
116fn assert_polyline_flags(caller: &str, flags: PolylineFlags) {
117    assert!(
118        flags.difference(PolylineFlags::CLOSED).is_empty(),
119        "{caller} flags contain unsupported ImDrawFlags bits"
120    );
121}
122
123fn assert_corner_flags(caller: &str, flags: DrawCornerFlags) {
124    let supported = sys::ImDrawFlags_RoundCornersMask_ as u32;
125    assert!(
126        flags.bits() & !supported == 0,
127        "{caller} flags contain unsupported ImDrawFlags bits"
128    );
129}
130
131#[cfg(test)]
132fn draw_list_counts(draw_list: *mut sys::ImDrawList) -> (i32, i32) {
133    unsafe { ((*draw_list).VtxBuffer.Size, (*draw_list).IdxBuffer.Size) }
134}
135
136/// Packed RGBA color compatible with imgui-rs
137#[repr(transparent)]
138#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
139pub struct ImColor32(u32);
140
141impl ImColor32 {
142    /// Convenience constant for solid black.
143    pub const BLACK: Self = Self(0xff_00_00_00);
144    /// Convenience constant for solid white.
145    pub const WHITE: Self = Self(0xff_ff_ff_ff);
146    /// Convenience constant for full transparency.
147    pub const TRANSPARENT: Self = Self(0);
148
149    /// Construct a color from 4 single-byte `u8` channel values
150    #[inline]
151    pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
152        Self(((a as u32) << 24) | (r as u32) | ((g as u32) << 8) | ((b as u32) << 16))
153    }
154
155    /// Construct a fully opaque color from 3 single-byte `u8` channel values
156    #[inline]
157    pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
158        Self::from_rgba(r, g, b, 0xff)
159    }
160
161    /// Construct from f32 values in range 0.0..=1.0
162    pub fn from_rgba_f32s(r: f32, g: f32, b: f32, a: f32) -> Self {
163        Self::from_rgba(
164            (r.clamp(0.0, 1.0) * 255.0) as u8,
165            (g.clamp(0.0, 1.0) * 255.0) as u8,
166            (b.clamp(0.0, 1.0) * 255.0) as u8,
167            (a.clamp(0.0, 1.0) * 255.0) as u8,
168        )
169    }
170
171    /// Return the bits of the color as a u32
172    #[inline]
173    pub const fn to_bits(self) -> u32 {
174        self.0
175    }
176}
177
178impl From<Color> for ImColor32 {
179    fn from(color: Color) -> Self {
180        Self::from_rgba_f32s(color.r, color.g, color.b, color.a)
181    }
182}
183
184impl From<[f32; 4]> for ImColor32 {
185    fn from(arr: [f32; 4]) -> Self {
186        Self::from_rgba_f32s(arr[0], arr[1], arr[2], arr[3])
187    }
188}
189
190impl From<(f32, f32, f32, f32)> for ImColor32 {
191    fn from((r, g, b, a): (f32, f32, f32, f32)) -> Self {
192        Self::from_rgba_f32s(r, g, b, a)
193    }
194}
195
196impl From<[f32; 3]> for ImColor32 {
197    fn from(arr: [f32; 3]) -> Self {
198        Self::from_rgba_f32s(arr[0], arr[1], arr[2], 1.0)
199    }
200}
201
202impl From<(f32, f32, f32)> for ImColor32 {
203    fn from((r, g, b): (f32, f32, f32)) -> Self {
204        Self::from_rgba_f32s(r, g, b, 1.0)
205    }
206}
207
208impl From<ImColor32> for u32 {
209    fn from(color: ImColor32) -> Self {
210        color.0
211    }
212}
213
214impl From<u32> for ImColor32 {
215    fn from(color: u32) -> Self {
216        ImColor32(color)
217    }
218}
219
220// Removed legacy local Vec2 in favor of passing `impl Into<sys::ImVec2>` and using arrays/tuples.
221bitflags! {
222    /// Draw list flags
223    #[repr(transparent)]
224    pub struct DrawListFlags: i32 {
225        /// No flags
226        const NONE = sys::ImDrawListFlags_None as i32;
227        /// Enable anti-aliased lines/borders
228        const ANTI_ALIASED_LINES = sys::ImDrawListFlags_AntiAliasedLines as i32;
229        /// Enable anti-aliased lines/borders using textures where possible
230        const ANTI_ALIASED_LINES_USE_TEX = sys::ImDrawListFlags_AntiAliasedLinesUseTex as i32;
231        /// Enable anti-aliased edge around filled shapes
232        const ANTI_ALIASED_FILL = sys::ImDrawListFlags_AntiAliasedFill as i32;
233        /// Can emit 'VtxOffset > 0' to allow large meshes
234        const ALLOW_VTX_OFFSET = sys::ImDrawListFlags_AllowVtxOffset as i32;
235    }
236}
237
238bitflags! {
239    /// Flags accepted by `AddPolyline()` and `PathStroke()`.
240    #[repr(transparent)]
241    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
242    pub struct PolylineFlags: u32 {
243        const NONE = sys::ImDrawFlags_None as u32;
244        const CLOSED = sys::ImDrawFlags_Closed as u32;
245    }
246}
247
248impl Default for PolylineFlags {
249    fn default() -> Self {
250        Self::NONE
251    }
252}
253
254bitflags! {
255    /// Corner rounding flags accepted by rectangle and rounded-image drawing APIs.
256    ///
257    /// Dear ImGui uses zero as "default to all corners" when `rounding > 0`.
258    /// Use [`DrawCornerFlags::NO_ROUNDING`] to explicitly disable rounding.
259    #[repr(transparent)]
260    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
261    pub struct DrawCornerFlags: u32 {
262        const DEFAULT = sys::ImDrawFlags_None as u32;
263        const TOP_LEFT = sys::ImDrawFlags_RoundCornersTopLeft as u32;
264        const TOP_RIGHT = sys::ImDrawFlags_RoundCornersTopRight as u32;
265        const BOTTOM_LEFT = sys::ImDrawFlags_RoundCornersBottomLeft as u32;
266        const BOTTOM_RIGHT = sys::ImDrawFlags_RoundCornersBottomRight as u32;
267        const TOP = sys::ImDrawFlags_RoundCornersTop as u32;
268        const BOTTOM = sys::ImDrawFlags_RoundCornersBottom as u32;
269        const LEFT = sys::ImDrawFlags_RoundCornersLeft as u32;
270        const RIGHT = sys::ImDrawFlags_RoundCornersRight as u32;
271        const ALL = sys::ImDrawFlags_RoundCornersAll as u32;
272        const NO_ROUNDING = sys::ImDrawFlags_RoundCornersNone as u32;
273    }
274}
275
276impl Default for DrawCornerFlags {
277    fn default() -> Self {
278        Self::DEFAULT
279    }
280}
281
282/// Object implementing the custom draw API.
283///
284/// Called from [`Ui::get_window_draw_list`], [`Ui::get_background_draw_list`] or [`Ui::get_foreground_draw_list`].
285/// Only one mutable wrapper can exist for the same raw draw list on the same thread at a time.
286/// The program will panic when attempting to wrap the same draw list twice.
287pub struct DrawListMut<'ui> {
288    draw_list: *mut sys::ImDrawList,
289    _phantom: PhantomData<&'ui crate::Ui>,
290}
291
292struct ChannelsSplitMergeGuard<'ui> {
293    draw_list: &'ui DrawListMut<'ui>,
294}
295
296impl Drop for ChannelsSplitMergeGuard<'_> {
297    fn drop(&mut self) {
298        unsafe { sys::ImDrawList_ChannelsMerge(self.draw_list.draw_list) };
299    }
300}
301
302struct DrawListClipRectGuard<'ui> {
303    draw_list: &'ui DrawListMut<'ui>,
304}
305
306impl Drop for DrawListClipRectGuard<'_> {
307    fn drop(&mut self) {
308        unsafe { sys::ImDrawList_PopClipRect(self.draw_list.draw_list) };
309    }
310}
311
312/// Tracks a texture pushed to a draw-list texture stack.
313///
314/// The texture is popped when the token is dropped or when [`Self::pop`] is
315/// called explicitly.
316#[must_use]
317pub struct DrawListTextureToken<'draw_list> {
318    draw_list: *mut sys::ImDrawList,
319    _phantom: PhantomData<&'draw_list ()>,
320}
321
322impl<'draw_list> DrawListTextureToken<'draw_list> {
323    fn new(draw_list: *mut sys::ImDrawList) -> Self {
324        Self {
325            draw_list,
326            _phantom: PhantomData,
327        }
328    }
329
330    /// Pop the texture immediately instead of waiting for drop.
331    #[doc(alias = "PopTexture")]
332    pub fn pop(self) {}
333}
334
335impl Drop for DrawListTextureToken<'_> {
336    fn drop(&mut self) {
337        unsafe { sys::ImDrawList_PopTexture(self.draw_list) }
338    }
339}
340
341impl Drop for DrawListMut<'_> {
342    fn drop(&mut self) {
343        let ptr = self.draw_list as usize;
344        BORROWED_DRAW_LISTS.with(|borrowed| {
345            let mut borrowed = borrowed.borrow_mut();
346            if let Some(index) = borrowed.iter().position(|&value| value == ptr) {
347                borrowed.swap_remove(index);
348            }
349        });
350    }
351}
352
353impl<'ui> DrawListMut<'ui> {
354    fn borrow_draw_list(draw_list: *mut sys::ImDrawList) {
355        assert!(
356            !draw_list.is_null(),
357            "DrawListMut::borrow_draw_list() received a null draw list"
358        );
359        let ptr = draw_list as usize;
360        BORROWED_DRAW_LISTS.with(|borrowed| {
361            let mut borrowed = borrowed.borrow_mut();
362            if borrowed.contains(&ptr) {
363                panic!("A DrawListMut is already in use for this draw list");
364            }
365            borrowed.push(ptr);
366        });
367    }
368
369    fn from_raw(draw_list: *mut sys::ImDrawList) -> Self {
370        Self::borrow_draw_list(draw_list);
371        Self {
372            draw_list,
373            _phantom: PhantomData,
374        }
375    }
376
377    /// Wrap a raw ImDrawList pointer for the current Dear ImGui frame.
378    ///
379    /// # Safety
380    ///
381    /// `draw_list` must be a valid mutable draw-list pointer owned by the active
382    /// Dear ImGui frame and remain valid for `'ui`. The caller must also ensure
383    /// the pointer is not independently mutated while the returned wrapper is
384    /// alive.
385    pub unsafe fn from_raw_mut(_ui: &'ui crate::Ui, draw_list: *mut sys::ImDrawList) -> Self {
386        Self::from_raw(draw_list)
387    }
388
389    pub(crate) fn window(_ui: &'ui crate::Ui) -> Self {
390        Self::from_raw(unsafe { sys::igGetWindowDrawList() })
391    }
392
393    pub(crate) fn background(_ui: &'ui crate::Ui) -> Self {
394        let viewport = unsafe { sys::igGetMainViewport() };
395        Self::from_raw(unsafe { sys::igGetBackgroundDrawList(viewport) })
396    }
397
398    pub(crate) fn foreground(_ui: &'ui crate::Ui) -> Self {
399        let viewport = unsafe { sys::igGetMainViewport() };
400        Self::from_raw(unsafe { sys::igGetForegroundDrawList_ViewportPtr(viewport) })
401    }
402}
403
404/// Drawing functions
405impl<'ui> DrawListMut<'ui> {
406    /// Split draw into multiple channels and merge automatically at the end of the closure.
407    #[doc(alias = "ChannelsSplit")]
408    pub fn channels_split<F: FnOnce(&ChannelsSplit<'ui>)>(&'ui self, channels_count: u32, f: F) {
409        assert!(channels_count > 0, "channels_count must be greater than 0");
410        let channels_count_i32 =
411            i32::try_from(channels_count).expect("channels_count exceeded ImGui's i32 range");
412
413        unsafe { sys::ImDrawList_ChannelsSplit(self.draw_list, channels_count_i32) };
414        let _merge_guard = ChannelsSplitMergeGuard { draw_list: self };
415        f(&ChannelsSplit {
416            draw_list: self,
417            channels_count,
418        });
419    }
420    /// Returns a line from point `p1` to `p2` with color `c`.
421    pub fn add_line<C>(
422        &'ui self,
423        p1: impl Into<sys::ImVec2>,
424        p2: impl Into<sys::ImVec2>,
425        c: C,
426    ) -> Line<'ui>
427    where
428        C: Into<ImColor32>,
429    {
430        Line::new(self, p1, p2, c)
431    }
432
433    /// Draw a horizontal line from `min_x` to `max_x` at `y`.
434    #[doc(alias = "AddLineH")]
435    pub fn add_line_h<C>(&self, min_x: f32, max_x: f32, y: f32, col: C, thickness: f32)
436    where
437        C: Into<ImColor32>,
438    {
439        assert_finite_f32("DrawListMut::add_line_h()", "min_x", min_x);
440        assert_finite_f32("DrawListMut::add_line_h()", "max_x", max_x);
441        assert_finite_f32("DrawListMut::add_line_h()", "y", y);
442        assert_positive_f32("DrawListMut::add_line_h()", "thickness", thickness);
443
444        unsafe {
445            sys::ImDrawList_AddLineH(
446                self.draw_list,
447                min_x,
448                max_x,
449                y,
450                col.into().into(),
451                thickness,
452            )
453        }
454    }
455
456    /// Draw a vertical line from `min_y` to `max_y` at `x`.
457    #[doc(alias = "AddLineV")]
458    pub fn add_line_v<C>(&self, x: f32, min_y: f32, max_y: f32, col: C, thickness: f32)
459    where
460        C: Into<ImColor32>,
461    {
462        assert_finite_f32("DrawListMut::add_line_v()", "x", x);
463        assert_finite_f32("DrawListMut::add_line_v()", "min_y", min_y);
464        assert_finite_f32("DrawListMut::add_line_v()", "max_y", max_y);
465        assert_positive_f32("DrawListMut::add_line_v()", "thickness", thickness);
466
467        unsafe {
468            sys::ImDrawList_AddLineV(
469                self.draw_list,
470                x,
471                min_y,
472                max_y,
473                col.into().into(),
474                thickness,
475            )
476        }
477    }
478
479    /// Returns a rectangle whose upper-left corner is at point `p1`
480    /// and lower-right corner is at point `p2`, with color `c`.
481    pub fn add_rect<C>(
482        &'ui self,
483        p1: impl Into<sys::ImVec2>,
484        p2: impl Into<sys::ImVec2>,
485        c: C,
486    ) -> Rect<'ui>
487    where
488        C: Into<ImColor32>,
489    {
490        Rect::new(self, p1, p2, c)
491    }
492
493    /// Draw a filled rectangle with per-corner colors (counter-clockwise from upper-left).
494    #[doc(alias = "AddRectFilledMultiColor")]
495    pub fn add_rect_filled_multicolor<C1, C2, C3, C4>(
496        &self,
497        p1: impl Into<sys::ImVec2>,
498        p2: impl Into<sys::ImVec2>,
499        col_upr_left: C1,
500        col_upr_right: C2,
501        col_bot_right: C3,
502        col_bot_left: C4,
503    ) where
504        C1: Into<ImColor32>,
505        C2: Into<ImColor32>,
506        C3: Into<ImColor32>,
507        C4: Into<ImColor32>,
508    {
509        let p_min = finite_vec2("DrawListMut::add_rect_filled_multicolor()", "p1", p1);
510        let p_max = finite_vec2("DrawListMut::add_rect_filled_multicolor()", "p2", p2);
511        let c_ul: u32 = col_upr_left.into().into();
512        let c_ur: u32 = col_upr_right.into().into();
513        let c_br: u32 = col_bot_right.into().into();
514        let c_bl: u32 = col_bot_left.into().into();
515        unsafe {
516            sys::ImDrawList_AddRectFilledMultiColor(
517                self.draw_list,
518                p_min,
519                p_max,
520                c_ul,
521                c_ur,
522                c_br,
523                c_bl,
524            );
525        }
526    }
527
528    /// Returns a circle with the given `center`, `radius` and `color`.
529    pub fn add_circle<C>(
530        &'ui self,
531        center: impl Into<sys::ImVec2>,
532        radius: f32,
533        color: C,
534    ) -> Circle<'ui>
535    where
536        C: Into<ImColor32>,
537    {
538        Circle::new(self, center, radius, color)
539    }
540
541    /// Returns a Bezier curve stretching from `pos0` to `pos1`, whose
542    /// curvature is defined by `cp0` and `cp1`.
543    #[doc(alias = "AddBezier", alias = "AddBezierCubic")]
544    pub fn add_bezier_curve(
545        &'ui self,
546        pos0: impl Into<sys::ImVec2>,
547        cp0: impl Into<sys::ImVec2>,
548        cp1: impl Into<sys::ImVec2>,
549        pos1: impl Into<sys::ImVec2>,
550        color: impl Into<ImColor32>,
551    ) -> BezierCurve<'ui> {
552        BezierCurve::new(self, pos0, cp0, cp1, pos1, color)
553    }
554
555    /// Returns a triangle with the given 3 vertices `p1`, `p2` and `p3` and color `c`.
556    #[doc(alias = "AddTriangleFilled", alias = "AddTriangle")]
557    pub fn add_triangle<C>(
558        &'ui self,
559        p1: impl Into<sys::ImVec2>,
560        p2: impl Into<sys::ImVec2>,
561        p3: impl Into<sys::ImVec2>,
562        c: C,
563    ) -> Triangle<'ui>
564    where
565        C: Into<ImColor32>,
566    {
567        Triangle::new(self, p1, p2, p3, c)
568    }
569
570    /// Returns a polygonal line. If filled is rendered as a convex
571    /// polygon, if not filled is drawn as a line specified by
572    /// [`Polyline::thickness`] (default 1.0)
573    #[doc(alias = "AddPolyline", alias = "AddConvexPolyFilled")]
574    pub fn add_polyline<C, P>(&'ui self, points: Vec<P>, c: C) -> Polyline<'ui>
575    where
576        C: Into<ImColor32>,
577        P: Into<sys::ImVec2>,
578    {
579        Polyline::new(self, points, c)
580    }
581
582    // ========== Path Drawing Functions ==========
583
584    /// Clear the current path (i.e. start a new path).
585    #[doc(alias = "PathClear")]
586    pub fn path_clear(&self) {
587        unsafe {
588            // PathClear is inline: _Path.Size = 0;
589            let draw_list = self.draw_list;
590            (*draw_list)._Path.Size = 0;
591        }
592    }
593
594    /// Add a point to the current path.
595    #[doc(alias = "PathLineTo")]
596    pub fn path_line_to(&self, pos: impl Into<sys::ImVec2>) {
597        let pos = finite_vec2("DrawListMut::path_line_to()", "pos", pos);
598        unsafe { sys::ImDrawList_PathLineTo(self.draw_list, pos) }
599    }
600
601    /// Add a point to the current path, merging duplicate points.
602    #[doc(alias = "PathLineToMergeDuplicate")]
603    pub fn path_line_to_merge_duplicate(&self, pos: impl Into<sys::ImVec2>) {
604        let pos = finite_vec2("DrawListMut::path_line_to_merge_duplicate()", "pos", pos);
605        unsafe { sys::ImDrawList_PathLineToMergeDuplicate(self.draw_list, pos) }
606    }
607
608    /// Add an arc to the current path.
609    #[doc(alias = "PathArcTo")]
610    pub fn path_arc_to(
611        &self,
612        center: impl Into<sys::ImVec2>,
613        radius: f32,
614        a_min: f32,
615        a_max: f32,
616        num_segments: i32,
617    ) {
618        let center_vec = finite_vec2("DrawListMut::path_arc_to()", "center", center);
619        assert_non_negative_f32("DrawListMut::path_arc_to()", "radius", radius);
620        assert_finite_f32("DrawListMut::path_arc_to()", "a_min", a_min);
621        assert_finite_f32("DrawListMut::path_arc_to()", "a_max", a_max);
622
623        unsafe {
624            sys::ImDrawList_PathArcTo(
625                self.draw_list,
626                center_vec,
627                radius,
628                a_min,
629                a_max,
630                num_segments,
631            );
632        }
633    }
634
635    /// Add an arc to the current path using fast precomputed angles.
636    #[doc(alias = "PathArcToFast")]
637    pub fn path_arc_to_fast(
638        &self,
639        center: impl Into<sys::ImVec2>,
640        radius: f32,
641        a_min_of_12: i32,
642        a_max_of_12: i32,
643    ) {
644        let center_vec = finite_vec2("DrawListMut::path_arc_to_fast()", "center", center);
645        assert_non_negative_f32("DrawListMut::path_arc_to_fast()", "radius", radius);
646        assert_arc_fast_steps("DrawListMut::path_arc_to_fast()", a_min_of_12, a_max_of_12);
647
648        unsafe {
649            sys::ImDrawList_PathArcToFast(
650                self.draw_list,
651                center_vec,
652                radius,
653                a_min_of_12,
654                a_max_of_12,
655            );
656        }
657    }
658
659    /// Add a rectangle to the current path.
660    #[doc(alias = "PathRect")]
661    pub fn path_rect(
662        &self,
663        rect_min: impl Into<sys::ImVec2>,
664        rect_max: impl Into<sys::ImVec2>,
665        rounding: f32,
666        flags: DrawCornerFlags,
667    ) {
668        let min_vec = finite_vec2("DrawListMut::path_rect()", "rect_min", rect_min);
669        let max_vec = finite_vec2("DrawListMut::path_rect()", "rect_max", rect_max);
670        assert_non_negative_f32("DrawListMut::path_rect()", "rounding", rounding);
671        assert_corner_flags("DrawListMut::path_rect()", flags);
672
673        unsafe {
674            sys::ImDrawList_PathRect(
675                self.draw_list,
676                min_vec,
677                max_vec,
678                rounding,
679                flags.bits() as sys::ImDrawFlags,
680            );
681        }
682    }
683
684    /// Add an elliptical arc to the current path.
685    #[doc(alias = "PathEllipticalArcTo")]
686    pub fn path_elliptical_arc_to(
687        &self,
688        center: impl Into<sys::ImVec2>,
689        radius: impl Into<sys::ImVec2>,
690        rot: f32,
691        a_min: f32,
692        a_max: f32,
693        num_segments: i32,
694    ) {
695        let center = finite_vec2("DrawListMut::path_elliptical_arc_to()", "center", center);
696        let radius = non_negative_vec2("DrawListMut::path_elliptical_arc_to()", "radius", radius);
697        assert_finite_f32("DrawListMut::path_elliptical_arc_to()", "rot", rot);
698        assert_finite_f32("DrawListMut::path_elliptical_arc_to()", "a_min", a_min);
699        assert_finite_f32("DrawListMut::path_elliptical_arc_to()", "a_max", a_max);
700
701        unsafe {
702            sys::ImDrawList_PathEllipticalArcTo(
703                self.draw_list,
704                center,
705                radius,
706                rot,
707                a_min,
708                a_max,
709                num_segments,
710            )
711        }
712    }
713
714    /// Add a quadratic bezier curve to the current path.
715    #[doc(alias = "PathBezierQuadraticCurveTo")]
716    pub fn path_bezier_quadratic_curve_to(
717        &self,
718        p2: impl Into<sys::ImVec2>,
719        p3: impl Into<sys::ImVec2>,
720        num_segments: i32,
721    ) {
722        let p2 = finite_vec2("DrawListMut::path_bezier_quadratic_curve_to()", "p2", p2);
723        let p3 = finite_vec2("DrawListMut::path_bezier_quadratic_curve_to()", "p3", p3);
724        assert_non_negative_i32(
725            "DrawListMut::path_bezier_quadratic_curve_to()",
726            "num_segments",
727            num_segments,
728        );
729        assert_path_not_empty(
730            self.draw_list,
731            "DrawListMut::path_bezier_quadratic_curve_to()",
732        );
733
734        unsafe { sys::ImDrawList_PathBezierQuadraticCurveTo(self.draw_list, p2, p3, num_segments) }
735    }
736
737    /// Add a cubic bezier curve to the current path.
738    #[doc(alias = "PathBezierCubicCurveTo")]
739    pub fn path_bezier_cubic_curve_to(
740        &self,
741        p2: impl Into<sys::ImVec2>,
742        p3: impl Into<sys::ImVec2>,
743        p4: impl Into<sys::ImVec2>,
744        num_segments: i32,
745    ) {
746        let p2 = finite_vec2("DrawListMut::path_bezier_cubic_curve_to()", "p2", p2);
747        let p3 = finite_vec2("DrawListMut::path_bezier_cubic_curve_to()", "p3", p3);
748        let p4 = finite_vec2("DrawListMut::path_bezier_cubic_curve_to()", "p4", p4);
749        assert_non_negative_i32(
750            "DrawListMut::path_bezier_cubic_curve_to()",
751            "num_segments",
752            num_segments,
753        );
754        assert_path_not_empty(self.draw_list, "DrawListMut::path_bezier_cubic_curve_to()");
755
756        unsafe { sys::ImDrawList_PathBezierCubicCurveTo(self.draw_list, p2, p3, p4, num_segments) }
757    }
758
759    /// Stroke the current path with the specified color and thickness.
760    #[doc(alias = "PathStroke")]
761    pub fn path_stroke(&self, color: impl Into<ImColor32>, flags: PolylineFlags, thickness: f32) {
762        assert_polyline_flags("DrawListMut::path_stroke()", flags);
763        assert_positive_f32("DrawListMut::path_stroke()", "thickness", thickness);
764
765        unsafe {
766            // PathStroke is inline: AddPolyline(_Path.Data, _Path.Size, col, thickness, flags); _Path.Size = 0;
767            let draw_list = self.draw_list;
768            let path = &mut (*draw_list)._Path;
769
770            if path.Size > 0 {
771                sys::ImDrawList_AddPolyline(
772                    self.draw_list,
773                    path.Data,
774                    path.Size,
775                    color.into().into(),
776                    thickness,
777                    flags.bits() as sys::ImDrawFlags,
778                );
779                path.Size = 0; // Clear path after stroking
780            }
781        }
782    }
783
784    /// Fill the current path as a convex polygon.
785    #[doc(alias = "PathFillConvex")]
786    pub fn path_fill_convex(&self, color: impl Into<ImColor32>) {
787        unsafe {
788            // PathFillConvex is inline: AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0;
789            let draw_list = self.draw_list;
790            let path = &mut (*draw_list)._Path;
791
792            if path.Size > 0 {
793                sys::ImDrawList_AddConvexPolyFilled(
794                    self.draw_list,
795                    path.Data,
796                    path.Size,
797                    color.into().into(),
798                );
799                path.Size = 0; // Clear path after filling
800            }
801        }
802    }
803
804    /// Draw a text whose upper-left corner is at point `pos`.
805    pub fn add_text(
806        &self,
807        pos: impl Into<sys::ImVec2>,
808        col: impl Into<ImColor32>,
809        text: impl AsRef<str>,
810    ) {
811        use std::os::raw::c_char;
812
813        let text = text.as_ref();
814        let pos = finite_vec2("DrawListMut::add_text()", "pos", pos);
815        let col = col.into();
816
817        unsafe {
818            let start = text.as_ptr() as *const c_char;
819            let end = (start as usize + text.len()) as *const c_char;
820            sys::ImDrawList_AddText_Vec2(self.draw_list, pos, col.into(), start, end);
821        }
822    }
823
824    /// Draw text with an explicit font and optional fine CPU clip rectangle.
825    ///
826    /// This mirrors Dear ImGui's `ImDrawList::AddText(ImFont*, ...)` overload.
827    #[doc(alias = "AddText")]
828    pub fn add_text_with_font(
829        &self,
830        font: &crate::fonts::Font,
831        font_size: f32,
832        pos: impl Into<sys::ImVec2>,
833        col: impl Into<ImColor32>,
834        text: impl AsRef<str>,
835        wrap_width: f32,
836        cpu_fine_clip_rect: Option<[f32; 4]>,
837    ) {
838        use std::os::raw::c_char;
839        let text = text.as_ref();
840        let pos = finite_vec2("DrawListMut::add_text_with_font()", "pos", pos);
841        assert_non_negative_f32("DrawListMut::add_text_with_font()", "font_size", font_size);
842        assert_non_negative_f32(
843            "DrawListMut::add_text_with_font()",
844            "wrap_width",
845            wrap_width,
846        );
847        let col = col.into();
848        let font_ptr = font.raw();
849
850        let clip_vec4 = cpu_fine_clip_rect
851            .map(|r| finite_vec4("DrawListMut::add_text_with_font()", "cpu_fine_clip_rect", r));
852        let clip_ptr = match clip_vec4.as_ref() {
853            Some(v) => v as *const sys::ImVec4,
854            None => std::ptr::null(),
855        };
856
857        unsafe {
858            let start = text.as_ptr() as *const c_char;
859            let end = (start as usize + text.len()) as *const c_char;
860            sys::ImDrawList_AddText_FontPtr(
861                self.draw_list,
862                font_ptr,
863                font_size,
864                pos,
865                col.into(),
866                start,
867                end,
868                wrap_width,
869                clip_ptr,
870            );
871        }
872    }
873
874    // channels_split is provided on DrawListMut
875
876    /// Push a texture on the drawlist texture stack (ImGui 1.92+)
877    ///
878    /// While pushed, image and primitives will use this texture unless otherwise specified.
879    ///
880    /// Example:
881    /// ```no_run
882    /// # use dear_imgui_rs::*;
883    /// # fn demo(ui: &Ui) {
884    /// let dl = ui.get_window_draw_list();
885    /// let tex = texture::TextureId::new(1);
886    /// dl.push_texture(tex);
887    /// dl.add_image(tex, [10.0,10.0], [110.0,110.0], [0.0,0.0], [1.0,1.0], Color::WHITE);
888    /// dl.pop_texture();
889    /// # }
890    /// ```
891    #[doc(alias = "PushTexture")]
892    pub fn push_texture(&self, texture: impl Into<crate::texture::TextureRef>) {
893        let tex_ref = texture.into().raw();
894        unsafe { sys::ImDrawList_PushTexture(self.draw_list, tex_ref) }
895    }
896
897    /// Push a texture on the draw-list texture stack and return an RAII token.
898    ///
899    /// Prefer this or [`Self::with_texture`] for scoped usage that remains
900    /// balanced if a panic unwinds through the scope. The manual
901    /// [`Self::push_texture`] / [`Self::pop_texture`] pair is kept for
902    /// compatibility with existing push/pop-style code.
903    #[doc(alias = "PushTexture")]
904    pub fn push_texture_token(
905        &self,
906        texture: impl Into<crate::texture::TextureRef>,
907    ) -> DrawListTextureToken<'_> {
908        self.push_texture(texture);
909        DrawListTextureToken::new(self.draw_list)
910    }
911
912    /// Pop the last texture from the drawlist texture stack (ImGui 1.92+)
913    #[doc(alias = "PopTexture")]
914    pub fn pop_texture(&self) {
915        unsafe {
916            sys::ImDrawList_PopTexture(self.draw_list);
917        }
918    }
919
920    /// Push a texture, run `f`, then pop the texture.
921    ///
922    /// The texture is popped during unwinding if `f` panics.
923    #[doc(alias = "PushTexture", alias = "PopTexture")]
924    pub fn with_texture<R>(
925        &self,
926        texture: impl Into<crate::texture::TextureRef>,
927        f: impl FnOnce() -> R,
928    ) -> R {
929        let _texture = self.push_texture_token(texture);
930        f()
931    }
932
933    /// Push a clip rectangle, optionally intersecting with the current clip rect.
934    #[doc(alias = "PushClipRect")]
935    pub fn push_clip_rect(
936        &self,
937        clip_rect_min: impl Into<sys::ImVec2>,
938        clip_rect_max: impl Into<sys::ImVec2>,
939        intersect_with_current: bool,
940    ) {
941        let clip_rect_min = finite_vec2(
942            "DrawListMut::push_clip_rect()",
943            "clip_rect_min",
944            clip_rect_min,
945        );
946        let clip_rect_max = finite_vec2(
947            "DrawListMut::push_clip_rect()",
948            "clip_rect_max",
949            clip_rect_max,
950        );
951
952        unsafe {
953            sys::ImDrawList_PushClipRect(
954                self.draw_list,
955                clip_rect_min,
956                clip_rect_max,
957                intersect_with_current,
958            )
959        }
960    }
961
962    /// Push a full-screen clip rectangle.
963    #[doc(alias = "PushClipRectFullScreen")]
964    pub fn push_clip_rect_full_screen(&self) {
965        unsafe { sys::ImDrawList_PushClipRectFullScreen(self.draw_list) }
966    }
967
968    /// Pop the last clip rectangle.
969    #[doc(alias = "PopClipRect")]
970    pub fn pop_clip_rect(&self) {
971        unsafe { sys::ImDrawList_PopClipRect(self.draw_list) }
972    }
973
974    /// Get current minimum clip rectangle point.
975    pub fn clip_rect_min(&self) -> [f32; 2] {
976        let out = unsafe { sys::ImDrawList_GetClipRectMin(self.draw_list) };
977        out.into()
978    }
979
980    /// Get current maximum clip rectangle point.
981    pub fn clip_rect_max(&self) -> [f32; 2] {
982        let out = unsafe { sys::ImDrawList_GetClipRectMax(self.draw_list) };
983        out.into()
984    }
985
986    /// Convenience: push a clip rect, run f, pop.
987    pub fn with_clip_rect<F>(
988        &self,
989        clip_rect_min: impl Into<sys::ImVec2>,
990        clip_rect_max: impl Into<sys::ImVec2>,
991        f: F,
992    ) where
993        F: FnOnce(),
994    {
995        self.push_clip_rect(clip_rect_min, clip_rect_max, false);
996        let _clip_rect_guard = DrawListClipRectGuard { draw_list: self };
997        f();
998    }
999
1000    /// Add an image quad (axis-aligned). Tint via `col`.
1001    #[doc(alias = "AddImage")]
1002    pub fn add_image(
1003        &self,
1004        texture: impl Into<crate::texture::TextureRef>,
1005        p_min: impl Into<sys::ImVec2>,
1006        p_max: impl Into<sys::ImVec2>,
1007        uv_min: impl Into<sys::ImVec2>,
1008        uv_max: impl Into<sys::ImVec2>,
1009        col: impl Into<ImColor32>,
1010    ) {
1011        // Example:
1012        // let tex = texture::TextureId::new(5);
1013        // self.add_image(tex, [10.0,10.0], [110.0,110.0], [0.0,0.0], [1.0,1.0], Color::WHITE);
1014        let p_min = finite_vec2("DrawListMut::add_image()", "p_min", p_min);
1015        let p_max = finite_vec2("DrawListMut::add_image()", "p_max", p_max);
1016        let uv_min = finite_vec2("DrawListMut::add_image()", "uv_min", uv_min);
1017        let uv_max = finite_vec2("DrawListMut::add_image()", "uv_max", uv_max);
1018        let col = col.into().to_bits();
1019        let tex_ref = texture.into().raw();
1020        unsafe {
1021            sys::ImDrawList_AddImage(self.draw_list, tex_ref, p_min, p_max, uv_min, uv_max, col)
1022        }
1023    }
1024
1025    /// Add an image with 4 arbitrary corners.
1026    #[doc(alias = "AddImageQuad")]
1027    pub fn add_image_quad(
1028        &self,
1029        texture: impl Into<crate::texture::TextureRef>,
1030        p1: impl Into<sys::ImVec2>,
1031        p2: impl Into<sys::ImVec2>,
1032        p3: impl Into<sys::ImVec2>,
1033        p4: impl Into<sys::ImVec2>,
1034        uv1: impl Into<sys::ImVec2>,
1035        uv2: impl Into<sys::ImVec2>,
1036        uv3: impl Into<sys::ImVec2>,
1037        uv4: impl Into<sys::ImVec2>,
1038        col: impl Into<ImColor32>,
1039    ) {
1040        // Example:
1041        // let tex = texture::TextureId::new(5);
1042        // self.add_image_quad(
1043        //     tex,
1044        //     [10.0,10.0], [110.0,20.0], [120.0,120.0], [5.0,100.0],
1045        //     [0.0,0.0], [1.0,0.0], [1.0,1.0], [0.0,1.0],
1046        //     Color::WHITE,
1047        // );
1048        let p1 = finite_vec2("DrawListMut::add_image_quad()", "p1", p1);
1049        let p2 = finite_vec2("DrawListMut::add_image_quad()", "p2", p2);
1050        let p3 = finite_vec2("DrawListMut::add_image_quad()", "p3", p3);
1051        let p4 = finite_vec2("DrawListMut::add_image_quad()", "p4", p4);
1052        let uv1 = finite_vec2("DrawListMut::add_image_quad()", "uv1", uv1);
1053        let uv2 = finite_vec2("DrawListMut::add_image_quad()", "uv2", uv2);
1054        let uv3 = finite_vec2("DrawListMut::add_image_quad()", "uv3", uv3);
1055        let uv4 = finite_vec2("DrawListMut::add_image_quad()", "uv4", uv4);
1056        let col = col.into().to_bits();
1057        let tex_ref = texture.into().raw();
1058        unsafe {
1059            sys::ImDrawList_AddImageQuad(
1060                self.draw_list,
1061                tex_ref,
1062                p1,
1063                p2,
1064                p3,
1065                p4,
1066                uv1,
1067                uv2,
1068                uv3,
1069                uv4,
1070                col,
1071            )
1072        }
1073    }
1074
1075    /// Add an axis-aligned rounded image.
1076    #[doc(alias = "AddImageRounded")]
1077    pub fn add_image_rounded(
1078        &self,
1079        texture: impl Into<crate::texture::TextureRef>,
1080        p_min: impl Into<sys::ImVec2>,
1081        p_max: impl Into<sys::ImVec2>,
1082        uv_min: impl Into<sys::ImVec2>,
1083        uv_max: impl Into<sys::ImVec2>,
1084        col: impl Into<ImColor32>,
1085        rounding: f32,
1086        flags: DrawCornerFlags,
1087    ) {
1088        // Example:
1089        // let tex = texture::TextureId::new(5);
1090        // self.add_image_rounded(
1091        //     tex,
1092        //     [10.0,10.0], [110.0,110.0],
1093        //     [0.0,0.0], [1.0,1.0],
1094        //     Color::WHITE,
1095        //     8.0,
1096        //     DrawCornerFlags::ALL,
1097        // );
1098        let p_min = finite_vec2("DrawListMut::add_image_rounded()", "p_min", p_min);
1099        let p_max = finite_vec2("DrawListMut::add_image_rounded()", "p_max", p_max);
1100        let uv_min = finite_vec2("DrawListMut::add_image_rounded()", "uv_min", uv_min);
1101        let uv_max = finite_vec2("DrawListMut::add_image_rounded()", "uv_max", uv_max);
1102        assert_non_negative_f32("DrawListMut::add_image_rounded()", "rounding", rounding);
1103        assert_corner_flags("DrawListMut::add_image_rounded()", flags);
1104        let col = col.into().to_bits();
1105        let tex_ref = texture.into().raw();
1106        unsafe {
1107            sys::ImDrawList_AddImageRounded(
1108                self.draw_list,
1109                tex_ref,
1110                p_min,
1111                p_max,
1112                uv_min,
1113                uv_max,
1114                col,
1115                rounding,
1116                flags.bits() as sys::ImDrawFlags,
1117            )
1118        }
1119    }
1120
1121    /// Draw a quadrilateral outline given four points.
1122    #[doc(alias = "AddQuad")]
1123    pub fn add_quad<C>(
1124        &self,
1125        p1: impl Into<sys::ImVec2>,
1126        p2: impl Into<sys::ImVec2>,
1127        p3: impl Into<sys::ImVec2>,
1128        p4: impl Into<sys::ImVec2>,
1129        col: C,
1130        thickness: f32,
1131    ) where
1132        C: Into<ImColor32>,
1133    {
1134        let p1 = finite_vec2("DrawListMut::add_quad()", "p1", p1);
1135        let p2 = finite_vec2("DrawListMut::add_quad()", "p2", p2);
1136        let p3 = finite_vec2("DrawListMut::add_quad()", "p3", p3);
1137        let p4 = finite_vec2("DrawListMut::add_quad()", "p4", p4);
1138        assert_positive_f32("DrawListMut::add_quad()", "thickness", thickness);
1139
1140        unsafe {
1141            sys::ImDrawList_AddQuad(self.draw_list, p1, p2, p3, p4, col.into().into(), thickness)
1142        }
1143    }
1144
1145    /// Draw a filled quadrilateral given four points.
1146    #[doc(alias = "AddQuadFilled")]
1147    pub fn add_quad_filled<C>(
1148        &self,
1149        p1: impl Into<sys::ImVec2>,
1150        p2: impl Into<sys::ImVec2>,
1151        p3: impl Into<sys::ImVec2>,
1152        p4: impl Into<sys::ImVec2>,
1153        col: C,
1154    ) where
1155        C: Into<ImColor32>,
1156    {
1157        let p1 = finite_vec2("DrawListMut::add_quad_filled()", "p1", p1);
1158        let p2 = finite_vec2("DrawListMut::add_quad_filled()", "p2", p2);
1159        let p3 = finite_vec2("DrawListMut::add_quad_filled()", "p3", p3);
1160        let p4 = finite_vec2("DrawListMut::add_quad_filled()", "p4", p4);
1161
1162        unsafe { sys::ImDrawList_AddQuadFilled(self.draw_list, p1, p2, p3, p4, col.into().into()) }
1163    }
1164
1165    /// Draw a regular n-gon outline.
1166    #[doc(alias = "AddNgon")]
1167    pub fn add_ngon<C>(
1168        &self,
1169        center: impl Into<sys::ImVec2>,
1170        radius: f32,
1171        col: C,
1172        num_segments: i32,
1173        thickness: f32,
1174    ) where
1175        C: Into<ImColor32>,
1176    {
1177        let center = finite_vec2("DrawListMut::add_ngon()", "center", center);
1178        assert_non_negative_f32("DrawListMut::add_ngon()", "radius", radius);
1179        assert_positive_f32("DrawListMut::add_ngon()", "thickness", thickness);
1180
1181        unsafe {
1182            sys::ImDrawList_AddNgon(
1183                self.draw_list,
1184                center,
1185                radius,
1186                col.into().into(),
1187                num_segments,
1188                thickness,
1189            )
1190        }
1191    }
1192
1193    /// Draw a filled regular n-gon.
1194    #[doc(alias = "AddNgonFilled")]
1195    pub fn add_ngon_filled<C>(
1196        &self,
1197        center: impl Into<sys::ImVec2>,
1198        radius: f32,
1199        col: C,
1200        num_segments: i32,
1201    ) where
1202        C: Into<ImColor32>,
1203    {
1204        let center = finite_vec2("DrawListMut::add_ngon_filled()", "center", center);
1205        assert_non_negative_f32("DrawListMut::add_ngon_filled()", "radius", radius);
1206
1207        unsafe {
1208            sys::ImDrawList_AddNgonFilled(
1209                self.draw_list,
1210                center,
1211                radius,
1212                col.into().into(),
1213                num_segments,
1214            )
1215        }
1216    }
1217
1218    /// Draw an ellipse outline.
1219    #[doc(alias = "AddEllipse")]
1220    pub fn add_ellipse<C>(
1221        &self,
1222        center: impl Into<sys::ImVec2>,
1223        radius: impl Into<sys::ImVec2>,
1224        col: C,
1225        rot: f32,
1226        num_segments: i32,
1227        thickness: f32,
1228    ) where
1229        C: Into<ImColor32>,
1230    {
1231        let center = finite_vec2("DrawListMut::add_ellipse()", "center", center);
1232        let radius = non_negative_vec2("DrawListMut::add_ellipse()", "radius", radius);
1233        assert_finite_f32("DrawListMut::add_ellipse()", "rot", rot);
1234        assert_positive_f32("DrawListMut::add_ellipse()", "thickness", thickness);
1235
1236        unsafe {
1237            sys::ImDrawList_AddEllipse(
1238                self.draw_list,
1239                center,
1240                radius,
1241                col.into().into(),
1242                rot,
1243                num_segments,
1244                thickness,
1245            )
1246        }
1247    }
1248
1249    /// Draw a filled ellipse.
1250    #[doc(alias = "AddEllipseFilled")]
1251    pub fn add_ellipse_filled<C>(
1252        &self,
1253        center: impl Into<sys::ImVec2>,
1254        radius: impl Into<sys::ImVec2>,
1255        col: C,
1256        rot: f32,
1257        num_segments: i32,
1258    ) where
1259        C: Into<ImColor32>,
1260    {
1261        let center = finite_vec2("DrawListMut::add_ellipse_filled()", "center", center);
1262        let radius = non_negative_vec2("DrawListMut::add_ellipse_filled()", "radius", radius);
1263        assert_finite_f32("DrawListMut::add_ellipse_filled()", "rot", rot);
1264
1265        unsafe {
1266            sys::ImDrawList_AddEllipseFilled(
1267                self.draw_list,
1268                center,
1269                radius,
1270                col.into().into(),
1271                rot,
1272                num_segments,
1273            )
1274        }
1275    }
1276
1277    /// Draw a quadratic Bezier curve directly.
1278    #[doc(alias = "AddBezierQuadratic")]
1279    pub fn add_bezier_quadratic<C>(
1280        &self,
1281        p1: impl Into<sys::ImVec2>,
1282        p2: impl Into<sys::ImVec2>,
1283        p3: impl Into<sys::ImVec2>,
1284        col: C,
1285        thickness: f32,
1286        num_segments: i32,
1287    ) where
1288        C: Into<ImColor32>,
1289    {
1290        let p1 = finite_vec2("DrawListMut::add_bezier_quadratic()", "p1", p1);
1291        let p2 = finite_vec2("DrawListMut::add_bezier_quadratic()", "p2", p2);
1292        let p3 = finite_vec2("DrawListMut::add_bezier_quadratic()", "p3", p3);
1293        assert_positive_f32(
1294            "DrawListMut::add_bezier_quadratic()",
1295            "thickness",
1296            thickness,
1297        );
1298        assert_non_negative_i32(
1299            "DrawListMut::add_bezier_quadratic()",
1300            "num_segments",
1301            num_segments,
1302        );
1303
1304        unsafe {
1305            sys::ImDrawList_AddBezierQuadratic(
1306                self.draw_list,
1307                p1,
1308                p2,
1309                p3,
1310                col.into().into(),
1311                thickness,
1312                num_segments,
1313            )
1314        }
1315    }
1316
1317    /// Fill a concave polygon (Dear ImGui 1.92+).
1318    #[doc(alias = "AddConcavePolyFilled")]
1319    pub fn add_concave_poly_filled<C, P>(&self, points: &[P], col: C)
1320    where
1321        C: Into<ImColor32>,
1322        P: Copy + Into<sys::ImVec2>,
1323    {
1324        let count = len_i32(
1325            "DrawListMut::add_concave_poly_filled()",
1326            "points",
1327            points.len(),
1328        );
1329        let mut buf: Vec<sys::ImVec2> = Vec::with_capacity(points.len());
1330        for (i, p) in points.iter().copied().enumerate() {
1331            let name = format!("points[{i}]");
1332            buf.push(finite_vec2(
1333                "DrawListMut::add_concave_poly_filled()",
1334                &name,
1335                p,
1336            ));
1337        }
1338        unsafe {
1339            sys::ImDrawList_AddConcavePolyFilled(
1340                self.draw_list,
1341                buf.as_ptr(),
1342                count,
1343                col.into().into(),
1344            )
1345        }
1346    }
1347
1348    /// Fill the current path as a concave polygon (Dear ImGui 1.92+).
1349    #[doc(alias = "PathFillConcave")]
1350    pub fn path_fill_concave(&self, color: impl Into<ImColor32>) {
1351        unsafe { sys::ImDrawList_PathFillConcave(self.draw_list, color.into().into()) }
1352    }
1353
1354    /// Insert a raw draw callback.
1355    ///
1356    /// # Safety
1357    ///
1358    /// - `callback` must be an `extern "C"` function compatible with `ImDrawCallback` and must not unwind
1359    ///   across the FFI boundary.
1360    /// - `userdata` must remain valid until the draw list is executed by the renderer.
1361    /// - If you allocate memory and store its pointer in `userdata`, you are responsible for reclaiming it
1362    ///   from within the callback or otherwise ensuring no leaks occur. Note that callbacks are only invoked
1363    ///   if the draw list is actually rendered.
1364    #[doc(alias = "AddCallback")]
1365    pub unsafe fn add_callback(
1366        &self,
1367        callback: sys::ImDrawCallback,
1368        userdata: *mut std::os::raw::c_void,
1369        userdata_size: usize,
1370    ) {
1371        unsafe { sys::ImDrawList_AddCallback(self.draw_list, callback, userdata, userdata_size) }
1372    }
1373
1374    /// Insert a new draw command (forces a new draw call boundary).
1375    #[doc(alias = "AddDrawCmd")]
1376    pub fn add_draw_cmd(&self) {
1377        unsafe { sys::ImDrawList_AddDrawCmd(self.draw_list) }
1378    }
1379
1380    /// Clone the current draw list output into an owned, independent copy.
1381    ///
1382    /// The returned draw list is heap-allocated by Dear ImGui and will be destroyed on drop.
1383    ///
1384    /// # Panics
1385    ///
1386    /// Panics if the draw list contains user callbacks. Dear ImGui's clone operation copies
1387    /// callback userdata as an opaque pointer, which cannot be duplicated safely by this safe API.
1388    #[doc(alias = "CloneOutput")]
1389    pub fn clone_output(&self) -> crate::render::OwnedDrawList {
1390        unsafe {
1391            crate::render::draw_data::assert_draw_list_cloneable(
1392                self.draw_list.cast_const(),
1393                "DrawListMut::clone_output",
1394            );
1395            crate::render::OwnedDrawList::from_raw(sys::ImDrawList_CloneOutput(self.draw_list))
1396        }
1397    }
1398}
1399
1400/// Represent the drawing interface within a call to `channels_split`.
1401pub struct ChannelsSplit<'ui> {
1402    draw_list: &'ui DrawListMut<'ui>,
1403    channels_count: u32,
1404}
1405
1406impl ChannelsSplit<'_> {
1407    /// Change current channel. Panics if `channel_index >= channels_count`.
1408    #[doc(alias = "ChannelsSetCurrent")]
1409    pub fn set_current(&self, channel_index: u32) {
1410        assert!(
1411            channel_index < self.channels_count,
1412            "Channel index {} out of range {}",
1413            channel_index,
1414            self.channels_count
1415        );
1416        unsafe {
1417            sys::ImDrawList_ChannelsSetCurrent(self.draw_list.draw_list, channel_index as i32)
1418        };
1419    }
1420}
1421
1422/// A safe builder for registering a Rust callback to be executed during draw.
1423#[must_use = "call .build() to register the callback"]
1424pub struct Callback<'ui, F> {
1425    draw_list: &'ui DrawListMut<'ui>,
1426    callback: F,
1427}
1428
1429impl<'ui, F: FnOnce() + 'static> Callback<'ui, F> {
1430    /// Construct a new callback builder. Typically created via `DrawListMut::add_callback_safe`.
1431    pub fn new(draw_list: &'ui DrawListMut<'_>, callback: F) -> Self {
1432        Self {
1433            draw_list,
1434            callback,
1435        }
1436    }
1437
1438    /// Register the callback with the draw list.
1439    pub fn build(self) {
1440        use std::os::raw::c_void;
1441        // Box the closure so we can pass an owning pointer to C.
1442        //
1443        // Note: Dear ImGui's `ImDrawList::AddCallback()` optionally copies `userdata` bytes into an
1444        // internal unaligned byte buffer when `userdata_size != 0`. That mode is suitable only for
1445        // plain-old-data payloads; it must not be used for Rust closures.
1446        let ptr: *mut F = Box::into_raw(Box::new(self.callback));
1447        unsafe {
1448            sys::ImDrawList_AddCallback(
1449                self.draw_list.draw_list,
1450                Some(Self::run_callback),
1451                ptr as *mut c_void,
1452                0,
1453            );
1454        }
1455    }
1456
1457    unsafe extern "C" fn run_callback(
1458        _parent_list: *const sys::ImDrawList,
1459        cmd: *const sys::ImDrawCmd,
1460    ) {
1461        if cmd.is_null() {
1462            return;
1463        }
1464        let cmd_ptr = cmd as *mut sys::ImDrawCmd;
1465        if unsafe { (*cmd_ptr).UserCallbackData.is_null() } {
1466            return;
1467        }
1468        if unsafe { (*cmd_ptr).UserCallbackDataOffset } != -1 {
1469            eprintln!("dear-imgui-rs: unexpected UserCallbackDataOffset (expected -1)");
1470            std::process::abort();
1471        }
1472        if unsafe { (*cmd_ptr).UserCallbackDataSize } != 0 {
1473            eprintln!("dear-imgui-rs: unexpected UserCallbackDataSize (expected 0)");
1474            std::process::abort();
1475        }
1476        // Compute pointer to our boxed closure (respect offset if ever used)
1477        let data_ptr = unsafe { (*cmd_ptr).UserCallbackData as *mut F };
1478        if data_ptr.is_null() {
1479            return;
1480        }
1481        // Take ownership and clear the pointer/size to avoid double-free or re-entry
1482        unsafe {
1483            (*cmd_ptr).UserCallbackData = std::ptr::null_mut();
1484            (*cmd_ptr).UserCallbackDataSize = 0;
1485            (*cmd_ptr).UserCallbackDataOffset = 0;
1486        }
1487        let cb = unsafe { Box::from_raw(data_ptr) };
1488        let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
1489            cb();
1490        }));
1491        if res.is_err() {
1492            eprintln!("dear-imgui-rs: panic in DrawList callback");
1493            std::process::abort();
1494        }
1495    }
1496}
1497
1498#[cfg(test)]
1499mod callback_tests {
1500    use super::*;
1501
1502    #[test]
1503    fn safe_draw_callback_uses_direct_user_data_pointer() {
1504        fn noop() {}
1505
1506        let shared = unsafe { sys::ImDrawListSharedData_ImDrawListSharedData() };
1507        assert!(!shared.is_null());
1508        let raw_draw_list = unsafe { sys::ImDrawList_ImDrawList(shared) };
1509        assert!(!raw_draw_list.is_null());
1510
1511        // Ensure CmdBuffer.Size > 0 (required by AddCallback).
1512        unsafe { sys::ImDrawList_AddDrawCmd(raw_draw_list) };
1513
1514        let draw_list = DrawListMut {
1515            draw_list: raw_draw_list,
1516            _phantom: PhantomData,
1517        };
1518        draw_list.add_callback_safe(noop).build();
1519
1520        let cmd_buffer = unsafe { &(*draw_list.draw_list).CmdBuffer };
1521        assert!(cmd_buffer.Size > 0);
1522        assert!(!cmd_buffer.Data.is_null());
1523
1524        let (cmd_ptr, cmd_copy) = {
1525            let cmds = unsafe {
1526                let len = usize::try_from(cmd_buffer.Size)
1527                    .expect("expected non-negative CmdBuffer.Size in test");
1528                std::slice::from_raw_parts(cmd_buffer.Data, len)
1529            };
1530            let (i, cmd) = cmds
1531                .iter()
1532                .enumerate()
1533                .find(|(_, cmd)| cmd.UserCallback.is_some() && !cmd.UserCallbackData.is_null())
1534                .expect("expected callback command to be present");
1535
1536            let cmd_ptr = unsafe { cmd_buffer.Data.add(i) as *const sys::ImDrawCmd };
1537            (cmd_ptr, *cmd)
1538        };
1539
1540        assert!(cmd_copy.UserCallback.is_some());
1541        assert_eq!(cmd_copy.UserCallbackDataOffset, -1);
1542        assert_eq!(cmd_copy.UserCallbackDataSize, 0);
1543        assert!(!cmd_copy.UserCallbackData.is_null());
1544
1545        // Run the callback once to reclaim the boxed closure and avoid leaking in the test.
1546        unsafe { cmd_copy.UserCallback.unwrap()(draw_list.draw_list as *const _, cmd_ptr) }
1547
1548        let cmd_after = unsafe { *cmd_ptr };
1549        assert!(cmd_after.UserCallbackData.is_null());
1550
1551        unsafe {
1552            sys::ImDrawList_destroy(raw_draw_list);
1553            sys::ImDrawListSharedData_destroy(shared);
1554        }
1555    }
1556
1557    #[test]
1558    fn clone_output_rejects_user_callbacks() {
1559        fn noop() {}
1560
1561        let shared = unsafe { sys::ImDrawListSharedData_ImDrawListSharedData() };
1562        assert!(!shared.is_null());
1563        let raw_draw_list = unsafe { sys::ImDrawList_ImDrawList(shared) };
1564        assert!(!raw_draw_list.is_null());
1565
1566        unsafe { sys::ImDrawList_AddDrawCmd(raw_draw_list) };
1567
1568        let draw_list = DrawListMut {
1569            draw_list: raw_draw_list,
1570            _phantom: PhantomData,
1571        };
1572        draw_list.add_callback_safe(noop).build();
1573
1574        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1575            let _ = draw_list.clone_output();
1576        }));
1577        assert!(result.is_err());
1578
1579        let cmd_buffer = unsafe { &(*draw_list.draw_list).CmdBuffer };
1580        let cmd_ptr = {
1581            let cmds = unsafe {
1582                let len = usize::try_from(cmd_buffer.Size)
1583                    .expect("expected non-negative CmdBuffer.Size in test");
1584                std::slice::from_raw_parts(cmd_buffer.Data, len)
1585            };
1586            let (i, _) = cmds
1587                .iter()
1588                .enumerate()
1589                .find(|(_, cmd)| cmd.UserCallback.is_some() && !cmd.UserCallbackData.is_null())
1590                .expect("expected callback command to be present");
1591
1592            unsafe { cmd_buffer.Data.add(i) as *const sys::ImDrawCmd }
1593        };
1594        let cmd_copy = unsafe { *cmd_ptr };
1595        unsafe { cmd_copy.UserCallback.unwrap()(draw_list.draw_list as *const _, cmd_ptr) }
1596
1597        unsafe {
1598            sys::ImDrawList_destroy(raw_draw_list);
1599            sys::ImDrawListSharedData_destroy(shared);
1600        }
1601    }
1602}
1603
1604#[cfg(test)]
1605mod channels_tests {
1606    use super::*;
1607
1608    #[test]
1609    fn with_clip_rect_pops_after_panic() {
1610        let mut ctx = crate::Context::create();
1611        {
1612            let io = ctx.io_mut();
1613            io.set_display_size([128.0, 128.0]);
1614            io.set_delta_time(1.0 / 60.0);
1615        }
1616        let _ = ctx.font_atlas_mut().build();
1617        let _ = ctx.set_ini_filename::<std::path::PathBuf>(None);
1618
1619        let ui = ctx.frame();
1620        let draw_list = ui.get_window_draw_list();
1621        let raw_draw_list = draw_list.draw_list;
1622        let initial_stack_size = unsafe { (*raw_draw_list)._ClipRectStack.Size };
1623
1624        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1625            draw_list.with_clip_rect([0.0, 0.0], [8.0, 8.0], || {
1626                assert_eq!(
1627                    unsafe { (*raw_draw_list)._ClipRectStack.Size },
1628                    initial_stack_size + 1
1629                );
1630                panic!("forced panic while draw-list clip rect is pushed");
1631            });
1632        }));
1633
1634        assert!(result.is_err());
1635        assert_eq!(
1636            unsafe { (*raw_draw_list)._ClipRectStack.Size },
1637            initial_stack_size
1638        );
1639    }
1640
1641    #[test]
1642    fn with_texture_pops_after_panic() {
1643        let mut ctx = crate::Context::create();
1644        {
1645            let io = ctx.io_mut();
1646            io.set_display_size([128.0, 128.0]);
1647            io.set_delta_time(1.0 / 60.0);
1648        }
1649        let _ = ctx.font_atlas_mut().build();
1650        let _ = ctx.set_ini_filename::<std::path::PathBuf>(None);
1651
1652        let ui = ctx.frame();
1653        let draw_list = ui.get_window_draw_list();
1654        let raw_draw_list = draw_list.draw_list;
1655        let initial_stack_size = unsafe { (*raw_draw_list)._TextureStack.Size };
1656
1657        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1658            draw_list.with_texture(crate::texture::TextureId::new(1), || {
1659                assert_eq!(
1660                    unsafe { (*raw_draw_list)._TextureStack.Size },
1661                    initial_stack_size + 1
1662                );
1663                panic!("forced panic while draw-list texture is pushed");
1664            });
1665        }));
1666
1667        assert!(result.is_err());
1668        assert_eq!(
1669            unsafe { (*raw_draw_list)._TextureStack.Size },
1670            initial_stack_size
1671        );
1672    }
1673
1674    #[test]
1675    fn channels_split_merges_after_panic() {
1676        let shared = unsafe { sys::ImDrawListSharedData_ImDrawListSharedData() };
1677        assert!(!shared.is_null());
1678        let raw_draw_list = unsafe { sys::ImDrawList_ImDrawList(shared) };
1679        assert!(!raw_draw_list.is_null());
1680
1681        unsafe { sys::ImDrawList_AddDrawCmd(raw_draw_list) };
1682
1683        let draw_list = DrawListMut {
1684            draw_list: raw_draw_list,
1685            _phantom: PhantomData,
1686        };
1687
1688        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1689            draw_list.channels_split(2, |channels| {
1690                channels.set_current(1);
1691                panic!("forced panic while channels are split");
1692            });
1693        }));
1694
1695        assert!(result.is_err());
1696        unsafe {
1697            assert_eq!((*raw_draw_list)._Splitter._Count, 1);
1698            assert_eq!((*raw_draw_list)._Splitter._Current, 0);
1699        }
1700
1701        unsafe {
1702            sys::ImDrawList_destroy(raw_draw_list);
1703            sys::ImDrawListSharedData_destroy(shared);
1704        }
1705    }
1706
1707    #[test]
1708    fn channels_split_rejects_zero_channels() {
1709        let shared = unsafe { sys::ImDrawListSharedData_ImDrawListSharedData() };
1710        assert!(!shared.is_null());
1711        let raw_draw_list = unsafe { sys::ImDrawList_ImDrawList(shared) };
1712        assert!(!raw_draw_list.is_null());
1713
1714        let draw_list = DrawListMut {
1715            draw_list: raw_draw_list,
1716            _phantom: PhantomData,
1717        };
1718        let initial_count = unsafe { (*raw_draw_list)._Splitter._Count };
1719        let initial_current = unsafe { (*raw_draw_list)._Splitter._Current };
1720
1721        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1722            draw_list.channels_split(0, |_| {});
1723        }));
1724
1725        assert!(result.is_err());
1726        unsafe {
1727            assert_eq!((*raw_draw_list)._Splitter._Count, initial_count);
1728            assert_eq!((*raw_draw_list)._Splitter._Current, initial_current);
1729        }
1730
1731        unsafe {
1732            sys::ImDrawList_destroy(raw_draw_list);
1733            sys::ImDrawListSharedData_destroy(shared);
1734        }
1735    }
1736
1737    #[test]
1738    fn draw_list_point_count_helpers_reject_overflow() {
1739        assert!(
1740            std::panic::catch_unwind(|| {
1741                let _ = len_i32("Polyline::build()", "points", (i32::MAX as usize) + 1);
1742            })
1743            .is_err()
1744        );
1745        assert!(
1746            std::panic::catch_unwind(|| {
1747                let _ = len_i32(
1748                    "DrawListMut::add_concave_poly_filled()",
1749                    "points",
1750                    (i32::MAX as usize) + 1,
1751                );
1752            })
1753            .is_err()
1754        );
1755    }
1756}
1757
1758#[cfg(test)]
1759mod draw_numeric_tests {
1760    use super::*;
1761
1762    struct TestDrawList {
1763        shared: *mut sys::ImDrawListSharedData,
1764        raw: *mut sys::ImDrawList,
1765    }
1766
1767    impl TestDrawList {
1768        fn new() -> Self {
1769            let shared = unsafe { sys::ImDrawListSharedData_ImDrawListSharedData() };
1770            assert!(!shared.is_null());
1771            let raw = unsafe { sys::ImDrawList_ImDrawList(shared) };
1772            assert!(!raw.is_null());
1773            Self { shared, raw }
1774        }
1775
1776        fn draw_list(&self) -> DrawListMut<'static> {
1777            DrawListMut {
1778                draw_list: self.raw,
1779                _phantom: PhantomData,
1780            }
1781        }
1782
1783        fn path_size(&self) -> i32 {
1784            unsafe { (*self.raw)._Path.Size }
1785        }
1786
1787        fn clip_stack_size(&self) -> i32 {
1788            unsafe { (*self.raw)._ClipRectStack.Size }
1789        }
1790    }
1791
1792    impl Drop for TestDrawList {
1793        fn drop(&mut self) {
1794            unsafe {
1795                sys::ImDrawList_destroy(self.raw);
1796                sys::ImDrawListSharedData_destroy(self.shared);
1797            }
1798        }
1799    }
1800
1801    fn assert_panics_without_buffer_change(
1802        fixture: &TestDrawList,
1803        f: impl FnOnce(&DrawListMut<'static>),
1804    ) {
1805        let draw_list = fixture.draw_list();
1806        let before = draw_list_counts(fixture.raw);
1807        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&draw_list)));
1808        assert!(result.is_err());
1809        assert_eq!(draw_list_counts(fixture.raw), before);
1810    }
1811
1812    #[test]
1813    fn direct_draw_inputs_validate_before_ffi() {
1814        let fixture = TestDrawList::new();
1815
1816        assert_panics_without_buffer_change(&fixture, |draw_list| {
1817            draw_list.add_line_h(f32::NAN, 1.0, 0.0, ImColor32::WHITE, 1.0);
1818        });
1819        assert_panics_without_buffer_change(&fixture, |draw_list| {
1820            draw_list.add_line_v(0.0, 0.0, 1.0, ImColor32::WHITE, 0.0);
1821        });
1822        assert_panics_without_buffer_change(&fixture, |draw_list| {
1823            draw_list.add_quad(
1824                [f32::NAN, 0.0],
1825                [1.0, 0.0],
1826                [1.0, 1.0],
1827                [0.0, 1.0],
1828                ImColor32::WHITE,
1829                1.0,
1830            );
1831        });
1832        assert_panics_without_buffer_change(&fixture, |draw_list| {
1833            draw_list.add_ellipse([0.0, 0.0], [-1.0, 4.0], ImColor32::WHITE, 0.0, 0, 1.0);
1834        });
1835        assert_panics_without_buffer_change(&fixture, |draw_list| {
1836            draw_list.add_bezier_quadratic(
1837                [0.0, 0.0],
1838                [1.0, 1.0],
1839                [2.0, 0.0],
1840                ImColor32::WHITE,
1841                1.0,
1842                -1,
1843            );
1844        });
1845        assert_panics_without_buffer_change(&fixture, |draw_list| {
1846            draw_list.add_image_rounded(
1847                crate::texture::TextureId::new(1),
1848                [0.0, 0.0],
1849                [16.0, 16.0],
1850                [0.0, 0.0],
1851                [1.0, 1.0],
1852                ImColor32::WHITE,
1853                f32::INFINITY,
1854                DrawCornerFlags::ALL,
1855            );
1856        });
1857        assert_panics_without_buffer_change(&fixture, |draw_list| {
1858            draw_list.path_rect(
1859                [0.0, 0.0],
1860                [1.0, 1.0],
1861                1.0,
1862                DrawCornerFlags::from_bits_retain(sys::ImDrawFlags_Closed as u32),
1863            );
1864        });
1865    }
1866
1867    #[test]
1868    fn path_inputs_validate_before_path_mutation() {
1869        let fixture = TestDrawList::new();
1870        let draw_list = fixture.draw_list();
1871
1872        draw_list.path_line_to([1.0, 1.0]);
1873        let path_size = fixture.path_size();
1874
1875        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1876            draw_list.path_line_to([f32::NAN, 2.0]);
1877        }));
1878        assert!(result.is_err());
1879        assert_eq!(fixture.path_size(), path_size);
1880
1881        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1882            draw_list.path_arc_to([0.0, 0.0], -1.0, 0.0, 1.0, 0);
1883        }));
1884        assert!(result.is_err());
1885        assert_eq!(fixture.path_size(), path_size);
1886
1887        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1888            draw_list.path_arc_to_fast([0.0, 0.0], 1.0, 0, 13);
1889        }));
1890        assert!(result.is_err());
1891        assert_eq!(fixture.path_size(), path_size);
1892
1893        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1894            draw_list.path_bezier_quadratic_curve_to([2.0, 2.0], [3.0, 3.0], -1);
1895        }));
1896        assert!(result.is_err());
1897        assert_eq!(fixture.path_size(), path_size);
1898
1899        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1900            draw_list.path_stroke(
1901                ImColor32::WHITE,
1902                PolylineFlags::from_bits_retain(sys::ImDrawFlags_RoundCornersTopLeft as u32),
1903                1.0,
1904            );
1905        }));
1906        assert!(result.is_err());
1907        assert_eq!(fixture.path_size(), path_size);
1908
1909        draw_list.path_clear();
1910        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1911            draw_list.path_bezier_cubic_curve_to([2.0, 2.0], [3.0, 3.0], [4.0, 4.0], 0);
1912        }));
1913        assert!(result.is_err());
1914        assert_eq!(fixture.path_size(), 0);
1915    }
1916
1917    #[test]
1918    fn builder_inputs_validate_before_ffi() {
1919        let fixture = TestDrawList::new();
1920
1921        assert_panics_without_buffer_change(&fixture, |draw_list| {
1922            let _ = draw_list
1923                .add_line([0.0, 0.0], [1.0, 1.0], ImColor32::WHITE)
1924                .thickness(0.0);
1925        });
1926        assert_panics_without_buffer_change(&fixture, |draw_list| {
1927            let _ = draw_list.add_circle([0.0, 0.0], -1.0, ImColor32::WHITE);
1928        });
1929        assert_panics_without_buffer_change(&fixture, |draw_list| {
1930            let _ = draw_list
1931                .add_rect([0.0, 0.0], [1.0, 1.0], ImColor32::WHITE)
1932                .rounding(f32::NAN);
1933        });
1934        assert_panics_without_buffer_change(&fixture, |draw_list| {
1935            let _ =
1936                draw_list.add_polyline(vec![[0.0, 0.0], [f32::INFINITY, 1.0]], ImColor32::WHITE);
1937        });
1938        assert_panics_without_buffer_change(&fixture, |draw_list| {
1939            let _ = draw_list
1940                .add_bezier_curve(
1941                    [0.0, 0.0],
1942                    [1.0, 1.0],
1943                    [2.0, 1.0],
1944                    [3.0, 0.0],
1945                    ImColor32::WHITE,
1946                )
1947                .num_segments((i32::MAX as u32) + 1);
1948        });
1949    }
1950
1951    #[test]
1952    fn text_and_clip_inputs_validate_before_ffi() {
1953        let mut ctx = crate::Context::create();
1954        {
1955            let io = ctx.io_mut();
1956            io.set_display_size([128.0, 128.0]);
1957            io.set_delta_time(1.0 / 60.0);
1958        }
1959        let _ = ctx.font_atlas_mut().build();
1960        let _ = ctx.set_ini_filename::<std::path::PathBuf>(None);
1961
1962        let ui = ctx.frame();
1963        let draw_list = ui.get_window_draw_list();
1964        let raw_draw_list = draw_list.draw_list;
1965        let font = ui.current_font();
1966        let before = draw_list_counts(raw_draw_list);
1967        let clip_stack_size = unsafe { (*raw_draw_list)._ClipRectStack.Size };
1968
1969        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1970            draw_list.add_text([f32::NAN, 0.0], ImColor32::WHITE, "hello");
1971        }));
1972        assert!(result.is_err());
1973        assert_eq!(draw_list_counts(raw_draw_list), before);
1974
1975        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1976            draw_list.add_text_with_font(
1977                font,
1978                -1.0,
1979                [0.0, 0.0],
1980                ImColor32::WHITE,
1981                "hello",
1982                0.0,
1983                None,
1984            );
1985        }));
1986        assert!(result.is_err());
1987        assert_eq!(draw_list_counts(raw_draw_list), before);
1988
1989        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1990            draw_list.push_clip_rect([f32::NAN, 0.0], [1.0, 1.0], false);
1991        }));
1992        assert!(result.is_err());
1993        assert_eq!(
1994            unsafe { (*raw_draw_list)._ClipRectStack.Size },
1995            clip_stack_size
1996        );
1997    }
1998
1999    #[test]
2000    fn raw_draw_list_clip_helper_reads_stack_without_mutation() {
2001        let fixture = TestDrawList::new();
2002        let before = fixture.clip_stack_size();
2003        let draw_list = fixture.draw_list();
2004
2005        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2006            draw_list.push_clip_rect([0.0, 0.0], [f32::INFINITY, 1.0], false);
2007        }));
2008
2009        assert!(result.is_err());
2010        assert_eq!(fixture.clip_stack_size(), before);
2011    }
2012}
2013
2014impl<'ui> DrawListMut<'ui> {
2015    /// Safe variant: add a Rust callback (executed when the draw list is rendered).
2016    /// Note: if the draw list is never rendered, the callback will not run and its resources won't be reclaimed.
2017    #[cfg(not(target_arch = "wasm32"))]
2018    pub fn add_callback_safe<F: FnOnce() + 'static>(&'ui self, callback: F) -> Callback<'ui, F> {
2019        Callback::new(self, callback)
2020    }
2021
2022    /// Safe variant: add a Rust callback (executed when the draw list is rendered).
2023    ///
2024    /// On wasm32 targets using the import-style Dear ImGui provider, C code cannot
2025    /// safely invoke Rust function pointers across module boundaries. For now this
2026    /// API is disabled on wasm to avoid undefined behaviour; use other mechanisms
2027    /// (e.g. higher-level rendering hooks) instead.
2028    #[cfg(target_arch = "wasm32")]
2029    pub fn add_callback_safe<F: FnOnce() + 'static>(&'ui self, _callback: F) -> Callback<'ui, F> {
2030        panic!(
2031            "DrawListMut::add_callback_safe is not supported on wasm32 targets; \
2032             C->Rust callbacks are not available in the import-style web build."
2033        );
2034    }
2035}
2036
2037impl<'ui> DrawListMut<'ui> {
2038    /// Unsafe low-level geometry API: reserve index and vertex space.
2039    ///
2040    /// # Safety
2041    /// Caller must write exactly the reserved amount using `prim_write_*` and ensure valid topology.
2042    pub unsafe fn prim_reserve(&self, idx_count: i32, vtx_count: i32) {
2043        assert_non_negative_i32("DrawListMut::prim_reserve()", "idx_count", idx_count);
2044        assert_non_negative_i32("DrawListMut::prim_reserve()", "vtx_count", vtx_count);
2045        unsafe { sys::ImDrawList_PrimReserve(self.draw_list, idx_count, vtx_count) }
2046    }
2047
2048    /// Unsafe low-level geometry API: unreserve previously reserved space.
2049    ///
2050    /// # Safety
2051    /// Must match a prior call to `prim_reserve` which hasn't been fully written.
2052    pub unsafe fn prim_unreserve(&self, idx_count: i32, vtx_count: i32) {
2053        assert_non_negative_i32("DrawListMut::prim_unreserve()", "idx_count", idx_count);
2054        assert_non_negative_i32("DrawListMut::prim_unreserve()", "vtx_count", vtx_count);
2055        unsafe { sys::ImDrawList_PrimUnreserve(self.draw_list, idx_count, vtx_count) }
2056    }
2057
2058    /// Unsafe low-level geometry API: append a rectangle primitive with a single color.
2059    ///
2060    /// # Safety
2061    /// Only use between `prim_reserve` and completing the reserved writes.
2062    pub unsafe fn prim_rect(
2063        &self,
2064        a: impl Into<sys::ImVec2>,
2065        b: impl Into<sys::ImVec2>,
2066        col: impl Into<ImColor32>,
2067    ) {
2068        let a = finite_vec2("DrawListMut::prim_rect()", "a", a);
2069        let b = finite_vec2("DrawListMut::prim_rect()", "b", b);
2070        unsafe { sys::ImDrawList_PrimRect(self.draw_list, a, b, col.into().into()) }
2071    }
2072
2073    /// Unsafe low-level geometry API: append a rectangle primitive with UVs and color.
2074    ///
2075    /// # Safety
2076    /// Only use between `prim_reserve` and completing the reserved writes.
2077    pub unsafe fn prim_rect_uv(
2078        &self,
2079        a: impl Into<sys::ImVec2>,
2080        b: impl Into<sys::ImVec2>,
2081        uv_a: impl Into<sys::ImVec2>,
2082        uv_b: impl Into<sys::ImVec2>,
2083        col: impl Into<ImColor32>,
2084    ) {
2085        let a = finite_vec2("DrawListMut::prim_rect_uv()", "a", a);
2086        let b = finite_vec2("DrawListMut::prim_rect_uv()", "b", b);
2087        let uv_a = finite_vec2("DrawListMut::prim_rect_uv()", "uv_a", uv_a);
2088        let uv_b = finite_vec2("DrawListMut::prim_rect_uv()", "uv_b", uv_b);
2089
2090        unsafe { sys::ImDrawList_PrimRectUV(self.draw_list, a, b, uv_a, uv_b, col.into().into()) }
2091    }
2092
2093    /// Unsafe low-level geometry API: append a quad primitive with UVs and color.
2094    ///
2095    /// # Safety
2096    /// Only use between `prim_reserve` and completing the reserved writes.
2097    pub unsafe fn prim_quad_uv(
2098        &self,
2099        a: impl Into<sys::ImVec2>,
2100        b: impl Into<sys::ImVec2>,
2101        c: impl Into<sys::ImVec2>,
2102        d: impl Into<sys::ImVec2>,
2103        uv_a: impl Into<sys::ImVec2>,
2104        uv_b: impl Into<sys::ImVec2>,
2105        uv_c: impl Into<sys::ImVec2>,
2106        uv_d: impl Into<sys::ImVec2>,
2107        col: impl Into<ImColor32>,
2108    ) {
2109        let a = finite_vec2("DrawListMut::prim_quad_uv()", "a", a);
2110        let b = finite_vec2("DrawListMut::prim_quad_uv()", "b", b);
2111        let c = finite_vec2("DrawListMut::prim_quad_uv()", "c", c);
2112        let d = finite_vec2("DrawListMut::prim_quad_uv()", "d", d);
2113        let uv_a = finite_vec2("DrawListMut::prim_quad_uv()", "uv_a", uv_a);
2114        let uv_b = finite_vec2("DrawListMut::prim_quad_uv()", "uv_b", uv_b);
2115        let uv_c = finite_vec2("DrawListMut::prim_quad_uv()", "uv_c", uv_c);
2116        let uv_d = finite_vec2("DrawListMut::prim_quad_uv()", "uv_d", uv_d);
2117
2118        unsafe {
2119            sys::ImDrawList_PrimQuadUV(
2120                self.draw_list,
2121                a,
2122                b,
2123                c,
2124                d,
2125                uv_a,
2126                uv_b,
2127                uv_c,
2128                uv_d,
2129                col.into().into(),
2130            )
2131        }
2132    }
2133
2134    /// Unsafe low-level geometry API: write a vertex.
2135    ///
2136    /// # Safety
2137    /// Only use to fill space reserved by `prim_reserve`.
2138    pub unsafe fn prim_write_vtx(
2139        &self,
2140        pos: impl Into<sys::ImVec2>,
2141        uv: impl Into<sys::ImVec2>,
2142        col: impl Into<ImColor32>,
2143    ) {
2144        let pos = finite_vec2("DrawListMut::prim_write_vtx()", "pos", pos);
2145        let uv = finite_vec2("DrawListMut::prim_write_vtx()", "uv", uv);
2146        unsafe { sys::ImDrawList_PrimWriteVtx(self.draw_list, pos, uv, col.into().into()) }
2147    }
2148
2149    /// Unsafe low-level geometry API: write an index.
2150    ///
2151    /// # Safety
2152    /// Only use to fill space reserved by `prim_reserve`.
2153    pub unsafe fn prim_write_idx(&self, idx: sys::ImDrawIdx) {
2154        unsafe { sys::ImDrawList_PrimWriteIdx(self.draw_list, idx) }
2155    }
2156
2157    /// Unsafe low-level geometry API: convenience to append one vertex (pos+uv+col).
2158    ///
2159    /// # Safety
2160    /// Only use between `prim_reserve` and completing the reserved writes.
2161    pub unsafe fn prim_vtx(
2162        &self,
2163        pos: impl Into<sys::ImVec2>,
2164        uv: impl Into<sys::ImVec2>,
2165        col: impl Into<ImColor32>,
2166    ) {
2167        let pos = finite_vec2("DrawListMut::prim_vtx()", "pos", pos);
2168        let uv = finite_vec2("DrawListMut::prim_vtx()", "uv", uv);
2169        unsafe { sys::ImDrawList_PrimVtx(self.draw_list, pos, uv, col.into().into()) }
2170    }
2171}
2172
2173/// Represents a line about to be drawn
2174#[must_use = "should call .build() to draw the object"]
2175pub struct Line<'ui> {
2176    p1: [f32; 2],
2177    p2: [f32; 2],
2178    color: ImColor32,
2179    thickness: f32,
2180    draw_list: &'ui DrawListMut<'ui>,
2181}
2182
2183impl<'ui> Line<'ui> {
2184    fn new<C>(
2185        draw_list: &'ui DrawListMut<'_>,
2186        p1: impl Into<sys::ImVec2>,
2187        p2: impl Into<sys::ImVec2>,
2188        c: C,
2189    ) -> Self
2190    where
2191        C: Into<ImColor32>,
2192    {
2193        Self {
2194            p1: finite_vec2("Line::new()", "p1", p1).into(),
2195            p2: finite_vec2("Line::new()", "p2", p2).into(),
2196            color: c.into(),
2197            thickness: 1.0,
2198            draw_list,
2199        }
2200    }
2201
2202    /// Set line's thickness (default to 1.0 pixel)
2203    pub fn thickness(mut self, thickness: f32) -> Self {
2204        assert_positive_f32("Line::thickness()", "thickness", thickness);
2205        self.thickness = thickness;
2206        self
2207    }
2208
2209    /// Draw the line on the window
2210    pub fn build(self) {
2211        unsafe {
2212            let p1 = sys::ImVec2 {
2213                x: self.p1[0],
2214                y: self.p1[1],
2215            };
2216            let p2 = sys::ImVec2 {
2217                x: self.p2[0],
2218                y: self.p2[1],
2219            };
2220            sys::ImDrawList_AddLine(
2221                self.draw_list.draw_list,
2222                p1,
2223                p2,
2224                self.color.into(),
2225                self.thickness,
2226            )
2227        }
2228    }
2229}
2230
2231/// Represents a rectangle about to be drawn
2232#[must_use = "should call .build() to draw the object"]
2233pub struct Rect<'ui> {
2234    p1: [f32; 2],
2235    p2: [f32; 2],
2236    color: ImColor32,
2237    rounding: f32,
2238    flags: DrawCornerFlags,
2239    thickness: f32,
2240    filled: bool,
2241    draw_list: &'ui DrawListMut<'ui>,
2242}
2243
2244impl<'ui> Rect<'ui> {
2245    fn new<C>(
2246        draw_list: &'ui DrawListMut<'_>,
2247        p1: impl Into<sys::ImVec2>,
2248        p2: impl Into<sys::ImVec2>,
2249        c: C,
2250    ) -> Self
2251    where
2252        C: Into<ImColor32>,
2253    {
2254        Self {
2255            p1: finite_vec2("Rect::new()", "p1", p1).into(),
2256            p2: finite_vec2("Rect::new()", "p2", p2).into(),
2257            color: c.into(),
2258            rounding: 0.0,
2259            flags: DrawCornerFlags::ALL,
2260            thickness: 1.0,
2261            filled: false,
2262            draw_list,
2263        }
2264    }
2265
2266    /// Set rectangle's corner rounding (default to 0.0 = no rounding)
2267    pub fn rounding(mut self, rounding: f32) -> Self {
2268        assert_non_negative_f32("Rect::rounding()", "rounding", rounding);
2269        self.rounding = rounding;
2270        self
2271    }
2272
2273    /// Set rectangle's thickness (default to 1.0 pixel). Has no effect if filled
2274    pub fn thickness(mut self, thickness: f32) -> Self {
2275        assert_positive_f32("Rect::thickness()", "thickness", thickness);
2276        self.thickness = thickness;
2277        self
2278    }
2279
2280    /// Draw rectangle as filled
2281    pub fn filled(mut self, filled: bool) -> Self {
2282        self.filled = filled;
2283        self
2284    }
2285
2286    /// Set rectangle's corner rounding flags
2287    pub fn flags(mut self, flags: DrawCornerFlags) -> Self {
2288        assert_corner_flags("Rect::flags()", flags);
2289        self.flags = flags;
2290        self
2291    }
2292
2293    /// Draw the rectangle on the window
2294    pub fn build(self) {
2295        let p1 = sys::ImVec2 {
2296            x: self.p1[0],
2297            y: self.p1[1],
2298        };
2299        let p2 = sys::ImVec2 {
2300            x: self.p2[0],
2301            y: self.p2[1],
2302        };
2303
2304        if self.filled {
2305            unsafe {
2306                sys::ImDrawList_AddRectFilled(
2307                    self.draw_list.draw_list,
2308                    p1,
2309                    p2,
2310                    self.color.into(),
2311                    self.rounding,
2312                    self.flags.bits() as sys::ImDrawFlags,
2313                )
2314            }
2315        } else {
2316            unsafe {
2317                sys::ImDrawList_AddRect(
2318                    self.draw_list.draw_list,
2319                    p1,
2320                    p2,
2321                    self.color.into(),
2322                    self.rounding,
2323                    self.thickness,
2324                    self.flags.bits() as sys::ImDrawFlags,
2325                )
2326            }
2327        }
2328    }
2329}
2330
2331/// Represents a circle about to be drawn
2332#[must_use = "should call .build() to draw the object"]
2333pub struct Circle<'ui> {
2334    center: [f32; 2],
2335    radius: f32,
2336    color: ImColor32,
2337    num_segments: i32,
2338    thickness: f32,
2339    filled: bool,
2340    draw_list: &'ui DrawListMut<'ui>,
2341}
2342
2343impl<'ui> Circle<'ui> {
2344    fn new<C>(
2345        draw_list: &'ui DrawListMut<'_>,
2346        center: impl Into<sys::ImVec2>,
2347        radius: f32,
2348        color: C,
2349    ) -> Self
2350    where
2351        C: Into<ImColor32>,
2352    {
2353        assert_non_negative_f32("Circle::new()", "radius", radius);
2354        Self {
2355            center: finite_vec2("Circle::new()", "center", center).into(),
2356            radius,
2357            color: color.into(),
2358            num_segments: 0, // 0 = auto
2359            thickness: 1.0,
2360            filled: false,
2361            draw_list,
2362        }
2363    }
2364
2365    /// Set circle's thickness (default to 1.0 pixel). Has no effect if filled
2366    pub fn thickness(mut self, thickness: f32) -> Self {
2367        assert_positive_f32("Circle::thickness()", "thickness", thickness);
2368        self.thickness = thickness;
2369        self
2370    }
2371
2372    /// Draw circle as filled
2373    pub fn filled(mut self, filled: bool) -> Self {
2374        self.filled = filled;
2375        self
2376    }
2377
2378    /// Set number of segments (default to 0 = auto)
2379    pub fn num_segments(mut self, num_segments: i32) -> Self {
2380        self.num_segments = num_segments;
2381        self
2382    }
2383
2384    /// Draw the circle on the window
2385    pub fn build(self) {
2386        let center = sys::ImVec2 {
2387            x: self.center[0],
2388            y: self.center[1],
2389        };
2390
2391        if self.filled {
2392            unsafe {
2393                sys::ImDrawList_AddCircleFilled(
2394                    self.draw_list.draw_list,
2395                    center,
2396                    self.radius,
2397                    self.color.into(),
2398                    self.num_segments,
2399                )
2400            }
2401        } else {
2402            unsafe {
2403                sys::ImDrawList_AddCircle(
2404                    self.draw_list.draw_list,
2405                    center,
2406                    self.radius,
2407                    self.color.into(),
2408                    self.num_segments,
2409                    self.thickness,
2410                )
2411            }
2412        }
2413    }
2414}
2415
2416/// Represents a Bezier curve about to be drawn
2417#[must_use = "should call .build() to draw the object"]
2418pub struct BezierCurve<'ui> {
2419    pos0: [f32; 2],
2420    cp0: [f32; 2],
2421    pos1: [f32; 2],
2422    cp1: [f32; 2],
2423    color: ImColor32,
2424    thickness: f32,
2425    /// If num_segments is not set, the bezier curve is auto-tessalated.
2426    num_segments: Option<i32>,
2427    draw_list: &'ui DrawListMut<'ui>,
2428}
2429
2430impl<'ui> BezierCurve<'ui> {
2431    /// Typically constructed by [`DrawListMut::add_bezier_curve`]
2432    pub fn new<C>(
2433        draw_list: &'ui DrawListMut<'_>,
2434        pos0: impl Into<sys::ImVec2>,
2435        cp0: impl Into<sys::ImVec2>,
2436        cp1: impl Into<sys::ImVec2>,
2437        pos1: impl Into<sys::ImVec2>,
2438        c: C,
2439    ) -> Self
2440    where
2441        C: Into<ImColor32>,
2442    {
2443        Self {
2444            pos0: finite_vec2("BezierCurve::new()", "pos0", pos0).into(),
2445            cp0: finite_vec2("BezierCurve::new()", "cp0", cp0).into(),
2446            cp1: finite_vec2("BezierCurve::new()", "cp1", cp1).into(),
2447            pos1: finite_vec2("BezierCurve::new()", "pos1", pos1).into(),
2448            color: c.into(),
2449            thickness: 1.0,
2450            num_segments: None,
2451            draw_list,
2452        }
2453    }
2454
2455    /// Set curve's thickness (default to 1.0 pixel)
2456    pub fn thickness(mut self, thickness: f32) -> Self {
2457        assert_positive_f32("BezierCurve::thickness()", "thickness", thickness);
2458        self.thickness = thickness;
2459        self
2460    }
2461
2462    /// Set number of segments used to draw the Bezier curve. If not set, the
2463    /// bezier curve is auto-tessalated.
2464    pub fn num_segments(mut self, num_segments: u32) -> Self {
2465        let num_segments = i32::try_from(num_segments)
2466            .expect("BezierCurve::num_segments() num_segments exceeded ImGui's i32 range");
2467        self.num_segments = Some(num_segments);
2468        self
2469    }
2470
2471    /// Draw the curve on the window.
2472    pub fn build(self) {
2473        unsafe {
2474            let pos0: sys::ImVec2 = self.pos0.into();
2475            let cp0: sys::ImVec2 = self.cp0.into();
2476            let cp1: sys::ImVec2 = self.cp1.into();
2477            let pos1: sys::ImVec2 = self.pos1.into();
2478
2479            sys::ImDrawList_AddBezierCubic(
2480                self.draw_list.draw_list,
2481                pos0,
2482                cp0,
2483                cp1,
2484                pos1,
2485                self.color.into(),
2486                self.thickness,
2487                self.num_segments.unwrap_or(0),
2488            )
2489        }
2490    }
2491}
2492
2493/// Represents a poly line about to be drawn
2494#[must_use = "should call .build() to draw the object"]
2495pub struct Polyline<'ui> {
2496    points: Vec<sys::ImVec2>,
2497    thickness: f32,
2498    flags: PolylineFlags,
2499    filled: bool,
2500    color: ImColor32,
2501    draw_list: &'ui DrawListMut<'ui>,
2502}
2503
2504impl<'ui> Polyline<'ui> {
2505    fn new<C, P>(draw_list: &'ui DrawListMut<'_>, points: Vec<P>, c: C) -> Self
2506    where
2507        C: Into<ImColor32>,
2508        P: Into<sys::ImVec2>,
2509    {
2510        Self {
2511            points: points
2512                .into_iter()
2513                .enumerate()
2514                .map(|(i, point)| {
2515                    let name = format!("points[{i}]");
2516                    finite_vec2("Polyline::new()", &name, point)
2517                })
2518                .collect(),
2519            color: c.into(),
2520            thickness: 1.0,
2521            flags: PolylineFlags::NONE,
2522            filled: false,
2523            draw_list,
2524        }
2525    }
2526
2527    /// Set line's thickness (default to 1.0 pixel). Has no effect if
2528    /// shape is filled
2529    pub fn thickness(mut self, thickness: f32) -> Self {
2530        assert_positive_f32("Polyline::thickness()", "thickness", thickness);
2531        self.thickness = thickness;
2532        self
2533    }
2534
2535    /// Set polyline flags. Has no effect if shape is filled.
2536    pub fn flags(mut self, flags: PolylineFlags) -> Self {
2537        assert_polyline_flags("Polyline::flags()", flags);
2538        self.flags = flags;
2539        self
2540    }
2541
2542    /// Draw the polyline as a closed shape. Has no effect if shape is filled.
2543    pub fn closed(mut self, closed: bool) -> Self {
2544        self.flags.set(PolylineFlags::CLOSED, closed);
2545        self
2546    }
2547
2548    /// Draw shape as filled convex polygon
2549    pub fn filled(mut self, filled: bool) -> Self {
2550        self.filled = filled;
2551        self
2552    }
2553
2554    /// Draw the line on the window
2555    pub fn build(self) {
2556        let count = len_i32("Polyline::build()", "points", self.points.len());
2557        if self.filled {
2558            unsafe {
2559                sys::ImDrawList_AddConvexPolyFilled(
2560                    self.draw_list.draw_list,
2561                    self.points.as_ptr(),
2562                    count,
2563                    self.color.into(),
2564                )
2565            }
2566        } else {
2567            unsafe {
2568                sys::ImDrawList_AddPolyline(
2569                    self.draw_list.draw_list,
2570                    self.points.as_ptr(),
2571                    count,
2572                    self.color.into(),
2573                    self.thickness,
2574                    self.flags.bits() as sys::ImDrawFlags,
2575                )
2576            }
2577        }
2578    }
2579}
2580
2581/// Represents a triangle about to be drawn on the window
2582#[must_use = "should call .build() to draw the object"]
2583pub struct Triangle<'ui> {
2584    p1: [f32; 2],
2585    p2: [f32; 2],
2586    p3: [f32; 2],
2587    color: ImColor32,
2588    thickness: f32,
2589    filled: bool,
2590    draw_list: &'ui DrawListMut<'ui>,
2591}
2592
2593impl<'ui> Triangle<'ui> {
2594    fn new<C>(
2595        draw_list: &'ui DrawListMut<'_>,
2596        p1: impl Into<sys::ImVec2>,
2597        p2: impl Into<sys::ImVec2>,
2598        p3: impl Into<sys::ImVec2>,
2599        c: C,
2600    ) -> Self
2601    where
2602        C: Into<ImColor32>,
2603    {
2604        Self {
2605            p1: finite_vec2("Triangle::new()", "p1", p1).into(),
2606            p2: finite_vec2("Triangle::new()", "p2", p2).into(),
2607            p3: finite_vec2("Triangle::new()", "p3", p3).into(),
2608            color: c.into(),
2609            thickness: 1.0,
2610            filled: false,
2611            draw_list,
2612        }
2613    }
2614
2615    /// Set triangle's thickness (default to 1.0 pixel)
2616    pub fn thickness(mut self, thickness: f32) -> Self {
2617        assert_positive_f32("Triangle::thickness()", "thickness", thickness);
2618        self.thickness = thickness;
2619        self
2620    }
2621
2622    /// Set to `true` to make a filled triangle (default to `false`).
2623    pub fn filled(mut self, filled: bool) -> Self {
2624        self.filled = filled;
2625        self
2626    }
2627
2628    /// Draw the triangle on the window.
2629    pub fn build(self) {
2630        let p1 = sys::ImVec2 {
2631            x: self.p1[0],
2632            y: self.p1[1],
2633        };
2634        let p2 = sys::ImVec2 {
2635            x: self.p2[0],
2636            y: self.p2[1],
2637        };
2638        let p3 = sys::ImVec2 {
2639            x: self.p3[0],
2640            y: self.p3[1],
2641        };
2642
2643        if self.filled {
2644            unsafe {
2645                sys::ImDrawList_AddTriangleFilled(
2646                    self.draw_list.draw_list,
2647                    p1,
2648                    p2,
2649                    p3,
2650                    self.color.into(),
2651                )
2652            }
2653        } else {
2654            unsafe {
2655                sys::ImDrawList_AddTriangle(
2656                    self.draw_list.draw_list,
2657                    p1,
2658                    p2,
2659                    p3,
2660                    self.color.into(),
2661                    self.thickness,
2662                )
2663            }
2664        }
2665    }
2666}