Skip to main content

dear_imgui_rs/widget/input/
multiline.rs

1use super::buffers::{finish_string_input_buffer, make_string_input_buffer};
2use super::callback_bridge::{
3    StringCallbackState, StringResizeCallbackState, im_string_multiline_resize_callback,
4    string_multiline_callback_router, string_multiline_resize_callback,
5};
6use super::callbacks::{InputTextCallback, InputTextCallbackHandler};
7use super::validation::validate_input_multiline_flags;
8use crate::InputTextMultilineFlags;
9use crate::string::ImString;
10use crate::sys;
11use crate::ui::Ui;
12use std::borrow::Cow;
13use std::ffi::c_void;
14
15/// Builder for multiline text input widget
16#[derive(Debug)]
17#[must_use]
18pub struct InputTextMultiline<'ui, 'p> {
19    ui: &'ui Ui,
20    label: Cow<'ui, str>,
21    buf: &'p mut String,
22    size: [f32; 2],
23    flags: InputTextMultilineFlags,
24    capacity_hint: Option<usize>,
25}
26
27/// Builder for multiline text input backed by ImString (zero-copy)
28#[derive(Debug)]
29#[must_use]
30pub struct InputTextMultilineImStr<'ui, 'p> {
31    ui: &'ui Ui,
32    label: Cow<'ui, str>,
33    buf: &'p mut ImString,
34    size: [f32; 2],
35    flags: InputTextMultilineFlags,
36}
37
38impl<'ui, 'p> InputTextMultilineImStr<'ui, 'p> {
39    pub fn new(
40        ui: &'ui Ui,
41        label: impl Into<Cow<'ui, str>>,
42        buf: &'p mut ImString,
43        size: impl Into<[f32; 2]>,
44    ) -> Self {
45        Self {
46            ui,
47            label: label.into(),
48            buf,
49            size: size.into(),
50            flags: InputTextMultilineFlags::NONE,
51        }
52    }
53    pub fn flags(mut self, flags: InputTextMultilineFlags) -> Self {
54        self.flags = flags;
55        self
56    }
57    pub fn read_only(mut self, v: bool) -> Self {
58        self.flags.set(InputTextMultilineFlags::READ_ONLY, v);
59        self
60    }
61    pub fn build(self) -> bool {
62        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
63        let buf_size = self.buf.capacity_with_nul().max(1);
64        self.buf.ensure_buf_size(buf_size);
65        let buf_ptr = self.buf.as_mut_ptr();
66        let user_ptr = self.buf as *mut ImString as *mut c_void;
67        let size_vec: sys::ImVec2 = self.size.into();
68
69        validate_input_multiline_flags("InputTextMultilineImStr::build()", self.flags);
70        let flags = self.flags.raw() | sys::ImGuiInputTextFlags_CallbackResize as i32;
71        let result = self.ui.run_with_bound_context(|| unsafe {
72            sys::igInputTextMultiline(
73                label_ptr,
74                buf_ptr,
75                buf_size,
76                size_vec,
77                flags,
78                Some(im_string_multiline_resize_callback),
79                user_ptr,
80            )
81        });
82        // Ensure ImString logical length reflects actual text (scan to NUL)
83        unsafe { self.buf.refresh_len() };
84        result
85    }
86}
87impl<'ui, 'p> InputTextMultiline<'ui, 'p> {
88    /// Creates a new multiline text input builder
89    pub fn new(
90        ui: &'ui Ui,
91        label: impl Into<Cow<'ui, str>>,
92        buf: &'p mut String,
93        size: impl Into<[f32; 2]>,
94    ) -> Self {
95        Self {
96            ui,
97            label: label.into(),
98            buf,
99            size: size.into(),
100            flags: InputTextMultilineFlags::NONE,
101            capacity_hint: None,
102        }
103    }
104
105    /// Sets the flags for the input
106    pub fn flags(mut self, flags: InputTextMultilineFlags) -> Self {
107        self.flags = flags;
108        self
109    }
110
111    /// Hint a minimum buffer capacity to reduce reallocations for large fields
112    pub fn capacity_hint(mut self, cap: usize) -> Self {
113        self.capacity_hint = Some(cap);
114        self
115    }
116
117    /// Makes the input read-only
118    pub fn read_only(mut self, read_only: bool) -> Self {
119        self.flags
120            .set(InputTextMultilineFlags::READ_ONLY, read_only);
121        self
122    }
123
124    /// Builds the multiline text input widget
125    pub fn build(self) -> bool {
126        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
127
128        let mut input_buffer = make_string_input_buffer(self.buf, self.capacity_hint);
129        let capacity = input_buffer.len();
130        let buf_ptr = input_buffer.as_mut_ptr() as *mut std::os::raw::c_char;
131
132        let mut callback_state = StringResizeCallbackState::new(&mut input_buffer);
133        let user_ptr = callback_state.user_ptr();
134
135        let size_vec: sys::ImVec2 = self.size.into();
136        validate_input_multiline_flags("InputTextMultiline::build()", self.flags);
137        let flags = self.flags.raw() | sys::ImGuiInputTextFlags_CallbackResize as i32;
138        let result = self.ui.run_with_bound_context(|| unsafe {
139            sys::igInputTextMultiline(
140                label_ptr,
141                buf_ptr,
142                capacity,
143                size_vec,
144                flags,
145                Some(string_multiline_resize_callback),
146                user_ptr,
147            )
148        });
149
150        finish_string_input_buffer(self.buf, input_buffer);
151        result
152    }
153
154    /// Enable ImGui callbacks for this multiline input and attach a handler.
155    pub fn callback<T2: InputTextCallbackHandler>(
156        mut self,
157        callbacks: InputTextCallback,
158        handler: T2,
159    ) -> InputTextMultilineWithCb<'ui, 'p, T2> {
160        // Note: ImGui forbids CallbackHistory/Completion with Multiline.
161        // We intentionally do NOT enable them here to avoid assertions.
162        if callbacks.contains(InputTextCallback::ALWAYS) {
163            self.flags |= InputTextMultilineFlags::from_bits_retain(
164                sys::ImGuiInputTextFlags_CallbackAlways as i32,
165            );
166        }
167        if callbacks.contains(InputTextCallback::CHAR_FILTER) {
168            self.flags |= InputTextMultilineFlags::from_bits_retain(
169                sys::ImGuiInputTextFlags_CallbackCharFilter as i32,
170            );
171        }
172        if callbacks.contains(InputTextCallback::EDIT) {
173            self.flags |= InputTextMultilineFlags::from_bits_retain(
174                sys::ImGuiInputTextFlags_CallbackEdit as i32,
175            );
176        }
177
178        InputTextMultilineWithCb {
179            ui: self.ui,
180            label: self.label,
181            buf: self.buf,
182            size: self.size,
183            flags: self.flags,
184            capacity_hint: self.capacity_hint,
185            handler,
186        }
187    }
188}
189
190/// Multiline InputText with attached callback handler
191pub struct InputTextMultilineWithCb<'ui, 'p, T> {
192    ui: &'ui Ui,
193    label: Cow<'ui, str>,
194    buf: &'p mut String,
195    size: [f32; 2],
196    flags: InputTextMultilineFlags,
197    capacity_hint: Option<usize>,
198    handler: T,
199}
200
201impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultilineWithCb<'ui, 'p, T> {
202    pub fn build(self) -> bool {
203        let label_ptr = self.ui.scratch_txt(self.label.as_ref());
204
205        let mut input_buffer = make_string_input_buffer(self.buf, self.capacity_hint);
206        let capacity = input_buffer.len();
207        let buf_ptr = input_buffer.as_mut_ptr() as *mut std::os::raw::c_char;
208
209        let mut callback_state = StringCallbackState::new(&mut input_buffer, self.handler);
210        let user_ptr = callback_state.user_ptr();
211
212        let size_vec: sys::ImVec2 = self.size.into();
213        validate_input_multiline_flags("InputTextMultilineWithCb::build()", self.flags);
214        let flags = self.flags.raw() | sys::ImGuiInputTextFlags_CallbackResize as i32;
215        let result = self.ui.run_with_bound_context(|| unsafe {
216            sys::igInputTextMultiline(
217                label_ptr,
218                buf_ptr,
219                capacity,
220                size_vec,
221                flags,
222                Some(string_multiline_callback_router::<T>),
223                user_ptr,
224            )
225        });
226
227        finish_string_input_buffer(self.buf, input_buffer);
228        result
229    }
230}