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 {}