1#![allow(
28 clippy::cast_possible_truncation,
29 clippy::cast_sign_loss,
30 clippy::as_conversions
31)]
32use crate::InputTextFlags;
37use crate::internal::DataTypeKind;
38use crate::string::ImString;
39use crate::sys;
40use crate::ui::Ui;
41use std::borrow::Cow;
42use std::ffi::{c_int, c_void};
43use std::marker::PhantomData;
44use std::ptr;
45
46mod callbacks;
47mod numeric;
48
49pub use callbacks::*;
50pub use numeric::*;
51
52pub(super) fn zero_string_spare_capacity(buf: &mut String) {
53 let len = buf.len();
54 let cap = buf.capacity();
55 if cap > len {
56 unsafe {
57 let dst = buf.as_mut_ptr().add(len);
58 ptr::write_bytes(dst, 0, cap - len);
59 }
60 }
61}
62
63pub(super) fn zero_string_new_capacity(buf: &mut String, old_cap: usize) {
64 let new_cap = buf.capacity();
65 if new_cap > old_cap {
66 unsafe {
67 let dst = buf.as_mut_ptr().add(old_cap);
68 ptr::write_bytes(dst, 0, new_cap - old_cap);
69 }
70 }
71}
72
73impl Ui {
75 #[doc(alias = "InputText", alias = "InputTextWithHint")]
89 pub fn input_text<'ui, 'p>(
90 &'ui self,
91 label: impl Into<Cow<'ui, str>>,
92 buf: &'p mut String,
93 ) -> InputText<'ui, 'p> {
94 InputText::new(self, label, buf)
95 }
96
97 pub fn input_text_imstr<'ui, 'p>(
99 &'ui self,
100 label: impl Into<Cow<'ui, str>>,
101 buf: &'p mut ImString,
102 ) -> InputTextImStr<'ui, 'p> {
103 InputTextImStr::new(self, label, buf)
104 }
105
106 #[doc(alias = "InputTextMultiline")]
120 pub fn input_text_multiline<'ui, 'p>(
121 &'ui self,
122 label: impl Into<Cow<'ui, str>>,
123 buf: &'p mut String,
124 size: impl Into<[f32; 2]>,
125 ) -> InputTextMultiline<'ui, 'p> {
126 InputTextMultiline::new(self, label, buf, size)
127 }
128
129 pub fn input_text_multiline_imstr<'ui, 'p>(
131 &'ui self,
132 label: impl Into<Cow<'ui, str>>,
133 buf: &'p mut ImString,
134 size: impl Into<[f32; 2]>,
135 ) -> InputTextMultilineImStr<'ui, 'p> {
136 InputTextMultilineImStr::new(self, label, buf, size)
137 }
138
139 #[doc(alias = "InputInt")]
143 pub fn input_int(&self, label: impl AsRef<str>, value: &mut i32) -> bool {
144 self.input_int_config(label.as_ref()).build(value)
145 }
146
147 #[doc(alias = "InputFloat")]
151 pub fn input_float(&self, label: impl AsRef<str>, value: &mut f32) -> bool {
152 self.input_float_config(label.as_ref()).build(value)
153 }
154
155 #[doc(alias = "InputDouble")]
159 pub fn input_double(&self, label: impl AsRef<str>, value: &mut f64) -> bool {
160 self.input_double_config(label.as_ref()).build(value)
161 }
162
163 pub fn input_int_config<'ui>(&'ui self, label: impl Into<Cow<'ui, str>>) -> InputInt<'ui> {
165 InputInt::new(self, label)
166 }
167
168 pub fn input_float_config<'ui>(&'ui self, label: impl Into<Cow<'ui, str>>) -> InputFloat<'ui> {
170 InputFloat::new(self, label)
171 }
172
173 pub fn input_double_config<'ui>(
175 &'ui self,
176 label: impl Into<Cow<'ui, str>>,
177 ) -> InputDouble<'ui> {
178 InputDouble::new(self, label)
179 }
180
181 #[doc(alias = "InputScalar")]
184 pub fn input_scalar<'p, L, T>(&self, label: L, value: &'p mut T) -> InputScalar<'_, 'p, T, L>
185 where
186 L: AsRef<str>,
187 T: DataTypeKind,
188 {
189 InputScalar::new(self, label, value)
190 }
191
192 #[doc(alias = "InputScalarN")]
196 pub fn input_scalar_n<'p, L, T>(
197 &self,
198 label: L,
199 values: &'p mut [T],
200 ) -> InputScalarN<'_, 'p, T, L>
201 where
202 L: AsRef<str>,
203 T: DataTypeKind,
204 {
205 InputScalarN::new(self, label, values)
206 }
207
208 #[doc(alias = "InputFloat2")]
210 pub fn input_float2<'p, L>(&self, label: L, value: &'p mut [f32; 2]) -> InputFloat2<'_, 'p, L>
211 where
212 L: AsRef<str>,
213 {
214 InputFloat2::new(self, label, value)
215 }
216
217 #[doc(alias = "InputFloat3")]
219 pub fn input_float3<'p, L>(&self, label: L, value: &'p mut [f32; 3]) -> InputFloat3<'_, 'p, L>
220 where
221 L: AsRef<str>,
222 {
223 InputFloat3::new(self, label, value)
224 }
225
226 #[doc(alias = "InputFloat4")]
228 pub fn input_float4<'p, L>(&self, label: L, value: &'p mut [f32; 4]) -> InputFloat4<'_, 'p, L>
229 where
230 L: AsRef<str>,
231 {
232 InputFloat4::new(self, label, value)
233 }
234
235 #[doc(alias = "InputInt2")]
237 pub fn input_int2<'p, L>(&self, label: L, value: &'p mut [i32; 2]) -> InputInt2<'_, 'p, L>
238 where
239 L: AsRef<str>,
240 {
241 InputInt2::new(self, label, value)
242 }
243
244 #[doc(alias = "InputInt3")]
246 pub fn input_int3<'p, L>(&self, label: L, value: &'p mut [i32; 3]) -> InputInt3<'_, 'p, L>
247 where
248 L: AsRef<str>,
249 {
250 InputInt3::new(self, label, value)
251 }
252
253 #[doc(alias = "InputInt4")]
255 pub fn input_int4<'p, L>(&self, label: L, value: &'p mut [i32; 4]) -> InputInt4<'_, 'p, L>
256 where
257 L: AsRef<str>,
258 {
259 InputInt4::new(self, label, value)
260 }
261}
262
263#[must_use]
265pub struct InputText<'ui, 'p, L = Cow<'ui, str>, H = Cow<'ui, str>, T = PassthroughCallback> {
266 ui: &'ui Ui,
267 label: L,
268 buf: &'p mut String,
269 flags: InputTextFlags,
270 capacity_hint: Option<usize>,
271 hint: Option<H>,
272 callback_handler: T,
273 _phantom: PhantomData<&'ui ()>,
274}
275
276#[must_use]
278pub struct InputTextImStr<'ui, 'p, L = Cow<'ui, str>, H = Cow<'ui, str>, T = PassthroughCallback> {
279 ui: &'ui Ui,
280 label: L,
281 buf: &'p mut ImString,
282 flags: InputTextFlags,
283 hint: Option<H>,
284 callback_handler: T,
285 _phantom: PhantomData<&'ui ()>,
286}
287
288impl<'ui, 'p> InputTextImStr<'ui, 'p, Cow<'ui, str>, Cow<'ui, str>, PassthroughCallback> {
289 pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>, buf: &'p mut ImString) -> Self {
290 Self {
291 ui,
292 label: label.into(),
293 buf,
294 flags: InputTextFlags::empty(),
295 hint: None,
296 callback_handler: PassthroughCallback,
297 _phantom: PhantomData,
298 }
299 }
300}
301
302impl<'ui, 'p, L: AsRef<str>, H: AsRef<str>, T> InputTextImStr<'ui, 'p, L, H, T> {
303 pub fn flags(mut self, flags: InputTextFlags) -> Self {
304 self.flags = flags;
305 self
306 }
307 pub fn hint<H2: AsRef<str>>(self, hint: H2) -> InputTextImStr<'ui, 'p, L, H2, T> {
308 InputTextImStr {
309 ui: self.ui,
310 label: self.label,
311 buf: self.buf,
312 flags: self.flags,
313 hint: Some(hint),
314 callback_handler: self.callback_handler,
315 _phantom: PhantomData,
316 }
317 }
318 pub fn read_only(mut self, ro: bool) -> Self {
319 self.flags.set(InputTextFlags::READ_ONLY, ro);
320 self
321 }
322 pub fn password(mut self, pw: bool) -> Self {
323 self.flags.set(InputTextFlags::PASSWORD, pw);
324 self
325 }
326 pub fn auto_select_all(mut self, v: bool) -> Self {
327 self.flags.set(InputTextFlags::AUTO_SELECT_ALL, v);
328 self
329 }
330 pub fn enter_returns_true(mut self, v: bool) -> Self {
331 self.flags.set(InputTextFlags::ENTER_RETURNS_TRUE, v);
332 self
333 }
334
335 pub fn build(self) -> bool {
336 let (label_ptr, hint_ptr) = self.ui.scratch_txt_with_opt(
337 self.label.as_ref(),
338 self.hint.as_ref().map(|hint| hint.as_ref()),
339 );
340 let buf_size = self.buf.capacity_with_nul().max(1);
341 self.buf.ensure_buf_size(buf_size);
342 let buf_ptr = self.buf.as_mut_ptr();
343 let user_ptr = self.buf as *mut ImString as *mut c_void;
344
345 extern "C" fn resize_cb_imstr(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
346 if data.is_null() {
347 return 0;
348 }
349 let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
350 if ((*data).EventFlag as i32) == (sys::ImGuiInputTextFlags_CallbackResize as i32) {
351 let user_data = (*data).UserData as *mut ImString;
352 if user_data.is_null() {
353 return;
354 }
355
356 let im = &mut *user_data;
357 let requested_i32 = (*data).BufSize;
358 if requested_i32 < 0 {
359 return;
360 }
361 let requested = requested_i32 as usize;
362 im.ensure_buf_size(requested);
363 (*data).Buf = im.as_mut_ptr();
364 (*data).BufDirty = true;
365 }
366 }));
367 if res.is_err() {
368 eprintln!("dear-imgui-rs: panic in ImString resize callback");
369 std::process::abort();
370 }
371 0
372 }
373
374 let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
375 let result = unsafe {
376 if hint_ptr.is_null() {
377 sys::igInputText(
378 label_ptr,
379 buf_ptr,
380 buf_size,
381 flags.raw(),
382 Some(resize_cb_imstr),
383 user_ptr,
384 )
385 } else {
386 sys::igInputTextWithHint(
387 label_ptr,
388 hint_ptr,
389 buf_ptr,
390 buf_size,
391 flags.raw(),
392 Some(resize_cb_imstr),
393 user_ptr,
394 )
395 }
396 };
397 unsafe { self.buf.refresh_len() };
399 result
400 }
401}
402impl<'ui, 'p> InputText<'ui, 'p, Cow<'ui, str>, Cow<'ui, str>, PassthroughCallback> {
403 pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>, buf: &'p mut String) -> Self {
405 Self {
406 ui,
407 label: label.into(),
408 buf,
409 flags: InputTextFlags::NONE,
410 capacity_hint: None,
411 hint: None,
412 callback_handler: PassthroughCallback,
413 _phantom: PhantomData,
414 }
415 }
416}
417
418impl<'ui, 'p, L, H, T> InputText<'ui, 'p, L, H, T> {
419 pub fn flags(mut self, flags: InputTextFlags) -> Self {
421 self.flags = flags;
422 self
423 }
424
425 pub fn capacity_hint(mut self, cap: usize) -> Self {
427 self.capacity_hint = Some(cap);
428 self
429 }
430
431 pub fn hint(self, hint: impl Into<Cow<'ui, str>>) -> InputText<'ui, 'p, L, Cow<'ui, str>, T> {
433 InputText {
434 ui: self.ui,
435 label: self.label,
436 buf: self.buf,
437 flags: self.flags,
438 capacity_hint: self.capacity_hint,
439 hint: Some(hint.into()),
440 callback_handler: self.callback_handler,
441 _phantom: PhantomData,
442 }
443 }
444
445 pub fn callback<T2: InputTextCallbackHandler>(
447 self,
448 callback_handler: T2,
449 ) -> InputText<'ui, 'p, L, H, T2> {
450 InputText {
451 ui: self.ui,
452 label: self.label,
453 buf: self.buf,
454 flags: self.flags,
455 capacity_hint: self.capacity_hint,
456 hint: self.hint,
457 callback_handler,
458 _phantom: PhantomData,
459 }
460 }
461
462 pub fn callback_flags(mut self, callback_flags: InputTextCallback) -> Self {
464 self.flags |= InputTextFlags::from_bits_truncate(callback_flags.bits() as i32);
465 self
466 }
467
468 pub fn read_only(mut self, read_only: bool) -> Self {
470 self.flags.set(InputTextFlags::READ_ONLY, read_only);
471 self
472 }
473
474 pub fn password(mut self, password: bool) -> Self {
476 self.flags.set(InputTextFlags::PASSWORD, password);
477 self
478 }
479
480 pub fn auto_select_all(mut self, auto_select: bool) -> Self {
482 self.flags.set(InputTextFlags::AUTO_SELECT_ALL, auto_select);
483 self
484 }
485
486 pub fn enter_returns_true(mut self, enter_returns: bool) -> Self {
488 self.flags
489 .set(InputTextFlags::ENTER_RETURNS_TRUE, enter_returns);
490 self
491 }
492
493 pub fn chars_decimal(mut self, decimal: bool) -> Self {
495 self.flags.set(InputTextFlags::CHARS_DECIMAL, decimal);
496 self
497 }
498
499 pub fn chars_hexadecimal(mut self, hex: bool) -> Self {
501 self.flags.set(InputTextFlags::CHARS_HEXADECIMAL, hex);
502 self
503 }
504
505 pub fn chars_uppercase(mut self, uppercase: bool) -> Self {
507 self.flags.set(InputTextFlags::CHARS_UPPERCASE, uppercase);
508 self
509 }
510
511 pub fn chars_no_blank(mut self, no_blank: bool) -> Self {
513 self.flags.set(InputTextFlags::CHARS_NO_BLANK, no_blank);
514 self
515 }
516}
517
518impl<'ui, 'p, L, H, T> InputText<'ui, 'p, L, H, T>
520where
521 L: AsRef<str>,
522 H: AsRef<str>,
523 T: InputTextCallbackHandler,
524{
525 pub fn build(self) -> bool {
527 let (label_ptr, hint_ptr) = self.ui.scratch_txt_with_opt(
528 self.label.as_ref(),
529 self.hint.as_ref().map(|hint| hint.as_ref()),
530 );
531
532 if let Some(extra) = self.capacity_hint {
533 let needed = extra.saturating_sub(self.buf.capacity().saturating_sub(self.buf.len()));
534 if needed > 0 {
535 self.buf.reserve(needed);
536 }
537 }
538
539 self.buf.push('\0');
541 zero_string_spare_capacity(self.buf);
543 let capacity = self.buf.capacity();
544 let buf_ptr = self.buf.as_mut_ptr() as *mut std::os::raw::c_char;
545
546 #[repr(C)]
547 struct UserData<T> {
548 container: *mut String,
549 handler: T,
550 }
551
552 extern "C" fn callback_router<T: InputTextCallbackHandler>(
553 data: *mut sys::ImGuiInputTextCallbackData,
554 ) -> c_int {
555 if data.is_null() {
556 return 0;
557 }
558
559 let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
560 let user_ptr = unsafe { (*data).UserData as *mut UserData<T> };
561 if user_ptr.is_null() {
562 return 0;
563 }
564 let user = unsafe { &mut *user_ptr };
565 if user.container.is_null() {
566 return 0;
567 }
568
569 let event_flag =
570 unsafe { InputTextFlags::from_bits_truncate((*data).EventFlag as i32) };
571 match event_flag {
572 InputTextFlags::CALLBACK_RESIZE => unsafe {
573 let requested_i32 = (*data).BufSize;
574 if requested_i32 < 0 {
575 return 0;
576 }
577 let requested = requested_i32 as usize;
578 let s = &mut *user.container;
579 debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
580 if requested > s.capacity() {
581 let old_cap = s.capacity();
582 let additional = requested.saturating_sub(s.len());
583 s.reserve(additional);
584 zero_string_new_capacity(s, old_cap);
585 (*data).Buf = s.as_mut_ptr() as *mut _;
586 (*data).BufDirty = true;
587 }
588 0
589 },
590 InputTextFlags::CALLBACK_COMPLETION => {
591 let info = unsafe { TextCallbackData::new(data) };
592 user.handler.on_completion(info);
593 0
594 }
595 InputTextFlags::CALLBACK_HISTORY => {
596 let key = unsafe { (*data).EventKey };
597 let dir = if key == sys::ImGuiKey_UpArrow {
598 HistoryDirection::Up
599 } else {
600 HistoryDirection::Down
601 };
602 let info = unsafe { TextCallbackData::new(data) };
603 user.handler.on_history(dir, info);
604 0
605 }
606 InputTextFlags::CALLBACK_ALWAYS => {
607 let info = unsafe { TextCallbackData::new(data) };
608 user.handler.on_always(info);
609 0
610 }
611 InputTextFlags::CALLBACK_EDIT => {
612 let info = unsafe { TextCallbackData::new(data) };
613 user.handler.on_edit(info);
614 0
615 }
616 InputTextFlags::CALLBACK_CHAR_FILTER => {
617 let ch = unsafe {
618 std::char::from_u32((*data).EventChar as u32).unwrap_or('\0')
619 };
620 let new_ch = user.handler.char_filter(ch).map(|c| c as u32).unwrap_or(0);
621 unsafe {
622 (*data).EventChar =
623 sys::ImWchar::try_from(new_ch).unwrap_or(0 as sys::ImWchar);
624 }
625 0
626 }
627 _ => 0,
628 }
629 }));
630
631 match res {
632 Ok(v) => v,
633 Err(_) => {
634 eprintln!("dear-imgui-rs: panic in InputText callback");
635 std::process::abort();
636 }
637 }
638 }
639
640 let mut user_data = UserData {
641 container: self.buf as *mut String,
642 handler: self.callback_handler,
643 };
644 let user_ptr = &mut user_data as *mut _ as *mut c_void;
645
646 let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
647 let result = unsafe {
648 if hint_ptr.is_null() {
649 sys::igInputText(
650 label_ptr,
651 buf_ptr,
652 capacity,
653 flags.raw(),
654 Some(callback_router::<T>),
655 user_ptr,
656 )
657 } else {
658 sys::igInputTextWithHint(
659 label_ptr,
660 hint_ptr,
661 buf_ptr,
662 capacity,
663 flags.raw(),
664 Some(callback_router::<T>),
665 user_ptr,
666 )
667 }
668 };
669
670 let cap = unsafe { (&*user_data.container).capacity() };
672 let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
673 if let Some(len) = slice.iter().position(|&b| b == 0) {
674 unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
675 }
676 result
677 }
678}
679
680#[derive(Debug)]
682#[must_use]
683pub struct InputTextMultiline<'ui, 'p> {
684 ui: &'ui Ui,
685 label: Cow<'ui, str>,
686 buf: &'p mut String,
687 size: [f32; 2],
688 flags: InputTextFlags,
689 capacity_hint: Option<usize>,
690}
691
692#[derive(Debug)]
694#[must_use]
695pub struct InputTextMultilineImStr<'ui, 'p> {
696 ui: &'ui Ui,
697 label: Cow<'ui, str>,
698 buf: &'p mut ImString,
699 size: [f32; 2],
700 flags: InputTextFlags,
701}
702
703impl<'ui, 'p> InputTextMultilineImStr<'ui, 'p> {
704 pub fn new(
705 ui: &'ui Ui,
706 label: impl Into<Cow<'ui, str>>,
707 buf: &'p mut ImString,
708 size: impl Into<[f32; 2]>,
709 ) -> Self {
710 Self {
711 ui,
712 label: label.into(),
713 buf,
714 size: size.into(),
715 flags: InputTextFlags::NONE,
716 }
717 }
718 pub fn flags(mut self, flags: InputTextFlags) -> Self {
719 self.flags = flags;
720 self
721 }
722 pub fn read_only(mut self, v: bool) -> Self {
723 self.flags.set(InputTextFlags::READ_ONLY, v);
724 self
725 }
726 pub fn build(self) -> bool {
727 let label_ptr = self.ui.scratch_txt(self.label.as_ref());
728 let buf_size = self.buf.capacity_with_nul().max(1);
729 self.buf.ensure_buf_size(buf_size);
730 let buf_ptr = self.buf.as_mut_ptr();
731 let user_ptr = self.buf as *mut ImString as *mut c_void;
732 let size_vec: sys::ImVec2 = self.size.into();
733
734 extern "C" fn resize_cb_imstr(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
735 if data.is_null() {
736 return 0;
737 }
738 let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
739 if ((*data).EventFlag as i32) == (sys::ImGuiInputTextFlags_CallbackResize as i32) {
740 let user_data = (*data).UserData as *mut ImString;
741 if user_data.is_null() {
742 return;
743 }
744
745 let im = &mut *user_data;
746 let requested_i32 = (*data).BufSize;
747 if requested_i32 < 0 {
748 return;
749 }
750 let requested = requested_i32 as usize;
751 im.ensure_buf_size(requested);
752 (*data).Buf = im.as_mut_ptr();
753 (*data).BufDirty = true;
754 }
755 }));
756 if res.is_err() {
757 eprintln!("dear-imgui-rs: panic in ImString multiline resize callback");
758 std::process::abort();
759 }
760 0
761 }
762
763 let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
764 let result = unsafe {
765 sys::igInputTextMultiline(
766 label_ptr,
767 buf_ptr,
768 buf_size,
769 size_vec,
770 flags.raw(),
771 Some(resize_cb_imstr),
772 user_ptr,
773 )
774 };
775 unsafe { self.buf.refresh_len() };
777 result
778 }
779}
780impl<'ui, 'p> InputTextMultiline<'ui, 'p> {
781 pub fn new(
783 ui: &'ui Ui,
784 label: impl Into<Cow<'ui, str>>,
785 buf: &'p mut String,
786 size: impl Into<[f32; 2]>,
787 ) -> Self {
788 Self {
789 ui,
790 label: label.into(),
791 buf,
792 size: size.into(),
793 flags: InputTextFlags::NONE,
794 capacity_hint: None,
795 }
796 }
797
798 pub fn flags(mut self, flags: InputTextFlags) -> Self {
800 self.flags = flags;
801 self
802 }
803
804 pub fn capacity_hint(mut self, cap: usize) -> Self {
806 self.capacity_hint = Some(cap);
807 self
808 }
809
810 pub fn read_only(mut self, read_only: bool) -> Self {
812 self.flags.set(InputTextFlags::READ_ONLY, read_only);
813 self
814 }
815
816 pub fn build(self) -> bool {
818 let label_ptr = self.ui.scratch_txt(self.label.as_ref());
819
820 if let Some(extra) = self.capacity_hint {
822 let needed = extra.saturating_sub(self.buf.capacity().saturating_sub(self.buf.len()));
823 if needed > 0 {
824 self.buf.reserve(needed);
825 }
826 }
827
828 self.buf.push('\0');
830 zero_string_spare_capacity(self.buf);
832 let capacity = self.buf.capacity();
833 let buf_ptr = self.buf.as_mut_ptr() as *mut std::os::raw::c_char;
834
835 #[repr(C)]
836 struct UserData {
837 container: *mut String,
838 }
839
840 extern "C" fn callback_router(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
841 if data.is_null() {
842 return 0;
843 }
844
845 let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
846 let event_flag = InputTextFlags::from_bits_truncate((*data).EventFlag as i32);
847 match event_flag {
848 InputTextFlags::CALLBACK_RESIZE => {
849 let user_ptr = (*data).UserData as *mut UserData;
850 if user_ptr.is_null() {
851 return 0;
852 }
853 let requested_i32 = (*data).BufSize;
854 if requested_i32 < 0 {
855 return 0;
856 }
857 let requested = requested_i32 as usize;
858
859 let user = &mut *user_ptr;
860 if user.container.is_null() {
861 return 0;
862 }
863 let s = &mut *user.container;
864 debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
865 if requested > s.capacity() {
866 let old_cap = s.capacity();
867 let additional = requested.saturating_sub(s.len());
868 s.reserve(additional);
869 zero_string_new_capacity(s, old_cap);
870 (*data).Buf = s.as_mut_ptr() as *mut _;
871 (*data).BufDirty = true;
872 }
873 0
874 }
875 _ => 0,
876 }
877 }));
878
879 match res {
880 Ok(v) => v,
881 Err(_) => {
882 eprintln!("dear-imgui-rs: panic in multiline InputText resize callback");
883 std::process::abort();
884 }
885 }
886 }
887
888 let mut user_data = UserData {
889 container: self.buf as *mut String,
890 };
891 let user_ptr = &mut user_data as *mut _ as *mut c_void;
892
893 let size_vec: sys::ImVec2 = self.size.into();
894 let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
895 let result = unsafe {
896 sys::igInputTextMultiline(
897 label_ptr,
898 buf_ptr,
899 capacity,
900 size_vec,
901 flags.raw(),
902 Some(callback_router),
903 user_ptr,
904 )
905 };
906
907 let cap = unsafe { (&*user_data.container).capacity() };
909 let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
910 if let Some(len) = slice.iter().position(|&b| b == 0) {
911 unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
912 }
913 result
914 }
915
916 pub fn callback<T2: InputTextCallbackHandler>(
918 mut self,
919 callbacks: InputTextCallback,
920 handler: T2,
921 ) -> InputTextMultilineWithCb<'ui, 'p, T2> {
922 if callbacks.contains(InputTextCallback::ALWAYS) {
925 self.flags.insert(InputTextFlags::CALLBACK_ALWAYS);
926 }
927 if callbacks.contains(InputTextCallback::CHAR_FILTER) {
928 self.flags.insert(InputTextFlags::CALLBACK_CHAR_FILTER);
929 }
930 if callbacks.contains(InputTextCallback::EDIT) {
931 self.flags.insert(InputTextFlags::CALLBACK_EDIT);
932 }
933
934 InputTextMultilineWithCb {
935 ui: self.ui,
936 label: self.label,
937 buf: self.buf,
938 size: self.size,
939 flags: self.flags,
940 capacity_hint: self.capacity_hint,
941 handler,
942 }
943 }
944}
945
946pub struct InputTextMultilineWithCb<'ui, 'p, T> {
948 ui: &'ui Ui,
949 label: Cow<'ui, str>,
950 buf: &'p mut String,
951 size: [f32; 2],
952 flags: InputTextFlags,
953 capacity_hint: Option<usize>,
954 handler: T,
955}
956
957impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultilineWithCb<'ui, 'p, T> {
958 pub fn build(self) -> bool {
959 let label_ptr = self.ui.scratch_txt(self.label.as_ref());
960
961 if let Some(extra) = self.capacity_hint {
962 let needed = extra.saturating_sub(self.buf.capacity().saturating_sub(self.buf.len()));
963 if needed > 0 {
964 self.buf.reserve(needed);
965 }
966 }
967
968 self.buf.push('\0');
970 zero_string_spare_capacity(self.buf);
972 let capacity = self.buf.capacity();
973 let buf_ptr = self.buf.as_mut_ptr() as *mut std::os::raw::c_char;
974
975 #[repr(C)]
976 struct UserData<T> {
977 container: *mut String,
978 handler: T,
979 }
980
981 extern "C" fn callback_router<T: InputTextCallbackHandler>(
982 data: *mut sys::ImGuiInputTextCallbackData,
983 ) -> c_int {
984 if data.is_null() {
985 return 0;
986 }
987
988 let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
989 let user_ptr = unsafe { (*data).UserData as *mut UserData<T> };
990 if user_ptr.is_null() {
991 return 0;
992 }
993 let user = unsafe { &mut *user_ptr };
994 if user.container.is_null() {
995 return 0;
996 }
997
998 let event_flag =
999 unsafe { InputTextFlags::from_bits_truncate((*data).EventFlag as i32) };
1000 match event_flag {
1001 InputTextFlags::CALLBACK_RESIZE => unsafe {
1002 let requested_i32 = (*data).BufSize;
1003 if requested_i32 < 0 {
1004 return 0;
1005 }
1006 let requested = requested_i32 as usize;
1007 let s = &mut *user.container;
1008 debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
1009 if requested > s.capacity() {
1010 let old_cap = s.capacity();
1011 let additional = requested.saturating_sub(s.len());
1012 s.reserve(additional);
1013 zero_string_new_capacity(s, old_cap);
1014 (*data).Buf = s.as_mut_ptr() as *mut _;
1015 (*data).BufDirty = true;
1016 }
1017 0
1018 },
1019 InputTextFlags::CALLBACK_COMPLETION => {
1020 let info = unsafe { TextCallbackData::new(data) };
1021 user.handler.on_completion(info);
1022 0
1023 }
1024 InputTextFlags::CALLBACK_HISTORY => {
1025 let key = unsafe { (*data).EventKey };
1026 let dir = if key == sys::ImGuiKey_UpArrow {
1027 HistoryDirection::Up
1028 } else {
1029 HistoryDirection::Down
1030 };
1031 let info = unsafe { TextCallbackData::new(data) };
1032 user.handler.on_history(dir, info);
1033 0
1034 }
1035 InputTextFlags::CALLBACK_ALWAYS => {
1036 let info = unsafe { TextCallbackData::new(data) };
1037 user.handler.on_always(info);
1038 0
1039 }
1040 InputTextFlags::CALLBACK_EDIT => {
1041 let info = unsafe { TextCallbackData::new(data) };
1042 user.handler.on_edit(info);
1043 0
1044 }
1045 InputTextFlags::CALLBACK_CHAR_FILTER => {
1046 let ch = unsafe {
1047 std::char::from_u32((*data).EventChar as u32).unwrap_or('\0')
1048 };
1049 let new_ch = user.handler.char_filter(ch).map(|c| c as u32).unwrap_or(0);
1050 unsafe {
1051 (*data).EventChar =
1052 sys::ImWchar::try_from(new_ch).unwrap_or(0 as sys::ImWchar);
1053 }
1054 0
1055 }
1056 _ => 0,
1057 }
1058 }));
1059
1060 match res {
1061 Ok(v) => v,
1062 Err(_) => {
1063 eprintln!("dear-imgui-rs: panic in InputText multiline callback");
1064 std::process::abort();
1065 }
1066 }
1067 }
1068
1069 let mut user_data = UserData {
1070 container: self.buf as *mut String,
1071 handler: self.handler,
1072 };
1073 let user_ptr = &mut user_data as *mut _ as *mut c_void;
1074
1075 let size_vec: sys::ImVec2 = self.size.into();
1076 let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
1077 let result = unsafe {
1078 sys::igInputTextMultiline(
1079 label_ptr,
1080 buf_ptr,
1081 capacity,
1082 size_vec,
1083 flags.raw(),
1084 Some(callback_router::<T>),
1085 user_ptr,
1086 )
1087 };
1088
1089 let cap = unsafe { (&*user_data.container).capacity() };
1091 let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
1092 if let Some(len) = slice.iter().position(|&b| b == 0) {
1093 unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
1094 }
1095 result
1096 }
1097}