arcdps_imgui/
input_widget.rs

1use bitflags::bitflags;
2use std::ops::Range;
3use std::os::raw::{c_char, c_int, c_void};
4
5use crate::sys;
6use crate::Ui;
7
8bitflags!(
9    /// Flags for text inputs
10    #[repr(C)]
11    pub struct InputTextFlags: u32 {
12        /// Allow 0123456789.+-*/
13        const CHARS_DECIMAL = sys::ImGuiInputTextFlags_CharsDecimal;
14        /// Allow 0123456789ABCDEFabcdef
15        const CHARS_HEXADECIMAL = sys::ImGuiInputTextFlags_CharsHexadecimal;
16        /// Turn a..z into A..Z
17        const CHARS_UPPERCASE = sys::ImGuiInputTextFlags_CharsUppercase;
18        /// Filter out spaces, tabs
19        const CHARS_NO_BLANK = sys::ImGuiInputTextFlags_CharsNoBlank;
20        /// Select entire text when first taking mouse focus
21        const AUTO_SELECT_ALL = sys::ImGuiInputTextFlags_AutoSelectAll;
22        /// Return 'true' when Enter is pressed (as opposed to when the value was modified)
23        const ENTER_RETURNS_TRUE = sys::ImGuiInputTextFlags_EnterReturnsTrue;
24        /// Call user function on pressing TAB (for completion handling)
25        const CALLBACK_COMPLETION = sys::ImGuiInputTextFlags_CallbackCompletion;
26        /// Call user function on pressing Up/Down arrows (for history handling)
27        const CALLBACK_HISTORY = sys::ImGuiInputTextFlags_CallbackHistory;
28        /// Call user function on pressing Up/Down arrows (for history handling)
29        const CALLBACK_EDIT = sys::ImGuiInputTextFlags_CallbackEdit;
30        /// Call user function every time. User code may query cursor position, modify text buffer.
31        const CALLBACK_ALWAYS = sys::ImGuiInputTextFlags_CallbackAlways;
32        /// Call user function to filter character.
33        const CALLBACK_CHAR_FILTER = sys::ImGuiInputTextFlags_CallbackCharFilter;
34        /// Pressing TAB input a '\t' character into the text field
35        const ALLOW_TAB_INPUT = sys::ImGuiInputTextFlags_AllowTabInput;
36        /// In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is
37        /// opposite: unfocus with Ctrl+Enter, add line with Enter).
38        const CTRL_ENTER_FOR_NEW_LINE = sys::ImGuiInputTextFlags_CtrlEnterForNewLine;
39        /// Disable following the cursor horizontally
40        const NO_HORIZONTAL_SCROLL = sys::ImGuiInputTextFlags_NoHorizontalScroll;
41        /// Always overwrite (aka "insert mode").
42        const ALWAYS_OVERWRITE = sys::ImGuiInputTextFlags_AlwaysInsertMode;
43        /// Read-only mode
44        const READ_ONLY = sys::ImGuiInputTextFlags_ReadOnly;
45        /// Password mode, display all characters as '*'
46        const PASSWORD = sys::ImGuiInputTextFlags_Password;
47        /// Disable undo/redo.
48        const NO_UNDO_REDO = sys::ImGuiInputTextFlags_NoUndoRedo;
49        /// Allow 0123456789.+-*/eE (Scientific notation input)
50        const CHARS_SCIENTIFIC = sys::ImGuiInputTextFlags_CharsScientific;
51        /// Allow buffer capacity resize + notify when the string wants to be resized
52        const CALLBACK_RESIZE = sys::ImGuiInputTextFlags_CallbackResize;
53    }
54);
55
56macro_rules! impl_text_flags {
57    ($InputType:ident) => {
58        #[inline]
59        pub fn flags(mut self, flags: InputTextFlags) -> Self {
60            self.flags = flags;
61            self
62        }
63
64        #[inline]
65        pub fn chars_decimal(mut self, value: bool) -> Self {
66            self.flags.set(InputTextFlags::CHARS_DECIMAL, value);
67            self
68        }
69
70        #[inline]
71        pub fn chars_hexadecimal(mut self, value: bool) -> Self {
72            self.flags.set(InputTextFlags::CHARS_HEXADECIMAL, value);
73            self
74        }
75
76        #[inline]
77        pub fn chars_uppercase(mut self, value: bool) -> Self {
78            self.flags.set(InputTextFlags::CHARS_UPPERCASE, value);
79            self
80        }
81
82        #[inline]
83        pub fn chars_noblank(mut self, value: bool) -> Self {
84            self.flags.set(InputTextFlags::CHARS_NO_BLANK, value);
85            self
86        }
87
88        #[inline]
89        pub fn auto_select_all(mut self, value: bool) -> Self {
90            self.flags.set(InputTextFlags::AUTO_SELECT_ALL, value);
91            self
92        }
93
94        #[inline]
95        pub fn enter_returns_true(mut self, value: bool) -> Self {
96            self.flags.set(InputTextFlags::ENTER_RETURNS_TRUE, value);
97            self
98        }
99
100        #[inline]
101        pub fn allow_tab_input(mut self, value: bool) -> Self {
102            self.flags.set(InputTextFlags::ALLOW_TAB_INPUT, value);
103            self
104        }
105
106        #[inline]
107        pub fn no_horizontal_scroll(mut self, value: bool) -> Self {
108            self.flags.set(InputTextFlags::NO_HORIZONTAL_SCROLL, value);
109            self
110        }
111
112        /// Note: this is equivalent to `always_overwrite`
113        #[inline]
114        pub fn always_insert_mode(self, value: bool) -> Self {
115            self.always_overwrite(value)
116        }
117
118        #[inline]
119        #[allow(deprecated)]
120        pub fn always_overwrite(mut self, value: bool) -> Self {
121            self.flags.set(InputTextFlags::ALWAYS_OVERWRITE, value);
122            self
123        }
124
125        #[inline]
126        pub fn read_only(mut self, value: bool) -> Self {
127            self.flags.set(InputTextFlags::READ_ONLY, value);
128            self
129        }
130
131        #[inline]
132        pub fn password(mut self, value: bool) -> Self {
133            self.flags.set(InputTextFlags::PASSWORD, value);
134            self
135        }
136
137        #[inline]
138        pub fn no_undo_redo(mut self, value: bool) -> Self {
139            self.flags.set(InputTextFlags::NO_UNDO_REDO, value);
140            self
141        }
142    };
143}
144
145macro_rules! impl_step_params {
146    ($InputType:ident, $Value:ty) => {
147        #[inline]
148        pub fn step(mut self, value: $Value) -> Self {
149            self.step = value;
150            self
151        }
152
153        #[inline]
154        pub fn step_fast(mut self, value: $Value) -> Self {
155            self.step_fast = value;
156            self
157        }
158    };
159}
160
161#[must_use]
162pub struct InputText<'ui, 'p, L, H = &'static str, T = PassthroughCallback> {
163    label: L,
164    hint: Option<H>,
165    buf: &'p mut String,
166    callback_handler: T,
167    flags: InputTextFlags,
168    ui: &'ui Ui<'ui>,
169}
170
171impl<'ui, 'p, L: AsRef<str>> InputText<'ui, 'p, L> {
172    /// Creates a new input text widget to edit the given string.
173    ///
174    /// # String Editing
175    ///
176    /// Please note, ImGui requires this string to be null-terminated. We accomplish this
177    /// by appending and then removing a null terminator (`\0`) from the String you pass in.
178    /// This has several consequences:
179    /// 1. The string's backing buffer may be resized and relocated even without edits as result
180    /// of this pushed char.
181    /// 2. **The string will appear truncated if the string contains `\0` inside it.** This will not
182    /// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
183    /// your string.
184    /// 3. Truncations by ImGui appear to be done primarily by insertions of `\0` to the truncation point.
185    /// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
186    pub fn new(ui: &'ui Ui<'ui>, label: L, buf: &'p mut String) -> Self {
187        InputText {
188            label,
189            hint: None,
190            // this is fine because no one else has access to this and imgui is single threaded.
191            callback_handler: PassthroughCallback,
192            buf,
193            flags: InputTextFlags::CALLBACK_RESIZE,
194            ui,
195        }
196    }
197}
198
199impl<'ui, 'p, T, L, H> InputText<'ui, 'p, L, H, T>
200where
201    L: AsRef<str>,
202    H: AsRef<str>,
203    T: InputTextCallbackHandler,
204{
205    /// Sets the hint displayed in the input text background.
206    #[inline]
207    pub fn hint<H2: AsRef<str>>(self, hint: H2) -> InputText<'ui, 'p, L, H2, T> {
208        InputText {
209            label: self.label,
210            hint: Some(hint),
211            buf: self.buf,
212            callback_handler: self.callback_handler,
213            flags: self.flags,
214            ui: self.ui,
215        }
216    }
217
218    impl_text_flags!(InputText);
219
220    // I am commenting this ability out for now -- because we need to push `\0` for imgui,
221    // we may resize the buffer no matter what, and we must do that.
222    // The solution for this will be, I suspect, to build a second api channel that takes
223    // an `&mut CStr`, which is ugly! I suspect few to none will want no-resizing, so I'm deferring
224    // fixing the problem. -- sanbox-irl 09/15/2021, see #523 for more
225    //
226    // /// By default (as of 0.8.0), imgui-rs will automatically handle string resizes
227    // /// for [InputText] and [InputTextMultiline].
228    // ///
229    // /// If, for some reason, you don't want this, you can run this function to prevent this.
230    // /// In that case, edits which would cause a resize will not occur.
231    // /// #[inline]
232    // pub unsafe fn do_not_resize(mut self) -> Self {
233    //     self.flags.remove(InputTextFlags::CALLBACK_RESIZE);
234    //     self
235    // }
236
237    #[inline]
238    pub fn callback<T2: InputTextCallbackHandler>(
239        mut self,
240        callbacks: InputTextCallback,
241        callback: T2,
242    ) -> InputText<'ui, 'p, L, H, T2> {
243        if callbacks.contains(InputTextCallback::COMPLETION) {
244            self.flags.insert(InputTextFlags::CALLBACK_COMPLETION);
245        }
246        if callbacks.contains(InputTextCallback::HISTORY) {
247            self.flags.insert(InputTextFlags::CALLBACK_HISTORY);
248        }
249        if callbacks.contains(InputTextCallback::ALWAYS) {
250            self.flags.insert(InputTextFlags::CALLBACK_ALWAYS);
251        }
252        if callbacks.contains(InputTextCallback::CHAR_FILTER) {
253            self.flags.insert(InputTextFlags::CALLBACK_CHAR_FILTER);
254        }
255        if callbacks.contains(InputTextCallback::EDIT) {
256            self.flags.insert(InputTextFlags::CALLBACK_EDIT);
257        }
258        InputText {
259            callback_handler: callback,
260            label: self.label,
261            hint: self.hint,
262            buf: self.buf,
263            flags: self.flags,
264            ui: self.ui,
265        }
266    }
267
268    /// Builds the string editor, performing string editing operations.
269    ///
270    /// # String Editing
271    ///
272    /// Please note, ImGui requires this string to be null-terminated. We accomplish this
273    /// by appending and then removing a null terminator (`\0`) from the String you pass in.
274    /// This has several consequences:
275    /// 1. The string's backing buffer may be resized and relocated even without edits as result
276    /// of this pushed char.
277    /// 2. **The string will appear truncated if the string contains `\0` inside it.** This will not
278    /// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
279    /// your string.
280    /// 3. Truncations by ImGui appear to be done primarily by insertions of `\0` to the truncation point.
281    /// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
282    pub fn build(self) -> bool {
283        // needs to be null-terminated! this is a hack!
284        self.buf.push('\0');
285
286        let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity());
287
288        let mut data = UserData {
289            container: self.buf,
290            cback_handler: self.callback_handler,
291        };
292        let data = &mut data as *mut _ as *mut c_void;
293
294        let o = unsafe {
295            if let Some(hint) = self.hint {
296                let (label, hint) = self.ui.scratch_txt_two(self.label, hint);
297                sys::igInputTextWithHint(
298                    label,
299                    hint,
300                    ptr as *mut sys::cty::c_char,
301                    capacity,
302                    self.flags.bits() as i32,
303                    Some(callback::<T>),
304                    data,
305                )
306            } else {
307                let label = self.ui.scratch_txt(self.label);
308
309                sys::igInputText(
310                    label,
311                    ptr as *mut sys::cty::c_char,
312                    capacity,
313                    self.flags.bits() as i32,
314                    Some(callback::<T>),
315                    data,
316                )
317            }
318        };
319
320        let cap = self.buf.capacity();
321
322        // SAFETY: this slice is simply a view into the underlying buffer
323        // of a String. We MAY be holding onto a view of uninitialized memory,
324        // however, since we're holding this as a u8 slice, I think it should be
325        // alright...
326        // additionally, we can go over the bytes directly, rather than char indices,
327        // because NUL will never appear in any UTF8 outside the NUL character (ie, within
328        // a char).
329        let buf = unsafe { std::slice::from_raw_parts(self.buf.as_ptr(), cap) };
330        if let Some(len) = buf.iter().position(|x| *x == b'\0') {
331            // `len` is the position of the first `\0` byte in the String
332            unsafe {
333                self.buf.as_mut_vec().set_len(len);
334            }
335        } else {
336            // There is no null terminator, the best we can do is to not
337            // update the string length.
338        }
339
340        o
341    }
342}
343
344#[must_use]
345pub struct InputTextMultiline<'ui, 'p, L, T = PassthroughCallback> {
346    label: L,
347    buf: &'p mut String,
348    flags: InputTextFlags,
349    size: [f32; 2],
350    callback_handler: T,
351    ui: &'ui Ui<'ui>,
352}
353
354impl<'ui, 'p, L: AsRef<str>> InputTextMultiline<'ui, 'p, L, PassthroughCallback> {
355    /// Creates a new input text widget to edit the given string.
356    ///
357    /// # String Editing
358    ///
359    /// Please note, ImGui requires this string to be null-terminated. We accomplish this
360    /// by appending and then removing a null terminator (`\0`) from the String you pass in.
361    /// This has several consequences:
362    /// 1. The string's backing buffer may be resized and relocated even without edits as result
363    /// of this pushed char.
364    /// 2. **The string will appear truncated if the string contains `\0` inside it.** This will not
365    /// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
366    /// your string.
367    /// 3. Truncations by ImGui appear to be done primarily by insertions of `\0` to the truncation point.
368    /// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
369    pub fn new(ui: &'ui Ui<'ui>, label: L, buf: &'p mut String, size: [f32; 2]) -> Self {
370        InputTextMultiline {
371            label,
372            buf,
373            flags: InputTextFlags::CALLBACK_RESIZE,
374            size,
375            callback_handler: PassthroughCallback,
376            ui,
377        }
378    }
379}
380
381impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef<str>> InputTextMultiline<'ui, 'p, L, T> {
382    impl_text_flags!(InputText);
383
384    // I am commenting this ability out for now -- because we need to push `\0` for imgui,
385    // we may resize the buffer no matter what, and we must do that.
386    // The solution for this will be, I suspect, to build a second api channel that takes
387    // an `&mut CStr`, which is ugly! I suspect few to none will want no-resizing, so I'm deferring
388    // fixing the problem. -- sanbox-irl 09/15/2021, see #523 for more
389    // /// By default (as of 0.8.0), imgui-rs will automatically handle string resizes
390    // /// for [InputText] and [InputTextMultiline].
391    // ///
392    // /// If, for some reason, you don't want this, you can run this function to prevent this.
393    // /// In that case, edits which would cause a resize will not occur.
394    // #[inline]
395    // pub fn do_not_resize(mut self) -> Self {
396    //     self.flags.remove(InputTextFlags::CALLBACK_RESIZE);
397    //     self
398    // }
399
400    #[inline]
401    pub fn callback<T2: InputTextCallbackHandler>(
402        mut self,
403        callbacks: InputTextMultilineCallback,
404        callback_handler: T2,
405    ) -> InputTextMultiline<'ui, 'p, L, T2> {
406        if callbacks.contains(InputTextMultilineCallback::COMPLETION) {
407            self.flags.insert(InputTextFlags::CALLBACK_COMPLETION);
408        }
409        if callbacks.contains(InputTextMultilineCallback::ALWAYS) {
410            self.flags.insert(InputTextFlags::CALLBACK_ALWAYS);
411        }
412        if callbacks.contains(InputTextMultilineCallback::CHAR_FILTER) {
413            self.flags.insert(InputTextFlags::CALLBACK_CHAR_FILTER);
414        }
415        if callbacks.contains(InputTextMultilineCallback::EDIT) {
416            self.flags.insert(InputTextFlags::CALLBACK_EDIT);
417        }
418
419        InputTextMultiline {
420            label: self.label,
421            buf: self.buf,
422            flags: self.flags,
423            size: self.size,
424            callback_handler,
425            ui: self.ui,
426        }
427    }
428
429    /// Builds the string editor, performing string editing operations.
430    ///
431    /// # String Editing
432    ///
433    /// Please note, ImGui requires this string to be null-terminated. We accomplish this
434    /// by appending and then removing a null terminator (`\0`) from the String you pass in.
435    /// This has several consequences:
436    /// 1. The string's backing buffer may be resized and relocated even without edits as result
437    /// of this pushed char.
438    /// 2. **The string will appear truncated if the string contains `\0` inside it.** This will not
439    /// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
440    /// your string.
441    /// 3. Truncations by ImGui appear to be done primarily by insertions of `\0` to the truncation point.
442    /// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
443    pub fn build(self) -> bool {
444        // needs to be null-terminated! this is a hack!
445        self.buf.push('\0');
446        let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity());
447
448        let mut data = UserData {
449            container: self.buf,
450            cback_handler: self.callback_handler,
451        };
452        let data = &mut data as *mut _ as *mut c_void;
453
454        let o = unsafe {
455            sys::igInputTextMultiline(
456                self.ui.scratch_txt(self.label),
457                ptr as *mut sys::cty::c_char,
458                capacity,
459                self.size.into(),
460                self.flags.bits() as i32,
461                Some(callback::<T>),
462                data,
463            )
464        };
465
466        let cap = self.buf.capacity();
467
468        // SAFETY: this slice is simply a view into the underlying buffer
469        // of a String. We MAY be holding onto a view of uninitialized memory,
470        // however, since we're holding this as a u8 slice, I think it should be
471        // alright...
472        // additionally, we can go over the bytes directly, rather than char indices,
473        // because NUL will never appear in any UTF8 outside the NUL character (ie, within
474        // a char).
475        let buf = unsafe { std::slice::from_raw_parts(self.buf.as_ptr(), cap) };
476        if let Some(len) = buf.iter().position(|x| *x == b'\0') {
477            // `len` is the position of the first `\0` byte in the String
478            unsafe {
479                self.buf.as_mut_vec().set_len(len);
480            }
481        } else {
482            // There is no null terminator, the best we can do is to not
483            // update the string length.
484        }
485
486        o
487    }
488}
489
490#[must_use]
491pub struct InputInt<'ui, 'p, L> {
492    label: L,
493    value: &'p mut i32,
494    step: i32,
495    step_fast: i32,
496    flags: InputTextFlags,
497    ui: &'ui Ui<'ui>,
498}
499
500impl<'ui, 'p, L: AsRef<str>> InputInt<'ui, 'p, L> {
501    pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut i32) -> Self {
502        InputInt {
503            label,
504            value,
505            step: 1,
506            step_fast: 100,
507            flags: InputTextFlags::empty(),
508            ui,
509        }
510    }
511
512    pub fn build(self) -> bool {
513        unsafe {
514            sys::igInputInt(
515                self.ui.scratch_txt(self.label),
516                self.value as *mut i32,
517                self.step,
518                self.step_fast,
519                self.flags.bits() as i32,
520            )
521        }
522    }
523
524    impl_step_params!(InputInt, i32);
525    impl_text_flags!(InputInt);
526}
527
528#[must_use]
529pub struct InputFloat<'ui, 'p, L> {
530    label: L,
531    value: &'p mut f32,
532    step: f32,
533    step_fast: f32,
534    flags: InputTextFlags,
535    ui: &'ui Ui<'ui>,
536}
537
538impl<'ui, 'p, L: AsRef<str>> InputFloat<'ui, 'p, L> {
539    pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut f32) -> Self {
540        InputFloat {
541            label,
542            value,
543            step: 0.0,
544            step_fast: 0.0,
545            flags: InputTextFlags::empty(),
546            ui,
547        }
548    }
549
550    pub fn build(self) -> bool {
551        unsafe {
552            sys::igInputFloat(
553                self.ui.scratch_txt(self.label),
554                self.value as *mut f32,
555                self.step,
556                self.step_fast,
557                b"%.3f\0".as_ptr() as *const _,
558                self.flags.bits() as i32,
559            )
560        }
561    }
562
563    impl_step_params!(InputFloat, f32);
564    impl_text_flags!(InputFloat);
565}
566
567macro_rules! impl_input_floatn {
568    ($InputFloatN:ident, $N:expr, $igInputFloatN:ident) => {
569        #[must_use]
570        pub struct $InputFloatN<'ui, 'p, L> {
571            label: L,
572            value: &'p mut [f32; $N],
573            flags: InputTextFlags,
574            ui: &'ui Ui<'ui>,
575        }
576
577        impl<'ui, 'p, L: AsRef<str>> $InputFloatN<'ui, 'p, L> {
578            pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut [f32; $N]) -> Self {
579                $InputFloatN {
580                    label,
581                    value,
582                    flags: InputTextFlags::empty(),
583                    ui,
584                }
585            }
586
587            pub fn build(self) -> bool {
588                unsafe {
589                    sys::$igInputFloatN(
590                        self.ui.scratch_txt(self.label),
591                        self.value.as_mut_ptr(),
592                        b"%.3f\0".as_ptr() as *const _,
593                        self.flags.bits() as i32,
594                    )
595                }
596            }
597
598            impl_text_flags!($InputFloatN);
599        }
600    };
601}
602
603impl_input_floatn!(InputFloat2, 2, igInputFloat2);
604impl_input_floatn!(InputFloat3, 3, igInputFloat3);
605impl_input_floatn!(InputFloat4, 4, igInputFloat4);
606
607macro_rules! impl_input_intn {
608    ($InputIntN:ident, $N:expr, $igInputIntN:ident) => {
609        #[must_use]
610        pub struct $InputIntN<'ui, 'p, L> {
611            label: L,
612            value: &'p mut [i32; $N],
613            flags: InputTextFlags,
614            ui: &'ui Ui<'ui>,
615        }
616
617        impl<'ui, 'p, L: AsRef<str>> $InputIntN<'ui, 'p, L> {
618            pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut [i32; $N]) -> Self {
619                $InputIntN {
620                    label,
621                    value,
622                    flags: InputTextFlags::empty(),
623                    ui,
624                }
625            }
626
627            pub fn build(self) -> bool {
628                unsafe {
629                    sys::$igInputIntN(
630                        self.ui.scratch_txt(self.label),
631                        self.value.as_mut_ptr(),
632                        self.flags.bits() as i32,
633                    )
634                }
635            }
636
637            impl_text_flags!($InputIntN);
638        }
639    };
640}
641
642impl_input_intn!(InputInt2, 2, igInputInt2);
643impl_input_intn!(InputInt3, 3, igInputInt3);
644impl_input_intn!(InputInt4, 4, igInputInt4);
645
646bitflags!(
647    /// Callback flags for an `InputText` widget. These correspond to
648    /// the general textflags.
649    pub struct InputTextCallback: u32 {
650        /// Call user function on pressing TAB (for completion handling)
651        const COMPLETION = sys::ImGuiInputTextFlags_CallbackCompletion;
652        /// Call user function on pressing Up/Down arrows (for history handling)
653        const HISTORY = sys::ImGuiInputTextFlags_CallbackHistory;
654        /// Call user function every time. User code may query cursor position, modify text buffer.
655        const ALWAYS = sys::ImGuiInputTextFlags_CallbackAlways;
656        /// Call user function to filter character.
657        const CHAR_FILTER = sys::ImGuiInputTextFlags_CallbackCharFilter;
658        /// Callback on buffer edit (note that InputText already returns true on edit, the
659        /// callback is useful mainly to manipulate the underlying buffer while focus is active)
660        const EDIT = sys::ImGuiInputTextFlags_CallbackEdit;
661    }
662);
663
664bitflags!(
665    /// Callback flags for an `InputTextMultiline` widget. These correspond to the
666    /// general textflags.
667    pub struct InputTextMultilineCallback: u32 {
668        /// Call user function on pressing TAB (for completion handling)
669        const COMPLETION = sys::ImGuiInputTextFlags_CallbackCompletion;
670        /// Call user function every time. User code may query cursor position, modify text buffer.
671        const ALWAYS = sys::ImGuiInputTextFlags_CallbackAlways;
672        /// Call user function to filter character.
673        const CHAR_FILTER = sys::ImGuiInputTextFlags_CallbackCharFilter;
674        /// Callback on buffer edit (note that InputText already returns true on edit, the
675        /// callback is useful mainly to manipulate the underlying buffer while focus is active)
676        const EDIT = sys::ImGuiInputTextFlags_CallbackEdit;
677    }
678);
679
680/// This trait provides an interface which ImGui will call on `InputText`
681/// and `InputTextMultiline` callbacks.
682///
683/// Each method is called *if and only if* the corresponding flag for each
684/// method is passed to ImGui in the `callback` builder.
685///
686/// Each method here lists the flag required to call it, and this module begins
687/// with an example of callbacks being used.
688pub trait InputTextCallbackHandler {
689    /// Filters a char -- returning a `None` means that the char is removed,
690    /// and returning another char substitutes it out.
691    ///
692    /// Because of upstream ImGui choices, you do not have access to the buffer
693    /// during this callback (for some reason).
694    ///
695    /// To make ImGui run this callback, use [InputTextCallback::CHAR_FILTER] or
696    /// [InputTextMultilineCallback::CHAR_FILTER].
697    ///
698    /// # arcdps compatibility note
699    /// arcdps uses ImGui compiled without support for 32bit characters.
700    /// For this reason, we changed this API from Rust `char`s to underlying
701    /// raw characters (16bit).
702    fn char_filter(&mut self, c: u16) -> Option<u16> {
703        Some(c)
704    }
705
706    /// Allows one to perform autocompletion work when the Tab key has been pressed.
707    ///
708    /// To make ImGui run this callback, use [InputTextCallback::COMPLETION] or
709    /// [InputTextMultilineCallback::COMPLETION].
710    fn on_completion(&mut self, _: TextCallbackData) {}
711
712    /// Allows one to edit the inner buffer whenever the buffer has been changed.
713    ///    
714    /// To make ImGui run this callback, use [InputTextCallback::EDIT] or
715    /// [InputTextMultilineCallback::EDIT].
716    fn on_edit(&mut self, _: TextCallbackData) {}
717
718    /// A callback when one of the direction keys have been pressed.
719    ///
720    /// To make ImGui run this callback, use [InputTextCallback::HISTORY]. It appears
721    /// that this callback will not be ran in a multiline input widget at all.
722    fn on_history(&mut self, _: HistoryDirection, _: TextCallbackData) {}
723
724    /// A callback which will always fire, each tick.
725    ///
726    /// To make ImGui run this callback, use [InputTextCallback::ALWAYS] or
727    /// [InputTextMultilineCallback::ALWAYS].
728    fn on_always(&mut self, _: TextCallbackData) {}
729}
730
731/// The arrow key a user pressed to trigger the `on_history` callback.
732#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
733pub enum HistoryDirection {
734    Up,
735    Down,
736}
737
738/// This struct provides methods to edit the underlying text buffer that
739/// Dear ImGui manipulates. Primarily, it gives [remove_chars](Self::remove_chars),
740/// [insert_chars](Self::insert_chars), and mutable access to what text is selected.
741pub struct TextCallbackData(*mut sys::ImGuiInputTextCallbackData);
742
743impl TextCallbackData {
744    /// Creates the buffer.
745    unsafe fn new(data: *mut sys::ImGuiInputTextCallbackData) -> Self {
746        Self(data)
747    }
748
749    /// Get a reference to the text callback buffer's str.
750    pub fn str(&self) -> &str {
751        unsafe {
752            std::str::from_utf8(std::slice::from_raw_parts(
753                (*(self.0)).Buf as *const _,
754                (*(self.0)).BufTextLen as usize,
755            ))
756            .expect("internal imgui error -- it boofed a utf8")
757        }
758    }
759
760    /// Gives access to the underlying byte array MUTABLY.
761    ///
762    /// ## Safety
763    ///
764    /// This is very unsafe, and the following invariants must be
765    /// upheld:
766    /// 1. Keep the data utf8 valid.
767    /// 2. After editing the string, call [set_dirty].
768    ///
769    /// To truncate the string, please use [remove_chars]. To extend
770    /// the string, please use [insert_chars] and [push_str].
771    ///
772    /// This function should have highly limited usage, but could be for
773    /// editing certain characters in the buffer based on some external condition.
774    ///
775    /// [remove_chars]: Self::remove_chars
776    /// [set_dirty]: Self::set_dirty
777    /// [insert_chars]: Self::insert_chars
778    /// [push_str]: Self::push_str
779    pub unsafe fn str_as_bytes_mut(&mut self) -> &mut [u8] {
780        let str = std::str::from_utf8_mut(std::slice::from_raw_parts_mut(
781            (*(self.0)).Buf as *const _ as *mut _,
782            (*(self.0)).BufTextLen as usize,
783        ))
784        .expect("internal imgui error -- it boofed a utf8");
785
786        str.as_bytes_mut()
787    }
788
789    /// Sets the dirty flag on the text to imgui, indicating that
790    /// it should reapply this string to its internal state.
791    ///
792    /// **NB:** You only need to use this method if you're using `[str_as_bytes_mut]`.
793    /// If you use the helper methods [remove_chars] and [insert_chars],
794    /// this will be set for you. However, this is no downside to setting
795    /// the dirty flag spuriously except the minor CPU time imgui will spend.
796    ///
797    /// [str_as_bytes_mut]: Self::str_as_bytes_mut
798    /// [remove_chars]: Self::remove_chars
799    /// [insert_chars]: Self::insert_chars
800    pub fn set_dirty(&mut self) {
801        unsafe {
802            (*(self.0)).BufDirty = true;
803        }
804    }
805
806    /// Gets a range of the selected text. See [selection_start_mut](Self::selection_start_mut)
807    /// and [selection_end_mut](Self::selection_end_mut) to mutably edit these values.
808    ///
809    /// This Range is given in `usize` so that it might be used in indexing
810    /// operations more easily. To quickly grab the selected text, use [selected](Self::selected).
811    pub fn selection(&self) -> Range<usize> {
812        unsafe { (*(self.0)).SelectionStart as usize..(*(self.0)).SelectionEnd as usize }
813    }
814
815    /// Returns the selected text directly. Note that if no text is selected,
816    /// an empty str slice will be returned.
817    pub fn selected(&self) -> &str {
818        &self.str()[self.selection()]
819    }
820
821    /// Sets the cursor to select all.
822    pub fn select_all(&mut self) {
823        unsafe {
824            sys::ImGuiInputTextCallbackData_SelectAll(self.0);
825        }
826    }
827
828    /// Clears the selection.
829    pub fn clear_selection(&mut self) {
830        unsafe {
831            sys::ImGuiInputTextCallbackData_ClearSelection(self.0);
832        }
833    }
834
835    /// Checks if there is a selection within the text.
836    pub fn has_selection(&self) -> bool {
837        !self.selection().is_empty()
838    }
839
840    /// Pushes the given str to the end of this buffer. If this
841    /// would require the String to resize, it will be resized.
842    /// This is automatically handled.
843    pub fn push_str(&mut self, s: &str) {
844        // this is safe because the ench of a self.str is a char_boundary.
845        unsafe {
846            self.insert_chars_unsafe((*self.0).BufTextLen as usize, s);
847        }
848    }
849
850    /// Inserts the given string at the given position. If this
851    /// would require the String to resize, it will be resized
852    /// automatically.
853    ///
854    /// ## Panics
855    /// Panics if the `pos` is not a char_boundary.
856    pub fn insert_chars(&mut self, pos: usize, s: &str) {
857        assert!(self.str().is_char_boundary(pos));
858        unsafe {
859            self.insert_chars_unsafe(pos, s);
860        }
861    }
862
863    /// Inserts the given string at the given position, unsafely. If this
864    /// would require the String to resize, it will be resized automatically.
865    ///
866    /// ## Safety
867    ///
868    /// It is up to the caller to confirm that the `pos` is a valid byte
869    /// position, or use [insert_chars](Self::insert_chars) which will panic
870    /// if it isn't.
871    pub unsafe fn insert_chars_unsafe(&mut self, pos: usize, s: &str) {
872        let start = s.as_ptr();
873        let end = start.add(s.len());
874
875        sys::ImGuiInputTextCallbackData_InsertChars(
876            self.0,
877            pos as i32,
878            start as *const c_char,
879            end as *const c_char,
880        );
881    }
882
883    /// Clears the string to an empty buffer.
884    pub fn clear(&mut self) {
885        unsafe {
886            self.remove_chars_unchecked(0, (*self.0).BufTextLen as usize);
887        }
888    }
889
890    /// Removes the given number of characters from the string starting
891    /// at some byte pos.
892    ///
893    /// ## Panics
894    /// Panics if the `pos` is not a char boundary.
895    pub fn remove_chars(&mut self, pos: usize, char_count: usize) {
896        let inner = &self.str()[pos..];
897        let byte_count = inner
898            .char_indices()
899            .nth(char_count)
900            .map(|v| v.0)
901            .unwrap_or_else(|| inner.len());
902
903        unsafe {
904            self.remove_chars_unchecked(pos, byte_count);
905        }
906    }
907
908    /// Removes the given number of bytes from the string starting
909    /// at some byte pos, without checking for utf8 validity. Use
910    /// [remove_chars](Self::remove_chars) for a safe variant.
911    ///
912    /// ## Safety
913    ///
914    /// It is up to the caller to ensure that the position is at a valid utf8 char_boundary
915    /// and that there are enough bytes within the string remaining.
916    pub unsafe fn remove_chars_unchecked(&mut self, pos: usize, byte_count: usize) {
917        sys::ImGuiInputTextCallbackData_DeleteChars(self.0, pos as i32, byte_count as i32);
918    }
919
920    /// Get a reference to the text callback buffer's cursor pos.
921    pub fn cursor_pos(&self) -> usize {
922        unsafe { (*self.0).CursorPos as usize }
923    }
924
925    /// Set the text callback buffer's cursor pos.
926    pub fn set_cursor_pos(&mut self, cursor_pos: usize) {
927        unsafe {
928            (*self.0).CursorPos = cursor_pos as i32;
929        }
930    }
931
932    /// Get a mutable reference to the text callback buffer's selection start.
933    pub fn selection_start_mut(&mut self) -> &mut i32 {
934        unsafe { &mut (*self.0).SelectionStart }
935    }
936
937    /// Get a mutable reference to the text callback buffer's selection end..
938    pub fn selection_end_mut(&mut self) -> &mut i32 {
939        unsafe { &mut (*self.0).SelectionEnd }
940    }
941}
942
943#[repr(C)]
944struct UserData<T> {
945    container: *mut String,
946    cback_handler: T,
947}
948
949/// This is our default callback.
950extern "C" fn callback<T: InputTextCallbackHandler>(
951    data: *mut sys::ImGuiInputTextCallbackData,
952) -> c_int {
953    struct CallbackData<'a, T> {
954        event_flag: InputTextFlags,
955        user_data: &'a mut UserData<T>,
956    }
957
958    let callback_data = unsafe {
959        CallbackData {
960            event_flag: InputTextFlags::from_bits((*data).EventFlag as u32).unwrap(),
961            user_data: &mut *((*data).UserData as *mut UserData<T>),
962        }
963    };
964
965    // check this callback.
966    match callback_data.event_flag {
967        InputTextFlags::CALLBACK_ALWAYS => {
968            let text_info = unsafe { TextCallbackData::new(&mut *data) };
969            callback_data.user_data.cback_handler.on_always(text_info);
970        }
971        InputTextFlags::CALLBACK_EDIT => {
972            let text_info = unsafe { TextCallbackData::new(&mut *data) };
973
974            callback_data.user_data.cback_handler.on_edit(text_info);
975        }
976        InputTextFlags::CALLBACK_COMPLETION => {
977            let text_info = unsafe { TextCallbackData::new(&mut *data) };
978            callback_data
979                .user_data
980                .cback_handler
981                .on_completion(text_info);
982        }
983        InputTextFlags::CALLBACK_RESIZE => {
984            unsafe {
985                let requested_size = (*data).BufSize as usize;
986                let buffer = &mut *callback_data.user_data.container;
987
988                // just confirm that we ARE working with our string.
989                debug_assert_eq!(buffer.as_ptr() as *const _, (*data).Buf);
990
991                if requested_size > buffer.capacity() {
992                    let additional_bytes = requested_size - buffer.len();
993
994                    // reserve more data...
995                    buffer.reserve(additional_bytes);
996
997                    (*data).Buf = buffer.as_mut_ptr() as *mut _;
998                    (*data).BufDirty = true;
999                }
1000            }
1001        }
1002        InputTextFlags::CALLBACK_CHAR_FILTER => {
1003            let chr = unsafe { (*data).EventChar };
1004            let new_data = match callback_data.user_data.cback_handler.char_filter(chr) {
1005                Some(value) => value,
1006                // 0 means "do not use this char" in imgui docs
1007                None => 0,
1008            };
1009            // set the new char...
1010            unsafe {
1011                (*data).EventChar = new_data;
1012            }
1013        }
1014        InputTextFlags::CALLBACK_HISTORY => {
1015            let key = unsafe {
1016                let key = (*data).EventKey as u32;
1017                match key {
1018                    sys::ImGuiKey_UpArrow => HistoryDirection::Up,
1019                    sys::ImGuiKey_DownArrow => HistoryDirection::Down,
1020                    _ => panic!("Unexpected key"),
1021                }
1022            };
1023            let text_info = unsafe { TextCallbackData::new(&mut *data) };
1024
1025            callback_data
1026                .user_data
1027                .cback_handler
1028                .on_history(key, text_info);
1029        }
1030
1031        _ => {}
1032    }
1033
1034    0
1035}
1036
1037/// This is a Zst which implements TextCallbackHandler as a passthrough.
1038///
1039/// If you do not set a callback handler, this will be used (but will never
1040/// actually run, since you will not have pass imgui any flags).
1041pub struct PassthroughCallback;
1042impl InputTextCallbackHandler for PassthroughCallback {}