Skip to main content

dear_imgui_rs/widget/input/
single_line.rs

1use super::buffers::{finish_string_input_buffer, make_string_input_buffer};
2use super::callback_bridge::{
3    StringCallbackState, im_string_resize_callback, string_callback_router,
4};
5use super::callbacks::{InputTextCallback, InputTextCallbackHandler, PassthroughCallback};
6use super::validation::validate_input_text_flags;
7use crate::InputTextFlags;
8use crate::string::ImString;
9use crate::sys;
10use crate::ui::Ui;
11use std::borrow::Cow;
12use std::ffi::c_void;
13use std::marker::PhantomData;
14
15/// Builder for a text input widget
16#[must_use]
17pub struct InputText<'ui, 'p, L = Cow<'ui, str>, H = Cow<'ui, str>, T = PassthroughCallback> {
18    ui: &'ui Ui,
19    label: L,
20    buf: &'p mut String,
21    flags: InputTextFlags,
22    capacity_hint: Option<usize>,
23    hint: Option<H>,
24    callback_handler: T,
25    _phantom: PhantomData<&'ui ()>,
26}
27
28/// Builder for a text input backed by ImString (zero-copy)
29#[must_use]
30pub struct InputTextImStr<'ui, 'p, L = Cow<'ui, str>, H = Cow<'ui, str>, T = PassthroughCallback> {
31    ui: &'ui Ui,
32    label: L,
33    buf: &'p mut ImString,
34    flags: InputTextFlags,
35    hint: Option<H>,
36    callback_handler: T,
37    _phantom: PhantomData<&'ui ()>,
38}
39
40impl<'ui, 'p> InputTextImStr<'ui, 'p, Cow<'ui, str>, Cow<'ui, str>, PassthroughCallback> {
41    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>, buf: &'p mut ImString) -> Self {
42        Self {
43            ui,
44            label: label.into(),
45            buf,
46            flags: InputTextFlags::empty(),
47            hint: None,
48            callback_handler: PassthroughCallback,
49            _phantom: PhantomData,
50        }
51    }
52}
53
54impl<'ui, 'p, L: AsRef<str>, H: AsRef<str>, T> InputTextImStr<'ui, 'p, L, H, T> {
55    pub fn flags(mut self, flags: InputTextFlags) -> Self {
56        self.flags = flags;
57        self
58    }
59    pub fn hint<H2: AsRef<str>>(self, hint: H2) -> InputTextImStr<'ui, 'p, L, H2, T> {
60        InputTextImStr {
61            ui: self.ui,
62            label: self.label,
63            buf: self.buf,
64            flags: self.flags,
65            hint: Some(hint),
66            callback_handler: self.callback_handler,
67            _phantom: PhantomData,
68        }
69    }
70    pub fn read_only(mut self, ro: bool) -> Self {
71        self.flags.set(InputTextFlags::READ_ONLY, ro);
72        self
73    }
74    pub fn password(mut self, pw: bool) -> Self {
75        self.flags.set(InputTextFlags::PASSWORD, pw);
76        self
77    }
78    pub fn auto_select_all(mut self, v: bool) -> Self {
79        self.flags.set(InputTextFlags::AUTO_SELECT_ALL, v);
80        self
81    }
82    pub fn enter_returns_true(mut self, v: bool) -> Self {
83        self.flags.set(InputTextFlags::ENTER_RETURNS_TRUE, v);
84        self
85    }
86
87    pub fn build(self) -> bool {
88        let (label_ptr, hint_ptr) = self.ui.scratch_txt_with_opt(
89            self.label.as_ref(),
90            self.hint.as_ref().map(|hint| hint.as_ref()),
91        );
92        let buf_size = self.buf.capacity_with_nul().max(1);
93        self.buf.ensure_buf_size(buf_size);
94        let buf_ptr = self.buf.as_mut_ptr();
95        let user_ptr = self.buf as *mut ImString as *mut c_void;
96
97        validate_input_text_flags("InputTextImStr::build()", self.flags);
98        let flags = self.flags.raw() | sys::ImGuiInputTextFlags_CallbackResize as i32;
99        let result = self.ui.run_with_bound_context(|| unsafe {
100            if hint_ptr.is_null() {
101                sys::igInputText(
102                    label_ptr,
103                    buf_ptr,
104                    buf_size,
105                    flags,
106                    Some(im_string_resize_callback),
107                    user_ptr,
108                )
109            } else {
110                sys::igInputTextWithHint(
111                    label_ptr,
112                    hint_ptr,
113                    buf_ptr,
114                    buf_size,
115                    flags,
116                    Some(im_string_resize_callback),
117                    user_ptr,
118                )
119            }
120        });
121        // Ensure ImString logical length reflects actual text (scan to NUL)
122        unsafe { self.buf.refresh_len() };
123        result
124    }
125}
126impl<'ui, 'p> InputText<'ui, 'p, Cow<'ui, str>, Cow<'ui, str>, PassthroughCallback> {
127    /// Creates a new text input builder
128    pub fn new(ui: &'ui Ui, label: impl Into<Cow<'ui, str>>, buf: &'p mut String) -> Self {
129        Self {
130            ui,
131            label: label.into(),
132            buf,
133            flags: InputTextFlags::NONE,
134            capacity_hint: None,
135            hint: None,
136            callback_handler: PassthroughCallback,
137            _phantom: PhantomData,
138        }
139    }
140}
141
142impl<'ui, 'p, L, H, T> InputText<'ui, 'p, L, H, T> {
143    /// Sets the flags for the input
144    pub fn flags(mut self, flags: InputTextFlags) -> Self {
145        self.flags = flags;
146        self
147    }
148
149    /// Hint a minimum buffer capacity to reduce reallocations for large fields
150    pub fn capacity_hint(mut self, cap: usize) -> Self {
151        self.capacity_hint = Some(cap);
152        self
153    }
154
155    /// Sets a hint text
156    pub fn hint(self, hint: impl Into<Cow<'ui, str>>) -> InputText<'ui, 'p, L, Cow<'ui, str>, T> {
157        InputText {
158            ui: self.ui,
159            label: self.label,
160            buf: self.buf,
161            flags: self.flags,
162            capacity_hint: self.capacity_hint,
163            hint: Some(hint.into()),
164            callback_handler: self.callback_handler,
165            _phantom: PhantomData,
166        }
167    }
168
169    /// Sets a callback handler for the input text
170    pub fn callback<T2: InputTextCallbackHandler>(
171        self,
172        callback_handler: T2,
173    ) -> InputText<'ui, 'p, L, H, T2> {
174        InputText {
175            ui: self.ui,
176            label: self.label,
177            buf: self.buf,
178            flags: self.flags,
179            capacity_hint: self.capacity_hint,
180            hint: self.hint,
181            callback_handler,
182            _phantom: PhantomData,
183        }
184    }
185
186    /// Sets callback flags for the input text
187    pub fn callback_flags(mut self, callback_flags: InputTextCallback) -> Self {
188        self.flags |= InputTextFlags::from_bits_retain(callback_flags.bits() as i32);
189        self
190    }
191
192    /// Makes the input read-only
193    pub fn read_only(mut self, read_only: bool) -> Self {
194        self.flags.set(InputTextFlags::READ_ONLY, read_only);
195        self
196    }
197
198    /// Enables password mode
199    pub fn password(mut self, password: bool) -> Self {
200        self.flags.set(InputTextFlags::PASSWORD, password);
201        self
202    }
203
204    /// Enables auto-select all when first taking focus
205    pub fn auto_select_all(mut self, auto_select: bool) -> Self {
206        self.flags.set(InputTextFlags::AUTO_SELECT_ALL, auto_select);
207        self
208    }
209
210    /// Makes Enter key return true instead of adding new line
211    pub fn enter_returns_true(mut self, enter_returns: bool) -> Self {
212        self.flags
213            .set(InputTextFlags::ENTER_RETURNS_TRUE, enter_returns);
214        self
215    }
216
217    /// Allows only decimal characters (0123456789.+-*/)
218    pub fn chars_decimal(mut self, decimal: bool) -> Self {
219        self.flags.set(InputTextFlags::CHARS_DECIMAL, decimal);
220        self
221    }
222
223    /// Allows only hexadecimal characters (0123456789ABCDEFabcdef)
224    pub fn chars_hexadecimal(mut self, hex: bool) -> Self {
225        self.flags.set(InputTextFlags::CHARS_HEXADECIMAL, hex);
226        self
227    }
228
229    /// Turns a..z into A..Z
230    pub fn chars_uppercase(mut self, uppercase: bool) -> Self {
231        self.flags.set(InputTextFlags::CHARS_UPPERCASE, uppercase);
232        self
233    }
234
235    /// Filters out spaces and tabs
236    pub fn chars_no_blank(mut self, no_blank: bool) -> Self {
237        self.flags.set(InputTextFlags::CHARS_NO_BLANK, no_blank);
238        self
239    }
240}
241
242// Implementation for all InputText types
243impl<'ui, 'p, L, H, T> InputText<'ui, 'p, L, H, T>
244where
245    L: AsRef<str>,
246    H: AsRef<str>,
247    T: InputTextCallbackHandler,
248{
249    /// Builds the text input widget
250    pub fn build(self) -> bool {
251        let (label_ptr, hint_ptr) = self.ui.scratch_txt_with_opt(
252            self.label.as_ref(),
253            self.hint.as_ref().map(|hint| hint.as_ref()),
254        );
255
256        let mut input_buffer = make_string_input_buffer(self.buf, self.capacity_hint);
257        let capacity = input_buffer.len();
258        let buf_ptr = input_buffer.as_mut_ptr() as *mut std::os::raw::c_char;
259
260        let mut callback_state = StringCallbackState::new(&mut input_buffer, self.callback_handler);
261        let user_ptr = callback_state.user_ptr();
262
263        validate_input_text_flags("InputText::build()", self.flags);
264        let flags = self.flags.raw() | sys::ImGuiInputTextFlags_CallbackResize as i32;
265        let result = self.ui.run_with_bound_context(|| unsafe {
266            if hint_ptr.is_null() {
267                sys::igInputText(
268                    label_ptr,
269                    buf_ptr,
270                    capacity,
271                    flags,
272                    Some(string_callback_router::<T>),
273                    user_ptr,
274                )
275            } else {
276                sys::igInputTextWithHint(
277                    label_ptr,
278                    hint_ptr,
279                    buf_ptr,
280                    capacity,
281                    flags,
282                    Some(string_callback_router::<T>),
283                    user_ptr,
284                )
285            }
286        });
287
288        finish_string_input_buffer(self.buf, input_buffer);
289        result
290    }
291}