1#![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
37fn 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#[repr(transparent)]
138#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
139pub struct ImColor32(u32);
140
141impl ImColor32 {
142 pub const BLACK: Self = Self(0xff_00_00_00);
144 pub const WHITE: Self = Self(0xff_ff_ff_ff);
146 pub const TRANSPARENT: Self = Self(0);
148
149 #[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 #[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 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 #[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
220bitflags! {
222 #[repr(transparent)]
224 pub struct DrawListFlags: i32 {
225 const NONE = sys::ImDrawListFlags_None as i32;
227 const ANTI_ALIASED_LINES = sys::ImDrawListFlags_AntiAliasedLines as i32;
229 const ANTI_ALIASED_LINES_USE_TEX = sys::ImDrawListFlags_AntiAliasedLinesUseTex as i32;
231 const ANTI_ALIASED_FILL = sys::ImDrawListFlags_AntiAliasedFill as i32;
233 const ALLOW_VTX_OFFSET = sys::ImDrawListFlags_AllowVtxOffset as i32;
235 }
236}
237
238bitflags! {
239 #[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 #[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
282pub 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#[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 #[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 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
404impl<'ui> DrawListMut<'ui> {
406 #[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 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 #[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 #[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 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 #[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 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 #[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 #[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 #[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 #[doc(alias = "PathClear")]
586 pub fn path_clear(&self) {
587 unsafe {
588 let draw_list = self.draw_list;
590 (*draw_list)._Path.Size = 0;
591 }
592 }
593
594 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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; }
781 }
782 }
783
784 #[doc(alias = "PathFillConvex")]
786 pub fn path_fill_convex(&self, color: impl Into<ImColor32>) {
787 unsafe {
788 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; }
801 }
802 }
803
804 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 #[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 #[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 #[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 #[doc(alias = "PopTexture")]
914 pub fn pop_texture(&self) {
915 unsafe {
916 sys::ImDrawList_PopTexture(self.draw_list);
917 }
918 }
919
920 #[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 #[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 #[doc(alias = "PushClipRectFullScreen")]
964 pub fn push_clip_rect_full_screen(&self) {
965 unsafe { sys::ImDrawList_PushClipRectFullScreen(self.draw_list) }
966 }
967
968 #[doc(alias = "PopClipRect")]
970 pub fn pop_clip_rect(&self) {
971 unsafe { sys::ImDrawList_PopClipRect(self.draw_list) }
972 }
973
974 pub fn clip_rect_min(&self) -> [f32; 2] {
976 let out = unsafe { sys::ImDrawList_GetClipRectMin(self.draw_list) };
977 out.into()
978 }
979
980 pub fn clip_rect_max(&self) -> [f32; 2] {
982 let out = unsafe { sys::ImDrawList_GetClipRectMax(self.draw_list) };
983 out.into()
984 }
985
986 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 #[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 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 #[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 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 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[doc(alias = "AddDrawCmd")]
1376 pub fn add_draw_cmd(&self) {
1377 unsafe { sys::ImDrawList_AddDrawCmd(self.draw_list) }
1378 }
1379
1380 #[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
1400pub struct ChannelsSplit<'ui> {
1402 draw_list: &'ui DrawListMut<'ui>,
1403 channels_count: u32,
1404}
1405
1406impl ChannelsSplit<'_> {
1407 #[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#[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 pub fn new(draw_list: &'ui DrawListMut<'_>, callback: F) -> Self {
1432 Self {
1433 draw_list,
1434 callback,
1435 }
1436 }
1437
1438 pub fn build(self) {
1440 use std::os::raw::c_void;
1441 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 let data_ptr = unsafe { (*cmd_ptr).UserCallbackData as *mut F };
1478 if data_ptr.is_null() {
1479 return;
1480 }
1481 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 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 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 #[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 #[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 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 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 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 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 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 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 pub unsafe fn prim_write_idx(&self, idx: sys::ImDrawIdx) {
2154 unsafe { sys::ImDrawList_PrimWriteIdx(self.draw_list, idx) }
2155 }
2156
2157 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#[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 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 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#[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 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 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 pub fn filled(mut self, filled: bool) -> Self {
2282 self.filled = filled;
2283 self
2284 }
2285
2286 pub fn flags(mut self, flags: DrawCornerFlags) -> Self {
2288 assert_corner_flags("Rect::flags()", flags);
2289 self.flags = flags;
2290 self
2291 }
2292
2293 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#[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, thickness: 1.0,
2360 filled: false,
2361 draw_list,
2362 }
2363 }
2364
2365 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 pub fn filled(mut self, filled: bool) -> Self {
2374 self.filled = filled;
2375 self
2376 }
2377
2378 pub fn num_segments(mut self, num_segments: i32) -> Self {
2380 self.num_segments = num_segments;
2381 self
2382 }
2383
2384 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#[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 num_segments: Option<i32>,
2427 draw_list: &'ui DrawListMut<'ui>,
2428}
2429
2430impl<'ui> BezierCurve<'ui> {
2431 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 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 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 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#[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 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 pub fn flags(mut self, flags: PolylineFlags) -> Self {
2537 assert_polyline_flags("Polyline::flags()", flags);
2538 self.flags = flags;
2539 self
2540 }
2541
2542 pub fn closed(mut self, closed: bool) -> Self {
2544 self.flags.set(PolylineFlags::CLOSED, closed);
2545 self
2546 }
2547
2548 pub fn filled(mut self, filled: bool) -> Self {
2550 self.filled = filled;
2551 self
2552 }
2553
2554 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#[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 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 pub fn filled(mut self, filled: bool) -> Self {
2624 self.filled = filled;
2625 self
2626 }
2627
2628 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}