fltk/
text.rs

1use crate::enums::{Color, Font, Key};
2use crate::prelude::*;
3use crate::utils::FlString;
4use fltk_sys::text::*;
5use std::{
6    ffi::{CStr, CString},
7    os::raw,
8};
9
10type BufWrapper = std::rc::Rc<*mut Fl_Text_Buffer>;
11
12/// Defines the text cursor styles supported by fltk
13#[repr(i32)]
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub enum Cursor {
16    /// Normal
17    Normal,
18    /// Caret
19    Caret,
20    /// Dim
21    Dim,
22    /// Block
23    Block,
24    /// Heavy
25    Heavy,
26    /// Simple
27    Simple,
28}
29
30/// Wraps a text buffer, Cloning a text buffer invalidates the underlying pointer, thus the no derive(Clone)
31#[derive(Debug)]
32pub struct TextBuffer {
33    inner: BufWrapper,
34}
35
36type BoxedModifyCallbackHandle = *mut Box<dyn FnMut(i32, i32, i32, i32, Option<&str>)>;
37
38/// Handle object for interacting with text buffer modify callbacks
39pub type ModifyCallbackHandle = *mut ();
40
41#[allow(clippy::type_complexity)]
42unsafe extern "C" fn modify_callback_shim(
43    pos: raw::c_int,
44    inserted: raw::c_int,
45    deleted: raw::c_int,
46    restyled: raw::c_int,
47    deleted_text: *const raw::c_char,
48    data: *mut raw::c_void,
49) {
50    unsafe {
51        let temp = if deleted_text.is_null() {
52            None
53        } else {
54            CStr::from_ptr(deleted_text).to_str().ok()
55        };
56        let a = data as *mut Box<dyn for<'r> FnMut(i32, i32, i32, i32, Option<&'r str>)>;
57        let f: &mut (dyn FnMut(i32, i32, i32, i32, Option<&str>)) = &mut **a;
58        let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
59            f(pos, inserted, deleted, restyled, temp);
60        }));
61    }
62}
63
64impl std::default::Default for TextBuffer {
65    /// Initialized a default text buffer
66    fn default() -> TextBuffer {
67        unsafe {
68            let text_buffer = Fl_Text_Buffer_new();
69            assert!(!text_buffer.is_null());
70            TextBuffer {
71                inner: BufWrapper::new(text_buffer),
72            }
73        }
74    }
75}
76
77impl TextBuffer {
78    /// Deletes the `TextBuffer`
79    /// # Safety
80    /// The buffer shouldn't be deleted while the Display widget still needs it
81    pub unsafe fn delete(buf: &Self) {
82        assert!(!buf.inner.is_null());
83        unsafe {
84            Fl_Text_Buffer_delete(*buf.inner);
85        }
86    }
87
88    /// Deletes the `TextBuffer`
89    /// # Safety
90    /// The buffer shouldn't be deleted while the Display widget still needs it
91    pub unsafe fn delete_buffer(buf: &Self) {
92        unsafe { Self::delete(buf) }
93    }
94
95    /// Initializes a text buffer from a pointer
96    /// # Safety
97    /// The pointer must be valid
98    pub unsafe fn from_ptr(ptr: *mut Fl_Text_Buffer) -> Self {
99        unsafe {
100            assert!(!ptr.is_null());
101            let inner = BufWrapper::from(ptr);
102            let ptr = BufWrapper::into_raw(inner);
103            BufWrapper::increment_strong_count(ptr);
104            let inner = BufWrapper::from_raw(ptr);
105            TextBuffer { inner }
106        }
107    }
108
109    /// Returns the inner pointer from a text buffer
110    /// # Safety
111    /// Can return multiple mutable pointers to the same buffer
112    pub unsafe fn as_ptr(&self) -> *mut Fl_Text_Buffer {
113        unsafe {
114            let ptr = BufWrapper::into_raw(BufWrapper::clone(&self.inner));
115            BufWrapper::increment_strong_count(ptr);
116            let inner = BufWrapper::from_raw(ptr);
117            *inner
118        }
119    }
120
121    /// Sets the text of the buffer
122    pub fn set_text(&mut self, txt: &str) {
123        assert!(!self.inner.is_null());
124        unsafe {
125            let txt = CString::safe_new(txt);
126            Fl_Text_Buffer_set_text(*self.inner, txt.as_ptr());
127        }
128    }
129
130    /// Returns the text of the buffer
131    pub fn text(&self) -> String {
132        assert!(!self.inner.is_null());
133        unsafe {
134            let text = Fl_Text_Buffer_text(*self.inner);
135            assert!(!text.is_null());
136            CStr::from_ptr(text as *mut raw::c_char)
137                .to_string_lossy()
138                .to_string()
139        }
140    }
141
142    /**
143        Appends to the buffer.
144        To append and scroll to the end of the buffer:
145        ```rust,no_run
146        use fltk::{prelude::*, *};
147        let txt = "Some long text!";
148        let buf = text::TextBuffer::default();
149        let mut disp = text::TextDisplay::default();
150        disp.set_buffer(Some(buf));
151        disp.buffer().unwrap().append(txt);
152        disp.set_insert_position(disp.buffer().unwrap().length());
153        disp.scroll(
154            disp.count_lines(0, disp.buffer().unwrap().length(), true),
155            0,
156        );
157        ```
158    */
159    pub fn append(&mut self, text: &str) {
160        assert!(!self.inner.is_null());
161        let text = CString::safe_new(text);
162        unsafe { Fl_Text_Buffer_append(*self.inner, text.as_ptr()) }
163    }
164
165    /// Append bytes to the buffer
166    pub fn append_bytes(&mut self, text: &[u8]) {
167        assert!(!self.inner.is_null());
168        unsafe { Fl_Text_Buffer_append_bytes(*self.inner, text.as_ptr() as _, text.len() as _) }
169    }
170
171    /// Get the length of the buffer
172    pub fn length(&self) -> i32 {
173        assert!(!self.inner.is_null());
174        unsafe { Fl_Text_Buffer_length(*self.inner) }
175    }
176
177    /// Removes from the buffer
178    pub fn remove(&mut self, start: i32, end: i32) {
179        assert!(!self.inner.is_null());
180        unsafe {
181            Fl_Text_Buffer_remove(*self.inner, start, end);
182        }
183    }
184
185    /// Returns the text within the range
186    pub fn text_range(&self, start: i32, end: i32) -> Option<String> {
187        assert!(!self.inner.is_null());
188        unsafe {
189            let x = Fl_Text_Buffer_text_range(*self.inner, start, end);
190            if x.is_null() {
191                None
192            } else {
193                Some(
194                    CStr::from_ptr(x as *mut raw::c_char)
195                        .to_string_lossy()
196                        .to_string(),
197                )
198            }
199        }
200    }
201
202    /// Inserts text into a position
203    pub fn insert(&mut self, pos: i32, text: &str) {
204        assert!(!self.inner.is_null());
205        let text = CString::safe_new(text);
206        unsafe { Fl_Text_Buffer_insert(*self.inner, pos, text.as_ptr()) }
207    }
208
209    /// Replaces text from position `start` to `end`
210    pub fn replace(&mut self, start: i32, end: i32, text: &str) {
211        assert!(!self.inner.is_null());
212        assert!(end >= start);
213        let text = CString::safe_new(text);
214        unsafe { Fl_Text_Buffer_replace(*self.inner, start, end, text.as_ptr()) }
215    }
216
217    /// Copies text from a source buffer into the current buffer
218    pub fn copy_from(&mut self, source_buf: &TextBuffer, start: i32, end: i32, to: i32) {
219        assert!(!self.inner.is_null());
220        unsafe { Fl_Text_Buffer_copy(*self.inner, *source_buf.inner, start, end, to) }
221    }
222
223    /// Copies whole text from a source buffer into a new buffer
224    #[must_use]
225    pub fn copy(&self) -> TextBuffer {
226        assert!(!self.inner.is_null());
227        let mut temp = TextBuffer::default();
228        temp.copy_from(self, 0, 0, self.length());
229        temp
230    }
231
232    /// Performs an undo operation on the buffer
233    /// # Errors
234    /// Errors on failure to undo
235    pub fn undo(&mut self) -> Result<(), FltkError> {
236        assert!(!self.inner.is_null());
237        unsafe {
238            match Fl_Text_Buffer_undo(*self.inner, std::ptr::null_mut()) {
239                0 => Err(FltkError::Unknown(String::from("Failed to undo"))),
240                _ => Ok(()),
241            }
242        }
243    }
244
245    /// Performs a redo operation on the buffer.
246    /// Returns the cursor position.
247    /// # Errors
248    /// Errors on failure to undo
249    pub fn redo(&mut self) -> Result<i32, FltkError> {
250        assert!(!self.inner.is_null());
251        unsafe {
252            let mut i = 0;
253            match Fl_Text_Buffer_redo(*self.inner, &mut i) {
254                0 => Err(FltkError::Unknown(String::from("Failed to redo"))),
255                _ => Ok(i),
256            }
257        }
258    }
259
260    /// Sets whether the buffer can undo
261    pub fn can_undo(&mut self, flag: bool) {
262        assert!(!self.inner.is_null());
263        unsafe { Fl_Text_Buffer_canUndo(*self.inner, raw::c_char::from(flag)) }
264    }
265
266    /// Gets whether the buffer can undo
267    pub fn get_can_undo(&mut self) -> bool {
268        assert!(!self.inner.is_null());
269        unsafe { Fl_Text_Buffer_can_undo(*self.inner) != 0 }
270    }
271
272    /// Gets whether the buffer can redo
273    pub fn can_redo(&mut self) -> bool {
274        assert!(!self.inner.is_null());
275        unsafe { Fl_Text_Buffer_can_redo(*self.inner) != 0 }
276    }
277
278    /// Loads a file into the buffer
279    /// # Errors
280    /// Errors on failure to load file
281    pub fn load_file<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), FltkError> {
282        assert!(!self.inner.is_null());
283        if !path.as_ref().exists() {
284            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
285        }
286        let path = path
287            .as_ref()
288            .to_str()
289            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
290        let path = CString::new(path)?;
291        unsafe {
292            match Fl_Text_Buffer_load_file(*self.inner, path.as_ptr()) {
293                0 => Ok(()),
294                _ => Err(FltkError::Internal(FltkErrorKind::ResourceNotFound)),
295            }
296        }
297    }
298
299    /// Saves a buffer into a file
300    /// # Errors
301    /// Errors on failure to save file
302    pub fn save_file<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), FltkError> {
303        assert!(!self.inner.is_null());
304        let path = path
305            .as_ref()
306            .to_str()
307            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
308        let path = CString::new(path)?;
309        unsafe {
310            match Fl_Text_Buffer_save_file(*self.inner, path.as_ptr()) {
311                0 => Ok(()),
312                _ => Err(FltkError::Internal(FltkErrorKind::ResourceNotFound)),
313            }
314        }
315    }
316
317    /// Returns the tab distance for the buffer
318    pub fn tab_distance(&self) -> i32 {
319        assert!(!self.inner.is_null());
320        unsafe { Fl_Text_Buffer_tab_distance(*self.inner) }
321    }
322
323    /// Sets the tab distance
324    pub fn set_tab_distance(&mut self, tab_dist: i32) {
325        assert!(!self.inner.is_null());
326        unsafe { Fl_Text_Buffer_set_tab_distance(*self.inner, tab_dist) }
327    }
328
329    /// Selects the text from start to end
330    pub fn select(&mut self, start: i32, end: i32) {
331        assert!(!self.inner.is_null());
332        unsafe { Fl_Text_Buffer_select(*self.inner, start, end) }
333    }
334
335    /// Returns whether text is selected
336    pub fn selected(&self) -> bool {
337        assert!(!self.inner.is_null());
338        unsafe { Fl_Text_Buffer_selected(*self.inner) != 0 }
339    }
340
341    /// Unselects text
342    pub fn unselect(&mut self) {
343        assert!(!self.inner.is_null());
344        unsafe { Fl_Text_Buffer_unselect(*self.inner) }
345    }
346
347    /// Returns the selection position
348    pub fn selection_position(&self) -> Option<(i32, i32)> {
349        assert!(!self.inner.is_null());
350        unsafe {
351            let mut start = 0;
352            let mut end = 0;
353            let ret = Fl_Text_Buffer_selection_position(
354                *self.inner,
355                std::ptr::from_mut(&mut start),
356                std::ptr::from_mut(&mut end),
357            );
358            if ret == 0 {
359                None
360            } else {
361                let x = (start, end);
362                Some(x)
363            }
364        }
365    }
366
367    /// Returns the selection text
368    pub fn selection_text(&self) -> String {
369        assert!(!self.inner.is_null());
370        unsafe {
371            let x = Fl_Text_Buffer_selection_text(*self.inner);
372            assert!(!x.is_null());
373            CStr::from_ptr(x as *mut raw::c_char)
374                .to_string_lossy()
375                .to_string()
376        }
377    }
378
379    /// Removes the selection
380    pub fn remove_selection(&mut self) {
381        assert!(!self.inner.is_null());
382        unsafe { Fl_Text_Buffer_remove_selection(*self.inner) }
383    }
384
385    /// Replaces selection
386    pub fn replace_selection(&mut self, text: &str) {
387        assert!(!self.inner.is_null());
388        let text = CString::safe_new(text);
389        unsafe { Fl_Text_Buffer_replace_selection(*self.inner, text.as_ptr()) }
390    }
391
392    /// Secondary selects the text from start to end
393    pub fn secondary_select(&mut self, start: i32, end: i32) {
394        assert!(!self.inner.is_null());
395        unsafe { Fl_Text_Buffer_secondary_select(*self.inner, start, end) }
396    }
397
398    /// Returns whether text is secondary selected
399    pub fn secondary_selected(&self) -> bool {
400        assert!(!self.inner.is_null());
401        unsafe { Fl_Text_Buffer_secondary_selected(*self.inner) != 0 }
402    }
403
404    /// Unselects text (secondary selection)
405    pub fn secondary_unselect(&mut self) {
406        assert!(!self.inner.is_null());
407        unsafe { Fl_Text_Buffer_secondary_unselect(*self.inner) }
408    }
409
410    /// Returns the secondary selection position
411    pub fn secondary_selection_position(&self) -> Option<(i32, i32)> {
412        assert!(!self.inner.is_null());
413        unsafe {
414            let mut start = 0;
415            let mut end = 0;
416            let ret = Fl_Text_Buffer_secondary_selection_position(
417                *self.inner,
418                std::ptr::from_mut(&mut start),
419                std::ptr::from_mut(&mut end),
420            );
421            if ret == 0 {
422                None
423            } else {
424                let x = (start, end);
425                Some(x)
426            }
427        }
428    }
429
430    /// Returns the secondary selection text
431    pub fn secondary_selection_text(&self) -> String {
432        assert!(!self.inner.is_null());
433        unsafe {
434            let x = Fl_Text_Buffer_secondary_selection_text(*self.inner);
435            assert!(!x.is_null());
436            CStr::from_ptr(x as *mut raw::c_char)
437                .to_string_lossy()
438                .to_string()
439        }
440    }
441
442    /// Removes the secondary selection
443    pub fn remove_secondary_selection(&mut self) {
444        assert!(!self.inner.is_null());
445        unsafe { Fl_Text_Buffer_remove_secondary_selection(*self.inner) }
446    }
447
448    /// Replaces the secondary selection
449    pub fn replace_secondary_selection(&mut self, text: &str) {
450        assert!(!self.inner.is_null());
451        let text = CString::safe_new(text);
452        unsafe { Fl_Text_Buffer_replace_secondary_selection(*self.inner, text.as_ptr()) }
453    }
454
455    /// Highlights selection
456    pub fn highlight(&mut self, start: i32, end: i32) {
457        assert!(!self.inner.is_null());
458        unsafe { Fl_Text_Buffer_highlight(*self.inner, start, end) }
459    }
460
461    /// Returns whether text is highlighted
462    pub fn is_highlighted(&self) -> bool {
463        assert!(!self.inner.is_null());
464        unsafe { Fl_Text_Buffer_is_highlighted(*self.inner) != 0 }
465    }
466
467    /// Unhighlights text
468    pub fn unhighlight(&mut self) {
469        assert!(!self.inner.is_null());
470        unsafe { Fl_Text_Buffer_unhighlight(*self.inner) }
471    }
472
473    /// Returns the highlight position
474    pub fn highlight_position(&self) -> Option<(i32, i32)> {
475        assert!(!self.inner.is_null());
476        unsafe {
477            let mut start = 0;
478            let mut end = 0;
479            let ret = Fl_Text_Buffer_highlight_position(
480                *self.inner,
481                std::ptr::from_mut(&mut start),
482                std::ptr::from_mut(&mut end),
483            );
484            if ret == 0 {
485                None
486            } else {
487                let x = (start, end);
488                Some(x)
489            }
490        }
491    }
492
493    /// Returns the highlighted text
494    pub fn highlight_text(&self) -> String {
495        assert!(!self.inner.is_null());
496        unsafe {
497            let x = Fl_Text_Buffer_highlight_text(*self.inner);
498            assert!(!x.is_null());
499            CStr::from_ptr(x as *mut raw::c_char)
500                .to_string_lossy()
501                .to_string()
502        }
503    }
504
505    /// Returns the line at pos
506    pub fn line_text(&self, pos: i32) -> String {
507        assert!(!self.inner.is_null());
508        unsafe {
509            let x = Fl_Text_Buffer_line_text(*self.inner, pos);
510            assert!(!x.is_null());
511            CStr::from_ptr(x as *mut raw::c_char)
512                .to_string_lossy()
513                .to_string()
514        }
515    }
516
517    /// Returns the index of the line's start position at pos
518    pub fn line_start(&self, pos: i32) -> i32 {
519        assert!(!self.inner.is_null());
520        unsafe { Fl_Text_Buffer_line_start(*self.inner, pos) }
521    }
522
523    /// Returns the index of the first character of a word at pos
524    pub fn word_start(&self, pos: i32) -> i32 {
525        assert!(!self.inner.is_null());
526        unsafe { Fl_Text_Buffer_word_start(*self.inner, pos) }
527    }
528
529    /// Returns the index of the last character of a word at pos
530    pub fn word_end(&self, pos: i32) -> i32 {
531        assert!(!self.inner.is_null());
532        unsafe { Fl_Text_Buffer_word_end(*self.inner, pos) }
533    }
534
535    /// Counts the lines from start to end
536    pub fn count_lines(&self, start: i32, end: i32) -> i32 {
537        assert!(!self.inner.is_null());
538        unsafe { Fl_Text_Buffer_count_lines(*self.inner, start, end) }
539    }
540
541    /// Calls the modify callbacks
542    pub fn call_modify_callbacks(&mut self) {
543        assert!(!self.inner.is_null());
544        unsafe { Fl_Text_Buffer_call_modify_callbacks(*self.inner) }
545    }
546
547    fn add_modify_callback_<F: FnMut(i32, i32, i32, i32, Option<&str>) + 'static>(
548        &mut self,
549        cb: F,
550    ) -> ModifyCallbackHandle {
551        assert!(!self.inner.is_null());
552        unsafe {
553            let a: BoxedModifyCallbackHandle = Box::into_raw(Box::new(Box::new(cb)));
554            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
555            let callback: Fl_Text_Modify_Cb = Some(modify_callback_shim);
556            Fl_Text_Buffer_add_modify_callback(*self.inner, callback, data);
557            data as _
558        }
559    }
560
561    /// Adds a modify callback.
562    /// callback args:
563    /// pos: i32, inserted items: i32, deleted items: i32, restyled items: i32, `deleted_text`
564    pub fn add_modify_callback<F: FnMut(&mut Self, i32, i32, i32, i32, Option<&str>) + 'static>(
565        &mut self,
566        mut cb: F,
567    ) -> ModifyCallbackHandle {
568        let mut s = self.clone();
569        self.add_modify_callback_(move |pos, ins, del, restyled, txt| {
570            cb(&mut s, pos, ins, del, restyled, txt);
571        })
572    }
573
574    /// Removes a modify callback.
575    /// callback args:
576    /// pos: i32, inserted items: i32, deleted items: i32, restyled items: i32, `deleted_text`
577    pub fn remove_modify_callback(&mut self, cb: ModifyCallbackHandle) {
578        assert!(!self.inner.is_null());
579        unsafe {
580            let data: *mut raw::c_void = cb as *mut std::ffi::c_void;
581            let callback: Fl_Text_Modify_Cb = Some(modify_callback_shim);
582            Fl_Text_Buffer_remove_modify_callback(*self.inner, callback, data);
583        }
584    }
585
586    /// Forward search for a string
587    pub fn search_forward(
588        &self,
589        start_pos: i32,
590        search_string: &str,
591        match_case: bool,
592    ) -> Option<i32> {
593        unsafe {
594            let search_string = CString::safe_new(search_string);
595            let mut found_pos = 0;
596            let ret = Fl_Text_Buffer_search_forward(
597                *self.inner,
598                start_pos,
599                search_string.as_ptr() as _,
600                std::ptr::from_mut(&mut found_pos),
601                match_case.into(),
602            );
603            if ret == 0 { None } else { Some(found_pos) }
604        }
605    }
606
607    /// Backward search for a string
608    pub fn search_backward(
609        &self,
610        start_pos: i32,
611        search_string: &str,
612        match_case: bool,
613    ) -> Option<i32> {
614        unsafe {
615            let search_string = CString::safe_new(search_string);
616            let mut found_pos = 0;
617            let ret = Fl_Text_Buffer_search_backward(
618                *self.inner,
619                start_pos,
620                search_string.as_ptr() as _,
621                std::ptr::from_mut(&mut found_pos),
622                match_case.into(),
623            );
624            if ret == 0 { None } else { Some(found_pos) }
625        }
626    }
627
628    /// Forward search for a char
629    pub fn find_char_forward(&self, start_pos: i32, search_char: char) -> Option<i32> {
630        unsafe {
631            let mut found_pos = 0;
632            let ret = Fl_Text_Buffer_findchar_forward(
633                *self.inner,
634                start_pos,
635                search_char as _,
636                std::ptr::from_mut(&mut found_pos),
637            );
638            if ret == 0 { None } else { Some(found_pos) }
639        }
640    }
641
642    /// Backward search for a char
643    pub fn find_char_backward(&self, start_pos: i32, search_char: char) -> Option<i32> {
644        unsafe {
645            let mut found_pos = 0;
646            let ret = Fl_Text_Buffer_findchar_backward(
647                *self.inner,
648                start_pos,
649                search_char as _,
650                std::ptr::from_mut(&mut found_pos),
651            );
652            if ret == 0 { None } else { Some(found_pos) }
653        }
654    }
655}
656
657#[cfg(not(feature = "single-threaded"))]
658unsafe impl Sync for TextBuffer {}
659
660#[cfg(not(feature = "single-threaded"))]
661unsafe impl Send for TextBuffer {}
662
663impl PartialEq for TextBuffer {
664    fn eq(&self, other: &Self) -> bool {
665        self.inner == other.inner
666    }
667}
668
669impl Eq for TextBuffer {}
670
671impl Clone for TextBuffer {
672    fn clone(&self) -> TextBuffer {
673        assert!(!self.inner.is_null());
674        TextBuffer {
675            inner: BufWrapper::clone(&self.inner),
676        }
677    }
678}
679
680impl Drop for TextBuffer {
681    fn drop(&mut self) {
682        assert!(!self.inner.is_null());
683        if BufWrapper::strong_count(&self.inner) == 1 {
684            unsafe {
685                Fl_Text_Buffer_delete(*self.inner);
686            }
687        }
688    }
689}
690
691/// Defines wrap modes
692#[repr(i32)]
693#[derive(Debug, Copy, Clone, PartialEq, Eq)]
694pub enum WrapMode {
695    /// No wrapping
696    None,
697    /// Wrap text at certain column
698    AtColumn,
699    /// Wrap text at certain pixel
700    AtPixel,
701    /// Wrap text at certain bounds
702    AtBounds,
703}
704
705/// Defines drag types
706#[repr(i32)]
707#[derive(Debug, Copy, Clone, PartialEq, Eq)]
708pub enum DragType {
709    /// No dragging
710    None = -2,
711    /// Drag Start "drag n drop" event
712    StartDnd = -1,
713    /// Drag single character
714    Char = 0,
715    /// Drag single word
716    Word = 1,
717    /// Drag single line
718    Line = 2,
719}
720
721/// Creates a non-editable text display widget
722#[derive(Debug)]
723pub struct TextDisplay {
724    inner: crate::widget::WidgetTracker,
725    is_derived: bool,
726}
727
728crate::macros::widget::impl_widget_ext!(TextDisplay, Fl_Text_Display);
729crate::macros::widget::impl_widget_base!(TextDisplay, Fl_Text_Display);
730crate::macros::widget::impl_widget_default!(TextDisplay, Fl_Text_Display);
731crate::macros::display::impl_display_ext!(TextDisplay, Fl_Text_Display);
732
733/// Creates an editable text display widget
734#[derive(Debug)]
735pub struct TextEditor {
736    inner: crate::widget::WidgetTracker,
737    is_derived: bool,
738}
739
740crate::macros::widget::impl_widget_ext!(TextEditor, Fl_Text_Editor);
741crate::macros::widget::impl_widget_base!(TextEditor, Fl_Text_Editor);
742crate::macros::widget::impl_widget_default!(TextEditor, Fl_Text_Editor);
743crate::macros::display::impl_display_ext!(TextEditor, Fl_Text_Editor);
744
745/// Alias `Fl_Text_Editor` for use in `add_key_binding`
746pub type TextEditorPtr = *mut Fl_Text_Editor;
747
748/// The attribute of the style entry
749#[derive(Debug, Clone, Copy, PartialEq, Eq)]
750#[repr(u32)]
751#[non_exhaustive]
752pub enum TextAttr {
753    /// No attribute
754    None = 0x0000,
755    /// Use the background color in the `bgcolor` field
756    BgColor = 0x0001,
757    /// Use the background color in the `bgcolor` field to highlight the whole line
758    BgColorExt = 0x0003,
759    /// A single underline, underline types are mutually exclusive
760    Underline = 0x0004,
761    /// Grammar suggestion (blue dotted underline)
762    Grammar = 0x0008,
763    /// Spelling suggestion (red dotted underline)
764    Spelling = 0x000C,
765    /// Line through the middle of the text
766    StrikeThrough = 0x0010,
767}
768
769/// Defines the styles used in the `set_highlight_data`, which is used with style buffers
770#[derive(Debug, Clone, Copy, PartialEq, Eq)]
771pub struct StyleTableEntry {
772    /// Font color
773    pub color: Color,
774    /// Font type
775    pub font: Font,
776    /// Font size
777    pub size: i32,
778    /// attribute
779    pub attr: TextAttr,
780    /// background color
781    pub bgcolor: Color,
782}
783
784impl Default for StyleTableEntry {
785    fn default() -> Self {
786        Self {
787            color: Color::Foreground,
788            font: Font::Helvetica,
789            size: crate::app::font_size(),
790            attr: TextAttr::None,
791            bgcolor: Color::Background2,
792        }
793    }
794}
795
796impl TextEditor {
797    /// Any state/shortcut
798    pub const AnyState: crate::enums::Shortcut = crate::enums::Shortcut::from_i32(-1);
799
800    /// Set to insert mode
801    pub fn set_insert_mode(&mut self, b: bool) {
802        assert!(self.has_buffer());
803        unsafe { Fl_Text_Editor_set_insert_mode(self.inner.widget() as _, i32::from(b)) }
804    }
805
806    /// Returns whether insert mode is set
807    pub fn insert_mode(&self) -> bool {
808        assert!(self.has_buffer());
809        unsafe { Fl_Text_Editor_insert_mode(self.inner.widget() as _) != 0 }
810    }
811
812    /// Set tab navigation
813    pub fn set_tab_nav(&mut self, val: bool) {
814        assert!(self.has_buffer());
815        unsafe { Fl_Text_Editor_set_tab_nav(self.inner.widget() as _, i32::from(val)) }
816    }
817
818    /// Returns whether tab navigation is set
819    pub fn tab_nav(&self) -> bool {
820        assert!(self.has_buffer());
821        unsafe { Fl_Text_Editor_tab_nav(self.inner.widget() as _) != 0 }
822    }
823
824    /// Copies the text within the `TextEditor` widget
825    pub fn copy(&self) {
826        assert!(self.has_buffer());
827        unsafe {
828            Fl_Text_Editor_kf_copy(self.inner.widget() as _);
829        }
830    }
831
832    /// Cuts the text within the `TextEditor` widget
833    pub fn cut(&self) {
834        assert!(self.has_buffer());
835        unsafe {
836            Fl_Text_Editor_kf_cut(self.inner.widget() as _);
837        }
838    }
839
840    /// Pastes text from the clipboard into the `TextEditor` widget
841    pub fn paste(&self) {
842        assert!(self.has_buffer());
843        unsafe {
844            Fl_Text_Editor_kf_paste(self.inner.widget() as _);
845        }
846    }
847
848    /// Undo changes in the `TextEditor` widget
849    pub fn undo(&self) {
850        assert!(self.has_buffer());
851        unsafe {
852            Fl_Text_Editor_kf_undo(self.inner.widget() as _);
853        }
854    }
855
856    /// Undo changes in the `TextEditor` widget
857    pub fn redo(&self) {
858        assert!(self.has_buffer());
859        unsafe {
860            Fl_Text_Editor_kf_redo(self.inner.widget() as _);
861        }
862    }
863
864    /// Inserts the text associated with key 'c'
865    pub fn kf_default(&mut self, c: Key) {
866        assert!(self.has_buffer());
867        unsafe {
868            Fl_Text_Editor_kf_default(c.bits(), self.inner.widget() as _);
869        }
870    }
871
872    /// Ignores the key 'c' in editor
873    pub fn kf_ignore(&mut self, c: Key) {
874        assert!(self.has_buffer());
875        unsafe {
876            Fl_Text_Editor_kf_ignore(c.bits(), self.inner.widget() as _);
877        }
878    }
879
880    /// Does a backspace
881    pub fn kf_backspace(&mut self) {
882        assert!(self.has_buffer());
883        unsafe {
884            Fl_Text_Editor_kf_backspace(self.inner.widget() as _);
885        }
886    }
887
888    /// Inserts a new line
889    pub fn kf_enter(&mut self) {
890        assert!(self.has_buffer());
891        unsafe {
892            Fl_Text_Editor_kf_enter(self.inner.widget() as _);
893        }
894    }
895
896    /// Moves the cursor in the direction indicated by the key
897    pub fn kf_move(&mut self, c: Key) {
898        assert!(self.has_buffer());
899        unsafe {
900            Fl_Text_Editor_kf_move(c.bits(), self.inner.widget() as _);
901        }
902    }
903
904    /// Extends the current selection in the direction of key 'c'
905    pub fn kf_shift_move(&mut self, c: Key) {
906        assert!(self.has_buffer());
907        unsafe {
908            Fl_Text_Editor_kf_shift_move(c.bits(), self.inner.widget() as _);
909        }
910    }
911
912    /// Moves the current text cursor in the direction indicated by control key 'c'
913    pub fn kf_ctrl_move(&mut self, c: Key) {
914        assert!(self.has_buffer());
915        unsafe {
916            Fl_Text_Editor_kf_ctrl_move(c.bits(), self.inner.widget() as _);
917        }
918    }
919
920    /// Extends the current selection in the direction indicated by control key 'c'
921    pub fn kf_c_s_move(&mut self, c: Key) {
922        assert!(self.has_buffer());
923        unsafe {
924            Fl_Text_Editor_kf_c_s_move(c.bits(), self.inner.widget() as _);
925        }
926    }
927
928    /// Moves the current text cursor in the direction indicated by meta key 'c'
929    pub fn kf_meta_move(&mut self, c: Key) {
930        assert!(self.has_buffer());
931        unsafe {
932            Fl_Text_Editor_kf_meta_move(c.bits(), self.inner.widget() as _);
933        }
934    }
935
936    /// Extends the current selection in the direction indicated by meta key 'c'
937    pub fn kf_m_s_move(&mut self, c: Key) {
938        assert!(self.has_buffer());
939        unsafe {
940            Fl_Text_Editor_kf_m_s_move(c.bits(), self.inner.widget() as _);
941        }
942    }
943
944    /// Moves the text cursor to the beginning of the current line
945    pub fn kf_home(&mut self) {
946        assert!(self.has_buffer());
947        unsafe {
948            Fl_Text_Editor_kf_home(self.inner.widget() as _);
949        }
950    }
951
952    /// Moves the text cursor to the end of the current line
953    pub fn kf_end(&mut self) {
954        assert!(self.has_buffer());
955        unsafe {
956            Fl_Text_Editor_kf_end(self.inner.widget() as _);
957        }
958    }
959
960    /// Moves the text cursor one character to the left
961    pub fn kf_left(&mut self) {
962        assert!(self.has_buffer());
963        unsafe {
964            Fl_Text_Editor_kf_left(self.inner.widget() as _);
965        }
966    }
967
968    /// Moves the text cursor one line up
969    pub fn kf_up(&mut self) {
970        assert!(self.has_buffer());
971        unsafe {
972            Fl_Text_Editor_kf_up(self.inner.widget() as _);
973        }
974    }
975
976    /// Moves the text cursor one character to the right
977    pub fn kf_right(&mut self) {
978        assert!(self.has_buffer());
979        unsafe {
980            Fl_Text_Editor_kf_right(self.inner.widget() as _);
981        }
982    }
983
984    /// Moves the text cursor one line down
985    pub fn kf_down(&mut self) {
986        assert!(self.has_buffer());
987        unsafe {
988            Fl_Text_Editor_kf_down(self.inner.widget() as _);
989        }
990    }
991
992    /// Moves the text cursor up one page
993    pub fn kf_page_up(&mut self) {
994        assert!(self.has_buffer());
995        unsafe {
996            Fl_Text_Editor_kf_page_up(self.inner.widget() as _);
997        }
998    }
999
1000    /// Moves the text cursor down one page
1001    pub fn kf_page_down(&mut self) {
1002        assert!(self.has_buffer());
1003        unsafe {
1004            Fl_Text_Editor_kf_page_down(self.inner.widget() as _);
1005        }
1006    }
1007
1008    /// Toggles the insert mode for the editor
1009    pub fn kf_insert(&mut self) {
1010        assert!(self.has_buffer());
1011        unsafe {
1012            Fl_Text_Editor_kf_insert(self.inner.widget() as _);
1013        }
1014    }
1015
1016    /// Does a delete of selected text or the current character in the current buffer
1017    pub fn kf_delete(&mut self) {
1018        assert!(self.has_buffer());
1019        unsafe {
1020            Fl_Text_Editor_kf_delete(self.inner.widget() as _);
1021        }
1022    }
1023
1024    /// Selects all text in the associated buffer
1025    pub fn kf_select_all(&mut self) {
1026        assert!(self.has_buffer());
1027        unsafe {
1028            Fl_Text_Editor_kf_select_all(self.inner.widget() as _);
1029        }
1030    }
1031
1032    /// Add a key binding
1033    pub fn add_key_binding(
1034        &mut self,
1035        key: crate::enums::Key,
1036        shortcut: crate::enums::Shortcut,
1037        cb: fn(key: crate::enums::Key, editor: TextEditorPtr) -> i32,
1038    ) {
1039        unsafe {
1040            Fl_Text_Editor_add_key_binding(
1041                self.inner.widget() as _,
1042                key.bits(),
1043                shortcut.bits(),
1044                std::mem::transmute(Some(cb)),
1045            );
1046        }
1047    }
1048
1049    /// Remove a key binding
1050    pub fn remove_key_binding(&mut self, key: crate::enums::Key, shortcut: crate::enums::Shortcut) {
1051        unsafe {
1052            Fl_Text_Editor_remove_key_binding(
1053                self.inner.widget() as _,
1054                key.bits(),
1055                shortcut.bits(),
1056            );
1057        }
1058    }
1059}