dear_imgui_rs/widget/
input.rs

1//! Text and scalar inputs
2//!
3//! Single-line and multi-line text inputs backed by `String` or `ImString`
4//! (zero-copy), plus number input helpers. Builders provide flags and
5//! callback hooks for validation and behavior tweaks.
6//!
7//! Quick examples:
8//! ```no_run
9//! # use dear_imgui_rs::*;
10//! # let mut ctx = Context::create();
11//! # let ui = ctx.frame();
12//! // Text (String)
13//! let mut s = String::from("hello");
14//! ui.input_text("Name", &mut s).build();
15//!
16//! // Text (ImString, zero-copy)
17//! let mut im = ImString::with_capacity(64);
18//! ui.input_text_imstr("ImStr", &mut im).build();
19//!
20//! // Numbers
21//! let mut i = 0i32;
22//! let mut f = 1.0f32;
23//! ui.input_int("Count", &mut i);
24//! ui.input_float("Scale", &mut f);
25//! ```
26//!
27#![allow(
28    clippy::cast_possible_truncation,
29    clippy::cast_sign_loss,
30    clippy::as_conversions
31)]
32// NOTE: Keep explicit `as i32`/`as u32` casts when bridging bindgen-generated flags into the
33// Dear ImGui C ABI. Bindgen may represent the same C enum/typedef with different Rust integer
34// types across platforms/toolchains; our wrappers intentionally pin the expected width/sign at
35// the FFI call sites.
36use 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
46fn zero_string_spare_capacity(buf: &mut String) {
47    let len = buf.len();
48    let cap = buf.capacity();
49    if cap > len {
50        unsafe {
51            let dst = buf.as_mut_ptr().add(len);
52            ptr::write_bytes(dst, 0, cap - len);
53        }
54    }
55}
56
57fn zero_string_new_capacity(buf: &mut String, old_cap: usize) {
58    let new_cap = buf.capacity();
59    if new_cap > old_cap {
60        unsafe {
61            let dst = buf.as_mut_ptr().add(old_cap);
62            ptr::write_bytes(dst, 0, new_cap - old_cap);
63        }
64    }
65}
66
67/// # Input Widgets
68impl Ui {
69    /// Creates a single-line text input widget builder.
70    ///
71    /// # Examples
72    ///
73    /// ```no_run
74    /// # use dear_imgui_rs::*;
75    /// # let mut ctx = Context::create();
76    /// # let ui = ctx.frame();
77    /// let mut text = String::new();
78    /// if ui.input_text("Label", &mut text).build() {
79    ///     println!("Text changed: {}", text);
80    /// }
81    /// ```
82    #[doc(alias = "InputText", alias = "InputTextWithHint")]
83    pub fn input_text<'ui, 'p>(
84        &'ui self,
85        label: impl Into<Cow<'ui, str>>,
86        buf: &'p mut String,
87    ) -> InputText<'ui, 'p> {
88        InputText::new(self, label, buf)
89    }
90
91    /// Creates a single-line text input backed by ImString (zero-copy)
92    pub fn input_text_imstr<'ui, 'p>(
93        &'ui self,
94        label: impl Into<Cow<'ui, str>>,
95        buf: &'p mut ImString,
96    ) -> InputTextImStr<'ui, 'p> {
97        InputTextImStr::new(self, label, buf)
98    }
99
100    /// Creates a multi-line text input widget builder.
101    ///
102    /// # Examples
103    ///
104    /// ```no_run
105    /// # use dear_imgui_rs::*;
106    /// # let mut ctx = Context::create();
107    /// # let ui = ctx.frame();
108    /// let mut text = String::new();
109    /// if ui.input_text_multiline("Label", &mut text, [200.0, 100.0]).build() {
110    ///     println!("Text changed: {}", text);
111    /// }
112    /// ```
113    #[doc(alias = "InputTextMultiline")]
114    pub fn input_text_multiline<'ui, 'p>(
115        &'ui self,
116        label: impl Into<Cow<'ui, str>>,
117        buf: &'p mut String,
118        size: impl Into<[f32; 2]>,
119    ) -> InputTextMultiline<'ui, 'p> {
120        InputTextMultiline::new(self, label, buf, size)
121    }
122
123    /// Creates a multi-line text input backed by ImString (zero-copy)
124    pub fn input_text_multiline_imstr<'ui, 'p>(
125        &'ui self,
126        label: impl Into<Cow<'ui, str>>,
127        buf: &'p mut ImString,
128        size: impl Into<[f32; 2]>,
129    ) -> InputTextMultilineImStr<'ui, 'p> {
130        InputTextMultilineImStr::new(self, label, buf, size)
131    }
132
133    /// Creates an integer input widget.
134    ///
135    /// Returns true if the value was edited.
136    #[doc(alias = "InputInt")]
137    pub fn input_int(&self, label: impl AsRef<str>, value: &mut i32) -> bool {
138        self.input_int_config(label.as_ref()).build(value)
139    }
140
141    /// Creates a float input widget.
142    ///
143    /// Returns true if the value was edited.
144    #[doc(alias = "InputFloat")]
145    pub fn input_float(&self, label: impl AsRef<str>, value: &mut f32) -> bool {
146        self.input_float_config(label.as_ref()).build(value)
147    }
148
149    /// Creates a double input widget.
150    ///
151    /// Returns true if the value was edited.
152    #[doc(alias = "InputDouble")]
153    pub fn input_double(&self, label: impl AsRef<str>, value: &mut f64) -> bool {
154        self.input_double_config(label.as_ref()).build(value)
155    }
156
157    /// Creates an integer input builder
158    pub fn input_int_config<'ui>(&'ui self, label: impl Into<Cow<'ui, str>>) -> InputInt<'ui> {
159        InputInt::new(self, label)
160    }
161
162    /// Creates a float input builder
163    pub fn input_float_config<'ui>(&'ui self, label: impl Into<Cow<'ui, str>>) -> InputFloat<'ui> {
164        InputFloat::new(self, label)
165    }
166
167    /// Creates a double input builder
168    pub fn input_double_config<'ui>(
169        &'ui self,
170        label: impl Into<Cow<'ui, str>>,
171    ) -> InputDouble<'ui> {
172        InputDouble::new(self, label)
173    }
174
175    /// Shows an input field for a scalar value. This is not limited to `f32` and `i32` and can be used with
176    /// any primitive scalar type e.g. `u8` and `f64`.
177    #[doc(alias = "InputScalar")]
178    pub fn input_scalar<'p, L, T>(&self, label: L, value: &'p mut T) -> InputScalar<'_, 'p, T, L>
179    where
180        L: AsRef<str>,
181        T: DataTypeKind,
182    {
183        InputScalar::new(self, label, value)
184    }
185
186    /// Shows a horizontal array of scalar value input fields. See [`input_scalar`].
187    ///
188    /// [`input_scalar`]: Self::input_scalar
189    #[doc(alias = "InputScalarN")]
190    pub fn input_scalar_n<'p, L, T>(
191        &self,
192        label: L,
193        values: &'p mut [T],
194    ) -> InputScalarN<'_, 'p, T, L>
195    where
196        L: AsRef<str>,
197        T: DataTypeKind,
198    {
199        InputScalarN::new(self, label, values)
200    }
201
202    /// Widget to edit two floats
203    #[doc(alias = "InputFloat2")]
204    pub fn input_float2<'p, L>(&self, label: L, value: &'p mut [f32; 2]) -> InputFloat2<'_, 'p, L>
205    where
206        L: AsRef<str>,
207    {
208        InputFloat2::new(self, label, value)
209    }
210
211    /// Widget to edit three floats
212    #[doc(alias = "InputFloat3")]
213    pub fn input_float3<'p, L>(&self, label: L, value: &'p mut [f32; 3]) -> InputFloat3<'_, 'p, L>
214    where
215        L: AsRef<str>,
216    {
217        InputFloat3::new(self, label, value)
218    }
219
220    /// Widget to edit four floats
221    #[doc(alias = "InputFloat4")]
222    pub fn input_float4<'p, L>(&self, label: L, value: &'p mut [f32; 4]) -> InputFloat4<'_, 'p, L>
223    where
224        L: AsRef<str>,
225    {
226        InputFloat4::new(self, label, value)
227    }
228
229    /// Widget to edit two integers
230    #[doc(alias = "InputInt2")]
231    pub fn input_int2<'p, L>(&self, label: L, value: &'p mut [i32; 2]) -> InputInt2<'_, 'p, L>
232    where
233        L: AsRef<str>,
234    {
235        InputInt2::new(self, label, value)
236    }
237
238    /// Widget to edit three integers
239    #[doc(alias = "InputInt3")]
240    pub fn input_int3<'p, L>(&self, label: L, value: &'p mut [i32; 3]) -> InputInt3<'_, 'p, L>
241    where
242        L: AsRef<str>,
243    {
244        InputInt3::new(self, label, value)
245    }
246
247    /// Widget to edit four integers
248    #[doc(alias = "InputInt4")]
249    pub fn input_int4<'p, L>(&self, label: L, value: &'p mut [i32; 4]) -> InputInt4<'_, 'p, L>
250    where
251        L: AsRef<str>,
252    {
253        InputInt4::new(self, label, value)
254    }
255}
256
257/// Builder for a text input widget
258#[must_use]
259pub struct InputText<'ui, 'p, L = Cow<'ui, str>, H = Cow<'ui, str>, T = PassthroughCallback> {
260    ui: &'ui Ui,
261    label: L,
262    buf: &'p mut String,
263    flags: InputTextFlags,
264    capacity_hint: Option<usize>,
265    hint: Option<H>,
266    callback_handler: T,
267    _phantom: PhantomData<&'ui ()>,
268}
269
270/// Builder for a text input backed by ImString (zero-copy)
271#[must_use]
272pub struct InputTextImStr<'ui, 'p, L = Cow<'ui, str>, H = Cow<'ui, str>, T = PassthroughCallback> {
273    ui: &'ui Ui,
274    label: L,
275    buf: &'p mut ImString,
276    flags: InputTextFlags,
277    hint: Option<H>,
278    callback_handler: T,
279    _phantom: PhantomData<&'ui ()>,
280}
281
282impl<'ui, 'p> InputTextImStr<'ui, 'p, Cow<'ui, str>, Cow<'ui, str>, PassthroughCallback> {
283    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>, buf: &'p mut ImString) -> Self {
284        Self {
285            ui,
286            label: label.into(),
287            buf,
288            flags: InputTextFlags::empty(),
289            hint: None,
290            callback_handler: PassthroughCallback,
291            _phantom: PhantomData,
292        }
293    }
294}
295
296impl<'ui, 'p, L: AsRef<str>, H: AsRef<str>, T> InputTextImStr<'ui, 'p, L, H, T> {
297    pub fn flags(mut self, flags: InputTextFlags) -> Self {
298        self.flags = flags;
299        self
300    }
301    pub fn hint<H2: AsRef<str>>(self, hint: H2) -> InputTextImStr<'ui, 'p, L, H2, T> {
302        InputTextImStr {
303            ui: self.ui,
304            label: self.label,
305            buf: self.buf,
306            flags: self.flags,
307            hint: Some(hint),
308            callback_handler: self.callback_handler,
309            _phantom: PhantomData,
310        }
311    }
312    pub fn read_only(mut self, ro: bool) -> Self {
313        self.flags.set(InputTextFlags::READ_ONLY, ro);
314        self
315    }
316    pub fn password(mut self, pw: bool) -> Self {
317        self.flags.set(InputTextFlags::PASSWORD, pw);
318        self
319    }
320    pub fn auto_select_all(mut self, v: bool) -> Self {
321        self.flags.set(InputTextFlags::AUTO_SELECT_ALL, v);
322        self
323    }
324    pub fn enter_returns_true(mut self, v: bool) -> Self {
325        self.flags.set(InputTextFlags::ENTER_RETURNS_TRUE, v);
326        self
327    }
328
329    pub fn build(self) -> bool {
330        let (label_ptr, hint_ptr) = self.ui.scratch_txt_with_opt(
331            self.label.as_ref(),
332            self.hint.as_ref().map(|hint| hint.as_ref()),
333        );
334        let buf_size = self.buf.capacity_with_nul().max(1);
335        self.buf.ensure_buf_size(buf_size);
336        let buf_ptr = self.buf.as_mut_ptr();
337        let user_ptr = self.buf as *mut ImString as *mut c_void;
338
339        extern "C" fn resize_cb_imstr(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
340            if data.is_null() {
341                return 0;
342            }
343            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
344                if (*data).EventFlag == (sys::ImGuiInputTextFlags_CallbackResize as i32) {
345                    let user_data = (*data).UserData as *mut ImString;
346                    if user_data.is_null() {
347                        return;
348                    }
349
350                    let im = &mut *user_data;
351                    let requested_i32 = (*data).BufSize;
352                    if requested_i32 < 0 {
353                        return;
354                    }
355                    let requested = requested_i32 as usize;
356                    im.ensure_buf_size(requested);
357                    (*data).Buf = im.as_mut_ptr();
358                    (*data).BufDirty = true;
359                }
360            }));
361            if res.is_err() {
362                eprintln!("dear-imgui-rs: panic in ImString resize callback");
363                std::process::abort();
364            }
365            0
366        }
367
368        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
369        let result = unsafe {
370            if hint_ptr.is_null() {
371                sys::igInputText(
372                    label_ptr,
373                    buf_ptr,
374                    buf_size,
375                    flags.bits(),
376                    Some(resize_cb_imstr),
377                    user_ptr,
378                )
379            } else {
380                sys::igInputTextWithHint(
381                    label_ptr,
382                    hint_ptr,
383                    buf_ptr,
384                    buf_size,
385                    flags.bits(),
386                    Some(resize_cb_imstr),
387                    user_ptr,
388                )
389            }
390        };
391        // Ensure ImString logical length reflects actual text (scan to NUL)
392        unsafe { self.buf.refresh_len() };
393        result
394    }
395}
396impl<'ui, 'p> InputText<'ui, 'p, Cow<'ui, str>, Cow<'ui, str>, PassthroughCallback> {
397    /// Creates a new text input builder
398    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>, buf: &'p mut String) -> Self {
399        Self {
400            ui,
401            label: label.into(),
402            buf,
403            flags: InputTextFlags::NONE,
404            capacity_hint: None,
405            hint: None,
406            callback_handler: PassthroughCallback,
407            _phantom: PhantomData,
408        }
409    }
410}
411
412impl<'ui, 'p, L, H, T> InputText<'ui, 'p, L, H, T> {
413    /// Sets the flags for the input
414    pub fn flags(mut self, flags: InputTextFlags) -> Self {
415        self.flags = flags;
416        self
417    }
418
419    /// Hint a minimum buffer capacity to reduce reallocations for large fields
420    pub fn capacity_hint(mut self, cap: usize) -> Self {
421        self.capacity_hint = Some(cap);
422        self
423    }
424
425    /// Sets a hint text
426    pub fn hint(self, hint: impl Into<Cow<'ui, str>>) -> InputText<'ui, 'p, L, Cow<'ui, str>, T> {
427        InputText {
428            ui: self.ui,
429            label: self.label,
430            buf: self.buf,
431            flags: self.flags,
432            capacity_hint: self.capacity_hint,
433            hint: Some(hint.into()),
434            callback_handler: self.callback_handler,
435            _phantom: PhantomData,
436        }
437    }
438
439    /// Sets a callback handler for the input text
440    pub fn callback<T2: InputTextCallbackHandler>(
441        self,
442        callback_handler: T2,
443    ) -> InputText<'ui, 'p, L, H, T2> {
444        InputText {
445            ui: self.ui,
446            label: self.label,
447            buf: self.buf,
448            flags: self.flags,
449            capacity_hint: self.capacity_hint,
450            hint: self.hint,
451            callback_handler,
452            _phantom: PhantomData,
453        }
454    }
455
456    /// Sets callback flags for the input text
457    pub fn callback_flags(mut self, callback_flags: InputTextCallback) -> Self {
458        self.flags |= InputTextFlags::from_bits_truncate(callback_flags.bits() as i32);
459        self
460    }
461
462    /// Makes the input read-only
463    pub fn read_only(mut self, read_only: bool) -> Self {
464        self.flags.set(InputTextFlags::READ_ONLY, read_only);
465        self
466    }
467
468    /// Enables password mode
469    pub fn password(mut self, password: bool) -> Self {
470        self.flags.set(InputTextFlags::PASSWORD, password);
471        self
472    }
473
474    /// Enables auto-select all when first taking focus
475    pub fn auto_select_all(mut self, auto_select: bool) -> Self {
476        self.flags.set(InputTextFlags::AUTO_SELECT_ALL, auto_select);
477        self
478    }
479
480    /// Makes Enter key return true instead of adding new line
481    pub fn enter_returns_true(mut self, enter_returns: bool) -> Self {
482        self.flags
483            .set(InputTextFlags::ENTER_RETURNS_TRUE, enter_returns);
484        self
485    }
486
487    /// Allows only decimal characters (0123456789.+-*/)
488    pub fn chars_decimal(mut self, decimal: bool) -> Self {
489        self.flags.set(InputTextFlags::CHARS_DECIMAL, decimal);
490        self
491    }
492
493    /// Allows only hexadecimal characters (0123456789ABCDEFabcdef)
494    pub fn chars_hexadecimal(mut self, hex: bool) -> Self {
495        self.flags.set(InputTextFlags::CHARS_HEXADECIMAL, hex);
496        self
497    }
498
499    /// Turns a..z into A..Z
500    pub fn chars_uppercase(mut self, uppercase: bool) -> Self {
501        self.flags.set(InputTextFlags::CHARS_UPPERCASE, uppercase);
502        self
503    }
504
505    /// Filters out spaces and tabs
506    pub fn chars_no_blank(mut self, no_blank: bool) -> Self {
507        self.flags.set(InputTextFlags::CHARS_NO_BLANK, no_blank);
508        self
509    }
510}
511
512// Implementation for all InputText types
513impl<'ui, 'p, L, H, T> InputText<'ui, 'p, L, H, T>
514where
515    L: AsRef<str>,
516    H: AsRef<str>,
517    T: InputTextCallbackHandler,
518{
519    /// Builds the text input widget
520    pub fn build(self) -> bool {
521        let (label_ptr, hint_ptr) = self.ui.scratch_txt_with_opt(
522            self.label.as_ref(),
523            self.hint.as_ref().map(|hint| hint.as_ref()),
524        );
525
526        if let Some(extra) = self.capacity_hint {
527            let needed = extra.saturating_sub(self.buf.capacity().saturating_sub(self.buf.len()));
528            if needed > 0 {
529                self.buf.reserve(needed);
530            }
531        }
532
533        // Ensure temporary NUL terminator
534        self.buf.push('\0');
535        // Ensure any uninitialized bytes are set to NUL so trimming does not read UB.
536        zero_string_spare_capacity(self.buf);
537        let capacity = self.buf.capacity();
538        let buf_ptr = self.buf.as_mut_ptr() as *mut std::os::raw::c_char;
539
540        #[repr(C)]
541        struct UserData<T> {
542            container: *mut String,
543            handler: T,
544        }
545
546        extern "C" fn callback_router<T: InputTextCallbackHandler>(
547            data: *mut sys::ImGuiInputTextCallbackData,
548        ) -> c_int {
549            if data.is_null() {
550                return 0;
551            }
552
553            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
554                let user_ptr = unsafe { (*data).UserData as *mut UserData<T> };
555                if user_ptr.is_null() {
556                    return 0;
557                }
558                let user = unsafe { &mut *user_ptr };
559                if user.container.is_null() {
560                    return 0;
561                }
562
563                let event_flag = unsafe { InputTextFlags::from_bits_truncate((*data).EventFlag) };
564                match event_flag {
565                    InputTextFlags::CALLBACK_RESIZE => unsafe {
566                        let requested_i32 = (*data).BufSize;
567                        if requested_i32 < 0 {
568                            return 0;
569                        }
570                        let requested = requested_i32 as usize;
571                        let s = &mut *user.container;
572                        debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
573                        if requested > s.capacity() {
574                            let old_cap = s.capacity();
575                            let additional = requested.saturating_sub(s.len());
576                            s.reserve(additional);
577                            zero_string_new_capacity(s, old_cap);
578                            (*data).Buf = s.as_mut_ptr() as *mut _;
579                            (*data).BufDirty = true;
580                        }
581                        0
582                    },
583                    InputTextFlags::CALLBACK_COMPLETION => {
584                        let info = unsafe { TextCallbackData::new(data) };
585                        user.handler.on_completion(info);
586                        0
587                    }
588                    InputTextFlags::CALLBACK_HISTORY => {
589                        let key = unsafe { (*data).EventKey };
590                        let dir = if key == sys::ImGuiKey_UpArrow {
591                            HistoryDirection::Up
592                        } else {
593                            HistoryDirection::Down
594                        };
595                        let info = unsafe { TextCallbackData::new(data) };
596                        user.handler.on_history(dir, info);
597                        0
598                    }
599                    InputTextFlags::CALLBACK_ALWAYS => {
600                        let info = unsafe { TextCallbackData::new(data) };
601                        user.handler.on_always(info);
602                        0
603                    }
604                    InputTextFlags::CALLBACK_EDIT => {
605                        let info = unsafe { TextCallbackData::new(data) };
606                        user.handler.on_edit(info);
607                        0
608                    }
609                    InputTextFlags::CALLBACK_CHAR_FILTER => {
610                        let ch = unsafe {
611                            std::char::from_u32((*data).EventChar as u32).unwrap_or('\0')
612                        };
613                        let new_ch = user.handler.char_filter(ch).map(|c| c as u32).unwrap_or(0);
614                        unsafe {
615                            (*data).EventChar =
616                                sys::ImWchar::try_from(new_ch).unwrap_or(0 as sys::ImWchar);
617                        }
618                        0
619                    }
620                    _ => 0,
621                }
622            }));
623
624            match res {
625                Ok(v) => v,
626                Err(_) => {
627                    eprintln!("dear-imgui-rs: panic in InputText callback");
628                    std::process::abort();
629                }
630            }
631        }
632
633        let mut user_data = UserData {
634            container: self.buf as *mut String,
635            handler: self.callback_handler,
636        };
637        let user_ptr = &mut user_data as *mut _ as *mut c_void;
638
639        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
640        let result = unsafe {
641            if hint_ptr.is_null() {
642                sys::igInputText(
643                    label_ptr,
644                    buf_ptr,
645                    capacity,
646                    flags.bits(),
647                    Some(callback_router::<T>),
648                    user_ptr,
649                )
650            } else {
651                sys::igInputTextWithHint(
652                    label_ptr,
653                    hint_ptr,
654                    buf_ptr,
655                    capacity,
656                    flags.bits(),
657                    Some(callback_router::<T>),
658                    user_ptr,
659                )
660            }
661        };
662
663        // Trim to first NUL (remove pushed terminator)
664        let cap = unsafe { (&*user_data.container).capacity() };
665        let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
666        if let Some(len) = slice.iter().position(|&b| b == 0) {
667            unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
668        }
669        result
670    }
671}
672
673/// Builder for multiline text input widget
674#[derive(Debug)]
675#[must_use]
676pub struct InputTextMultiline<'ui, 'p> {
677    ui: &'ui Ui,
678    label: Cow<'ui, str>,
679    buf: &'p mut String,
680    size: [f32; 2],
681    flags: InputTextFlags,
682    capacity_hint: Option<usize>,
683}
684
685/// Builder for multiline text input backed by ImString (zero-copy)
686#[derive(Debug)]
687#[must_use]
688pub struct InputTextMultilineImStr<'ui, 'p> {
689    ui: &'ui Ui,
690    label: Cow<'ui, str>,
691    buf: &'p mut ImString,
692    size: [f32; 2],
693    flags: InputTextFlags,
694}
695
696impl<'ui, 'p> InputTextMultilineImStr<'ui, 'p> {
697    pub fn new(
698        ui: &'ui Ui,
699        label: impl Into<Cow<'ui, str>>,
700        buf: &'p mut ImString,
701        size: impl Into<[f32; 2]>,
702    ) -> Self {
703        Self {
704            ui,
705            label: label.into(),
706            buf,
707            size: size.into(),
708            flags: InputTextFlags::NONE,
709        }
710    }
711    pub fn flags(mut self, flags: InputTextFlags) -> Self {
712        self.flags = flags;
713        self
714    }
715    pub fn read_only(mut self, v: bool) -> Self {
716        self.flags.set(InputTextFlags::READ_ONLY, v);
717        self
718    }
719    pub fn build(self) -> bool {
720        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
721        let buf_size = self.buf.capacity_with_nul().max(1);
722        self.buf.ensure_buf_size(buf_size);
723        let buf_ptr = self.buf.as_mut_ptr();
724        let user_ptr = self.buf as *mut ImString as *mut c_void;
725        let size_vec: sys::ImVec2 = self.size.into();
726
727        extern "C" fn resize_cb_imstr(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
728            if data.is_null() {
729                return 0;
730            }
731            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
732                if (*data).EventFlag == (sys::ImGuiInputTextFlags_CallbackResize as i32) {
733                    let user_data = (*data).UserData as *mut ImString;
734                    if user_data.is_null() {
735                        return;
736                    }
737
738                    let im = &mut *user_data;
739                    let requested_i32 = (*data).BufSize;
740                    if requested_i32 < 0 {
741                        return;
742                    }
743                    let requested = requested_i32 as usize;
744                    im.ensure_buf_size(requested);
745                    (*data).Buf = im.as_mut_ptr();
746                    (*data).BufDirty = true;
747                }
748            }));
749            if res.is_err() {
750                eprintln!("dear-imgui-rs: panic in ImString multiline resize callback");
751                std::process::abort();
752            }
753            0
754        }
755
756        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
757        let result = unsafe {
758            sys::igInputTextMultiline(
759                label_ptr,
760                buf_ptr,
761                buf_size,
762                size_vec,
763                flags.bits(),
764                Some(resize_cb_imstr),
765                user_ptr,
766            )
767        };
768        // Ensure ImString logical length reflects actual text (scan to NUL)
769        unsafe { self.buf.refresh_len() };
770        result
771    }
772}
773impl<'ui, 'p> InputTextMultiline<'ui, 'p> {
774    /// Creates a new multiline text input builder
775    pub fn new(
776        ui: &'ui Ui,
777        label: impl Into<Cow<'ui, str>>,
778        buf: &'p mut String,
779        size: impl Into<[f32; 2]>,
780    ) -> Self {
781        Self {
782            ui,
783            label: label.into(),
784            buf,
785            size: size.into(),
786            flags: InputTextFlags::NONE,
787            capacity_hint: None,
788        }
789    }
790
791    /// Sets the flags for the input
792    pub fn flags(mut self, flags: InputTextFlags) -> Self {
793        self.flags = flags;
794        self
795    }
796
797    /// Hint a minimum buffer capacity to reduce reallocations for large fields
798    pub fn capacity_hint(mut self, cap: usize) -> Self {
799        self.capacity_hint = Some(cap);
800        self
801    }
802
803    /// Makes the input read-only
804    pub fn read_only(mut self, read_only: bool) -> Self {
805        self.flags.set(InputTextFlags::READ_ONLY, read_only);
806        self
807    }
808
809    /// Builds the multiline text input widget
810    pub fn build(self) -> bool {
811        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
812
813        // Optional pre-reserve
814        if let Some(extra) = self.capacity_hint {
815            let needed = extra.saturating_sub(self.buf.capacity().saturating_sub(self.buf.len()));
816            if needed > 0 {
817                self.buf.reserve(needed);
818            }
819        }
820
821        // Ensure a NUL terminator and use String's capacity directly
822        self.buf.push('\0');
823        // Ensure any uninitialized bytes are set to NUL so trimming does not read UB.
824        zero_string_spare_capacity(self.buf);
825        let capacity = self.buf.capacity();
826        let buf_ptr = self.buf.as_mut_ptr() as *mut std::os::raw::c_char;
827
828        #[repr(C)]
829        struct UserData {
830            container: *mut String,
831        }
832
833        extern "C" fn callback_router(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
834            if data.is_null() {
835                return 0;
836            }
837
838            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
839                let event_flag = InputTextFlags::from_bits_truncate((*data).EventFlag);
840                match event_flag {
841                    InputTextFlags::CALLBACK_RESIZE => {
842                        let user_ptr = (*data).UserData as *mut UserData;
843                        if user_ptr.is_null() {
844                            return 0;
845                        }
846                        let requested_i32 = (*data).BufSize;
847                        if requested_i32 < 0 {
848                            return 0;
849                        }
850                        let requested = requested_i32 as usize;
851
852                        let user = &mut *user_ptr;
853                        if user.container.is_null() {
854                            return 0;
855                        }
856                        let s = &mut *user.container;
857                        debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
858                        if requested > s.capacity() {
859                            let old_cap = s.capacity();
860                            let additional = requested.saturating_sub(s.len());
861                            s.reserve(additional);
862                            zero_string_new_capacity(s, old_cap);
863                            (*data).Buf = s.as_mut_ptr() as *mut _;
864                            (*data).BufDirty = true;
865                        }
866                        0
867                    }
868                    _ => 0,
869                }
870            }));
871
872            match res {
873                Ok(v) => v,
874                Err(_) => {
875                    eprintln!("dear-imgui-rs: panic in multiline InputText resize callback");
876                    std::process::abort();
877                }
878            }
879        }
880
881        let mut user_data = UserData {
882            container: self.buf as *mut String,
883        };
884        let user_ptr = &mut user_data as *mut _ as *mut c_void;
885
886        let size_vec: sys::ImVec2 = self.size.into();
887        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
888        let result = unsafe {
889            sys::igInputTextMultiline(
890                label_ptr,
891                buf_ptr,
892                capacity,
893                size_vec,
894                flags.bits(),
895                Some(callback_router),
896                user_ptr,
897            )
898        };
899
900        // Trim at NUL to restore real length
901        let cap = unsafe { (&*user_data.container).capacity() };
902        let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
903        if let Some(len) = slice.iter().position(|&b| b == 0) {
904            unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
905        }
906        result
907    }
908
909    /// Enable ImGui callbacks for this multiline input and attach a handler.
910    pub fn callback<T2: InputTextCallbackHandler>(
911        mut self,
912        callbacks: InputTextCallback,
913        handler: T2,
914    ) -> InputTextMultilineWithCb<'ui, 'p, T2> {
915        // Note: ImGui forbids CallbackHistory/Completion with Multiline.
916        // We intentionally do NOT enable them here to avoid assertions.
917        if callbacks.contains(InputTextCallback::ALWAYS) {
918            self.flags.insert(InputTextFlags::CALLBACK_ALWAYS);
919        }
920        if callbacks.contains(InputTextCallback::CHAR_FILTER) {
921            self.flags.insert(InputTextFlags::CALLBACK_CHAR_FILTER);
922        }
923        if callbacks.contains(InputTextCallback::EDIT) {
924            self.flags.insert(InputTextFlags::CALLBACK_EDIT);
925        }
926
927        InputTextMultilineWithCb {
928            ui: self.ui,
929            label: self.label,
930            buf: self.buf,
931            size: self.size,
932            flags: self.flags,
933            capacity_hint: self.capacity_hint,
934            handler,
935        }
936    }
937}
938
939/// Multiline InputText with attached callback handler
940pub struct InputTextMultilineWithCb<'ui, 'p, T> {
941    ui: &'ui Ui,
942    label: Cow<'ui, str>,
943    buf: &'p mut String,
944    size: [f32; 2],
945    flags: InputTextFlags,
946    capacity_hint: Option<usize>,
947    handler: T,
948}
949
950impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultilineWithCb<'ui, 'p, T> {
951    pub fn build(self) -> bool {
952        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
953
954        if let Some(extra) = self.capacity_hint {
955            let needed = extra.saturating_sub(self.buf.capacity().saturating_sub(self.buf.len()));
956            if needed > 0 {
957                self.buf.reserve(needed);
958            }
959        }
960
961        // Ensure NUL terminator
962        self.buf.push('\0');
963        // Ensure any uninitialized bytes are set to NUL so trimming does not read UB.
964        zero_string_spare_capacity(self.buf);
965        let capacity = self.buf.capacity();
966        let buf_ptr = self.buf.as_mut_ptr() as *mut std::os::raw::c_char;
967
968        #[repr(C)]
969        struct UserData<T> {
970            container: *mut String,
971            handler: T,
972        }
973
974        extern "C" fn callback_router<T: InputTextCallbackHandler>(
975            data: *mut sys::ImGuiInputTextCallbackData,
976        ) -> c_int {
977            if data.is_null() {
978                return 0;
979            }
980
981            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
982                let user_ptr = unsafe { (*data).UserData as *mut UserData<T> };
983                if user_ptr.is_null() {
984                    return 0;
985                }
986                let user = unsafe { &mut *user_ptr };
987                if user.container.is_null() {
988                    return 0;
989                }
990
991                let event_flag = unsafe { InputTextFlags::from_bits_truncate((*data).EventFlag) };
992                match event_flag {
993                    InputTextFlags::CALLBACK_RESIZE => unsafe {
994                        let requested_i32 = (*data).BufSize;
995                        if requested_i32 < 0 {
996                            return 0;
997                        }
998                        let requested = requested_i32 as usize;
999                        let s = &mut *user.container;
1000                        debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
1001                        if requested > s.capacity() {
1002                            let old_cap = s.capacity();
1003                            let additional = requested.saturating_sub(s.len());
1004                            s.reserve(additional);
1005                            zero_string_new_capacity(s, old_cap);
1006                            (*data).Buf = s.as_mut_ptr() as *mut _;
1007                            (*data).BufDirty = true;
1008                        }
1009                        0
1010                    },
1011                    InputTextFlags::CALLBACK_COMPLETION => {
1012                        let info = unsafe { TextCallbackData::new(data) };
1013                        user.handler.on_completion(info);
1014                        0
1015                    }
1016                    InputTextFlags::CALLBACK_HISTORY => {
1017                        let key = unsafe { (*data).EventKey };
1018                        let dir = if key == sys::ImGuiKey_UpArrow {
1019                            HistoryDirection::Up
1020                        } else {
1021                            HistoryDirection::Down
1022                        };
1023                        let info = unsafe { TextCallbackData::new(data) };
1024                        user.handler.on_history(dir, info);
1025                        0
1026                    }
1027                    InputTextFlags::CALLBACK_ALWAYS => {
1028                        let info = unsafe { TextCallbackData::new(data) };
1029                        user.handler.on_always(info);
1030                        0
1031                    }
1032                    InputTextFlags::CALLBACK_EDIT => {
1033                        let info = unsafe { TextCallbackData::new(data) };
1034                        user.handler.on_edit(info);
1035                        0
1036                    }
1037                    InputTextFlags::CALLBACK_CHAR_FILTER => {
1038                        let ch = unsafe {
1039                            std::char::from_u32((*data).EventChar as u32).unwrap_or('\0')
1040                        };
1041                        let new_ch = user.handler.char_filter(ch).map(|c| c as u32).unwrap_or(0);
1042                        unsafe {
1043                            (*data).EventChar =
1044                                sys::ImWchar::try_from(new_ch).unwrap_or(0 as sys::ImWchar);
1045                        }
1046                        0
1047                    }
1048                    _ => 0,
1049                }
1050            }));
1051
1052            match res {
1053                Ok(v) => v,
1054                Err(_) => {
1055                    eprintln!("dear-imgui-rs: panic in InputText multiline callback");
1056                    std::process::abort();
1057                }
1058            }
1059        }
1060
1061        let mut user_data = UserData {
1062            container: self.buf as *mut String,
1063            handler: self.handler,
1064        };
1065        let user_ptr = &mut user_data as *mut _ as *mut c_void;
1066
1067        let size_vec: sys::ImVec2 = self.size.into();
1068        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
1069        let result = unsafe {
1070            sys::igInputTextMultiline(
1071                label_ptr,
1072                buf_ptr,
1073                capacity,
1074                size_vec,
1075                flags.bits(),
1076                Some(callback_router::<T>),
1077                user_ptr,
1078            )
1079        };
1080
1081        // Trim at NUL
1082        let cap = unsafe { (&*user_data.container).capacity() };
1083        let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
1084        if let Some(len) = slice.iter().position(|&b| b == 0) {
1085            unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
1086        }
1087        result
1088    }
1089}
1090
1091/// Builder for integer input widget
1092#[derive(Debug)]
1093#[must_use]
1094pub struct InputInt<'ui> {
1095    ui: &'ui Ui,
1096    label: Cow<'ui, str>,
1097    step: i32,
1098    step_fast: i32,
1099    flags: InputTextFlags,
1100}
1101
1102impl<'ui> InputInt<'ui> {
1103    /// Creates a new integer input builder
1104    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>) -> Self {
1105        Self {
1106            ui,
1107            label: label.into(),
1108            step: 1,
1109            step_fast: 100,
1110            flags: InputTextFlags::NONE,
1111        }
1112    }
1113
1114    /// Sets the step value
1115    pub fn step(mut self, step: i32) -> Self {
1116        self.step = step;
1117        self
1118    }
1119
1120    /// Sets the fast step value
1121    pub fn step_fast(mut self, step_fast: i32) -> Self {
1122        self.step_fast = step_fast;
1123        self
1124    }
1125
1126    /// Sets the flags for the input
1127    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1128        self.flags = flags;
1129        self
1130    }
1131
1132    /// Builds the integer input widget
1133    pub fn build(self, value: &mut i32) -> bool {
1134        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
1135        unsafe {
1136            sys::igInputInt(
1137                label_ptr,
1138                value as *mut i32,
1139                self.step,
1140                self.step_fast,
1141                self.flags.bits(),
1142            )
1143        }
1144    }
1145}
1146
1147/// Builder for float input widget
1148#[derive(Debug)]
1149#[must_use]
1150pub struct InputFloat<'ui> {
1151    ui: &'ui Ui,
1152    label: Cow<'ui, str>,
1153    step: f32,
1154    step_fast: f32,
1155    format: Option<Cow<'ui, str>>,
1156    flags: InputTextFlags,
1157}
1158
1159impl<'ui> InputFloat<'ui> {
1160    /// Creates a new float input builder
1161    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>) -> Self {
1162        Self {
1163            ui,
1164            label: label.into(),
1165            step: 0.0,
1166            step_fast: 0.0,
1167            format: None,
1168            flags: InputTextFlags::NONE,
1169        }
1170    }
1171
1172    /// Sets the step value
1173    pub fn step(mut self, step: f32) -> Self {
1174        self.step = step;
1175        self
1176    }
1177
1178    /// Sets the fast step value
1179    pub fn step_fast(mut self, step_fast: f32) -> Self {
1180        self.step_fast = step_fast;
1181        self
1182    }
1183
1184    /// Sets the display format
1185    pub fn format(mut self, format: impl Into<Cow<'ui, str>>) -> Self {
1186        self.format = Some(format.into());
1187        self
1188    }
1189
1190    /// Sets the flags for the input
1191    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1192        self.flags = flags;
1193        self
1194    }
1195
1196    /// Builds the float input widget
1197    pub fn build(self, value: &mut f32) -> bool {
1198        let format = self.format.as_deref().unwrap_or("%.3f");
1199        let (label_ptr, format_ptr) = self.ui.scratch_txt_two(self.label.as_ref(), format);
1200
1201        unsafe {
1202            sys::igInputFloat(
1203                label_ptr,
1204                value as *mut f32,
1205                self.step,
1206                self.step_fast,
1207                format_ptr,
1208                self.flags.bits(),
1209            )
1210        }
1211    }
1212}
1213
1214/// Builder for double input widget
1215#[derive(Debug)]
1216#[must_use]
1217pub struct InputDouble<'ui> {
1218    ui: &'ui Ui,
1219    label: Cow<'ui, str>,
1220    step: f64,
1221    step_fast: f64,
1222    format: Option<Cow<'ui, str>>,
1223    flags: InputTextFlags,
1224}
1225
1226impl<'ui> InputDouble<'ui> {
1227    /// Creates a new double input builder
1228    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>) -> Self {
1229        Self {
1230            ui,
1231            label: label.into(),
1232            step: 0.0,
1233            step_fast: 0.0,
1234            format: None,
1235            flags: InputTextFlags::NONE,
1236        }
1237    }
1238
1239    /// Sets the step value
1240    pub fn step(mut self, step: f64) -> Self {
1241        self.step = step;
1242        self
1243    }
1244
1245    /// Sets the fast step value
1246    pub fn step_fast(mut self, step_fast: f64) -> Self {
1247        self.step_fast = step_fast;
1248        self
1249    }
1250
1251    /// Sets the display format
1252    pub fn format(mut self, format: impl Into<Cow<'ui, str>>) -> Self {
1253        self.format = Some(format.into());
1254        self
1255    }
1256
1257    /// Sets the flags for the input
1258    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1259        self.flags = flags;
1260        self
1261    }
1262
1263    /// Builds the double input widget
1264    pub fn build(self, value: &mut f64) -> bool {
1265        let format = self.format.as_deref().unwrap_or("%.6f");
1266        let (label_ptr, format_ptr) = self.ui.scratch_txt_two(self.label.as_ref(), format);
1267
1268        unsafe {
1269            sys::igInputDouble(
1270                label_ptr,
1271                value as *mut f64,
1272                self.step,
1273                self.step_fast,
1274                format_ptr,
1275                self.flags.bits(),
1276            )
1277        }
1278    }
1279}
1280
1281// InputText Callback System
1282// =========================
1283
1284bitflags::bitflags! {
1285    /// Callback flags for InputText widgets
1286    #[repr(transparent)]
1287    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1288    pub struct InputTextCallback: u32 {
1289        /// Call user function on pressing TAB (for completion handling)
1290        const COMPLETION = sys::ImGuiInputTextFlags_CallbackCompletion as u32;
1291        /// Call user function on pressing Up/Down arrows (for history handling)
1292        const HISTORY = sys::ImGuiInputTextFlags_CallbackHistory as u32;
1293        /// Call user function every time. User code may query cursor position, modify text buffer.
1294        const ALWAYS = sys::ImGuiInputTextFlags_CallbackAlways as u32;
1295        /// Call user function to filter character.
1296        const CHAR_FILTER = sys::ImGuiInputTextFlags_CallbackCharFilter as u32;
1297        /// Callback on buffer edit (note that InputText already returns true on edit, the
1298        /// callback is useful mainly to manipulate the underlying buffer while focus is active)
1299        const EDIT = sys::ImGuiInputTextFlags_CallbackEdit as u32;
1300    }
1301}
1302
1303/// Direction for history navigation
1304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1305pub enum HistoryDirection {
1306    /// Up arrow key pressed
1307    Up,
1308    /// Down arrow key pressed
1309    Down,
1310}
1311
1312/// This trait provides an interface which ImGui will call on InputText callbacks.
1313///
1314/// Each method is called *if and only if* the corresponding flag for each
1315/// method is passed to ImGui in the `callback` builder.
1316pub trait InputTextCallbackHandler {
1317    /// Filters a char -- returning a `None` means that the char is removed,
1318    /// and returning another char substitutes it out.
1319    ///
1320    /// To make ImGui run this callback, use [InputTextCallback::CHAR_FILTER].
1321    fn char_filter(&mut self, _c: char) -> Option<char> {
1322        None
1323    }
1324
1325    /// Called when the user presses the completion key (TAB by default).
1326    ///
1327    /// To make ImGui run this callback, use [InputTextCallback::COMPLETION].
1328    fn on_completion(&mut self, _data: TextCallbackData) {}
1329
1330    /// Called when the user presses Up/Down arrow keys for history navigation.
1331    ///
1332    /// To make ImGui run this callback, use [InputTextCallback::HISTORY].
1333    fn on_history(&mut self, _direction: HistoryDirection, _data: TextCallbackData) {}
1334
1335    /// Called every frame when the input text is active.
1336    ///
1337    /// To make ImGui run this callback, use [InputTextCallback::ALWAYS].
1338    fn on_always(&mut self, _data: TextCallbackData) {}
1339
1340    /// Called when the text buffer is edited.
1341    ///
1342    /// To make ImGui run this callback, use [InputTextCallback::EDIT].
1343    fn on_edit(&mut self, _data: TextCallbackData) {}
1344}
1345
1346/// This struct provides methods to edit the underlying text buffer that
1347/// Dear ImGui manipulates. Primarily, it gives [remove_chars](Self::remove_chars),
1348/// [insert_chars](Self::insert_chars), and mutable access to what text is selected.
1349pub struct TextCallbackData(*mut sys::ImGuiInputTextCallbackData);
1350
1351impl TextCallbackData {
1352    /// Creates the buffer.
1353    unsafe fn new(data: *mut sys::ImGuiInputTextCallbackData) -> Self {
1354        Self(data)
1355    }
1356
1357    /// Get a reference to the text callback buffer's str.
1358    pub fn str(&self) -> &str {
1359        unsafe {
1360            std::str::from_utf8(std::slice::from_raw_parts(
1361                (*(self.0)).Buf as *const _,
1362                (*(self.0)).BufTextLen as usize,
1363            ))
1364            .expect("internal imgui error -- it boofed a utf8")
1365        }
1366    }
1367
1368    /// Get the current cursor position
1369    pub fn cursor_pos(&self) -> usize {
1370        unsafe { (*(self.0)).CursorPos as usize }
1371    }
1372
1373    /// Set the cursor position
1374    pub fn set_cursor_pos(&mut self, pos: usize) {
1375        unsafe {
1376            (*(self.0)).CursorPos = pos as i32;
1377        }
1378    }
1379
1380    /// Get the selection start position
1381    pub fn selection_start(&self) -> usize {
1382        unsafe { (*(self.0)).SelectionStart as usize }
1383    }
1384
1385    /// Set the selection start position
1386    pub fn set_selection_start(&mut self, pos: usize) {
1387        unsafe {
1388            (*(self.0)).SelectionStart = pos as i32;
1389        }
1390    }
1391
1392    /// Get the selection end position
1393    pub fn selection_end(&self) -> usize {
1394        unsafe { (*(self.0)).SelectionEnd as usize }
1395    }
1396
1397    /// Set the selection end position
1398    pub fn set_selection_end(&mut self, pos: usize) {
1399        unsafe {
1400            (*(self.0)).SelectionEnd = pos as i32;
1401        }
1402    }
1403
1404    /// Select all text
1405    pub fn select_all(&mut self) {
1406        unsafe {
1407            (*(self.0)).SelectionStart = 0;
1408            (*(self.0)).SelectionEnd = (*(self.0)).BufTextLen;
1409        }
1410    }
1411
1412    /// Clear selection
1413    pub fn clear_selection(&mut self) {
1414        unsafe {
1415            (*(self.0)).SelectionStart = (*(self.0)).CursorPos;
1416            (*(self.0)).SelectionEnd = (*(self.0)).CursorPos;
1417        }
1418    }
1419
1420    /// Returns true if there is a selection
1421    pub fn has_selection(&self) -> bool {
1422        unsafe { (*(self.0)).SelectionStart != (*(self.0)).SelectionEnd }
1423    }
1424
1425    /// Delete characters in the range [pos, pos+bytes_count)
1426    pub fn remove_chars(&mut self, pos: usize, bytes_count: usize) {
1427        unsafe {
1428            sys::ImGuiInputTextCallbackData_DeleteChars(self.0, pos as i32, bytes_count as i32);
1429        }
1430    }
1431
1432    /// Insert text at the given position
1433    pub fn insert_chars(&mut self, pos: usize, text: &str) {
1434        let text_ptr = text.as_ptr() as *const std::os::raw::c_char;
1435        unsafe {
1436            sys::ImGuiInputTextCallbackData_InsertChars(
1437                self.0,
1438                pos as i32,
1439                text_ptr,
1440                text_ptr.add(text.len()),
1441            );
1442        }
1443    }
1444
1445    /// Gives access to the underlying byte array MUTABLY.
1446    ///
1447    /// ## Safety
1448    ///
1449    /// This is very unsafe, and the following invariants must be
1450    /// upheld:
1451    /// 1. Keep the data utf8 valid.
1452    /// 2. After editing the string, call [set_dirty].
1453    ///
1454    /// To truncate the string, please use [remove_chars]. To extend
1455    /// the string, please use [insert_chars] and [push_str].
1456    ///
1457    /// This function should have highly limited usage, but could be for
1458    /// editing certain characters in the buffer based on some external condition.
1459    ///
1460    /// [remove_chars]: Self::remove_chars
1461    /// [set_dirty]: Self::set_dirty
1462    /// [insert_chars]: Self::insert_chars
1463    /// [push_str]: Self::push_str
1464    pub unsafe fn str_as_bytes_mut(&mut self) -> &mut [u8] {
1465        unsafe {
1466            assert!(
1467                !(*(self.0)).Buf.is_null(),
1468                "internal imgui error: Buf was null"
1469            );
1470            assert!(
1471                (*(self.0)).BufTextLen >= 0,
1472                "internal imgui error: BufTextLen was negative"
1473            );
1474            assert!(
1475                (*(self.0)).BufSize >= 0,
1476                "internal imgui error: BufSize was negative"
1477            );
1478            assert!(
1479                (*(self.0)).BufTextLen <= (*(self.0)).BufSize,
1480                "internal imgui error: BufTextLen exceeded BufSize"
1481            );
1482
1483            let str = std::str::from_utf8_mut(std::slice::from_raw_parts_mut(
1484                (*(self.0)).Buf as *mut u8,
1485                (*(self.0)).BufTextLen as usize,
1486            ))
1487            .expect("internal imgui error -- it boofed a utf8");
1488
1489            str.as_bytes_mut()
1490        }
1491    }
1492
1493    /// Sets the dirty flag on the text to imgui, indicating that
1494    /// it should reapply this string to its internal state.
1495    ///
1496    /// **NB:** You only need to use this method if you're using `[str_as_bytes_mut]`.
1497    /// If you use the helper methods [remove_chars] and [insert_chars],
1498    /// this will be set for you. However, this is no downside to setting
1499    /// the dirty flag spuriously except the minor CPU time imgui will spend.
1500    ///
1501    /// [str_as_bytes_mut]: Self::str_as_bytes_mut
1502    /// [remove_chars]: Self::remove_chars
1503    /// [insert_chars]: Self::insert_chars
1504    pub fn set_dirty(&mut self) {
1505        unsafe {
1506            (*(self.0)).BufDirty = true;
1507        }
1508    }
1509
1510    /// Returns the selected text directly. Note that if no text is selected,
1511    /// an empty str slice will be returned.
1512    pub fn selected(&self) -> &str {
1513        let start = self.selection_start().min(self.selection_end());
1514        let end = self.selection_start().max(self.selection_end());
1515        &self.str()[start..end]
1516    }
1517
1518    /// Pushes the given str to the end of this buffer. If this
1519    /// would require the String to resize, it will be resized.
1520    /// This is automatically handled.
1521    pub fn push_str(&mut self, text: &str) {
1522        let current_len = unsafe { (*(self.0)).BufTextLen as usize };
1523        self.insert_chars(current_len, text);
1524    }
1525}
1526
1527/// This is a ZST which implements InputTextCallbackHandler as a passthrough.
1528///
1529/// If you do not set a callback handler, this will be used (but will never
1530/// actually run, since you will not have passed imgui any flags).
1531pub struct PassthroughCallback;
1532impl InputTextCallbackHandler for PassthroughCallback {}
1533
1534/// This is our default callback function that routes ImGui callbacks to our trait methods.
1535extern "C" fn callback(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
1536    if data.is_null() {
1537        return 0;
1538    }
1539
1540    let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
1541        let event_flag = InputTextFlags::from_bits_truncate((*data).EventFlag);
1542        let buffer_ptr = (*data).UserData as *mut String;
1543
1544        if buffer_ptr.is_null() {
1545            return;
1546        }
1547
1548        match event_flag {
1549            InputTextFlags::CALLBACK_RESIZE => {
1550                let requested_i32 = (*data).BufSize;
1551                if requested_i32 < 0 {
1552                    return;
1553                }
1554                let requested_size = requested_i32 as usize;
1555                let buffer = &mut *buffer_ptr;
1556
1557                debug_assert_eq!(buffer.as_ptr() as *const _, (*data).Buf);
1558
1559                if requested_size > buffer.capacity() {
1560                    let additional_bytes = requested_size.saturating_sub(buffer.len());
1561                    buffer.reserve(additional_bytes);
1562
1563                    (*data).Buf = buffer.as_mut_ptr() as *mut _;
1564                    (*data).BufDirty = true;
1565                }
1566            }
1567            _ => {}
1568        }
1569    }));
1570
1571    if res.is_err() {
1572        eprintln!("dear-imgui-rs: panic in legacy InputText callback");
1573        std::process::abort();
1574    }
1575    0
1576}
1577
1578/// Builder for an input scalar widget.
1579#[must_use]
1580pub struct InputScalar<'ui, 'p, T, L, F = &'static str> {
1581    value: &'p mut T,
1582    label: L,
1583    step: Option<T>,
1584    step_fast: Option<T>,
1585    display_format: Option<F>,
1586    flags: InputTextFlags,
1587    ui: &'ui Ui,
1588}
1589
1590impl<'ui, 'p, L: AsRef<str>, T: DataTypeKind> InputScalar<'ui, 'p, T, L> {
1591    /// Constructs a new input scalar builder.
1592    #[doc(alias = "InputScalar")]
1593    pub fn new(ui: &'ui Ui, label: L, value: &'p mut T) -> Self {
1594        InputScalar {
1595            value,
1596            label,
1597            step: None,
1598            step_fast: None,
1599            display_format: None,
1600            flags: InputTextFlags::empty(),
1601            ui,
1602        }
1603    }
1604}
1605
1606impl<'ui, 'p, L: AsRef<str>, T: DataTypeKind, F: AsRef<str>> InputScalar<'ui, 'p, T, L, F> {
1607    /// Sets the display format using *a C-style printf string*
1608    pub fn display_format<F2: AsRef<str>>(
1609        self,
1610        display_format: F2,
1611    ) -> InputScalar<'ui, 'p, T, L, F2> {
1612        InputScalar {
1613            value: self.value,
1614            label: self.label,
1615            step: self.step,
1616            step_fast: self.step_fast,
1617            display_format: Some(display_format),
1618            flags: self.flags,
1619            ui: self.ui,
1620        }
1621    }
1622
1623    /// Sets the step value for the input
1624    #[inline]
1625    pub fn step(mut self, value: T) -> Self {
1626        self.step = Some(value);
1627        self
1628    }
1629
1630    /// Sets the fast step value for the input
1631    #[inline]
1632    pub fn step_fast(mut self, value: T) -> Self {
1633        self.step_fast = Some(value);
1634        self
1635    }
1636
1637    /// Sets the input text flags
1638    #[inline]
1639    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1640        self.flags = flags;
1641        self
1642    }
1643
1644    /// Builds an input scalar that is bound to the given value.
1645    ///
1646    /// Returns true if the value was changed.
1647    pub fn build(self) -> bool {
1648        unsafe {
1649            let (one, two) = self
1650                .ui
1651                .scratch_txt_with_opt(self.label, self.display_format);
1652
1653            sys::igInputScalar(
1654                one,
1655                T::KIND as i32,
1656                self.value as *mut T as *mut c_void,
1657                self.step
1658                    .as_ref()
1659                    .map(|step| step as *const T)
1660                    .unwrap_or(ptr::null()) as *const c_void,
1661                self.step_fast
1662                    .as_ref()
1663                    .map(|step| step as *const T)
1664                    .unwrap_or(ptr::null()) as *const c_void,
1665                two,
1666                self.flags.bits() as i32,
1667            )
1668        }
1669    }
1670}
1671
1672/// Builder for an input scalar array widget.
1673#[must_use]
1674pub struct InputScalarN<'ui, 'p, T, L, F = &'static str> {
1675    values: &'p mut [T],
1676    label: L,
1677    step: Option<T>,
1678    step_fast: Option<T>,
1679    display_format: Option<F>,
1680    flags: InputTextFlags,
1681    ui: &'ui Ui,
1682}
1683
1684impl<'ui, 'p, L: AsRef<str>, T: DataTypeKind> InputScalarN<'ui, 'p, T, L> {
1685    /// Constructs a new input scalar array builder.
1686    #[doc(alias = "InputScalarN")]
1687    pub fn new(ui: &'ui Ui, label: L, values: &'p mut [T]) -> Self {
1688        InputScalarN {
1689            values,
1690            label,
1691            step: None,
1692            step_fast: None,
1693            display_format: None,
1694            flags: InputTextFlags::empty(),
1695            ui,
1696        }
1697    }
1698}
1699
1700impl<'ui, 'p, L: AsRef<str>, T: DataTypeKind, F: AsRef<str>> InputScalarN<'ui, 'p, T, L, F> {
1701    /// Sets the display format using *a C-style printf string*
1702    pub fn display_format<F2: AsRef<str>>(
1703        self,
1704        display_format: F2,
1705    ) -> InputScalarN<'ui, 'p, T, L, F2> {
1706        InputScalarN {
1707            values: self.values,
1708            label: self.label,
1709            step: self.step,
1710            step_fast: self.step_fast,
1711            display_format: Some(display_format),
1712            flags: self.flags,
1713            ui: self.ui,
1714        }
1715    }
1716
1717    /// Sets the step value for the input
1718    #[inline]
1719    pub fn step(mut self, value: T) -> Self {
1720        self.step = Some(value);
1721        self
1722    }
1723
1724    /// Sets the fast step value for the input
1725    #[inline]
1726    pub fn step_fast(mut self, value: T) -> Self {
1727        self.step_fast = Some(value);
1728        self
1729    }
1730
1731    /// Sets the input text flags
1732    #[inline]
1733    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1734        self.flags = flags;
1735        self
1736    }
1737
1738    /// Builds a horizontal array of multiple input scalars attached to the given slice.
1739    ///
1740    /// Returns true if any value was changed.
1741    pub fn build(self) -> bool {
1742        let count = match i32::try_from(self.values.len()) {
1743            Ok(n) => n,
1744            Err(_) => return false,
1745        };
1746        unsafe {
1747            let (one, two) = self
1748                .ui
1749                .scratch_txt_with_opt(self.label, self.display_format);
1750
1751            sys::igInputScalarN(
1752                one,
1753                T::KIND as i32,
1754                self.values.as_mut_ptr() as *mut c_void,
1755                count,
1756                self.step
1757                    .as_ref()
1758                    .map(|step| step as *const T)
1759                    .unwrap_or(ptr::null()) as *const c_void,
1760                self.step_fast
1761                    .as_ref()
1762                    .map(|step| step as *const T)
1763                    .unwrap_or(ptr::null()) as *const c_void,
1764                two,
1765                self.flags.bits() as i32,
1766            )
1767        }
1768    }
1769}
1770
1771/// Builder for a 2-component float input widget.
1772#[must_use]
1773pub struct InputFloat2<'ui, 'p, L, F = &'static str> {
1774    label: L,
1775    value: &'p mut [f32; 2],
1776    display_format: Option<F>,
1777    flags: InputTextFlags,
1778    ui: &'ui Ui,
1779}
1780
1781impl<'ui, 'p, L: AsRef<str>> InputFloat2<'ui, 'p, L> {
1782    /// Constructs a new input float2 builder.
1783    #[doc(alias = "InputFloat2")]
1784    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [f32; 2]) -> Self {
1785        InputFloat2 {
1786            label,
1787            value,
1788            display_format: None,
1789            flags: InputTextFlags::empty(),
1790            ui,
1791        }
1792    }
1793}
1794
1795impl<'ui, 'p, L: AsRef<str>, F: AsRef<str>> InputFloat2<'ui, 'p, L, F> {
1796    /// Sets the display format using *a C-style printf string*
1797    pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> InputFloat2<'ui, 'p, L, F2> {
1798        InputFloat2 {
1799            label: self.label,
1800            value: self.value,
1801            display_format: Some(display_format),
1802            flags: self.flags,
1803            ui: self.ui,
1804        }
1805    }
1806
1807    /// Sets the input text flags
1808    #[inline]
1809    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1810        self.flags = flags;
1811        self
1812    }
1813
1814    /// Builds the input float2 widget.
1815    ///
1816    /// Returns true if any value was changed.
1817    pub fn build(self) -> bool {
1818        unsafe {
1819            let (one, two) = self
1820                .ui
1821                .scratch_txt_with_opt(self.label, self.display_format);
1822
1823            sys::igInputFloat2(one, self.value.as_mut_ptr(), two, self.flags.bits() as i32)
1824        }
1825    }
1826}
1827
1828/// Builder for a 3-component float input widget.
1829#[must_use]
1830pub struct InputFloat3<'ui, 'p, L, F = &'static str> {
1831    label: L,
1832    value: &'p mut [f32; 3],
1833    display_format: Option<F>,
1834    flags: InputTextFlags,
1835    ui: &'ui Ui,
1836}
1837
1838impl<'ui, 'p, L: AsRef<str>> InputFloat3<'ui, 'p, L> {
1839    /// Constructs a new input float3 builder.
1840    #[doc(alias = "InputFloat3")]
1841    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [f32; 3]) -> Self {
1842        InputFloat3 {
1843            label,
1844            value,
1845            display_format: None,
1846            flags: InputTextFlags::empty(),
1847            ui,
1848        }
1849    }
1850}
1851
1852impl<'ui, 'p, L: AsRef<str>, F: AsRef<str>> InputFloat3<'ui, 'p, L, F> {
1853    /// Sets the display format using *a C-style printf string*
1854    pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> InputFloat3<'ui, 'p, L, F2> {
1855        InputFloat3 {
1856            label: self.label,
1857            value: self.value,
1858            display_format: Some(display_format),
1859            flags: self.flags,
1860            ui: self.ui,
1861        }
1862    }
1863
1864    /// Sets the input text flags
1865    #[inline]
1866    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1867        self.flags = flags;
1868        self
1869    }
1870
1871    /// Builds the input float3 widget.
1872    ///
1873    /// Returns true if any value was changed.
1874    pub fn build(self) -> bool {
1875        unsafe {
1876            let (one, two) = self
1877                .ui
1878                .scratch_txt_with_opt(self.label, self.display_format);
1879
1880            sys::igInputFloat3(one, self.value.as_mut_ptr(), two, self.flags.bits() as i32)
1881        }
1882    }
1883}
1884
1885/// Builder for a 4-component float input widget.
1886#[must_use]
1887pub struct InputFloat4<'ui, 'p, L, F = &'static str> {
1888    label: L,
1889    value: &'p mut [f32; 4],
1890    display_format: Option<F>,
1891    flags: InputTextFlags,
1892    ui: &'ui Ui,
1893}
1894
1895impl<'ui, 'p, L: AsRef<str>> InputFloat4<'ui, 'p, L> {
1896    /// Constructs a new input float4 builder.
1897    #[doc(alias = "InputFloat4")]
1898    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [f32; 4]) -> Self {
1899        InputFloat4 {
1900            label,
1901            value,
1902            display_format: None,
1903            flags: InputTextFlags::empty(),
1904            ui,
1905        }
1906    }
1907}
1908
1909impl<'ui, 'p, L: AsRef<str>, F: AsRef<str>> InputFloat4<'ui, 'p, L, F> {
1910    /// Sets the display format using *a C-style printf string*
1911    pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> InputFloat4<'ui, 'p, L, F2> {
1912        InputFloat4 {
1913            label: self.label,
1914            value: self.value,
1915            display_format: Some(display_format),
1916            flags: self.flags,
1917            ui: self.ui,
1918        }
1919    }
1920
1921    /// Sets the input text flags
1922    #[inline]
1923    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1924        self.flags = flags;
1925        self
1926    }
1927
1928    /// Builds the input float4 widget.
1929    ///
1930    /// Returns true if any value was changed.
1931    pub fn build(self) -> bool {
1932        unsafe {
1933            let (one, two) = self
1934                .ui
1935                .scratch_txt_with_opt(self.label, self.display_format);
1936
1937            sys::igInputFloat4(one, self.value.as_mut_ptr(), two, self.flags.bits() as i32)
1938        }
1939    }
1940}
1941
1942/// Builder for a 2-component int input widget.
1943#[must_use]
1944pub struct InputInt2<'ui, 'p, L> {
1945    label: L,
1946    value: &'p mut [i32; 2],
1947    flags: InputTextFlags,
1948    ui: &'ui Ui,
1949}
1950
1951impl<'ui, 'p, L: AsRef<str>> InputInt2<'ui, 'p, L> {
1952    /// Constructs a new input int2 builder.
1953    #[doc(alias = "InputInt2")]
1954    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [i32; 2]) -> Self {
1955        InputInt2 {
1956            label,
1957            value,
1958            flags: InputTextFlags::empty(),
1959            ui,
1960        }
1961    }
1962
1963    /// Sets the input text flags
1964    #[inline]
1965    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1966        self.flags = flags;
1967        self
1968    }
1969
1970    /// Builds the input int2 widget.
1971    ///
1972    /// Returns true if any value was changed.
1973    pub fn build(self) -> bool {
1974        unsafe {
1975            let label_cstr = self.ui.scratch_txt(self.label);
1976
1977            sys::igInputInt2(
1978                label_cstr,
1979                self.value.as_mut_ptr(),
1980                self.flags.bits() as i32,
1981            )
1982        }
1983    }
1984}
1985
1986/// Builder for a 3-component int input widget.
1987#[must_use]
1988pub struct InputInt3<'ui, 'p, L> {
1989    label: L,
1990    value: &'p mut [i32; 3],
1991    flags: InputTextFlags,
1992    ui: &'ui Ui,
1993}
1994
1995impl<'ui, 'p, L: AsRef<str>> InputInt3<'ui, 'p, L> {
1996    /// Constructs a new input int3 builder.
1997    #[doc(alias = "InputInt3")]
1998    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [i32; 3]) -> Self {
1999        InputInt3 {
2000            label,
2001            value,
2002            flags: InputTextFlags::empty(),
2003            ui,
2004        }
2005    }
2006
2007    /// Sets the input text flags
2008    #[inline]
2009    pub fn flags(mut self, flags: InputTextFlags) -> Self {
2010        self.flags = flags;
2011        self
2012    }
2013
2014    /// Builds the input int3 widget.
2015    ///
2016    /// Returns true if any value was changed.
2017    pub fn build(self) -> bool {
2018        unsafe {
2019            let label_cstr = self.ui.scratch_txt(self.label);
2020
2021            sys::igInputInt3(
2022                label_cstr,
2023                self.value.as_mut_ptr(),
2024                self.flags.bits() as i32,
2025            )
2026        }
2027    }
2028}
2029
2030/// Builder for a 4-component int input widget.
2031#[must_use]
2032pub struct InputInt4<'ui, 'p, L> {
2033    label: L,
2034    value: &'p mut [i32; 4],
2035    flags: InputTextFlags,
2036    ui: &'ui Ui,
2037}
2038
2039impl<'ui, 'p, L: AsRef<str>> InputInt4<'ui, 'p, L> {
2040    /// Constructs a new input int4 builder.
2041    #[doc(alias = "InputInt4")]
2042    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [i32; 4]) -> Self {
2043        InputInt4 {
2044            label,
2045            value,
2046            flags: InputTextFlags::empty(),
2047            ui,
2048        }
2049    }
2050
2051    /// Sets the input text flags
2052    #[inline]
2053    pub fn flags(mut self, flags: InputTextFlags) -> Self {
2054        self.flags = flags;
2055        self
2056    }
2057
2058    /// Builds the input int4 widget.
2059    ///
2060    /// Returns true if any value was changed.
2061    pub fn build(self) -> bool {
2062        unsafe {
2063            let label_cstr = self.ui.scratch_txt(self.label);
2064
2065            sys::igInputInt4(
2066                label_cstr,
2067                self.value.as_mut_ptr(),
2068                self.flags.bits() as i32,
2069            )
2070        }
2071    }
2072}
2073
2074#[cfg(test)]
2075mod tests {
2076    use super::*;
2077
2078    #[test]
2079    fn zero_string_spare_capacity_writes_nul_bytes() {
2080        let mut s = String::with_capacity(16);
2081        s.push_str("abc");
2082        let len = s.len();
2083        let cap = s.capacity();
2084
2085        zero_string_spare_capacity(&mut s);
2086
2087        unsafe {
2088            let bytes = std::slice::from_raw_parts(s.as_ptr(), cap);
2089            assert_eq!(&bytes[..len], b"abc");
2090            assert!(bytes[len..].iter().all(|&b| b == 0));
2091        }
2092    }
2093
2094    #[test]
2095    fn zero_string_new_capacity_writes_new_region() {
2096        let mut s = String::with_capacity(4);
2097        s.push_str("abc");
2098        let old_cap = s.capacity();
2099
2100        s.reserve(64);
2101        let new_cap = s.capacity();
2102        assert!(new_cap > old_cap);
2103
2104        zero_string_new_capacity(&mut s, old_cap);
2105
2106        unsafe {
2107            let tail = std::slice::from_raw_parts(s.as_ptr().add(old_cap), new_cap - old_cap);
2108            assert!(tail.iter().all(|&b| b == 0));
2109        }
2110    }
2111}