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