Skip to main content

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 as i32) == (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.raw(),
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.raw(),
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 =
564                    unsafe { InputTextFlags::from_bits_truncate((*data).EventFlag as i32) };
565                match event_flag {
566                    InputTextFlags::CALLBACK_RESIZE => unsafe {
567                        let requested_i32 = (*data).BufSize;
568                        if requested_i32 < 0 {
569                            return 0;
570                        }
571                        let requested = requested_i32 as usize;
572                        let s = &mut *user.container;
573                        debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
574                        if requested > s.capacity() {
575                            let old_cap = s.capacity();
576                            let additional = requested.saturating_sub(s.len());
577                            s.reserve(additional);
578                            zero_string_new_capacity(s, old_cap);
579                            (*data).Buf = s.as_mut_ptr() as *mut _;
580                            (*data).BufDirty = true;
581                        }
582                        0
583                    },
584                    InputTextFlags::CALLBACK_COMPLETION => {
585                        let info = unsafe { TextCallbackData::new(data) };
586                        user.handler.on_completion(info);
587                        0
588                    }
589                    InputTextFlags::CALLBACK_HISTORY => {
590                        let key = unsafe { (*data).EventKey };
591                        let dir = if key == sys::ImGuiKey_UpArrow {
592                            HistoryDirection::Up
593                        } else {
594                            HistoryDirection::Down
595                        };
596                        let info = unsafe { TextCallbackData::new(data) };
597                        user.handler.on_history(dir, info);
598                        0
599                    }
600                    InputTextFlags::CALLBACK_ALWAYS => {
601                        let info = unsafe { TextCallbackData::new(data) };
602                        user.handler.on_always(info);
603                        0
604                    }
605                    InputTextFlags::CALLBACK_EDIT => {
606                        let info = unsafe { TextCallbackData::new(data) };
607                        user.handler.on_edit(info);
608                        0
609                    }
610                    InputTextFlags::CALLBACK_CHAR_FILTER => {
611                        let ch = unsafe {
612                            std::char::from_u32((*data).EventChar as u32).unwrap_or('\0')
613                        };
614                        let new_ch = user.handler.char_filter(ch).map(|c| c as u32).unwrap_or(0);
615                        unsafe {
616                            (*data).EventChar =
617                                sys::ImWchar::try_from(new_ch).unwrap_or(0 as sys::ImWchar);
618                        }
619                        0
620                    }
621                    _ => 0,
622                }
623            }));
624
625            match res {
626                Ok(v) => v,
627                Err(_) => {
628                    eprintln!("dear-imgui-rs: panic in InputText callback");
629                    std::process::abort();
630                }
631            }
632        }
633
634        let mut user_data = UserData {
635            container: self.buf as *mut String,
636            handler: self.callback_handler,
637        };
638        let user_ptr = &mut user_data as *mut _ as *mut c_void;
639
640        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
641        let result = unsafe {
642            if hint_ptr.is_null() {
643                sys::igInputText(
644                    label_ptr,
645                    buf_ptr,
646                    capacity,
647                    flags.raw(),
648                    Some(callback_router::<T>),
649                    user_ptr,
650                )
651            } else {
652                sys::igInputTextWithHint(
653                    label_ptr,
654                    hint_ptr,
655                    buf_ptr,
656                    capacity,
657                    flags.raw(),
658                    Some(callback_router::<T>),
659                    user_ptr,
660                )
661            }
662        };
663
664        // Trim to first NUL (remove pushed terminator)
665        let cap = unsafe { (&*user_data.container).capacity() };
666        let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
667        if let Some(len) = slice.iter().position(|&b| b == 0) {
668            unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
669        }
670        result
671    }
672}
673
674/// Builder for multiline text input widget
675#[derive(Debug)]
676#[must_use]
677pub struct InputTextMultiline<'ui, 'p> {
678    ui: &'ui Ui,
679    label: Cow<'ui, str>,
680    buf: &'p mut String,
681    size: [f32; 2],
682    flags: InputTextFlags,
683    capacity_hint: Option<usize>,
684}
685
686/// Builder for multiline text input backed by ImString (zero-copy)
687#[derive(Debug)]
688#[must_use]
689pub struct InputTextMultilineImStr<'ui, 'p> {
690    ui: &'ui Ui,
691    label: Cow<'ui, str>,
692    buf: &'p mut ImString,
693    size: [f32; 2],
694    flags: InputTextFlags,
695}
696
697impl<'ui, 'p> InputTextMultilineImStr<'ui, 'p> {
698    pub fn new(
699        ui: &'ui Ui,
700        label: impl Into<Cow<'ui, str>>,
701        buf: &'p mut ImString,
702        size: impl Into<[f32; 2]>,
703    ) -> Self {
704        Self {
705            ui,
706            label: label.into(),
707            buf,
708            size: size.into(),
709            flags: InputTextFlags::NONE,
710        }
711    }
712    pub fn flags(mut self, flags: InputTextFlags) -> Self {
713        self.flags = flags;
714        self
715    }
716    pub fn read_only(mut self, v: bool) -> Self {
717        self.flags.set(InputTextFlags::READ_ONLY, v);
718        self
719    }
720    pub fn build(self) -> bool {
721        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
722        let buf_size = self.buf.capacity_with_nul().max(1);
723        self.buf.ensure_buf_size(buf_size);
724        let buf_ptr = self.buf.as_mut_ptr();
725        let user_ptr = self.buf as *mut ImString as *mut c_void;
726        let size_vec: sys::ImVec2 = self.size.into();
727
728        extern "C" fn resize_cb_imstr(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
729            if data.is_null() {
730                return 0;
731            }
732            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
733                if ((*data).EventFlag as i32) == (sys::ImGuiInputTextFlags_CallbackResize as i32) {
734                    let user_data = (*data).UserData as *mut ImString;
735                    if user_data.is_null() {
736                        return;
737                    }
738
739                    let im = &mut *user_data;
740                    let requested_i32 = (*data).BufSize;
741                    if requested_i32 < 0 {
742                        return;
743                    }
744                    let requested = requested_i32 as usize;
745                    im.ensure_buf_size(requested);
746                    (*data).Buf = im.as_mut_ptr();
747                    (*data).BufDirty = true;
748                }
749            }));
750            if res.is_err() {
751                eprintln!("dear-imgui-rs: panic in ImString multiline resize callback");
752                std::process::abort();
753            }
754            0
755        }
756
757        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
758        let result = unsafe {
759            sys::igInputTextMultiline(
760                label_ptr,
761                buf_ptr,
762                buf_size,
763                size_vec,
764                flags.raw(),
765                Some(resize_cb_imstr),
766                user_ptr,
767            )
768        };
769        // Ensure ImString logical length reflects actual text (scan to NUL)
770        unsafe { self.buf.refresh_len() };
771        result
772    }
773}
774impl<'ui, 'p> InputTextMultiline<'ui, 'p> {
775    /// Creates a new multiline text input builder
776    pub fn new(
777        ui: &'ui Ui,
778        label: impl Into<Cow<'ui, str>>,
779        buf: &'p mut String,
780        size: impl Into<[f32; 2]>,
781    ) -> Self {
782        Self {
783            ui,
784            label: label.into(),
785            buf,
786            size: size.into(),
787            flags: InputTextFlags::NONE,
788            capacity_hint: None,
789        }
790    }
791
792    /// Sets the flags for the input
793    pub fn flags(mut self, flags: InputTextFlags) -> Self {
794        self.flags = flags;
795        self
796    }
797
798    /// Hint a minimum buffer capacity to reduce reallocations for large fields
799    pub fn capacity_hint(mut self, cap: usize) -> Self {
800        self.capacity_hint = Some(cap);
801        self
802    }
803
804    /// Makes the input read-only
805    pub fn read_only(mut self, read_only: bool) -> Self {
806        self.flags.set(InputTextFlags::READ_ONLY, read_only);
807        self
808    }
809
810    /// Builds the multiline text input widget
811    pub fn build(self) -> bool {
812        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
813
814        // Optional pre-reserve
815        if let Some(extra) = self.capacity_hint {
816            let needed = extra.saturating_sub(self.buf.capacity().saturating_sub(self.buf.len()));
817            if needed > 0 {
818                self.buf.reserve(needed);
819            }
820        }
821
822        // Ensure a NUL terminator and use String's capacity directly
823        self.buf.push('\0');
824        // Ensure any uninitialized bytes are set to NUL so trimming does not read UB.
825        zero_string_spare_capacity(self.buf);
826        let capacity = self.buf.capacity();
827        let buf_ptr = self.buf.as_mut_ptr() as *mut std::os::raw::c_char;
828
829        #[repr(C)]
830        struct UserData {
831            container: *mut String,
832        }
833
834        extern "C" fn callback_router(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
835            if data.is_null() {
836                return 0;
837            }
838
839            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
840                let event_flag = InputTextFlags::from_bits_truncate((*data).EventFlag as i32);
841                match event_flag {
842                    InputTextFlags::CALLBACK_RESIZE => {
843                        let user_ptr = (*data).UserData as *mut UserData;
844                        if user_ptr.is_null() {
845                            return 0;
846                        }
847                        let requested_i32 = (*data).BufSize;
848                        if requested_i32 < 0 {
849                            return 0;
850                        }
851                        let requested = requested_i32 as usize;
852
853                        let user = &mut *user_ptr;
854                        if user.container.is_null() {
855                            return 0;
856                        }
857                        let s = &mut *user.container;
858                        debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
859                        if requested > s.capacity() {
860                            let old_cap = s.capacity();
861                            let additional = requested.saturating_sub(s.len());
862                            s.reserve(additional);
863                            zero_string_new_capacity(s, old_cap);
864                            (*data).Buf = s.as_mut_ptr() as *mut _;
865                            (*data).BufDirty = true;
866                        }
867                        0
868                    }
869                    _ => 0,
870                }
871            }));
872
873            match res {
874                Ok(v) => v,
875                Err(_) => {
876                    eprintln!("dear-imgui-rs: panic in multiline InputText resize callback");
877                    std::process::abort();
878                }
879            }
880        }
881
882        let mut user_data = UserData {
883            container: self.buf as *mut String,
884        };
885        let user_ptr = &mut user_data as *mut _ as *mut c_void;
886
887        let size_vec: sys::ImVec2 = self.size.into();
888        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
889        let result = unsafe {
890            sys::igInputTextMultiline(
891                label_ptr,
892                buf_ptr,
893                capacity,
894                size_vec,
895                flags.raw(),
896                Some(callback_router),
897                user_ptr,
898            )
899        };
900
901        // Trim at NUL to restore real length
902        let cap = unsafe { (&*user_data.container).capacity() };
903        let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
904        if let Some(len) = slice.iter().position(|&b| b == 0) {
905            unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
906        }
907        result
908    }
909
910    /// Enable ImGui callbacks for this multiline input and attach a handler.
911    pub fn callback<T2: InputTextCallbackHandler>(
912        mut self,
913        callbacks: InputTextCallback,
914        handler: T2,
915    ) -> InputTextMultilineWithCb<'ui, 'p, T2> {
916        // Note: ImGui forbids CallbackHistory/Completion with Multiline.
917        // We intentionally do NOT enable them here to avoid assertions.
918        if callbacks.contains(InputTextCallback::ALWAYS) {
919            self.flags.insert(InputTextFlags::CALLBACK_ALWAYS);
920        }
921        if callbacks.contains(InputTextCallback::CHAR_FILTER) {
922            self.flags.insert(InputTextFlags::CALLBACK_CHAR_FILTER);
923        }
924        if callbacks.contains(InputTextCallback::EDIT) {
925            self.flags.insert(InputTextFlags::CALLBACK_EDIT);
926        }
927
928        InputTextMultilineWithCb {
929            ui: self.ui,
930            label: self.label,
931            buf: self.buf,
932            size: self.size,
933            flags: self.flags,
934            capacity_hint: self.capacity_hint,
935            handler,
936        }
937    }
938}
939
940/// Multiline InputText with attached callback handler
941pub struct InputTextMultilineWithCb<'ui, 'p, T> {
942    ui: &'ui Ui,
943    label: Cow<'ui, str>,
944    buf: &'p mut String,
945    size: [f32; 2],
946    flags: InputTextFlags,
947    capacity_hint: Option<usize>,
948    handler: T,
949}
950
951impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultilineWithCb<'ui, 'p, T> {
952    pub fn build(self) -> bool {
953        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
954
955        if let Some(extra) = self.capacity_hint {
956            let needed = extra.saturating_sub(self.buf.capacity().saturating_sub(self.buf.len()));
957            if needed > 0 {
958                self.buf.reserve(needed);
959            }
960        }
961
962        // Ensure NUL terminator
963        self.buf.push('\0');
964        // Ensure any uninitialized bytes are set to NUL so trimming does not read UB.
965        zero_string_spare_capacity(self.buf);
966        let capacity = self.buf.capacity();
967        let buf_ptr = self.buf.as_mut_ptr() as *mut std::os::raw::c_char;
968
969        #[repr(C)]
970        struct UserData<T> {
971            container: *mut String,
972            handler: T,
973        }
974
975        extern "C" fn callback_router<T: InputTextCallbackHandler>(
976            data: *mut sys::ImGuiInputTextCallbackData,
977        ) -> c_int {
978            if data.is_null() {
979                return 0;
980            }
981
982            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
983                let user_ptr = unsafe { (*data).UserData as *mut UserData<T> };
984                if user_ptr.is_null() {
985                    return 0;
986                }
987                let user = unsafe { &mut *user_ptr };
988                if user.container.is_null() {
989                    return 0;
990                }
991
992                let event_flag =
993                    unsafe { InputTextFlags::from_bits_truncate((*data).EventFlag as i32) };
994                match event_flag {
995                    InputTextFlags::CALLBACK_RESIZE => unsafe {
996                        let requested_i32 = (*data).BufSize;
997                        if requested_i32 < 0 {
998                            return 0;
999                        }
1000                        let requested = requested_i32 as usize;
1001                        let s = &mut *user.container;
1002                        debug_assert_eq!(s.as_ptr() as *const _, (*data).Buf);
1003                        if requested > s.capacity() {
1004                            let old_cap = s.capacity();
1005                            let additional = requested.saturating_sub(s.len());
1006                            s.reserve(additional);
1007                            zero_string_new_capacity(s, old_cap);
1008                            (*data).Buf = s.as_mut_ptr() as *mut _;
1009                            (*data).BufDirty = true;
1010                        }
1011                        0
1012                    },
1013                    InputTextFlags::CALLBACK_COMPLETION => {
1014                        let info = unsafe { TextCallbackData::new(data) };
1015                        user.handler.on_completion(info);
1016                        0
1017                    }
1018                    InputTextFlags::CALLBACK_HISTORY => {
1019                        let key = unsafe { (*data).EventKey };
1020                        let dir = if key == sys::ImGuiKey_UpArrow {
1021                            HistoryDirection::Up
1022                        } else {
1023                            HistoryDirection::Down
1024                        };
1025                        let info = unsafe { TextCallbackData::new(data) };
1026                        user.handler.on_history(dir, info);
1027                        0
1028                    }
1029                    InputTextFlags::CALLBACK_ALWAYS => {
1030                        let info = unsafe { TextCallbackData::new(data) };
1031                        user.handler.on_always(info);
1032                        0
1033                    }
1034                    InputTextFlags::CALLBACK_EDIT => {
1035                        let info = unsafe { TextCallbackData::new(data) };
1036                        user.handler.on_edit(info);
1037                        0
1038                    }
1039                    InputTextFlags::CALLBACK_CHAR_FILTER => {
1040                        let ch = unsafe {
1041                            std::char::from_u32((*data).EventChar as u32).unwrap_or('\0')
1042                        };
1043                        let new_ch = user.handler.char_filter(ch).map(|c| c as u32).unwrap_or(0);
1044                        unsafe {
1045                            (*data).EventChar =
1046                                sys::ImWchar::try_from(new_ch).unwrap_or(0 as sys::ImWchar);
1047                        }
1048                        0
1049                    }
1050                    _ => 0,
1051                }
1052            }));
1053
1054            match res {
1055                Ok(v) => v,
1056                Err(_) => {
1057                    eprintln!("dear-imgui-rs: panic in InputText multiline callback");
1058                    std::process::abort();
1059                }
1060            }
1061        }
1062
1063        let mut user_data = UserData {
1064            container: self.buf as *mut String,
1065            handler: self.handler,
1066        };
1067        let user_ptr = &mut user_data as *mut _ as *mut c_void;
1068
1069        let size_vec: sys::ImVec2 = self.size.into();
1070        let flags = self.flags | InputTextFlags::CALLBACK_RESIZE;
1071        let result = unsafe {
1072            sys::igInputTextMultiline(
1073                label_ptr,
1074                buf_ptr,
1075                capacity,
1076                size_vec,
1077                flags.raw(),
1078                Some(callback_router::<T>),
1079                user_ptr,
1080            )
1081        };
1082
1083        // Trim at NUL
1084        let cap = unsafe { (&*user_data.container).capacity() };
1085        let slice = unsafe { std::slice::from_raw_parts((&*user_data.container).as_ptr(), cap) };
1086        if let Some(len) = slice.iter().position(|&b| b == 0) {
1087            unsafe { (&mut *user_data.container).as_mut_vec().set_len(len) };
1088        }
1089        result
1090    }
1091}
1092
1093/// Builder for integer input widget
1094#[derive(Debug)]
1095#[must_use]
1096pub struct InputInt<'ui> {
1097    ui: &'ui Ui,
1098    label: Cow<'ui, str>,
1099    step: i32,
1100    step_fast: i32,
1101    flags: InputTextFlags,
1102}
1103
1104impl<'ui> InputInt<'ui> {
1105    /// Creates a new integer input builder
1106    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>) -> Self {
1107        Self {
1108            ui,
1109            label: label.into(),
1110            step: 1,
1111            step_fast: 100,
1112            flags: InputTextFlags::NONE,
1113        }
1114    }
1115
1116    /// Sets the step value
1117    pub fn step(mut self, step: i32) -> Self {
1118        self.step = step;
1119        self
1120    }
1121
1122    /// Sets the fast step value
1123    pub fn step_fast(mut self, step_fast: i32) -> Self {
1124        self.step_fast = step_fast;
1125        self
1126    }
1127
1128    /// Sets the flags for the input
1129    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1130        self.flags = flags;
1131        self
1132    }
1133
1134    /// Builds the integer input widget
1135    pub fn build(self, value: &mut i32) -> bool {
1136        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
1137        unsafe {
1138            sys::igInputInt(
1139                label_ptr,
1140                value as *mut i32,
1141                self.step,
1142                self.step_fast,
1143                self.flags.raw(),
1144            )
1145        }
1146    }
1147}
1148
1149/// Builder for float input widget
1150#[derive(Debug)]
1151#[must_use]
1152pub struct InputFloat<'ui> {
1153    ui: &'ui Ui,
1154    label: Cow<'ui, str>,
1155    step: f32,
1156    step_fast: f32,
1157    format: Option<Cow<'ui, str>>,
1158    flags: InputTextFlags,
1159}
1160
1161impl<'ui> InputFloat<'ui> {
1162    /// Creates a new float input builder
1163    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>) -> Self {
1164        Self {
1165            ui,
1166            label: label.into(),
1167            step: 0.0,
1168            step_fast: 0.0,
1169            format: None,
1170            flags: InputTextFlags::NONE,
1171        }
1172    }
1173
1174    /// Sets the step value
1175    pub fn step(mut self, step: f32) -> Self {
1176        self.step = step;
1177        self
1178    }
1179
1180    /// Sets the fast step value
1181    pub fn step_fast(mut self, step_fast: f32) -> Self {
1182        self.step_fast = step_fast;
1183        self
1184    }
1185
1186    /// Sets the display format
1187    pub fn format(mut self, format: impl Into<Cow<'ui, str>>) -> Self {
1188        self.format = Some(format.into());
1189        self
1190    }
1191
1192    /// Sets the flags for the input
1193    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1194        self.flags = flags;
1195        self
1196    }
1197
1198    /// Builds the float input widget
1199    pub fn build(self, value: &mut f32) -> bool {
1200        let format = self.format.as_deref().unwrap_or("%.3f");
1201        let (label_ptr, format_ptr) = self.ui.scratch_txt_two(self.label.as_ref(), format);
1202
1203        unsafe {
1204            sys::igInputFloat(
1205                label_ptr,
1206                value as *mut f32,
1207                self.step,
1208                self.step_fast,
1209                format_ptr,
1210                self.flags.raw(),
1211            )
1212        }
1213    }
1214}
1215
1216/// Builder for double input widget
1217#[derive(Debug)]
1218#[must_use]
1219pub struct InputDouble<'ui> {
1220    ui: &'ui Ui,
1221    label: Cow<'ui, str>,
1222    step: f64,
1223    step_fast: f64,
1224    format: Option<Cow<'ui, str>>,
1225    flags: InputTextFlags,
1226}
1227
1228impl<'ui> InputDouble<'ui> {
1229    /// Creates a new double input builder
1230    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>) -> Self {
1231        Self {
1232            ui,
1233            label: label.into(),
1234            step: 0.0,
1235            step_fast: 0.0,
1236            format: None,
1237            flags: InputTextFlags::NONE,
1238        }
1239    }
1240
1241    /// Sets the step value
1242    pub fn step(mut self, step: f64) -> Self {
1243        self.step = step;
1244        self
1245    }
1246
1247    /// Sets the fast step value
1248    pub fn step_fast(mut self, step_fast: f64) -> Self {
1249        self.step_fast = step_fast;
1250        self
1251    }
1252
1253    /// Sets the display format
1254    pub fn format(mut self, format: impl Into<Cow<'ui, str>>) -> Self {
1255        self.format = Some(format.into());
1256        self
1257    }
1258
1259    /// Sets the flags for the input
1260    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1261        self.flags = flags;
1262        self
1263    }
1264
1265    /// Builds the double input widget
1266    pub fn build(self, value: &mut f64) -> bool {
1267        let format = self.format.as_deref().unwrap_or("%.6f");
1268        let (label_ptr, format_ptr) = self.ui.scratch_txt_two(self.label.as_ref(), format);
1269
1270        unsafe {
1271            sys::igInputDouble(
1272                label_ptr,
1273                value as *mut f64,
1274                self.step,
1275                self.step_fast,
1276                format_ptr,
1277                self.flags.raw(),
1278            )
1279        }
1280    }
1281}
1282
1283// InputText Callback System
1284// =========================
1285
1286bitflags::bitflags! {
1287    /// Callback flags for InputText widgets
1288    #[repr(transparent)]
1289    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1290    pub struct InputTextCallback: u32 {
1291        /// Call user function on pressing TAB (for completion handling)
1292        const COMPLETION = sys::ImGuiInputTextFlags_CallbackCompletion as u32;
1293        /// Call user function on pressing Up/Down arrows (for history handling)
1294        const HISTORY = sys::ImGuiInputTextFlags_CallbackHistory as u32;
1295        /// Call user function every time. User code may query cursor position, modify text buffer.
1296        const ALWAYS = sys::ImGuiInputTextFlags_CallbackAlways as u32;
1297        /// Call user function to filter character.
1298        const CHAR_FILTER = sys::ImGuiInputTextFlags_CallbackCharFilter as u32;
1299        /// Callback on buffer edit (note that InputText already returns true on edit, the
1300        /// callback is useful mainly to manipulate the underlying buffer while focus is active)
1301        const EDIT = sys::ImGuiInputTextFlags_CallbackEdit as u32;
1302    }
1303}
1304
1305/// Direction for history navigation
1306#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1307pub enum HistoryDirection {
1308    /// Up arrow key pressed
1309    Up,
1310    /// Down arrow key pressed
1311    Down,
1312}
1313
1314/// This trait provides an interface which ImGui will call on InputText callbacks.
1315///
1316/// Each method is called *if and only if* the corresponding flag for each
1317/// method is passed to ImGui in the `callback` builder.
1318pub trait InputTextCallbackHandler {
1319    /// Filters a char -- returning a `None` means that the char is removed,
1320    /// and returning another char substitutes it out.
1321    ///
1322    /// To make ImGui run this callback, use [InputTextCallback::CHAR_FILTER].
1323    fn char_filter(&mut self, _c: char) -> Option<char> {
1324        None
1325    }
1326
1327    /// Called when the user presses the completion key (TAB by default).
1328    ///
1329    /// To make ImGui run this callback, use [InputTextCallback::COMPLETION].
1330    fn on_completion(&mut self, _data: TextCallbackData) {}
1331
1332    /// Called when the user presses Up/Down arrow keys for history navigation.
1333    ///
1334    /// To make ImGui run this callback, use [InputTextCallback::HISTORY].
1335    fn on_history(&mut self, _direction: HistoryDirection, _data: TextCallbackData) {}
1336
1337    /// Called every frame when the input text is active.
1338    ///
1339    /// To make ImGui run this callback, use [InputTextCallback::ALWAYS].
1340    fn on_always(&mut self, _data: TextCallbackData) {}
1341
1342    /// Called when the text buffer is edited.
1343    ///
1344    /// To make ImGui run this callback, use [InputTextCallback::EDIT].
1345    fn on_edit(&mut self, _data: TextCallbackData) {}
1346}
1347
1348/// This struct provides methods to edit the underlying text buffer that
1349/// Dear ImGui manipulates. Primarily, it gives [remove_chars](Self::remove_chars),
1350/// [insert_chars](Self::insert_chars), and mutable access to what text is selected.
1351pub struct TextCallbackData(*mut sys::ImGuiInputTextCallbackData);
1352
1353impl TextCallbackData {
1354    /// Creates the buffer.
1355    unsafe fn new(data: *mut sys::ImGuiInputTextCallbackData) -> Self {
1356        Self(data)
1357    }
1358
1359    /// Get a reference to the text callback buffer's str.
1360    pub fn str(&self) -> &str {
1361        unsafe {
1362            std::str::from_utf8(std::slice::from_raw_parts(
1363                (*(self.0)).Buf as *const _,
1364                (*(self.0)).BufTextLen as usize,
1365            ))
1366            .expect("internal imgui error -- it boofed a utf8")
1367        }
1368    }
1369
1370    /// Get the current cursor position
1371    pub fn cursor_pos(&self) -> usize {
1372        unsafe { (*(self.0)).CursorPos as usize }
1373    }
1374
1375    /// Set the cursor position
1376    pub fn set_cursor_pos(&mut self, pos: usize) {
1377        unsafe {
1378            (*(self.0)).CursorPos = pos as i32;
1379        }
1380    }
1381
1382    /// Get the selection start position
1383    pub fn selection_start(&self) -> usize {
1384        unsafe { (*(self.0)).SelectionStart as usize }
1385    }
1386
1387    /// Set the selection start position
1388    pub fn set_selection_start(&mut self, pos: usize) {
1389        unsafe {
1390            (*(self.0)).SelectionStart = pos as i32;
1391        }
1392    }
1393
1394    /// Get the selection end position
1395    pub fn selection_end(&self) -> usize {
1396        unsafe { (*(self.0)).SelectionEnd as usize }
1397    }
1398
1399    /// Set the selection end position
1400    pub fn set_selection_end(&mut self, pos: usize) {
1401        unsafe {
1402            (*(self.0)).SelectionEnd = pos as i32;
1403        }
1404    }
1405
1406    /// Select all text
1407    pub fn select_all(&mut self) {
1408        unsafe {
1409            (*(self.0)).SelectionStart = 0;
1410            (*(self.0)).SelectionEnd = (*(self.0)).BufTextLen;
1411        }
1412    }
1413
1414    /// Clear selection
1415    pub fn clear_selection(&mut self) {
1416        unsafe {
1417            (*(self.0)).SelectionStart = (*(self.0)).CursorPos;
1418            (*(self.0)).SelectionEnd = (*(self.0)).CursorPos;
1419        }
1420    }
1421
1422    /// Returns true if there is a selection
1423    pub fn has_selection(&self) -> bool {
1424        unsafe { (*(self.0)).SelectionStart != (*(self.0)).SelectionEnd }
1425    }
1426
1427    /// Delete characters in the range [pos, pos+bytes_count)
1428    pub fn remove_chars(&mut self, pos: usize, bytes_count: usize) {
1429        unsafe {
1430            sys::ImGuiInputTextCallbackData_DeleteChars(self.0, pos as i32, bytes_count as i32);
1431        }
1432    }
1433
1434    /// Insert text at the given position
1435    pub fn insert_chars(&mut self, pos: usize, text: &str) {
1436        let text_ptr = text.as_ptr() as *const std::os::raw::c_char;
1437        unsafe {
1438            sys::ImGuiInputTextCallbackData_InsertChars(
1439                self.0,
1440                pos as i32,
1441                text_ptr,
1442                text_ptr.add(text.len()),
1443            );
1444        }
1445    }
1446
1447    /// Gives access to the underlying byte array MUTABLY.
1448    ///
1449    /// ## Safety
1450    ///
1451    /// This is very unsafe, and the following invariants must be
1452    /// upheld:
1453    /// 1. Keep the data utf8 valid.
1454    /// 2. After editing the string, call [set_dirty].
1455    ///
1456    /// To truncate the string, please use [remove_chars]. To extend
1457    /// the string, please use [insert_chars] and [push_str].
1458    ///
1459    /// This function should have highly limited usage, but could be for
1460    /// editing certain characters in the buffer based on some external condition.
1461    ///
1462    /// [remove_chars]: Self::remove_chars
1463    /// [set_dirty]: Self::set_dirty
1464    /// [insert_chars]: Self::insert_chars
1465    /// [push_str]: Self::push_str
1466    pub unsafe fn str_as_bytes_mut(&mut self) -> &mut [u8] {
1467        unsafe {
1468            assert!(
1469                !(*(self.0)).Buf.is_null(),
1470                "internal imgui error: Buf was null"
1471            );
1472            assert!(
1473                (*(self.0)).BufTextLen >= 0,
1474                "internal imgui error: BufTextLen was negative"
1475            );
1476            assert!(
1477                (*(self.0)).BufSize >= 0,
1478                "internal imgui error: BufSize was negative"
1479            );
1480            assert!(
1481                (*(self.0)).BufTextLen <= (*(self.0)).BufSize,
1482                "internal imgui error: BufTextLen exceeded BufSize"
1483            );
1484
1485            let str = std::str::from_utf8_mut(std::slice::from_raw_parts_mut(
1486                (*(self.0)).Buf as *mut u8,
1487                (*(self.0)).BufTextLen as usize,
1488            ))
1489            .expect("internal imgui error -- it boofed a utf8");
1490
1491            str.as_bytes_mut()
1492        }
1493    }
1494
1495    /// Sets the dirty flag on the text to imgui, indicating that
1496    /// it should reapply this string to its internal state.
1497    ///
1498    /// **NB:** You only need to use this method if you're using `[str_as_bytes_mut]`.
1499    /// If you use the helper methods [remove_chars] and [insert_chars],
1500    /// this will be set for you. However, this is no downside to setting
1501    /// the dirty flag spuriously except the minor CPU time imgui will spend.
1502    ///
1503    /// [str_as_bytes_mut]: Self::str_as_bytes_mut
1504    /// [remove_chars]: Self::remove_chars
1505    /// [insert_chars]: Self::insert_chars
1506    pub fn set_dirty(&mut self) {
1507        unsafe {
1508            (*(self.0)).BufDirty = true;
1509        }
1510    }
1511
1512    /// Returns the selected text directly. Note that if no text is selected,
1513    /// an empty str slice will be returned.
1514    pub fn selected(&self) -> &str {
1515        let start = self.selection_start().min(self.selection_end());
1516        let end = self.selection_start().max(self.selection_end());
1517        &self.str()[start..end]
1518    }
1519
1520    /// Pushes the given str to the end of this buffer. If this
1521    /// would require the String to resize, it will be resized.
1522    /// This is automatically handled.
1523    pub fn push_str(&mut self, text: &str) {
1524        let current_len = unsafe { (*(self.0)).BufTextLen as usize };
1525        self.insert_chars(current_len, text);
1526    }
1527}
1528
1529/// This is a ZST which implements InputTextCallbackHandler as a passthrough.
1530///
1531/// If you do not set a callback handler, this will be used (but will never
1532/// actually run, since you will not have passed imgui any flags).
1533pub struct PassthroughCallback;
1534impl InputTextCallbackHandler for PassthroughCallback {}
1535
1536/// This is our default callback function that routes ImGui callbacks to our trait methods.
1537extern "C" fn callback(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
1538    if data.is_null() {
1539        return 0;
1540    }
1541
1542    let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
1543        let event_flag = InputTextFlags::from_bits_truncate((*data).EventFlag as i32);
1544        let buffer_ptr = (*data).UserData as *mut String;
1545
1546        if buffer_ptr.is_null() {
1547            return;
1548        }
1549
1550        match event_flag {
1551            InputTextFlags::CALLBACK_RESIZE => {
1552                let requested_i32 = (*data).BufSize;
1553                if requested_i32 < 0 {
1554                    return;
1555                }
1556                let requested_size = requested_i32 as usize;
1557                let buffer = &mut *buffer_ptr;
1558
1559                debug_assert_eq!(buffer.as_ptr() as *const _, (*data).Buf);
1560
1561                if requested_size > buffer.capacity() {
1562                    let additional_bytes = requested_size.saturating_sub(buffer.len());
1563                    buffer.reserve(additional_bytes);
1564
1565                    (*data).Buf = buffer.as_mut_ptr() as *mut _;
1566                    (*data).BufDirty = true;
1567                }
1568            }
1569            _ => {}
1570        }
1571    }));
1572
1573    if res.is_err() {
1574        eprintln!("dear-imgui-rs: panic in legacy InputText callback");
1575        std::process::abort();
1576    }
1577    0
1578}
1579
1580/// Builder for an input scalar widget.
1581#[must_use]
1582pub struct InputScalar<'ui, 'p, T, L, F = &'static str> {
1583    value: &'p mut T,
1584    label: L,
1585    step: Option<T>,
1586    step_fast: Option<T>,
1587    display_format: Option<F>,
1588    flags: InputTextFlags,
1589    ui: &'ui Ui,
1590}
1591
1592impl<'ui, 'p, L: AsRef<str>, T: DataTypeKind> InputScalar<'ui, 'p, T, L> {
1593    /// Constructs a new input scalar builder.
1594    #[doc(alias = "InputScalar")]
1595    pub fn new(ui: &'ui Ui, label: L, value: &'p mut T) -> Self {
1596        InputScalar {
1597            value,
1598            label,
1599            step: None,
1600            step_fast: None,
1601            display_format: None,
1602            flags: InputTextFlags::empty(),
1603            ui,
1604        }
1605    }
1606}
1607
1608impl<'ui, 'p, L: AsRef<str>, T: DataTypeKind, F: AsRef<str>> InputScalar<'ui, 'p, T, L, F> {
1609    /// Sets the display format using *a C-style printf string*
1610    pub fn display_format<F2: AsRef<str>>(
1611        self,
1612        display_format: F2,
1613    ) -> InputScalar<'ui, 'p, T, L, F2> {
1614        InputScalar {
1615            value: self.value,
1616            label: self.label,
1617            step: self.step,
1618            step_fast: self.step_fast,
1619            display_format: Some(display_format),
1620            flags: self.flags,
1621            ui: self.ui,
1622        }
1623    }
1624
1625    /// Sets the step value for the input
1626    #[inline]
1627    pub fn step(mut self, value: T) -> Self {
1628        self.step = Some(value);
1629        self
1630    }
1631
1632    /// Sets the fast step value for the input
1633    #[inline]
1634    pub fn step_fast(mut self, value: T) -> Self {
1635        self.step_fast = Some(value);
1636        self
1637    }
1638
1639    /// Sets the input text flags
1640    #[inline]
1641    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1642        self.flags = flags;
1643        self
1644    }
1645
1646    /// Builds an input scalar that is bound to the given value.
1647    ///
1648    /// Returns true if the value was changed.
1649    pub fn build(self) -> bool {
1650        unsafe {
1651            let (one, two) = self
1652                .ui
1653                .scratch_txt_with_opt(self.label, self.display_format);
1654
1655            sys::igInputScalar(
1656                one,
1657                T::KIND as i32,
1658                self.value as *mut T as *mut c_void,
1659                self.step
1660                    .as_ref()
1661                    .map(|step| step as *const T)
1662                    .unwrap_or(ptr::null()) as *const c_void,
1663                self.step_fast
1664                    .as_ref()
1665                    .map(|step| step as *const T)
1666                    .unwrap_or(ptr::null()) as *const c_void,
1667                two,
1668                self.flags.raw(),
1669            )
1670        }
1671    }
1672}
1673
1674/// Builder for an input scalar array widget.
1675#[must_use]
1676pub struct InputScalarN<'ui, 'p, T, L, F = &'static str> {
1677    values: &'p mut [T],
1678    label: L,
1679    step: Option<T>,
1680    step_fast: Option<T>,
1681    display_format: Option<F>,
1682    flags: InputTextFlags,
1683    ui: &'ui Ui,
1684}
1685
1686impl<'ui, 'p, L: AsRef<str>, T: DataTypeKind> InputScalarN<'ui, 'p, T, L> {
1687    /// Constructs a new input scalar array builder.
1688    #[doc(alias = "InputScalarN")]
1689    pub fn new(ui: &'ui Ui, label: L, values: &'p mut [T]) -> Self {
1690        InputScalarN {
1691            values,
1692            label,
1693            step: None,
1694            step_fast: None,
1695            display_format: None,
1696            flags: InputTextFlags::empty(),
1697            ui,
1698        }
1699    }
1700}
1701
1702impl<'ui, 'p, L: AsRef<str>, T: DataTypeKind, F: AsRef<str>> InputScalarN<'ui, 'p, T, L, F> {
1703    /// Sets the display format using *a C-style printf string*
1704    pub fn display_format<F2: AsRef<str>>(
1705        self,
1706        display_format: F2,
1707    ) -> InputScalarN<'ui, 'p, T, L, F2> {
1708        InputScalarN {
1709            values: self.values,
1710            label: self.label,
1711            step: self.step,
1712            step_fast: self.step_fast,
1713            display_format: Some(display_format),
1714            flags: self.flags,
1715            ui: self.ui,
1716        }
1717    }
1718
1719    /// Sets the step value for the input
1720    #[inline]
1721    pub fn step(mut self, value: T) -> Self {
1722        self.step = Some(value);
1723        self
1724    }
1725
1726    /// Sets the fast step value for the input
1727    #[inline]
1728    pub fn step_fast(mut self, value: T) -> Self {
1729        self.step_fast = Some(value);
1730        self
1731    }
1732
1733    /// Sets the input text flags
1734    #[inline]
1735    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1736        self.flags = flags;
1737        self
1738    }
1739
1740    /// Builds a horizontal array of multiple input scalars attached to the given slice.
1741    ///
1742    /// Returns true if any value was changed.
1743    pub fn build(self) -> bool {
1744        let count = match i32::try_from(self.values.len()) {
1745            Ok(n) => n,
1746            Err(_) => return false,
1747        };
1748        unsafe {
1749            let (one, two) = self
1750                .ui
1751                .scratch_txt_with_opt(self.label, self.display_format);
1752
1753            sys::igInputScalarN(
1754                one,
1755                T::KIND as i32,
1756                self.values.as_mut_ptr() as *mut c_void,
1757                count,
1758                self.step
1759                    .as_ref()
1760                    .map(|step| step as *const T)
1761                    .unwrap_or(ptr::null()) as *const c_void,
1762                self.step_fast
1763                    .as_ref()
1764                    .map(|step| step as *const T)
1765                    .unwrap_or(ptr::null()) as *const c_void,
1766                two,
1767                self.flags.raw(),
1768            )
1769        }
1770    }
1771}
1772
1773/// Builder for a 2-component float input widget.
1774#[must_use]
1775pub struct InputFloat2<'ui, 'p, L, F = &'static str> {
1776    label: L,
1777    value: &'p mut [f32; 2],
1778    display_format: Option<F>,
1779    flags: InputTextFlags,
1780    ui: &'ui Ui,
1781}
1782
1783impl<'ui, 'p, L: AsRef<str>> InputFloat2<'ui, 'p, L> {
1784    /// Constructs a new input float2 builder.
1785    #[doc(alias = "InputFloat2")]
1786    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [f32; 2]) -> Self {
1787        InputFloat2 {
1788            label,
1789            value,
1790            display_format: None,
1791            flags: InputTextFlags::empty(),
1792            ui,
1793        }
1794    }
1795}
1796
1797impl<'ui, 'p, L: AsRef<str>, F: AsRef<str>> InputFloat2<'ui, 'p, L, F> {
1798    /// Sets the display format using *a C-style printf string*
1799    pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> InputFloat2<'ui, 'p, L, F2> {
1800        InputFloat2 {
1801            label: self.label,
1802            value: self.value,
1803            display_format: Some(display_format),
1804            flags: self.flags,
1805            ui: self.ui,
1806        }
1807    }
1808
1809    /// Sets the input text flags
1810    #[inline]
1811    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1812        self.flags = flags;
1813        self
1814    }
1815
1816    /// Builds the input float2 widget.
1817    ///
1818    /// Returns true if any value was changed.
1819    pub fn build(self) -> bool {
1820        unsafe {
1821            let (one, two) = self
1822                .ui
1823                .scratch_txt_with_opt(self.label, self.display_format);
1824
1825            sys::igInputFloat2(one, self.value.as_mut_ptr(), two, self.flags.raw())
1826        }
1827    }
1828}
1829
1830/// Builder for a 3-component float input widget.
1831#[must_use]
1832pub struct InputFloat3<'ui, 'p, L, F = &'static str> {
1833    label: L,
1834    value: &'p mut [f32; 3],
1835    display_format: Option<F>,
1836    flags: InputTextFlags,
1837    ui: &'ui Ui,
1838}
1839
1840impl<'ui, 'p, L: AsRef<str>> InputFloat3<'ui, 'p, L> {
1841    /// Constructs a new input float3 builder.
1842    #[doc(alias = "InputFloat3")]
1843    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [f32; 3]) -> Self {
1844        InputFloat3 {
1845            label,
1846            value,
1847            display_format: None,
1848            flags: InputTextFlags::empty(),
1849            ui,
1850        }
1851    }
1852}
1853
1854impl<'ui, 'p, L: AsRef<str>, F: AsRef<str>> InputFloat3<'ui, 'p, L, F> {
1855    /// Sets the display format using *a C-style printf string*
1856    pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> InputFloat3<'ui, 'p, L, F2> {
1857        InputFloat3 {
1858            label: self.label,
1859            value: self.value,
1860            display_format: Some(display_format),
1861            flags: self.flags,
1862            ui: self.ui,
1863        }
1864    }
1865
1866    /// Sets the input text flags
1867    #[inline]
1868    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1869        self.flags = flags;
1870        self
1871    }
1872
1873    /// Builds the input float3 widget.
1874    ///
1875    /// Returns true if any value was changed.
1876    pub fn build(self) -> bool {
1877        unsafe {
1878            let (one, two) = self
1879                .ui
1880                .scratch_txt_with_opt(self.label, self.display_format);
1881
1882            sys::igInputFloat3(one, self.value.as_mut_ptr(), two, self.flags.raw())
1883        }
1884    }
1885}
1886
1887/// Builder for a 4-component float input widget.
1888#[must_use]
1889pub struct InputFloat4<'ui, 'p, L, F = &'static str> {
1890    label: L,
1891    value: &'p mut [f32; 4],
1892    display_format: Option<F>,
1893    flags: InputTextFlags,
1894    ui: &'ui Ui,
1895}
1896
1897impl<'ui, 'p, L: AsRef<str>> InputFloat4<'ui, 'p, L> {
1898    /// Constructs a new input float4 builder.
1899    #[doc(alias = "InputFloat4")]
1900    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [f32; 4]) -> Self {
1901        InputFloat4 {
1902            label,
1903            value,
1904            display_format: None,
1905            flags: InputTextFlags::empty(),
1906            ui,
1907        }
1908    }
1909}
1910
1911impl<'ui, 'p, L: AsRef<str>, F: AsRef<str>> InputFloat4<'ui, 'p, L, F> {
1912    /// Sets the display format using *a C-style printf string*
1913    pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> InputFloat4<'ui, 'p, L, F2> {
1914        InputFloat4 {
1915            label: self.label,
1916            value: self.value,
1917            display_format: Some(display_format),
1918            flags: self.flags,
1919            ui: self.ui,
1920        }
1921    }
1922
1923    /// Sets the input text flags
1924    #[inline]
1925    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1926        self.flags = flags;
1927        self
1928    }
1929
1930    /// Builds the input float4 widget.
1931    ///
1932    /// Returns true if any value was changed.
1933    pub fn build(self) -> bool {
1934        unsafe {
1935            let (one, two) = self
1936                .ui
1937                .scratch_txt_with_opt(self.label, self.display_format);
1938
1939            sys::igInputFloat4(one, self.value.as_mut_ptr(), two, self.flags.raw())
1940        }
1941    }
1942}
1943
1944/// Builder for a 2-component int input widget.
1945#[must_use]
1946pub struct InputInt2<'ui, 'p, L> {
1947    label: L,
1948    value: &'p mut [i32; 2],
1949    flags: InputTextFlags,
1950    ui: &'ui Ui,
1951}
1952
1953impl<'ui, 'p, L: AsRef<str>> InputInt2<'ui, 'p, L> {
1954    /// Constructs a new input int2 builder.
1955    #[doc(alias = "InputInt2")]
1956    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [i32; 2]) -> Self {
1957        InputInt2 {
1958            label,
1959            value,
1960            flags: InputTextFlags::empty(),
1961            ui,
1962        }
1963    }
1964
1965    /// Sets the input text flags
1966    #[inline]
1967    pub fn flags(mut self, flags: InputTextFlags) -> Self {
1968        self.flags = flags;
1969        self
1970    }
1971
1972    /// Builds the input int2 widget.
1973    ///
1974    /// Returns true if any value was changed.
1975    pub fn build(self) -> bool {
1976        unsafe {
1977            let label_cstr = self.ui.scratch_txt(self.label);
1978
1979            sys::igInputInt2(label_cstr, self.value.as_mut_ptr(), self.flags.raw())
1980        }
1981    }
1982}
1983
1984/// Builder for a 3-component int input widget.
1985#[must_use]
1986pub struct InputInt3<'ui, 'p, L> {
1987    label: L,
1988    value: &'p mut [i32; 3],
1989    flags: InputTextFlags,
1990    ui: &'ui Ui,
1991}
1992
1993impl<'ui, 'p, L: AsRef<str>> InputInt3<'ui, 'p, L> {
1994    /// Constructs a new input int3 builder.
1995    #[doc(alias = "InputInt3")]
1996    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [i32; 3]) -> Self {
1997        InputInt3 {
1998            label,
1999            value,
2000            flags: InputTextFlags::empty(),
2001            ui,
2002        }
2003    }
2004
2005    /// Sets the input text flags
2006    #[inline]
2007    pub fn flags(mut self, flags: InputTextFlags) -> Self {
2008        self.flags = flags;
2009        self
2010    }
2011
2012    /// Builds the input int3 widget.
2013    ///
2014    /// Returns true if any value was changed.
2015    pub fn build(self) -> bool {
2016        unsafe {
2017            let label_cstr = self.ui.scratch_txt(self.label);
2018
2019            sys::igInputInt3(label_cstr, self.value.as_mut_ptr(), self.flags.raw())
2020        }
2021    }
2022}
2023
2024/// Builder for a 4-component int input widget.
2025#[must_use]
2026pub struct InputInt4<'ui, 'p, L> {
2027    label: L,
2028    value: &'p mut [i32; 4],
2029    flags: InputTextFlags,
2030    ui: &'ui Ui,
2031}
2032
2033impl<'ui, 'p, L: AsRef<str>> InputInt4<'ui, 'p, L> {
2034    /// Constructs a new input int4 builder.
2035    #[doc(alias = "InputInt4")]
2036    pub fn new(ui: &'ui Ui, label: L, value: &'p mut [i32; 4]) -> Self {
2037        InputInt4 {
2038            label,
2039            value,
2040            flags: InputTextFlags::empty(),
2041            ui,
2042        }
2043    }
2044
2045    /// Sets the input text flags
2046    #[inline]
2047    pub fn flags(mut self, flags: InputTextFlags) -> Self {
2048        self.flags = flags;
2049        self
2050    }
2051
2052    /// Builds the input int4 widget.
2053    ///
2054    /// Returns true if any value was changed.
2055    pub fn build(self) -> bool {
2056        unsafe {
2057            let label_cstr = self.ui.scratch_txt(self.label);
2058
2059            sys::igInputInt4(label_cstr, self.value.as_mut_ptr(), self.flags.raw())
2060        }
2061    }
2062}
2063
2064#[cfg(test)]
2065mod tests {
2066    use super::*;
2067
2068    #[test]
2069    fn zero_string_spare_capacity_writes_nul_bytes() {
2070        let mut s = String::with_capacity(16);
2071        s.push_str("abc");
2072        let len = s.len();
2073        let cap = s.capacity();
2074
2075        zero_string_spare_capacity(&mut s);
2076
2077        unsafe {
2078            let bytes = std::slice::from_raw_parts(s.as_ptr(), cap);
2079            assert_eq!(&bytes[..len], b"abc");
2080            assert!(bytes[len..].iter().all(|&b| b == 0));
2081        }
2082    }
2083
2084    #[test]
2085    fn zero_string_new_capacity_writes_new_region() {
2086        let mut s = String::with_capacity(4);
2087        s.push_str("abc");
2088        let old_cap = s.capacity();
2089
2090        s.reserve(64);
2091        let new_cap = s.capacity();
2092        assert!(new_cap > old_cap);
2093
2094        zero_string_new_capacity(&mut s, old_cap);
2095
2096        unsafe {
2097            let tail = std::slice::from_raw_parts(s.as_ptr().add(old_cap), new_cap - old_cap);
2098            assert!(tail.iter().all(|&b| b == 0));
2099        }
2100    }
2101}