fltk/
text.rs

1#![allow(deprecated)]
2
3use crate::enums::{Color, Font, Key};
4use crate::prelude::*;
5use crate::utils::FlString;
6use fltk_sys::text::*;
7use std::{
8    ffi::{CStr, CString},
9    os::raw,
10};
11
12// Stable shims for callbacks (must be outside functions so add/remove use same fn ptr)
13unsafe extern "C" fn text_modify_shim(
14    pos: raw::c_int,
15    inserted: raw::c_int,
16    deleted: raw::c_int,
17    restyled: raw::c_int,
18    deleted_text: *const raw::c_char,
19    data: *mut raw::c_void,
20) {
21    let temp = if deleted_text.is_null() {
22        String::from("")
23    } else {
24        CStr::from_ptr(deleted_text).to_string_lossy().to_string()
25    };
26    let a: *mut Box<dyn FnMut(i32, i32, i32, i32, &str)> =
27        data as *mut Box<dyn for<'r> FnMut(i32, i32, i32, i32, &'r str)>;
28    let f: &mut (dyn FnMut(i32, i32, i32, i32, &str)) = &mut **a;
29    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
30        f(pos, inserted, deleted, restyled, &temp)
31    }));
32}
33
34unsafe extern "C" fn text_predelete_shim(
35    pos: raw::c_int,
36    deleted: raw::c_int,
37    data: *mut raw::c_void,
38) {
39    let a: *mut Box<dyn FnMut(i32, i32)> = data as *mut Box<dyn FnMut(i32, i32)>;
40    let f: &mut (dyn FnMut(i32, i32)) = &mut **a;
41    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(pos, deleted)));
42}
43
44type BufWrapper = std::rc::Rc<*mut Fl_Text_Buffer>;
45
46/// Defines the text cursor styles supported by fltk
47#[repr(i32)]
48#[derive(Debug, Copy, Clone, PartialEq, Eq)]
49pub enum Cursor {
50    /// Normal
51    Normal,
52    /// Caret
53    Caret,
54    /// Dim
55    Dim,
56    /// Block
57    Block,
58    /// Heavy
59    Heavy,
60    /// Simple
61    Simple,
62}
63
64/// Wraps a text buffer, Cloning a text buffer invalidates the underlying pointer, thus the no derive(Clone)
65#[derive(Debug)]
66pub struct TextBuffer {
67    inner: BufWrapper,
68}
69
70impl std::default::Default for TextBuffer {
71    /// Initialized a default text buffer
72    fn default() -> TextBuffer {
73        unsafe {
74            let text_buffer = Fl_Text_Buffer_new();
75            assert!(!text_buffer.is_null());
76            TextBuffer {
77                inner: BufWrapper::new(text_buffer),
78            }
79        }
80    }
81}
82
83impl TextBuffer {
84    /// Deletes the `TextBuffer`
85    /// # Safety
86    /// The buffer shouldn't be deleted while the Display widget still needs it
87    pub unsafe fn delete(buf: Self) {
88        assert!(!buf.inner.is_null());
89        unsafe {
90            Fl_Text_Buffer_delete(*buf.inner);
91        }
92    }
93
94    /// Deletes the `TextBuffer`
95    /// # Safety
96    /// The buffer shouldn't be deleted while the Display widget still needs it
97    pub unsafe fn delete_buffer(buf: Self) {
98        Self::delete(buf)
99    }
100
101    /// Initializes a text buffer from a pointer
102    /// # Safety
103    /// The pointer must be valid
104    pub unsafe fn from_ptr(ptr: *mut Fl_Text_Buffer) -> Self {
105        assert!(!ptr.is_null());
106        let inner = BufWrapper::from(ptr);
107        let ptr = BufWrapper::into_raw(inner);
108        BufWrapper::increment_strong_count(ptr);
109        let inner = BufWrapper::from_raw(ptr);
110        TextBuffer { inner }
111    }
112
113    /// Returns the inner pointer from a text buffer
114    /// # Safety
115    /// Can return multiple mutable pointers to the same buffer
116    pub unsafe fn as_ptr(&self) -> *mut Fl_Text_Buffer {
117        let ptr = BufWrapper::into_raw(BufWrapper::clone(&self.inner));
118        BufWrapper::increment_strong_count(ptr);
119        let inner = BufWrapper::from_raw(ptr);
120        *inner
121    }
122
123    /// Sets the text of the buffer
124    pub fn set_text(&mut self, txt: &str) {
125        assert!(!self.inner.is_null());
126        unsafe {
127            let txt = CString::safe_new(txt);
128            Fl_Text_Buffer_set_text(*self.inner, txt.as_ptr())
129        }
130    }
131
132    /// Returns the text of the buffer
133    pub fn text(&self) -> String {
134        assert!(!self.inner.is_null());
135        unsafe {
136            let text = Fl_Text_Buffer_text(*self.inner);
137            assert!(!text.is_null());
138            CStr::from_ptr(text as *mut raw::c_char)
139                .to_string_lossy()
140                .to_string()
141        }
142    }
143
144    /**
145        Appends to the buffer.
146        To append and scroll to the end of the buffer:
147        ```rust,no_run
148        use fltk::{prelude::*, *};
149        let txt = "Some long text!";
150        let buf = text::TextBuffer::default();
151        let mut disp = text::TextDisplay::default();
152        disp.set_buffer(Some(buf));
153        disp.buffer().unwrap().append(txt);
154        disp.set_insert_position(disp.buffer().unwrap().length());
155        disp.scroll(
156            disp.count_lines(0, disp.buffer().unwrap().length(), true),
157            0,
158        );
159        ```
160    */
161    pub fn append(&mut self, text: &str) {
162        assert!(!self.inner.is_null());
163        let text = CString::safe_new(text);
164        unsafe { Fl_Text_Buffer_append(*self.inner, text.as_ptr()) }
165    }
166
167    /// Append bytes to the buffer
168    pub fn append2(&mut self, text: &[u8]) {
169        assert!(!self.inner.is_null());
170        unsafe { Fl_Text_Buffer_append2(*self.inner, text.as_ptr() as _, text.len() as _) }
171    }
172
173    /// Get the length of the buffer
174    pub fn length(&self) -> i32 {
175        assert!(!self.inner.is_null());
176        unsafe { Fl_Text_Buffer_length(*self.inner) }
177    }
178
179    /// Removes from the buffer
180    pub fn remove(&mut self, start: i32, end: i32) {
181        assert!(!self.inner.is_null());
182        unsafe {
183            Fl_Text_Buffer_remove(*self.inner, start, end);
184        }
185    }
186
187    /// Returns the text within the range
188    pub fn text_range(&self, start: i32, end: i32) -> Option<String> {
189        assert!(!self.inner.is_null());
190        unsafe {
191            let x = Fl_Text_Buffer_text_range(*self.inner, start, end);
192            if x.is_null() {
193                None
194            } else {
195                Some(
196                    CStr::from_ptr(x as *mut raw::c_char)
197                        .to_string_lossy()
198                        .to_string(),
199                )
200            }
201        }
202    }
203
204    /// Inserts text into a position
205    pub fn insert(&mut self, pos: i32, text: &str) {
206        assert!(!self.inner.is_null());
207        let text = CString::safe_new(text);
208        unsafe { Fl_Text_Buffer_insert(*self.inner, pos, text.as_ptr()) }
209    }
210
211    /// Replaces text from position `start` to `end`
212    pub fn replace(&mut self, start: i32, end: i32, text: &str) {
213        assert!(!self.inner.is_null());
214        assert!(end >= start);
215        let text = CString::safe_new(text);
216        unsafe { Fl_Text_Buffer_replace(*self.inner, start, end, text.as_ptr()) }
217    }
218
219    /// Copies text from a source buffer into the current buffer
220    pub fn copy_from(&mut self, source_buf: &TextBuffer, start: i32, end: i32, to: i32) {
221        assert!(!self.inner.is_null());
222        unsafe { Fl_Text_Buffer_copy(*self.inner, *source_buf.inner, start, end, to) }
223    }
224
225    /// Copies whole text from a source buffer into a new buffer
226    pub fn copy(&self) -> TextBuffer {
227        assert!(!self.inner.is_null());
228        let mut temp = TextBuffer::default();
229        temp.copy_from(self, 0, 0, self.length());
230        temp
231    }
232
233    /// Performs an undo operation on the buffer
234    /// # Errors
235    /// Errors on failure to undo
236    pub fn undo(&mut self) -> Result<(), FltkError> {
237        assert!(!self.inner.is_null());
238        unsafe {
239            match Fl_Text_Buffer_undo(*self.inner, std::ptr::null_mut()) {
240                0 => Err(FltkError::Unknown(String::from("Failed to undo"))),
241                _ => Ok(()),
242            }
243        }
244    }
245
246    /// Performs a redo operation on the buffer.
247    /// Returns the cursor position.
248    /// # Errors
249    /// Errors on failure to undo
250    pub fn redo(&mut self) -> Result<i32, FltkError> {
251        assert!(!self.inner.is_null());
252        unsafe {
253            let mut i = 0;
254            match Fl_Text_Buffer_redo(*self.inner, &mut i) {
255                0 => Err(FltkError::Unknown(String::from("Failed to redo"))),
256                _ => Ok(i),
257            }
258        }
259    }
260
261    /// Sets whether the buffer can undo
262    pub fn can_undo(&mut self, flag: bool) {
263        assert!(!self.inner.is_null());
264        unsafe { Fl_Text_Buffer_canUndo(*self.inner, flag as raw::c_char) }
265    }
266
267    /// Gets whether the buffer can undo
268    pub fn get_can_undo(&mut self) -> bool {
269        assert!(!self.inner.is_null());
270        unsafe { Fl_Text_Buffer_can_undo(*self.inner) != 0 }
271    }
272
273    /// Gets whether the buffer can redo
274    pub fn can_redo(&mut self) -> bool {
275        assert!(!self.inner.is_null());
276        unsafe { Fl_Text_Buffer_can_redo(*self.inner) != 0 }
277    }
278
279    /// Loads a file into the buffer
280    /// # Errors
281    /// Errors on failure to load file
282    pub fn load_file<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), FltkError> {
283        assert!(!self.inner.is_null());
284        if !path.as_ref().exists() {
285            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
286        }
287        let path = path
288            .as_ref()
289            .to_str()
290            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
291        let path = CString::new(path)?;
292        unsafe {
293            match Fl_Text_Buffer_load_file(*self.inner, path.as_ptr()) {
294                0 => Ok(()),
295                _ => Err(FltkError::Internal(FltkErrorKind::ResourceNotFound)),
296            }
297        }
298    }
299
300    /// Saves a buffer into a file
301    /// # Errors
302    /// Errors on failure to save file
303    pub fn save_file<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), FltkError> {
304        assert!(!self.inner.is_null());
305        let path = path
306            .as_ref()
307            .to_str()
308            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
309        let path = CString::new(path)?;
310        unsafe {
311            match Fl_Text_Buffer_save_file(*self.inner, path.as_ptr()) {
312                0 => Ok(()),
313                _ => Err(FltkError::Internal(FltkErrorKind::ResourceNotFound)),
314            }
315        }
316    }
317
318    /// Inserts a file at a position in the buffer
319    /// # Errors
320    /// Errors on failure to open or read file
321    pub fn insert_file<P: AsRef<std::path::Path>>(
322        &mut self,
323        path: P,
324        pos: i32,
325        buflen: i32,
326    ) -> Result<(), FltkError> {
327        assert!(!self.inner.is_null());
328        let path_ref = path.as_ref();
329        if !path_ref.exists() {
330            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
331        }
332        let path = path_ref
333            .to_str()
334            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
335        let path = CString::new(path)?;
336        unsafe {
337            match Fl_Text_Buffer_insertfile(*self.inner, path.as_ptr(), pos, buflen) {
338                0 => Ok(()),
339                _ => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
340            }
341        }
342    }
343
344    /// Appends a file to the end of the buffer
345    /// # Errors
346    /// Errors on failure to open or read file
347    pub fn append_file<P: AsRef<std::path::Path>>(
348        &mut self,
349        path: P,
350        buflen: i32,
351    ) -> Result<(), FltkError> {
352        assert!(!self.inner.is_null());
353        let path_ref = path.as_ref();
354        if !path_ref.exists() {
355            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
356        }
357        let path = path_ref
358            .to_str()
359            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
360        let path = CString::new(path)?;
361        unsafe {
362            match Fl_Text_Buffer_appendfile(*self.inner, path.as_ptr(), buflen) {
363                0 => Ok(()),
364                _ => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
365            }
366        }
367    }
368
369    /// Outputs a slice of the buffer into a file
370    /// # Errors
371    /// Errors on failure to open or write file
372    pub fn output_file<P: AsRef<std::path::Path>>(
373        &mut self,
374        path: P,
375        start: i32,
376        end: i32,
377        buflen: i32,
378    ) -> Result<(), FltkError> {
379        assert!(!self.inner.is_null());
380        let path_ref = path.as_ref();
381        let path = path_ref
382            .to_str()
383            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
384        let path = CString::new(path)?;
385        unsafe {
386            match Fl_Text_Buffer_outputfile(*self.inner, path.as_ptr(), start, end, buflen) {
387                0 => Ok(()),
388                _ => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
389            }
390        }
391    }
392
393    /// Returns the tab distance for the buffer
394    pub fn tab_distance(&self) -> i32 {
395        assert!(!self.inner.is_null());
396        unsafe { Fl_Text_Buffer_tab_distance(*self.inner) }
397    }
398
399    /// Sets the tab distance
400    pub fn set_tab_distance(&mut self, tab_dist: i32) {
401        assert!(!self.inner.is_null());
402        unsafe { Fl_Text_Buffer_set_tab_distance(*self.inner, tab_dist) }
403    }
404
405    /// Selects the text from start to end
406    pub fn select(&mut self, start: i32, end: i32) {
407        assert!(!self.inner.is_null());
408        unsafe { Fl_Text_Buffer_select(*self.inner, start, end) }
409    }
410
411    /// Returns whether text is selected
412    pub fn selected(&self) -> bool {
413        assert!(!self.inner.is_null());
414        unsafe { Fl_Text_Buffer_selected(*self.inner) != 0 }
415    }
416
417    /// Unselects text
418    pub fn unselect(&mut self) {
419        assert!(!self.inner.is_null());
420        unsafe { Fl_Text_Buffer_unselect(*self.inner) }
421    }
422
423    /// Returns the selection position
424    pub fn selection_position(&self) -> Option<(i32, i32)> {
425        assert!(!self.inner.is_null());
426        unsafe {
427            let mut start = 0;
428            let mut end = 0;
429            let ret =
430                Fl_Text_Buffer_selection_position(*self.inner, &mut start as _, &mut end as _);
431            if ret == 0 {
432                None
433            } else {
434                let x = (start, end);
435                Some(x)
436            }
437        }
438    }
439
440    /// Returns the selection text
441    pub fn selection_text(&self) -> String {
442        assert!(!self.inner.is_null());
443        unsafe {
444            let x = Fl_Text_Buffer_selection_text(*self.inner);
445            assert!(!x.is_null());
446            CStr::from_ptr(x as *mut raw::c_char)
447                .to_string_lossy()
448                .to_string()
449        }
450    }
451
452    /// Removes the selection
453    pub fn remove_selection(&mut self) {
454        assert!(!self.inner.is_null());
455        unsafe { Fl_Text_Buffer_remove_selection(*self.inner) }
456    }
457
458    /// Replaces selection
459    pub fn replace_selection(&mut self, text: &str) {
460        assert!(!self.inner.is_null());
461        let text = CString::safe_new(text);
462        unsafe { Fl_Text_Buffer_replace_selection(*self.inner, text.as_ptr()) }
463    }
464
465    /// Secondary selects the text from start to end
466    pub fn secondary_select(&mut self, start: i32, end: i32) {
467        assert!(!self.inner.is_null());
468        unsafe { Fl_Text_Buffer_secondary_select(*self.inner, start, end) }
469    }
470
471    /// Returns whether text is secondary selected
472    pub fn secondary_selected(&self) -> bool {
473        assert!(!self.inner.is_null());
474        unsafe { Fl_Text_Buffer_secondary_selected(*self.inner) != 0 }
475    }
476
477    /// Unselects text (secondary selection)
478    pub fn secondary_unselect(&mut self) {
479        assert!(!self.inner.is_null());
480        unsafe { Fl_Text_Buffer_secondary_unselect(*self.inner) }
481    }
482
483    /// Returns the secondary selection position
484    pub fn secondary_selection_position(&self) -> Option<(i32, i32)> {
485        assert!(!self.inner.is_null());
486        unsafe {
487            let mut start = 0;
488            let mut end = 0;
489            let ret = Fl_Text_Buffer_secondary_selection_position(
490                *self.inner,
491                &mut start as _,
492                &mut end as _,
493            );
494            if ret == 0 {
495                None
496            } else {
497                let x = (start, end);
498                Some(x)
499            }
500        }
501    }
502
503    /// Returns the secondary selection text
504    pub fn secondary_selection_text(&self) -> String {
505        assert!(!self.inner.is_null());
506        unsafe {
507            let x = Fl_Text_Buffer_secondary_selection_text(*self.inner);
508            assert!(!x.is_null());
509            CStr::from_ptr(x as *mut raw::c_char)
510                .to_string_lossy()
511                .to_string()
512        }
513    }
514
515    /// Removes the secondary selection
516    pub fn remove_secondary_selection(&mut self) {
517        assert!(!self.inner.is_null());
518        unsafe { Fl_Text_Buffer_remove_secondary_selection(*self.inner) }
519    }
520
521    /// Replaces the secondary selection
522    pub fn replace_secondary_selection(&mut self, text: &str) {
523        assert!(!self.inner.is_null());
524        let text = CString::safe_new(text);
525        unsafe { Fl_Text_Buffer_replace_secondary_selection(*self.inner, text.as_ptr()) }
526    }
527
528    /// Highlights selection
529    pub fn highlight(&mut self, start: i32, end: i32) {
530        assert!(!self.inner.is_null());
531        unsafe { Fl_Text_Buffer_highlight(*self.inner, start, end) }
532    }
533
534    /// Returns whether text is highlighted
535    pub fn is_highlighted(&self) -> bool {
536        assert!(!self.inner.is_null());
537        unsafe { Fl_Text_Buffer_is_highlighted(*self.inner) != 0 }
538    }
539
540    /// Unhighlights text
541    pub fn unhighlight(&mut self) {
542        assert!(!self.inner.is_null());
543        unsafe { Fl_Text_Buffer_unhighlight(*self.inner) }
544    }
545
546    /// Returns the highlight position
547    pub fn highlight_position(&self) -> Option<(i32, i32)> {
548        assert!(!self.inner.is_null());
549        unsafe {
550            let mut start = 0;
551            let mut end = 0;
552            let ret =
553                Fl_Text_Buffer_highlight_position(*self.inner, &mut start as _, &mut end as _);
554            if ret == 0 {
555                None
556            } else {
557                let x = (start, end);
558                Some(x)
559            }
560        }
561    }
562
563    /// Returns the highlighted text
564    pub fn highlight_text(&self) -> String {
565        assert!(!self.inner.is_null());
566        unsafe {
567            let x = Fl_Text_Buffer_highlight_text(*self.inner);
568            assert!(!x.is_null());
569            CStr::from_ptr(x as *mut raw::c_char)
570                .to_string_lossy()
571                .to_string()
572        }
573    }
574
575    /// Returns the line at pos
576    pub fn line_text(&self, pos: i32) -> String {
577        assert!(!self.inner.is_null());
578        unsafe {
579            let x = Fl_Text_Buffer_line_text(*self.inner, pos);
580            assert!(!x.is_null());
581            CStr::from_ptr(x as *mut raw::c_char)
582                .to_string_lossy()
583                .to_string()
584        }
585    }
586
587    /// Returns the index of the line's start position at pos
588    pub fn line_start(&self, pos: i32) -> i32 {
589        assert!(!self.inner.is_null());
590        unsafe { Fl_Text_Buffer_line_start(*self.inner, pos) }
591    }
592
593    /// Returns the index of the line's end position at pos
594    pub fn line_end(&self, pos: i32) -> i32 {
595        assert!(!self.inner.is_null());
596        unsafe { Fl_Text_Buffer_line_end(*self.inner, pos) }
597    }
598
599    /// Returns the index of the first character of a word at pos
600    pub fn word_start(&self, pos: i32) -> i32 {
601        assert!(!self.inner.is_null());
602        unsafe { Fl_Text_Buffer_word_start(*self.inner, pos) }
603    }
604
605    /// Returns the index of the last character of a word at pos
606    pub fn word_end(&self, pos: i32) -> i32 {
607        assert!(!self.inner.is_null());
608        unsafe { Fl_Text_Buffer_word_end(*self.inner, pos) }
609    }
610
611    /// Counts the lines from start to end
612    pub fn count_lines(&self, start: i32, end: i32) -> i32 {
613        assert!(!self.inner.is_null());
614        unsafe { Fl_Text_Buffer_count_lines(*self.inner, start, end) }
615    }
616
617    /// Calls the modify callbacks
618    pub fn call_modify_callbacks(&mut self) {
619        assert!(!self.inner.is_null());
620        unsafe { Fl_Text_Buffer_call_modify_callbacks(*self.inner) }
621    }
622
623    /// Calls the pre-delete callbacks
624    pub fn call_predelete_callbacks(&mut self) {
625        assert!(!self.inner.is_null());
626        unsafe { Fl_Text_Buffer_call_predelete_callbacks(*self.inner) }
627    }
628
629    /// Adds a modify callback.
630    /// callback args:
631    /// pos: i32, inserted items: i32, deleted items: i32, restyled items: i32, `deleted_text`
632    pub fn add_modify_callback<F: FnMut(i32, i32, i32, i32, &str) + 'static>(&mut self, cb: F) {
633        assert!(!self.inner.is_null());
634        unsafe {
635            let a: *mut Box<dyn FnMut(i32, i32, i32, i32, &str)> =
636                Box::into_raw(Box::new(Box::new(cb)));
637            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
638            let callback: Fl_Text_Modify_Cb = Some(text_modify_shim);
639            Fl_Text_Buffer_add_modify_callback(*self.inner, callback, data);
640        }
641    }
642
643    /// Removes a modify callback.
644    /// callback args:
645    /// pos: i32, inserted items: i32, deleted items: i32, restyled items: i32, `deleted_text`
646    pub fn remove_modify_callback<F: FnMut(i32, i32, i32, i32, &str) + 'static>(&mut self, cb: F) {
647        assert!(!self.inner.is_null());
648        unsafe {
649            let a: *mut Box<dyn FnMut(i32, i32, i32, i32, &str)> =
650                Box::into_raw(Box::new(Box::new(cb)));
651            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
652            let callback: Fl_Text_Modify_Cb = Some(text_modify_shim);
653            Fl_Text_Buffer_remove_modify_callback(*self.inner, callback, data);
654        }
655    }
656
657    /// Adds a pre-delete callback.
658    /// callback args: pos: i32, deleted items: i32
659    pub fn add_predelete_callback<F: FnMut(i32, i32) + 'static>(&mut self, cb: F) {
660        assert!(!self.inner.is_null());
661        unsafe {
662            let a: *mut Box<dyn FnMut(i32, i32)> = Box::into_raw(Box::new(Box::new(cb)));
663            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
664            let callback: Fl_Text_Predelete_Cb = Some(text_predelete_shim);
665            Fl_Text_Buffer_add_predelete_callback(*self.inner, callback, data);
666        }
667    }
668
669    /// Removes a pre-delete callback.
670    /// callback args: pos: i32, deleted items: i32
671    pub fn remove_predelete_callback<F: FnMut(i32, i32) + 'static>(&mut self, cb: F) {
672        assert!(!self.inner.is_null());
673        unsafe {
674            let a: *mut Box<dyn FnMut(i32, i32)> = Box::into_raw(Box::new(Box::new(cb)));
675            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
676            let callback: Fl_Text_Predelete_Cb = Some(text_predelete_shim);
677            Fl_Text_Buffer_remove_predelete_callback(*self.inner, callback, data);
678        }
679    }
680
681    /// Forward search for a string
682    pub fn search_forward(
683        &self,
684        start_pos: i32,
685        search_string: &str,
686        match_case: bool,
687    ) -> Option<i32> {
688        unsafe {
689            let search_string = CString::safe_new(search_string);
690            let mut found_pos = 0;
691            let ret = Fl_Text_Buffer_search_forward(
692                *self.inner,
693                start_pos,
694                search_string.as_ptr() as _,
695                &mut found_pos as _,
696                match_case as _,
697            );
698            if ret == 0 {
699                None
700            } else {
701                Some(found_pos)
702            }
703        }
704    }
705
706    /// Backward search for a string
707    pub fn search_backward(
708        &self,
709        start_pos: i32,
710        search_string: &str,
711        match_case: bool,
712    ) -> Option<i32> {
713        unsafe {
714            let search_string = CString::safe_new(search_string);
715            let mut found_pos = 0;
716            let ret = Fl_Text_Buffer_search_backward(
717                *self.inner,
718                start_pos,
719                search_string.as_ptr() as _,
720                &mut found_pos as _,
721                match_case as _,
722            );
723            if ret == 0 {
724                None
725            } else {
726                Some(found_pos)
727            }
728        }
729    }
730
731    /// Forward search for a char
732    pub fn find_char_forward(&self, start_pos: i32, search_char: char) -> Option<i32> {
733        unsafe {
734            let mut found_pos = 0;
735            let ret = Fl_Text_Buffer_findchar_forward(
736                *self.inner,
737                start_pos,
738                search_char as _,
739                &mut found_pos as _,
740            );
741            if ret == 0 {
742                None
743            } else {
744                Some(found_pos)
745            }
746        }
747    }
748
749    /// Backward search for a char
750    pub fn find_char_backward(&self, start_pos: i32, search_char: char) -> Option<i32> {
751        unsafe {
752            let mut found_pos = 0;
753            let ret = Fl_Text_Buffer_findchar_backward(
754                *self.inner,
755                start_pos,
756                search_char as _,
757                &mut found_pos as _,
758            );
759            if ret == 0 {
760                None
761            } else {
762                Some(found_pos)
763            }
764        }
765    }
766
767    /// Returns the Unicode character at byte position `pos` (aligned to UTF-8 boundary)
768    pub fn char_at(&self, pos: i32) -> Option<char> {
769        assert!(!self.inner.is_null());
770        unsafe {
771            let ch = Fl_Text_Buffer_char_at(*self.inner, pos);
772            std::char::from_u32(ch)
773        }
774    }
775
776    /// Returns the raw byte at byte position `pos`
777    pub fn byte_at(&self, pos: i32) -> u8 {
778        assert!(!self.inner.is_null());
779        unsafe { Fl_Text_Buffer_byte_at(*self.inner, pos) as u8 }
780    }
781
782    /// Returns a raw pointer to the underlying buffer at byte position `pos`
783    /// # Safety
784    /// The pointer becomes invalid on subsequent mutations of the buffer.
785    pub unsafe fn address(&self, pos: i32) -> *const raw::c_char {
786        assert!(!self.inner.is_null());
787        Fl_Text_Buffer_address(*self.inner as *const _, pos)
788    }
789
790    /// Returns a mutable raw pointer to the underlying buffer at byte position `pos`
791    /// # Safety
792    /// The pointer becomes invalid on subsequent mutations of the buffer.
793    pub unsafe fn address_mut(&mut self, pos: i32) -> *mut raw::c_char {
794        assert!(!self.inner.is_null());
795        Fl_Text_Buffer_address2(*self.inner, pos)
796    }
797
798    /// Align an index into the buffer to the current or previous UTF-8 boundary.
799    pub fn utf8_align(&self, pos: i32) -> i32 {
800        assert!(!self.inner.is_null());
801        unsafe { Fl_Text_Buffer_utf8_align(*self.inner as *const _, pos) }
802    }
803
804    /// Returns true if position `pos` is a word separator
805    pub fn is_word_separator(&self, pos: i32) -> bool {
806        assert!(!self.inner.is_null());
807        unsafe { Fl_Text_Buffer_is_word_separator(*self.inner as *const _, pos) != 0 }
808    }
809}
810
811#[cfg(not(feature = "single-threaded"))]
812unsafe impl Sync for TextBuffer {}
813
814#[cfg(not(feature = "single-threaded"))]
815unsafe impl Send for TextBuffer {}
816
817impl PartialEq for TextBuffer {
818    fn eq(&self, other: &Self) -> bool {
819        self.inner == other.inner
820    }
821}
822
823impl Eq for TextBuffer {}
824
825impl Clone for TextBuffer {
826    fn clone(&self) -> TextBuffer {
827        assert!(!self.inner.is_null());
828        TextBuffer {
829            inner: BufWrapper::clone(&self.inner),
830        }
831    }
832}
833
834impl Drop for TextBuffer {
835    fn drop(&mut self) {
836        assert!(!self.inner.is_null());
837        if BufWrapper::strong_count(&self.inner) == 1 {
838            unsafe {
839                Fl_Text_Buffer_delete(*self.inner);
840            }
841        }
842    }
843}
844
845/// Defines wrap modes
846#[repr(i32)]
847#[derive(Debug, Copy, Clone, PartialEq, Eq)]
848pub enum WrapMode {
849    /// No wrapping
850    None,
851    /// Wrap text at certain column
852    AtColumn,
853    /// Wrap text at certain pixel
854    AtPixel,
855    /// Wrap text at certain bounds
856    AtBounds,
857}
858
859/// Defines drag types
860#[repr(i32)]
861#[derive(Debug, Copy, Clone, PartialEq, Eq)]
862pub enum DragType {
863    /// No dragging
864    None = -2,
865    /// Drag Start "drag n drop" event
866    StartDnd = -1,
867    /// Drag single character
868    Char = 0,
869    /// Drag single word
870    Word = 1,
871    /// Drag single line
872    Line = 2,
873}
874
875/// Creates a non-editable text display widget
876#[derive(Debug)]
877pub struct TextDisplay {
878    inner: crate::widget::WidgetTracker,
879    is_derived: bool,
880}
881
882crate::macros::widget::impl_widget_ext!(TextDisplay, Fl_Text_Display);
883crate::macros::widget::impl_widget_base!(TextDisplay, Fl_Text_Display);
884crate::macros::widget::impl_widget_default!(TextDisplay);
885crate::macros::display::impl_display_ext!(TextDisplay, Fl_Text_Display);
886
887/// Creates an editable text display widget
888#[derive(Debug)]
889pub struct TextEditor {
890    inner: crate::widget::WidgetTracker,
891    is_derived: bool,
892}
893
894crate::macros::widget::impl_widget_ext!(TextEditor, Fl_Text_Editor);
895crate::macros::widget::impl_widget_base!(TextEditor, Fl_Text_Editor);
896crate::macros::widget::impl_widget_default!(TextEditor);
897crate::macros::display::impl_display_ext!(TextEditor, Fl_Text_Editor);
898
899/// Alias Fl_Text_Editor for use in `add_key_binding`
900pub type TextEditorPtr = *mut Fl_Text_Editor;
901
902#[deprecated(
903    since = "1.5.0",
904    note = "please use `fltk::terminal::Terminal` instead"
905)]
906/// Creates an editable text display widget to handle terminal-like behavior, such as
907/// logging events or debug information.
908/// `SimpleTerminal` already has an internal buffer.
909/// It is NOT is a full terminal emulator; it does NOT
910/// handle stdio redirection, pipes, pseudo ttys, termio character cooking,
911/// keyboard input processing, screen addressing, random cursor positioning,
912/// curses compatibility, or VT100/xterm emulation.
913#[derive(Debug)]
914pub struct SimpleTerminal {
915    inner: crate::widget::WidgetTracker,
916    is_derived: bool,
917}
918
919crate::macros::widget::impl_widget_ext!(SimpleTerminal, Fl_Simple_Terminal);
920crate::macros::widget::impl_widget_base!(SimpleTerminal, Fl_Simple_Terminal);
921crate::macros::widget::impl_widget_default!(SimpleTerminal);
922crate::macros::display::impl_display_ext!(SimpleTerminal, Fl_Simple_Terminal);
923
924/// The attribute of the style entry
925#[derive(Debug, Clone, Copy, PartialEq, Eq)]
926#[repr(u32)]
927#[non_exhaustive]
928pub enum TextAttr {
929    /// No attribute
930    None = 0x0000,
931    /// Use the background color in the `bgcolor` field
932    BgColor = 0x0001,
933    /// Use the background color in the `bgcolor` field to highlight the whole line
934    BgColorExt = 0x0003,
935    /// A single underline, underline types are mutually exclusive
936    Underline = 0x0004,
937    /// Grammar suggestion (blue dotted underline)
938    Grammar = 0x0008,
939    /// Spelling suggestion (red dotted underline)
940    Spelling = 0x000C,
941    /// Line through the middle of the text
942    StrikeThrough = 0x0010,
943}
944
945/// Defines the styles used in the `set_highlight_data`, which is used with style buffers
946#[derive(Debug, Clone, Copy, PartialEq, Eq)]
947pub struct StyleTableEntry {
948    /// Font color
949    pub color: Color,
950    /// Font type
951    pub font: Font,
952    /// Font size
953    pub size: i32,
954}
955
956impl Default for StyleTableEntry {
957    fn default() -> Self {
958        Self {
959            color: Color::Foreground,
960            font: Font::Helvetica,
961            size: crate::app::font_size(),
962        }
963    }
964}
965
966/// Defines the styles used in the `set_highlight_data`, which is used with style buffers
967#[derive(Debug, Clone, Copy, PartialEq, Eq)]
968pub struct StyleTableEntryExt {
969    /// Font color
970    pub color: Color,
971    /// Font type
972    pub font: Font,
973    /// Font size
974    pub size: i32,
975    /// attribute
976    pub attr: TextAttr,
977    /// background color
978    pub bgcolor: Color,
979}
980
981impl Default for StyleTableEntryExt {
982    fn default() -> Self {
983        Self {
984            color: Color::Foreground,
985            font: Font::Helvetica,
986            size: crate::app::font_size(),
987            attr: TextAttr::None,
988            bgcolor: Color::Background2,
989        }
990    }
991}
992
993impl TextEditor {
994    /// Any state/shortcut
995    pub const AnyState: crate::enums::Shortcut = crate::enums::Shortcut::from_i32(-1);
996
997    /// Set to insert mode
998    pub fn set_insert_mode(&mut self, b: bool) {
999        assert!(self.has_buffer());
1000        unsafe { Fl_Text_Editor_set_insert_mode(self.inner.widget() as _, b as i32) }
1001    }
1002
1003    /// Returns whether insert mode is set
1004    pub fn insert_mode(&self) -> bool {
1005        assert!(self.has_buffer());
1006        unsafe { Fl_Text_Editor_insert_mode(self.inner.widget() as _) != 0 }
1007    }
1008
1009    /// Set tab navigation
1010    pub fn set_tab_nav(&mut self, val: bool) {
1011        assert!(self.has_buffer());
1012        unsafe { Fl_Text_Editor_set_tab_nav(self.inner.widget() as _, val as i32) }
1013    }
1014
1015    /// Returns whether tab navigation is set
1016    pub fn tab_nav(&self) -> bool {
1017        assert!(self.has_buffer());
1018        unsafe { Fl_Text_Editor_tab_nav(self.inner.widget() as _) != 0 }
1019    }
1020
1021    /// Copies the text within the `TextEditor` widget
1022    pub fn copy(&self) {
1023        assert!(self.has_buffer());
1024        unsafe {
1025            Fl_Text_Editor_kf_copy(self.inner.widget() as _);
1026        }
1027    }
1028
1029    /// Cuts the text within the `TextEditor` widget
1030    pub fn cut(&self) {
1031        assert!(self.has_buffer());
1032        unsafe {
1033            Fl_Text_Editor_kf_cut(self.inner.widget() as _);
1034        }
1035    }
1036
1037    /// Pastes text from the clipboard into the `TextEditor` widget
1038    pub fn paste(&self) {
1039        assert!(self.has_buffer());
1040        unsafe {
1041            Fl_Text_Editor_kf_paste(self.inner.widget() as _);
1042        }
1043    }
1044
1045    /// Undo changes in the `TextEditor` widget
1046    pub fn undo(&self) {
1047        assert!(self.has_buffer());
1048        unsafe {
1049            Fl_Text_Editor_kf_undo(self.inner.widget() as _);
1050        }
1051    }
1052
1053    /// Undo changes in the `TextEditor` widget
1054    pub fn redo(&self) {
1055        assert!(self.has_buffer());
1056        unsafe {
1057            Fl_Text_Editor_kf_redo(self.inner.widget() as _);
1058        }
1059    }
1060
1061    /// Inserts the text associated with key 'c'
1062    pub fn kf_default(&mut self, c: Key) {
1063        assert!(self.has_buffer());
1064        unsafe {
1065            Fl_Text_Editor_kf_default(c.bits(), self.inner.widget() as _);
1066        }
1067    }
1068
1069    /// Ignores the key 'c' in editor
1070    pub fn kf_ignore(&mut self, c: Key) {
1071        assert!(self.has_buffer());
1072        unsafe {
1073            Fl_Text_Editor_kf_ignore(c.bits(), self.inner.widget() as _);
1074        }
1075    }
1076
1077    /// Does a backspace
1078    pub fn kf_backspace(&mut self) {
1079        assert!(self.has_buffer());
1080        unsafe {
1081            Fl_Text_Editor_kf_backspace(self.inner.widget() as _);
1082        }
1083    }
1084
1085    /// Inserts a new line
1086    pub fn kf_enter(&mut self) {
1087        assert!(self.has_buffer());
1088        unsafe {
1089            Fl_Text_Editor_kf_enter(self.inner.widget() as _);
1090        }
1091    }
1092
1093    /// Moves the cursor in the direction indicated by the key
1094    pub fn kf_move(&mut self, c: Key) {
1095        assert!(self.has_buffer());
1096        unsafe {
1097            Fl_Text_Editor_kf_move(c.bits(), self.inner.widget() as _);
1098        }
1099    }
1100
1101    /// Extends the current selection in the direction of key 'c'
1102    pub fn kf_shift_move(&mut self, c: Key) {
1103        assert!(self.has_buffer());
1104        unsafe {
1105            Fl_Text_Editor_kf_shift_move(c.bits(), self.inner.widget() as _);
1106        }
1107    }
1108
1109    /// Moves the current text cursor in the direction indicated by control key 'c'
1110    pub fn kf_ctrl_move(&mut self, c: Key) {
1111        assert!(self.has_buffer());
1112        unsafe {
1113            Fl_Text_Editor_kf_ctrl_move(c.bits(), self.inner.widget() as _);
1114        }
1115    }
1116
1117    /// Extends the current selection in the direction indicated by control key 'c'
1118    pub fn kf_c_s_move(&mut self, c: Key) {
1119        assert!(self.has_buffer());
1120        unsafe {
1121            Fl_Text_Editor_kf_c_s_move(c.bits(), self.inner.widget() as _);
1122        }
1123    }
1124
1125    /// Moves the current text cursor in the direction indicated by meta key 'c'
1126    pub fn kf_meta_move(&mut self, c: Key) {
1127        assert!(self.has_buffer());
1128        unsafe {
1129            Fl_Text_Editor_kf_meta_move(c.bits(), self.inner.widget() as _);
1130        }
1131    }
1132
1133    /// Extends the current selection in the direction indicated by meta key 'c'
1134    pub fn kf_m_s_move(&mut self, c: Key) {
1135        assert!(self.has_buffer());
1136        unsafe {
1137            Fl_Text_Editor_kf_m_s_move(c.bits(), self.inner.widget() as _);
1138        }
1139    }
1140
1141    /// Moves the text cursor to the beginning of the current line
1142    pub fn kf_home(&mut self) {
1143        assert!(self.has_buffer());
1144        unsafe {
1145            Fl_Text_Editor_kf_home(self.inner.widget() as _);
1146        }
1147    }
1148
1149    /// Moves the text cursor to the end of the current line
1150    pub fn kf_end(&mut self) {
1151        assert!(self.has_buffer());
1152        unsafe {
1153            Fl_Text_Editor_kf_end(self.inner.widget() as _);
1154        }
1155    }
1156
1157    /// Moves the text cursor one character to the left
1158    pub fn kf_left(&mut self) {
1159        assert!(self.has_buffer());
1160        unsafe {
1161            Fl_Text_Editor_kf_left(self.inner.widget() as _);
1162        }
1163    }
1164
1165    /// Moves the text cursor one line up
1166    pub fn kf_up(&mut self) {
1167        assert!(self.has_buffer());
1168        unsafe {
1169            Fl_Text_Editor_kf_up(self.inner.widget() as _);
1170        }
1171    }
1172
1173    /// Moves the text cursor one character to the right
1174    pub fn kf_right(&mut self) {
1175        assert!(self.has_buffer());
1176        unsafe {
1177            Fl_Text_Editor_kf_right(self.inner.widget() as _);
1178        }
1179    }
1180
1181    /// Moves the text cursor one line down
1182    pub fn kf_down(&mut self) {
1183        assert!(self.has_buffer());
1184        unsafe {
1185            Fl_Text_Editor_kf_down(self.inner.widget() as _);
1186        }
1187    }
1188
1189    /// Moves the text cursor up one page
1190    pub fn kf_page_up(&mut self) {
1191        assert!(self.has_buffer());
1192        unsafe {
1193            Fl_Text_Editor_kf_page_up(self.inner.widget() as _);
1194        }
1195    }
1196
1197    /// Moves the text cursor down one page
1198    pub fn kf_page_down(&mut self) {
1199        assert!(self.has_buffer());
1200        unsafe {
1201            Fl_Text_Editor_kf_page_down(self.inner.widget() as _);
1202        }
1203    }
1204
1205    /// Toggles the insert mode for the editor
1206    pub fn kf_insert(&mut self) {
1207        assert!(self.has_buffer());
1208        unsafe {
1209            Fl_Text_Editor_kf_insert(self.inner.widget() as _);
1210        }
1211    }
1212
1213    /// Does a delete of selected text or the current character in the current buffer
1214    pub fn kf_delete(&mut self) {
1215        assert!(self.has_buffer());
1216        unsafe {
1217            Fl_Text_Editor_kf_delete(self.inner.widget() as _);
1218        }
1219    }
1220
1221    /// Selects all text in the associated buffer
1222    pub fn kf_select_all(&mut self) {
1223        assert!(self.has_buffer());
1224        unsafe {
1225            Fl_Text_Editor_kf_select_all(self.inner.widget() as _);
1226        }
1227    }
1228
1229    /// Add a key binding
1230    pub fn add_key_binding(
1231        &mut self,
1232        key: crate::enums::Key,
1233        shortcut: crate::enums::Shortcut,
1234        cb: fn(key: crate::enums::Key, editor: TextEditorPtr) -> i32,
1235    ) {
1236        unsafe {
1237            Fl_Text_Editor_add_key_binding(
1238                self.inner.widget() as _,
1239                key.bits(),
1240                shortcut.bits(),
1241                std::mem::transmute(Some(cb)),
1242            );
1243        }
1244    }
1245
1246    /// Remove a key binding
1247    pub fn remove_key_binding(&mut self, key: crate::enums::Key, shortcut: crate::enums::Shortcut) {
1248        unsafe {
1249            Fl_Text_Editor_remove_key_binding(
1250                self.inner.widget() as _,
1251                key.bits(),
1252                shortcut.bits(),
1253            );
1254        }
1255    }
1256}
1257
1258impl SimpleTerminal {
1259    /// Sets whether the terminal automatically stays at the bottom
1260    pub fn set_stay_at_bottom(&mut self, arg1: bool) {
1261        assert!(self.has_buffer());
1262        unsafe { Fl_Simple_Terminal_set_stay_at_bottom(self.inner.widget() as _, arg1 as i32) }
1263    }
1264
1265    /// Returns whether the terminal automatically stays at the bottom
1266    pub fn stay_at_bottom(&self) -> bool {
1267        assert!(self.has_buffer());
1268        unsafe { Fl_Simple_Terminal_stay_at_bottom(self.inner.widget() as _) != 0 }
1269    }
1270
1271    /// Sets the max lines allowed in history
1272    pub fn set_history_lines(&mut self, arg1: i32) {
1273        assert!(self.has_buffer());
1274        unsafe { Fl_Simple_Terminal_set_history_lines(self.inner.widget() as _, arg1) }
1275    }
1276
1277    /// Gets the max lines allowed in history
1278    pub fn history_lines(&self) -> i32 {
1279        assert!(self.has_buffer());
1280        unsafe { Fl_Simple_Terminal_history_lines(self.inner.widget() as _) }
1281    }
1282
1283    /// Enables ANSI sequences within the text to control text colors
1284    pub fn set_ansi(&mut self, val: bool) {
1285        assert!(self.has_buffer());
1286        unsafe { Fl_Simple_Terminal_set_ansi(self.inner.widget() as _, val as i32) }
1287    }
1288
1289    /// Returns whether ANSI sequences are enabled
1290    pub fn ansi(&self) -> bool {
1291        assert!(self.has_buffer());
1292        unsafe { Fl_Simple_Terminal_ansi(self.inner.widget() as _) != 0 }
1293    }
1294
1295    /// Appends text to the terminal buffer
1296    pub fn append(&mut self, s: &str) {
1297        assert!(self.has_buffer());
1298        let raw_s = CString::safe_new(s).into_raw();
1299        unsafe {
1300            Fl_Simple_Terminal_append(self.inner.widget() as _, raw_s as _);
1301            // Take ownership of raw_s back so it will be dropped
1302            let _raw_s = CString::from_raw(raw_s);
1303        }
1304    }
1305
1306    /// Appends data to the terminal buffer
1307    pub fn append2(&mut self, s: &[u8]) {
1308        assert!(self.has_buffer());
1309        unsafe {
1310            Fl_Simple_Terminal_append2(self.inner.widget() as _, s.as_ptr() as _, s.len() as _)
1311        }
1312    }
1313
1314    /// Sets the text of the terminal buffer
1315    pub fn set_text(&mut self, s: &str) {
1316        assert!(self.has_buffer());
1317        let raw_s = CString::safe_new(s).into_raw();
1318        unsafe {
1319            Fl_Simple_Terminal_set_text(self.inner.widget() as _, raw_s as _);
1320            // Take ownership of raw_s back so it will be dropped
1321            let _raw_s = CString::from_raw(raw_s);
1322        }
1323    }
1324
1325    /// Sets the byte content of the terminal buffer
1326    pub fn set_bytes(&mut self, s: &[u8]) {
1327        assert!(self.has_buffer());
1328        unsafe {
1329            Fl_Simple_Terminal_set_text2(self.inner.widget() as _, s.as_ptr() as _, s.len() as _)
1330        }
1331    }
1332
1333    /// Gets the text of the terminal buffer
1334    pub fn text(&self) -> String {
1335        assert!(self.has_buffer());
1336        unsafe {
1337            let ptr = Fl_Simple_Terminal_text(self.inner.widget() as _);
1338            assert!(!ptr.is_null());
1339            CStr::from_ptr(ptr as *mut raw::c_char)
1340                .to_string_lossy()
1341                .to_string()
1342        }
1343    }
1344
1345    /// Clears the terminal
1346    pub fn clear(&mut self) {
1347        assert!(self.has_buffer());
1348        unsafe { Fl_Simple_Terminal_clear(self.inner.widget() as _) }
1349    }
1350
1351    /// Removes `count` lines from `start`
1352    pub fn remove_lines(&mut self, start: i32, count: i32) {
1353        assert!(self.has_buffer());
1354        unsafe { Fl_Simple_Terminal_remove_lines(self.inner.widget() as _, start, count) }
1355    }
1356}